fab cursor rotation
All checks were successful
studiorailgun/Renderer/pipeline/head This commit looks good

This commit is contained in:
austin 2025-04-27 14:09:21 -04:00
parent 0e50845ec6
commit 85c4cd2b6d
9 changed files with 188 additions and 15 deletions

View File

@ -1566,6 +1566,9 @@ More block types
Transparent blocks
Fix block meshgen
(04/27/2025)
Fab cursor rotation + actually place rotated fab

View File

@ -1,6 +1,7 @@
package electrosphere.client.interact.select;
import org.joml.Vector3d;
import org.joml.Vector3i;
/**
* An area of space that is selected by the client
@ -46,6 +47,19 @@ public class AreaSelection {
return rVal;
}
/**
* Calculates the rectangular area of empty voxels in the block chunks starting at a given position
* @param chunkPos The chunk position to start from
* @param blockPos The block position to start from
* @param maxRadius The maximum radius to expand the area to
* @return The AreaSelection
*/
public static AreaSelection selectRectangularBlockCavity(Vector3i chunkPos, Vector3i blockPos, int maxRadius){
AreaSelection rVal = null;
return rVal;
}
/**
* Gets the type of area
* @return The type of area
@ -71,5 +85,4 @@ public class AreaSelection {
}
}

View File

@ -146,9 +146,11 @@ public class ScriptClientVoxelUtils {
Vector3d fabCursorPos = EntityUtils.getPosition(CursorState.getFabCursor());
Vector3i chunkPos = Globals.clientWorldData.convertRealToWorldSpace(fabCursorPos);
Vector3i voxelPos = Globals.clientWorldData.convertRealToBlockSpace(fabCursorPos);
int rotation = Globals.cursorState.getFabCursorRotation();
Globals.clientConnection.queueOutgoingMessage(TerrainMessage.constructRequestPlaceFabMessage(
chunkPos.x, chunkPos.y, chunkPos.z,
voxelPos.x, voxelPos.y, voxelPos.z,
rotation,
fabPath
));
}

View File

@ -612,7 +612,10 @@ public class ControlCategoryMainGame {
mainGameControlList.add(controlMap.get(TOOLBAR_SCROLL));
controlMap.get(TOOLBAR_SCROLL).setOnScroll(new Control.ScrollCallback() {public void execute(MouseState mouseState, ScrollEvent scrollEvent){
boolean handled = false;
if(Globals.controlCallback.getKey(GLFW.GLFW_KEY_LEFT_CONTROL)){
if(
Globals.controlCallback.getKey(GLFW.GLFW_KEY_LEFT_CONTROL) &&
!Globals.controlCallback.getKey(GLFW.GLFW_KEY_LEFT_SHIFT)
){
//if the block cursor is visible, capture this input and instead modify block cursor
if(Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE).contains(Globals.playerBlockCursor)){
Globals.cursorState.updateCursorSize(scrollEvent);
@ -620,6 +623,18 @@ public class ControlCategoryMainGame {
}
}
if(
!Globals.controlCallback.getKey(GLFW.GLFW_KEY_LEFT_CONTROL) &&
Globals.controlCallback.getKey(GLFW.GLFW_KEY_LEFT_SHIFT)
){
//if the fab cursor is visible, capture this input and instead modify fab cursor
if(Globals.clientScene.getEntitiesWithTag(EntityTags.DRAWABLE).contains(CursorState.getFabCursor())){
Globals.cursorState.rotateBlockCursor(scrollEvent);
handled = true;
}
}
if(!handled){
if(Globals.playerEntity != null && ClientToolbarState.getClientToolbarState(Globals.playerEntity) != null){
ClientToolbarState clientToolbarState = ClientToolbarState.getClientToolbarState(Globals.playerEntity);

View File

@ -2,6 +2,7 @@ package electrosphere.controls.cursor;
import java.util.Arrays;
import org.joml.Quaterniond;
import org.joml.Vector3d;
import org.joml.Vector3i;
@ -86,6 +87,18 @@ public class CursorState {
*/
static Entity playerFabCursor;
/**
* Maximum value to rotate to
*/
private static final int MAX_ROTATION_VAL = 15;
/**
* The rotation of the fab cursor
* The first two bits encode a rotation about the x axis
* The next two bits encode a subsequent rotation about the y axis
*/
private int fabCursorRotation = 0;
/**
* The currently selected fab
*/
@ -198,6 +211,7 @@ public class CursorState {
CursorState.hide();
Globals.cursorState.setClampToExistingBlock(false);
Globals.clientSceneWrapper.getScene().registerEntityToTag(CursorState.playerFabCursor, EntityTags.DRAWABLE);
Globals.cursorState.fabCursorRotation = 0;
}
/**
@ -311,6 +325,81 @@ public class CursorState {
EntityUtils.getScale(Globals.playerBlockCursor).set(BLOCK_CURSOR_SCALE_MULTIPLIER * this.blockSize);
}
/**
* Rotates the block cursor
* @param scrollEvent The scroll event
*/
public void rotateBlockCursor(ScrollEvent scrollEvent){
if(scrollEvent.getScrollAmount() > 0){
if(this.fabCursorRotation > 0){
this.fabCursorRotation--;
} else {
this.fabCursorRotation = MAX_ROTATION_VAL;
}
} else {
if(this.fabCursorRotation < MAX_ROTATION_VAL){
this.fabCursorRotation++;
} else {
this.fabCursorRotation = 0;
}
}
this.setFabCursorRotation(this.fabCursorRotation);;
}
/**
* Rotates the fab cursor
* @param rotationVal The value that encodes the rotation
*/
public void setFabCursorRotation(int rotationVal){
Quaterniond rotations = CursorState.getBlockRotation(rotationVal);
EntityUtils.getRotation(playerFabCursor).set(rotations);
}
/**
* Gets the rotation of a block based on its int-encoded rotation value
* @param rotationVal The int-encoded rotation value
* @return The corresponding quaterniond
*/
public static Quaterniond getBlockRotation(int rotationVal){
int tempVal = rotationVal;
int xRotation = tempVal & 0x3;
int yRotation = tempVal >> 2 & 0x3;
Quaterniond rotations = new Quaterniond();
double xRot = 0;
double yRot = 0;
switch(xRotation){
case 0: {
//no rotation applied
} break;
case 1: {
xRot = Math.PI / 2.0;
} break;
case 2: {
xRot = Math.PI;
} break;
case 3: {
xRot = 3.0 * Math.PI / 2.0;
} break;
}
switch(yRotation){
case 0: {
//no rotation applied
} break;
case 1: {
yRot = Math.PI / 2.0;
} break;
case 2: {
yRot = Math.PI;
} break;
case 3: {
yRot = 3.0 * Math.PI / 2.0;
} break;
}
rotations.rotateLocalZ(xRot);
rotations.rotateLocalY(yRot);
return rotations;
}
/**
* Sets the block fab cursor's current fab
* @param fab The fab
@ -433,4 +522,12 @@ public class CursorState {
return playerFabCursor;
}
/**
* Gets the rotation of the fab cursor
* @return The rotation of the fab cursor
*/
public int getFabCursorRotation(){
return this.fabCursorRotation;
}
}

View File

@ -64,6 +64,7 @@ public class TerrainMessage extends NetworkMessage {
int blockMetadata;
int blockEditSize;
String fabPath;
int blockRotation;
/**
* Constructor
@ -477,6 +478,20 @@ public class TerrainMessage extends NetworkMessage {
this.fabPath = fabPath;
}
/**
* Gets blockRotation
*/
public int getblockRotation() {
return blockRotation;
}
/**
* Sets blockRotation
*/
public void setblockRotation(int blockRotation) {
this.blockRotation = blockRotation;
}
/**
* Removes the packet header from the buffer
* @param byteBuffer The buffer
@ -1304,17 +1319,20 @@ public class TerrainMessage extends NetworkMessage {
if(currentStreamLength < 26){
return false;
}
int fabPathSize = 0;
if(currentStreamLength < 30){
return false;
}
int fabPathSize = 0;
if(currentStreamLength < 34){
return false;
} else {
temporaryByteQueue.add(byteBuffer.peek(26 + 0));
temporaryByteQueue.add(byteBuffer.peek(26 + 1));
temporaryByteQueue.add(byteBuffer.peek(26 + 2));
temporaryByteQueue.add(byteBuffer.peek(26 + 3));
temporaryByteQueue.add(byteBuffer.peek(30 + 0));
temporaryByteQueue.add(byteBuffer.peek(30 + 1));
temporaryByteQueue.add(byteBuffer.peek(30 + 2));
temporaryByteQueue.add(byteBuffer.peek(30 + 3));
fabPathSize = ByteStreamUtils.popIntFromByteQueue(temporaryByteQueue);
}
if(currentStreamLength < 30 + fabPathSize){
if(currentStreamLength < 34 + fabPathSize){
return false;
}
return true;
@ -1333,6 +1351,7 @@ public class TerrainMessage extends NetworkMessage {
rVal.setvoxelX(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setvoxelY(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setvoxelZ(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setblockRotation(ByteStreamUtils.popIntFromByteQueue(byteBuffer));
rVal.setfabPath(ByteStreamUtils.popStringFromByteQueue(byteBuffer));
return rVal;
}
@ -1340,7 +1359,7 @@ public class TerrainMessage extends NetworkMessage {
/**
* Constructs a message of type RequestPlaceFab
*/
public static TerrainMessage constructRequestPlaceFabMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,String fabPath){
public static TerrainMessage constructRequestPlaceFabMessage(int worldX,int worldY,int worldZ,int voxelX,int voxelY,int voxelZ,int blockRotation,String fabPath){
TerrainMessage rVal = new TerrainMessage(TerrainMessageType.REQUESTPLACEFAB);
rVal.setworldX(worldX);
rVal.setworldY(worldY);
@ -1348,6 +1367,7 @@ public class TerrainMessage extends NetworkMessage {
rVal.setvoxelX(voxelX);
rVal.setvoxelY(voxelY);
rVal.setvoxelZ(voxelZ);
rVal.setblockRotation(blockRotation);
rVal.setfabPath(fabPath);
rVal.serialize();
return rVal;
@ -1862,7 +1882,7 @@ public class TerrainMessage extends NetworkMessage {
}
break;
case REQUESTPLACEFAB:
rawBytes = new byte[2+4+4+4+4+4+4+4+fabPath.length()];
rawBytes = new byte[2+4+4+4+4+4+4+4+4+fabPath.length()];
//message header
rawBytes[0] = TypeBytes.MESSAGE_TYPE_TERRAIN;
//entity messaage header
@ -1891,13 +1911,17 @@ public class TerrainMessage extends NetworkMessage {
for(int i = 0; i < 4; i++){
rawBytes[22+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(fabPath.length());
intValues = ByteStreamUtils.serializeIntToBytes(blockRotation);
for(int i = 0; i < 4; i++){
rawBytes[26+i] = intValues[i];
}
intValues = ByteStreamUtils.serializeIntToBytes(fabPath.length());
for(int i = 0; i < 4; i++){
rawBytes[30+i] = intValues[i];
}
stringBytes = fabPath.getBytes();
for(int i = 0; i < fabPath.length(); i++){
rawBytes[30+i] = stringBytes[i];
rawBytes[34+i] = stringBytes[i];
}
break;
}

View File

@ -97,7 +97,7 @@ public class TerrainProtocol implements ServerProtocolTemplate<TerrainMessage> {
Vector3i blockPos = new Vector3i(message.getvoxelX(),message.getvoxelY(),message.getvoxelZ());
Entity targetEntity = EntityLookupUtils.getEntityById(connectionHandler.getPlayerEntityId());
Realm playerRealm = Globals.realmManager.getEntityRealm(targetEntity);
ServerBlockEditing.placeBlockFab(playerRealm, worldPos, blockPos, message.getfabPath());
ServerBlockEditing.placeBlockFab(playerRealm, worldPos, blockPos, message.getblockRotation(), message.getfabPath());
} break;
//all ignored message types
case UPDATEFLUIDDATA:

View File

@ -2,7 +2,11 @@ package electrosphere.server.physics.block.editing;
import java.io.File;
import org.joml.Matrix4f;
import org.joml.Quaterniond;
import org.joml.Quaternionf;
import org.joml.Vector3i;
import org.joml.Vector4f;
import electrosphere.client.block.BlockChunkData;
import electrosphere.controls.cursor.CursorState;
@ -77,18 +81,28 @@ public class ServerBlockEditing {
* @param realm The realm
* @param chunkPos The chunk position
* @param voxelPos The voxel position
* @param rotation The rotation of the fab
* @param fabPath The fab
*/
public static void placeBlockFab(Realm realm, Vector3i chunkPos, Vector3i voxelPos, String fabPath){
public static void placeBlockFab(Realm realm, Vector3i chunkPos, Vector3i voxelPos, int rotation, String fabPath){
BlockFab fab = BlockFab.read(new File(fabPath));
Vector3i dims = fab.getDimensions();
Vector3i currChunkPos = new Vector3i();
Vector3i currVoxelPos = new Vector3i();
VoxelCellManager voxelCellManager = (VoxelCellManager) realm.getDataCellManager();
Quaterniond rotationQuatd = CursorState.getBlockRotation(rotation);
Quaternionf rotationQuatf = new Quaternionf((float)rotationQuatd.x,(float)rotationQuatd.y,(float)rotationQuatd.z,(float)rotationQuatd.w);
Matrix4f rotMat = new Matrix4f().rotate(rotationQuatf);
Vector4f rotationHolder = new Vector4f();
for(int x = 0; x < dims.x; x++){
for(int y = 0; y < dims.y; y++){
for(int z = 0; z < dims.z; z++){
currVoxelPos.set(voxelPos).add(x,y,z);
rotationHolder.set(x,y,z,1);
rotMat.transform(rotationHolder);
currVoxelPos.set(voxelPos).add(Math.round(rotationHolder.x),Math.round(rotationHolder.y),Math.round(rotationHolder.z));
currChunkPos.set(chunkPos).add(
currVoxelPos.x / BlockChunkData.CHUNK_DATA_WIDTH,
currVoxelPos.y / BlockChunkData.CHUNK_DATA_WIDTH,

View File

@ -132,6 +132,10 @@
{
"name" : "fabPath",
"type" : "VAR_STRING"
},
{
"name" : "blockRotation",
"type" : "FIXED_INT"
}
],
"messageTypes" : [
@ -345,6 +349,7 @@
"voxelX",
"voxelY",
"voxelZ",
"blockRotation",
"fabPath"
]
}