area selection utility
	
		
			
	
		
	
	
		
	
		
			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
							
								
									c96c07334a
								
							
						
					
					
						commit
						cad9a994d0
					
				| @ -158,6 +158,37 @@ | ||||
|                 "offsetZ" : 0 | ||||
|             }, | ||||
|             "iconPath" : "Textures/icons/hammer.png" | ||||
|         }, | ||||
|         { | ||||
|             "id" : "roomTool", | ||||
|             "tokens" : [ | ||||
|                 "GRAVITY", | ||||
|                 "TARGETABLE", | ||||
|                 "CURSOR_FAB" | ||||
|             ], | ||||
|             "equipData": { | ||||
|                 "equipClass" : "tool" | ||||
|             }, | ||||
|             "graphicsTemplate": { | ||||
|                 "model": { | ||||
|                     "path" : "Models/basic/geometry/unitvector.glb" | ||||
|                 } | ||||
|             }, | ||||
|             "clientSideSecondary": "SELECT_ROOM", | ||||
|             "collidable": { | ||||
|                 "type" : "CUBE", | ||||
|                 "dimension1" : 0.1, | ||||
|                 "dimension2" : 0.1, | ||||
|                 "dimension3" : 0.35, | ||||
|                 "rotX": 0, | ||||
|                 "rotY": 0, | ||||
|                 "rotZ": 0, | ||||
|                 "rotW": 1, | ||||
|                 "offsetX" : 0, | ||||
|                 "offsetY" : 0.05, | ||||
|                 "offsetZ" : 0 | ||||
|             }, | ||||
|             "iconPath" : "Textures/icons/hammer.png" | ||||
|         } | ||||
|     ], | ||||
|     "files" : [ | ||||
|  | ||||
| @ -58,5 +58,11 @@ export const clientHooks: Hook[] = [ | ||||
|         callback: (engine: Engine) => { | ||||
|             engine.classes.voxelUtils.static.placeFab() | ||||
|         } | ||||
|     }, | ||||
|     { | ||||
|         signal: "SELECT_ROOM", | ||||
|         callback: (engine: Engine) => { | ||||
|             engine.classes.areaUtils.static.selectAreaRectangular() | ||||
|         } | ||||
|     } | ||||
| ] | ||||
							
								
								
									
										11
									
								
								assets/Scripts/types/host/client/client-area-utils.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								assets/Scripts/types/host/client/client-area-utils.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | ||||
| /** | ||||
|  * Utilities for managing areas on the client | ||||
|  */ | ||||
| export interface ClientAreaUtils { | ||||
| 
 | ||||
|     /** | ||||
|      * Selects a rectangular area | ||||
|      */ | ||||
|     readonly selectAreaRectangular: () => void | ||||
| 
 | ||||
| } | ||||
| @ -1,3 +1,4 @@ | ||||
| import { ClientAreaUtils } from "/Scripts/types/host/client/client-area-utils"; | ||||
| import { ClientLevelEditorUtils } from "/Scripts/types/host/client/client-level-editor-utils"; | ||||
| import { ClientVoxelUtils } from "/Scripts/types/host/client/client-voxel-utils"; | ||||
| import { Entity } from "/Scripts/types/host/entity/entity"; | ||||
| @ -51,6 +52,11 @@ export interface StaticClasses { | ||||
|      */ | ||||
|     readonly math?: Class<Math> | ||||
| 
 | ||||
|     /** | ||||
|      * Utilities for area management on the client | ||||
|      */ | ||||
|     readonly areaUtils?: Class<ClientAreaUtils>, | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  | ||||
| @ -1574,6 +1574,10 @@ Block cursor work | ||||
| ChunkDiskMap code org | ||||
| Filter procedural worlds out of level select menu | ||||
| 
 | ||||
| (04/28/2025) | ||||
| Area selection utility | ||||
| RoomTool item | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -287,6 +287,15 @@ public class BlockChunkData implements BlockMeshgenData { | ||||
|         boolean empty = this.getType(x,y,z) == BlockChunkData.BLOCK_TYPE_EMPTY; | ||||
|         return empty; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks if a given location is empty | ||||
|      * @param vec The position | ||||
|      * @return true if empty, false otherwise | ||||
|      */ | ||||
|     public boolean isEmpty(Vector3i vec){ | ||||
|         return this.isEmpty(vec.x,vec.y,vec.z); | ||||
|     } | ||||
|      | ||||
|     /** | ||||
|      * Checks if this block chunk is homogenous or not | ||||
|  | ||||
| @ -3,6 +3,9 @@ package electrosphere.client.interact.select; | ||||
| import org.joml.Vector3d; | ||||
| import org.joml.Vector3i; | ||||
| 
 | ||||
| import electrosphere.client.block.BlockChunkData; | ||||
| import electrosphere.engine.Globals; | ||||
| 
 | ||||
| /** | ||||
|  * An area of space that is selected by the client | ||||
|  */ | ||||
| @ -18,6 +21,11 @@ public class AreaSelection { | ||||
|         RECTANGULAR, | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Default radius to select | ||||
|      */ | ||||
|     public static final int DEFAULT_SELECTION_RADIUS = 10; | ||||
| 
 | ||||
|     /** | ||||
|      * The type of selection | ||||
|      */ | ||||
| @ -40,6 +48,15 @@ public class AreaSelection { | ||||
|      * @return The selection | ||||
|      */ | ||||
|     public static AreaSelection createRect(Vector3d start, Vector3d end){ | ||||
|         if(start.x > end.x){ | ||||
|             throw new Error("Start x is less than end x! " + start.x + " " + end.x); | ||||
|         } | ||||
|         if(start.y > end.y){ | ||||
|             throw new Error("Start y is less than end y! " + start.y + " " + end.y); | ||||
|         } | ||||
|         if(start.z > end.z){ | ||||
|             throw new Error("Start y is less than end y! " + start.z + " " + end.z); | ||||
|         } | ||||
|         AreaSelection rVal = new AreaSelection(); | ||||
|         rVal.type = AreaSelectionType.RECTANGULAR; | ||||
|         rVal.rectStart = start; | ||||
| @ -57,6 +74,100 @@ public class AreaSelection { | ||||
|     public static AreaSelection selectRectangularBlockCavity(Vector3i chunkPos, Vector3i blockPos, int maxRadius){ | ||||
|         AreaSelection rVal = null; | ||||
|          | ||||
|         int radStart = 0; | ||||
|         int radEnd = 1; | ||||
|         int increment = 0; | ||||
|         boolean expandPositive = true; | ||||
|         boolean expandNegative = true; | ||||
|         Vector3i currVoxelPos = new Vector3i(); | ||||
|         Vector3i currChunkPos = new Vector3i(); | ||||
|         while(radStart > -maxRadius && radEnd < maxRadius && (expandPositive || expandNegative)){ | ||||
|             if(increment % 2 == 0){ | ||||
|                 if(!expandPositive){ | ||||
|                     continue; | ||||
|                 } | ||||
|             } else { | ||||
|                 if(!expandNegative){ | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             for(int x = radStart; x < radEnd; x++){ | ||||
|                 for(int y = radStart; y < radEnd; y++){ | ||||
|                     for(int z = radStart; z < radEnd; z++){ | ||||
|                         currVoxelPos.set(blockPos).add(x,y,z); | ||||
|                         currChunkPos.set(chunkPos).add( | ||||
|                             currVoxelPos.x / BlockChunkData.CHUNK_DATA_WIDTH, | ||||
|                             currVoxelPos.y / BlockChunkData.CHUNK_DATA_WIDTH, | ||||
|                             currVoxelPos.z / BlockChunkData.CHUNK_DATA_WIDTH | ||||
|                         ); | ||||
|                         currVoxelPos.set( | ||||
|                             currVoxelPos.x % BlockChunkData.CHUNK_DATA_WIDTH, | ||||
|                             currVoxelPos.y % BlockChunkData.CHUNK_DATA_WIDTH, | ||||
|                             currVoxelPos.z % BlockChunkData.CHUNK_DATA_WIDTH | ||||
|                         ); | ||||
|                         if(!Globals.clientWorldData.chunkInBounds(currChunkPos)){ | ||||
|                             if(increment % 2 == 0){ | ||||
|                                 expandPositive = false; | ||||
|                             } else { | ||||
|                                 expandNegative = false; | ||||
|                             } | ||||
|                             continue; | ||||
|                         } | ||||
|                         BlockChunkData chunkData = Globals.clientBlockManager.getChunkDataAtWorldPoint(currChunkPos, BlockChunkData.LOD_FULL_RES); | ||||
|                         if(chunkData == null){ | ||||
|                             if(increment % 2 == 0){ | ||||
|                                 expandPositive = false; | ||||
|                             } else { | ||||
|                                 expandNegative = false; | ||||
|                             } | ||||
|                             continue; | ||||
|                         } | ||||
|                         while(currVoxelPos.x < 0){ | ||||
|                             currVoxelPos.x = currVoxelPos.x + BlockChunkData.CHUNK_DATA_WIDTH; | ||||
|                         } | ||||
|                         while(currVoxelPos.y < 0){ | ||||
|                             currVoxelPos.y = currVoxelPos.y + BlockChunkData.CHUNK_DATA_WIDTH; | ||||
|                         } | ||||
|                         while(currVoxelPos.z < 0){ | ||||
|                             currVoxelPos.z = currVoxelPos.z + BlockChunkData.CHUNK_DATA_WIDTH; | ||||
|                         } | ||||
|                         if(!chunkData.isEmpty(currVoxelPos)){ | ||||
|                             if(increment % 2 == 0){ | ||||
|                                 expandPositive = false; | ||||
|                             } else { | ||||
|                                 expandNegative = false; | ||||
|                             } | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             if(increment % 2 == 0){ | ||||
|                 if(expandPositive){ | ||||
|                     radEnd++; | ||||
|                 } | ||||
|             } else { | ||||
|                 if(expandNegative){ | ||||
|                     radStart--; | ||||
|                 } | ||||
|             } | ||||
|             increment++; | ||||
|         } | ||||
| 
 | ||||
|         Vector3d startPos = new Vector3d(Globals.clientWorldData.convertBlockToRealSpace(chunkPos, blockPos)) | ||||
|         .add( | ||||
|             radStart * BlockChunkData.BLOCK_SIZE_MULTIPLIER, | ||||
|             radStart * BlockChunkData.BLOCK_SIZE_MULTIPLIER, | ||||
|             radStart * BlockChunkData.BLOCK_SIZE_MULTIPLIER | ||||
|         ); | ||||
|         Vector3d endPos = new Vector3d(Globals.clientWorldData.convertBlockToRealSpace(chunkPos, blockPos)) | ||||
|         .add( | ||||
|             radEnd * BlockChunkData.BLOCK_SIZE_MULTIPLIER, | ||||
|             radEnd * BlockChunkData.BLOCK_SIZE_MULTIPLIER, | ||||
|             radEnd * BlockChunkData.BLOCK_SIZE_MULTIPLIER | ||||
|         ); | ||||
| 
 | ||||
|         rVal = AreaSelection.createRect(startPos, endPos); | ||||
| 
 | ||||
|         return rVal; | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -239,4 +239,14 @@ public class ClientWorldData { | ||||
|         ); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks that a chunk position is in bounds | ||||
|      * @param chunkPos The chunk pos | ||||
|      * @return true if it is in bounds, false otherwise | ||||
|      */ | ||||
|     public boolean chunkInBounds(Vector3i chunkPos){ | ||||
|         return chunkPos.x >= 0 && chunkPos.y >= 0 && chunkPos.z >= 0 && | ||||
|         chunkPos.x < this.worldDiscreteSize && chunkPos.y < this.worldDiscreteSize && chunkPos.z < this.worldDiscreteSize; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  | ||||
| @ -0,0 +1,31 @@ | ||||
| package electrosphere.client.script; | ||||
| 
 | ||||
| import org.graalvm.polyglot.HostAccess.Export; | ||||
| import org.joml.Vector3d; | ||||
| import org.joml.Vector3i; | ||||
| 
 | ||||
| import electrosphere.client.interact.select.AreaSelection; | ||||
| import electrosphere.controls.cursor.CursorState; | ||||
| import electrosphere.engine.Globals; | ||||
| import electrosphere.entity.EntityUtils; | ||||
| 
 | ||||
| /** | ||||
|  * Script utils for clients dealing with areas | ||||
|  */ | ||||
| public class ScriptClientAreaUtils { | ||||
|      | ||||
|     /** | ||||
|      * Tries to select a rectangular area | ||||
|      */ | ||||
|     @Export | ||||
|     public static void selectAreaRectangular(){ | ||||
|         // Vector3d blockCursorPos = Globals.cursorState.getBlockCursorPos(); | ||||
|         Vector3d cursorPos = new Vector3d(EntityUtils.getPosition(Globals.playerCursor)); | ||||
|         Vector3i chunkPos = Globals.clientWorldData.convertRealToWorldSpace(cursorPos); | ||||
|         Vector3i blockPos = Globals.clientWorldData.convertRealToBlockSpace(cursorPos); | ||||
|         AreaSelection selection = AreaSelection.selectRectangularBlockCavity(chunkPos, blockPos, AreaSelection.DEFAULT_SELECTION_RADIUS); | ||||
|         Globals.cursorState.selectRectangularArea(selection); | ||||
|         CursorState.makeAreaVisible(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -169,6 +169,7 @@ public class LoadingUtils { | ||||
|         template.getCreatureToolbarData().setSlotItem("2", new ToolbarItem(73, "entityinspector")); | ||||
|         template.getCreatureToolbarData().setSlotItem("3", new ToolbarItem(74, "waterSpawner")); | ||||
|         template.getCreatureToolbarData().setSlotItem("4", new ToolbarItem(75, "fabTool")); | ||||
|         template.getCreatureToolbarData().setSlotItem("5", new ToolbarItem(76, "roomTool")); | ||||
|         //set player character template | ||||
|         serverPlayerConnection.setCreatureTemplate(template); | ||||
|         Globals.clientConnection.queueOutgoingMessage(CharacterMessage.constructRequestSpawnCharacterMessage(CharacterProtocol.SPAWN_EXISTING_TEMPLATE + "")); | ||||
|  | ||||
| @ -143,6 +143,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag | ||||
|             template.getCreatureToolbarData().setSlotItem("2", new ToolbarItem(73, "entityinspector")); | ||||
|             template.getCreatureToolbarData().setSlotItem("3", new ToolbarItem(74, "waterSpawner")); | ||||
|             template.getCreatureToolbarData().setSlotItem("4", new ToolbarItem(75, "fabTool")); | ||||
|             template.getCreatureToolbarData().setSlotItem("5", new ToolbarItem(76, "roomTool")); | ||||
|             //set player character template | ||||
|             connectionHandler.setCreatureTemplate(template); | ||||
|         } else { | ||||
| @ -165,6 +166,7 @@ public class CharacterProtocol implements ServerProtocolTemplate<CharacterMessag | ||||
|             template.getCreatureToolbarData().setSlotItem("2", new ToolbarItem(73, "entityinspector")); | ||||
|             template.getCreatureToolbarData().setSlotItem("3", new ToolbarItem(74, "waterSpawner")); | ||||
|             template.getCreatureToolbarData().setSlotItem("4", new ToolbarItem(75, "fabTool")); | ||||
|             template.getCreatureToolbarData().setSlotItem("5", new ToolbarItem(76, "roomTool")); | ||||
|             //set player character template | ||||
|             connectionHandler.setCreatureTemplate(template); | ||||
|         } | ||||
|  | ||||
| @ -21,6 +21,7 @@ import java.util.Map; | ||||
| import org.graalvm.polyglot.Source; | ||||
| import org.graalvm.polyglot.Value; | ||||
| 
 | ||||
| import electrosphere.client.script.ScriptClientAreaUtils; | ||||
| import electrosphere.client.script.ScriptClientVoxelUtils; | ||||
| import electrosphere.client.ui.menu.script.ScriptLevelEditorUtils; | ||||
| import electrosphere.client.ui.menu.script.ScriptMenuUtils; | ||||
| @ -121,6 +122,7 @@ public class ScriptEngine extends SignalServiceImpl { | ||||
|         {"voxelUtils",ScriptClientVoxelUtils.class}, | ||||
|         {"levelEditorUtils",ScriptLevelEditorUtils.class}, | ||||
|         {"math",ScriptMathInterface.class}, | ||||
|         {"areaUtils",ScriptClientAreaUtils.class}, | ||||
|     }; | ||||
| 
 | ||||
|     //singletons from the host that are provided to the javascript context | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user