From 75fe5a9d1a59cd86714fee6fcadb751668e918f8 Mon Sep 17 00:00:00 2001 From: austin Date: Sat, 26 Apr 2025 15:58:29 -0400 Subject: [PATCH] interaction target work --- docs/src/progress/renderertodo.md | 1 + .../interact/ClientInteractionEngine.java | 63 ++++++++++++++++++- .../terrain/manager/ClientTerrainManager.java | 12 +++- .../collision/CollisionEngine.java | 2 +- .../collision/RayCastCallback.java | 29 +++++++-- .../entity/types/EntityTypes.java | 8 ++- .../entity/types/terrain/TerrainChunk.java | 12 ++++ 7 files changed, 115 insertions(+), 12 deletions(-) diff --git a/docs/src/progress/renderertodo.md b/docs/src/progress/renderertodo.md index 06a6ea0c..8b0eab39 100644 --- a/docs/src/progress/renderertodo.md +++ b/docs/src/progress/renderertodo.md @@ -1547,6 +1547,7 @@ Fab selection tool actually loads fab files Fix fab file reading Fab tool can show transparent, loaded version of fab file Interaction target tooltip at top of window +Interaction target tooltip shows entity target, voxel targets diff --git a/src/main/java/electrosphere/client/interact/ClientInteractionEngine.java b/src/main/java/electrosphere/client/interact/ClientInteractionEngine.java index a312fc62..d7c62a30 100644 --- a/src/main/java/electrosphere/client/interact/ClientInteractionEngine.java +++ b/src/main/java/electrosphere/client/interact/ClientInteractionEngine.java @@ -7,9 +7,12 @@ import java.util.concurrent.locks.ReentrantLock; import org.joml.Matrix4d; import org.joml.Quaterniond; import org.joml.Vector3d; +import org.joml.Vector3i; import org.ode4j.ode.DBody; +import electrosphere.client.block.BlockChunkData; import electrosphere.client.entity.camera.CameraEntityUtils; +import electrosphere.client.terrain.cache.ChunkData; import electrosphere.client.ui.menu.ingame.InteractionTargetMenu; import electrosphere.collision.CollisionBodyCreation; import electrosphere.collision.CollisionEngine; @@ -19,8 +22,10 @@ import electrosphere.engine.Globals; import electrosphere.entity.Entity; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.EntityTypes.EntityType; import electrosphere.entity.types.common.CommonEntityUtils; import electrosphere.game.data.collidable.CollidableTemplate; +import electrosphere.server.physics.terrain.manager.ServerTerrainChunk; /** * Manages the interaction state @@ -220,14 +225,68 @@ public class ClientInteractionEngine { if(Globals.playerEntity != null && Globals.playerCamera != null){ boolean set = false; Entity camera = Globals.playerCamera; - Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera)).mul(-1.0); + Vector3d eyePos = new Vector3d(CameraEntityUtils.getCameraEye(camera)); Vector3d centerPos = new Vector3d(CameraEntityUtils.getCameraCenter(camera)); - Entity target = ClientInteractionEngine.rayCast(centerPos, eyePos); + Entity target = ClientInteractionEngine.rayCast(centerPos, new Vector3d(eyePos).mul(-1)); if(target != null){ String text = CommonEntityUtils.getEntitySubtype(target); InteractionTargetMenu.setInteractionTargetString(text); set = true; } + if(!set){ + target = Globals.clientSceneWrapper.getCollisionEngine().rayCast(new Vector3d(centerPos), new Vector3d(eyePos).mul(-1), CollisionEngine.DEFAULT_INTERACT_DISTANCE); + if(target != null){ + EntityType type = CommonEntityUtils.getEntityType(target); + if(type == null){ + throw new Error("Entity does not have a type defined!"); + } + switch(type){ + case CREATURE: { + InteractionTargetMenu.setInteractionTargetString(CommonEntityUtils.getEntitySubtype(target)); + set = true; + } break; + case ITEM: { + InteractionTargetMenu.setInteractionTargetString(CommonEntityUtils.getEntitySubtype(target)); + set = true; + } break; + case FOLIAGE: { + InteractionTargetMenu.setInteractionTargetString(CommonEntityUtils.getEntitySubtype(target)); + set = true; + } break; + default: { + //silently ignore + } break; + } + } + } + if(!set){ + Vector3d collisionPosition = Globals.clientSceneWrapper.getCollisionEngine().rayCastPosition(centerPos, new Vector3d(eyePos).mul(-1), CollisionEngine.DEFAULT_INTERACT_DISTANCE); + if(collisionPosition != null && collisionPosition.distance(centerPos) < CollisionEngine.DEFAULT_INTERACT_DISTANCE){ + //grab block at point + BlockChunkData blockChunkData = Globals.clientBlockManager.getChunkDataAtWorldPoint(Globals.clientWorldData.convertRealToWorldSpace(collisionPosition), 0); + if(blockChunkData != null){ + Vector3i blockPos = Globals.clientWorldData.convertRealToBlockSpace(collisionPosition); + if(!blockChunkData.isEmpty(blockPos.x, blockPos.y, blockPos.z)){ + short type = blockChunkData.getType(blockPos.x, blockPos.y, blockPos.z); + String text = Globals.gameConfigCurrent.getBlockData().getTypeFromId(type).getName(); + InteractionTargetMenu.setInteractionTargetString(text); + set = true; + } + } + //if we didn't find a block type, try terrain + if(!set){ + ChunkData chunkData = Globals.clientTerrainManager.getChunkDataAtWorldPoint(Globals.clientWorldData.convertRealToWorldSpace(collisionPosition), 0); + if(chunkData != null){ + int voxelType = chunkData.getType(Globals.clientWorldData.convertRealToVoxelSpace(collisionPosition)); + if(voxelType != ServerTerrainChunk.VOXEL_TYPE_AIR){ + String text = Globals.gameConfigCurrent.getVoxelData().getTypeFromId(voxelType).getName(); + InteractionTargetMenu.setInteractionTargetString(text); + set = true; + } + } + } + } + } if(!set){ InteractionTargetMenu.setInteractionTargetString(""); } diff --git a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java index 25c78186..7fb36df2 100644 --- a/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java +++ b/src/main/java/electrosphere/client/terrain/manager/ClientTerrainManager.java @@ -68,7 +68,9 @@ public class ClientTerrainManager { */ static final int CACHE_SIZE_IN_MB = (CACHE_SIZE * ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION * ServerTerrainChunk.CHUNK_DIMENSION * 2 * 4) / 1024 / 1024; - //used for caching the macro values + /** + * used for caching the macro values + */ ClientTerrainCache terrainCache; /** @@ -76,10 +78,14 @@ public class ClientTerrainManager { */ BlockChunkCache blockCache; - //The world data for the client + /** + * The world data for the client + */ ClientWorldData clientWorldData; - //The queue of terrain chunk data to be buffered to gpu + /** + * The queue of terrain chunk data to be buffered to gpu + */ static List terrainChunkGenerationQueue = new LinkedList(); /** diff --git a/src/main/java/electrosphere/collision/CollisionEngine.java b/src/main/java/electrosphere/collision/CollisionEngine.java index 8936a50e..1170b296 100644 --- a/src/main/java/electrosphere/collision/CollisionEngine.java +++ b/src/main/java/electrosphere/collision/CollisionEngine.java @@ -634,7 +634,7 @@ public class CollisionEngine { * @return The entity that the ray collides with if successful, null otherwise */ public Entity rayCast(Vector3d start, Vector3d direction, double length){ - return rayCast(start,direction,length,null); + return this.rayCast(start,direction,length,null); } /** diff --git a/src/main/java/electrosphere/collision/RayCastCallback.java b/src/main/java/electrosphere/collision/RayCastCallback.java index 654b09ea..31a49645 100644 --- a/src/main/java/electrosphere/collision/RayCastCallback.java +++ b/src/main/java/electrosphere/collision/RayCastCallback.java @@ -18,6 +18,7 @@ import org.ode4j.ode.OdeHelper; import electrosphere.collision.collidable.Collidable; import electrosphere.engine.Globals; import electrosphere.entity.Entity; +import electrosphere.entity.state.attach.AttachUtils; import electrosphere.logger.LoggerInterface; public class RayCastCallback implements DNearCallback { @@ -79,6 +80,13 @@ void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) { if(collidable2 != null && collidable2.getParent() == Globals.playerEntity){ return; } + //don't collide with entities that are attached to the parent either + if(collidable1 != null && AttachUtils.getParent(collidable1.getParent()) != null && AttachUtils.getParent(collidable1.getParent()) == Globals.playerEntity){ + return; + } + if(collidable2 != null && AttachUtils.getParent(collidable2.getParent()) != null && AttachUtils.getParent(collidable2.getParent()) == Globals.playerEntity){ + return; + } Globals.profiler.beginAggregateCpuSample("RayCastCallback - try collisions"); if( @@ -137,14 +145,27 @@ void RayCallback(void *Data, dGeomID Geometry1, dGeomID Geometry2) { * Data object that contains the information for a ray cast check */ static class RayCastCallbackData { - //The map of ode DBody -> collidable + + /** + * The map of ode DBody -> collidable + */ Map bodyEntityMap; - //The mask of collidable types to filter collisions by. Can be null. + + /** + * The mask of collidable types to filter collisions by. Can be null. + */ List collidableTypeMask; - //The entity that the ray cast collided with. If null, no entity was collided with. + + /** + * The entity that the ray cast collided with. If null, no entity was collided with. + */ Entity collidedEntity = null; - //The position in world space that the collision happened + + /** + * The position in world space that the collision happened + */ Vector3d collisionPosition = null; + /** * Constructor * @param bodyEntityMap The map of ode DBody -> collidable diff --git a/src/main/java/electrosphere/entity/types/EntityTypes.java b/src/main/java/electrosphere/entity/types/EntityTypes.java index 98e5946b..dbe54200 100644 --- a/src/main/java/electrosphere/entity/types/EntityTypes.java +++ b/src/main/java/electrosphere/entity/types/EntityTypes.java @@ -26,7 +26,11 @@ public class EntityTypes { /** * A piece of foliage */ - FOLIAGE(3) + FOLIAGE(3), + /** + * Special engine-created entities (ie not defined in a json file) + */ + ENGINE(4), ; /** @@ -38,7 +42,7 @@ public class EntityTypes { * Constructor * @param newValue The value */ - EntityType(final int newValue){ + private EntityType(final int newValue){ value = newValue; } diff --git a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java index bb0c34b2..89824e10 100644 --- a/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java +++ b/src/main/java/electrosphere/entity/types/terrain/TerrainChunk.java @@ -22,7 +22,9 @@ import electrosphere.entity.Entity; import electrosphere.entity.EntityCreationUtils; import electrosphere.entity.EntityDataStrings; import electrosphere.entity.EntityUtils; +import electrosphere.entity.types.EntityTypes.EntityType; import electrosphere.entity.types.collision.CollisionObjUtils; +import electrosphere.entity.types.common.CommonEntityUtils; import electrosphere.logger.LoggerInterface; import electrosphere.renderer.meshgen.TransvoxelModelGeneration; import electrosphere.renderer.meshgen.TransvoxelModelGeneration.TransvoxelChunkData; @@ -59,6 +61,7 @@ public class TerrainChunk { Globals.profiler.beginAggregateCpuSample("TerrainChunk.clientCreateTerrainChunkEntity"); Entity rVal = EntityCreationUtils.createClientSpatialEntity(); + CommonEntityUtils.setEntityType(rVal, EntityType.ENGINE); if(hasPolygons && chunkData.terrainGrid != null && chunkData.textureGrid != null){ generationService.submit(() -> { TerrainChunkData data; @@ -149,4 +152,13 @@ public class TerrainChunk { generationService.shutdownNow(); } + /** + * Checks if this is a terrain entity + * @param entity The entity + * @return True if it is a terrain entity, false otherwise + */ + public static boolean isTerrainEntity(Entity entity){ + return entity.containsKey(EntityDataStrings.TERRAIN_IS_TERRAIN); + } + }