fab cursor rotation
	
		
			
	
		
	
	
		
	
		
			All checks were successful
		
		
	
	
		
			
				
	
				studiorailgun/Renderer/pipeline/head This commit looks good
				
			
		
		
	
	
				
					
				
			
		
			All checks were successful
		
		
	
	studiorailgun/Renderer/pipeline/head This commit looks good
				
			This commit is contained in:
		
							parent
							
								
									0e50845ec6
								
							
						
					
					
						commit
						85c4cd2b6d
					
				| @ -1566,6 +1566,9 @@ More block types | ||||
| Transparent blocks | ||||
| Fix block meshgen | ||||
| 
 | ||||
| (04/27/2025) | ||||
| Fab cursor rotation + actually place rotated fab | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
| @ -69,7 +83,6 @@ public class AreaSelection { | ||||
|     public Vector3d getRectEnd() { | ||||
|         return rectEnd; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -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 | ||||
|             )); | ||||
|         } | ||||
|  | ||||
| @ -612,13 +612,28 @@ 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); | ||||
|                     handled = true; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|             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){ | ||||
|  | ||||
| @ -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; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
| @ -310,6 +324,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 | ||||
| @ -432,5 +521,13 @@ public class CursorState { | ||||
|     public static Entity getFabCursor(){ | ||||
|         return playerFabCursor; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Gets the rotation of the fab cursor | ||||
|      * @return The rotation of the fab cursor | ||||
|      */ | ||||
|     public int getFabCursorRotation(){ | ||||
|         return this.fabCursorRotation; | ||||
|     } | ||||
|      | ||||
| } | ||||
|  | ||||
| @ -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; | ||||
|         } | ||||
|  | ||||
| @ -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: | ||||
|  | ||||
| @ -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, | ||||
|  | ||||
| @ -132,6 +132,10 @@ | ||||
|                 { | ||||
|                     "name" : "fabPath", | ||||
|                     "type" : "VAR_STRING" | ||||
|                 }, | ||||
|                 { | ||||
|                     "name" : "blockRotation", | ||||
|                     "type" : "FIXED_INT" | ||||
|                 } | ||||
|             ], | ||||
|             "messageTypes" : [ | ||||
| @ -345,6 +349,7 @@ | ||||
|                         "voxelX", | ||||
|                         "voxelY", | ||||
|                         "voxelZ", | ||||
|                         "blockRotation", | ||||
|                         "fabPath" | ||||
|                     ] | ||||
|                 } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user