From 7c9e643917625c6ce3a2d73635c5a02a394e9068 Mon Sep 17 00:00:00 2001 From: Aaron Date: Wed, 1 Jan 2025 09:44:30 -0800 Subject: [PATCH] Generate box shape for air voxels. This lets sound and lore voxels use a sensor collider. Need to actually make that the primary logic since it's still checking the player's position instead. --- OpenTESArena/src/Collision/CollisionChunk.cpp | 8 +-- .../src/Collision/CollisionChunkManager.cpp | 56 ++++++++++++------- .../Collision/CollisionShapeDefinition.cpp | 7 +-- .../src/Collision/CollisionShapeDefinition.h | 4 +- .../src/Rendering/RenderVoxelChunkManager.cpp | 8 ++- OpenTESArena/src/Voxels/VoxelChunkManager.cpp | 5 +- .../src/Voxels/VoxelShapeDefinition.cpp | 22 ++------ .../src/Voxels/VoxelShapeDefinition.h | 2 - 8 files changed, 55 insertions(+), 57 deletions(-) diff --git a/OpenTESArena/src/Collision/CollisionChunk.cpp b/OpenTESArena/src/Collision/CollisionChunk.cpp index 9ff869567..3c5552437 100644 --- a/OpenTESArena/src/Collision/CollisionChunk.cpp +++ b/OpenTESArena/src/Collision/CollisionChunk.cpp @@ -111,14 +111,14 @@ CollisionChunk::CollisionShapeDefID CollisionChunk::getOrAddShapeDefIdMapping(co const VoxelShapeDefinition &voxelShapeDef = voxelChunk.getShapeDef(voxelShapeDefID); CollisionShapeDefinition collisionShapeDef; - if (voxelShapeDef.type == VoxelShapeType::None) + if (voxelShapeDef.type == VoxelShapeType::Box) { - collisionShapeDef.initNone(); + const VoxelBoxShapeDefinition &voxelBoxShapeDef = voxelShapeDef.box; + collisionShapeDef.initBox(voxelBoxShapeDef.width, voxelBoxShapeDef.height, voxelBoxShapeDef.depth, voxelBoxShapeDef.yOffset, voxelBoxShapeDef.yRotation); } else { - const VoxelBoxShapeDefinition &voxelBoxShapeDef = voxelShapeDef.box; - collisionShapeDef.initBox(voxelBoxShapeDef.width, voxelBoxShapeDef.height, voxelBoxShapeDef.depth, voxelBoxShapeDef.yOffset, voxelBoxShapeDef.yRotation); + DebugNotImplementedMsg(std::to_string(static_cast(voxelShapeDef.type))); } collisionShapeDefID = this->addCollisionShapeDef(std::move(collisionShapeDef)); diff --git a/OpenTESArena/src/Collision/CollisionChunkManager.cpp b/OpenTESArena/src/Collision/CollisionChunkManager.cpp index 6ed2b15c6..5cc83e8b8 100644 --- a/OpenTESArena/src/Collision/CollisionChunkManager.cpp +++ b/OpenTESArena/src/Collision/CollisionChunkManager.cpp @@ -12,15 +12,13 @@ namespace { // Creates collider but does not add it to simulation. - bool TryCreatePhysicsCollider(SNInt x, int y, WEInt z, const ChunkInt2 &chunkPos, const VoxelShapeDefinition &voxelShapeDef, - const CollisionShapeDefinition &collisionShapeDef, double ceilingScale, JPH::PhysicsSystem &physicsSystem, - JPH::BodyID *outBodyID) + bool TryCreatePhysicsCollider(SNInt x, int y, WEInt z, const ChunkInt2 &chunkPos, const CollisionShapeDefinition &collisionShapeDef, + VoxelShapeScaleType scaleType, double ceilingScale, bool isSensor, JPH::PhysicsSystem &physicsSystem, JPH::BodyID *outBodyID) { const double voxelYBottom = static_cast(y) * ceilingScale; DebugAssert(collisionShapeDef.type == CollisionShapeType::Box); const CollisionBoxShapeDefinition &boxShapeDef = collisionShapeDef.box; - const VoxelShapeScaleType scaleType = voxelShapeDef.scaleType; const double scaledYBottom = voxelYBottom + MeshUtils::getScaledVertexY(boxShapeDef.yOffset, scaleType, ceilingScale); const double scaledYTop = voxelYBottom + MeshUtils::getScaledVertexY(boxShapeDef.yOffset + boxShapeDef.height, scaleType, ceilingScale); const double scaledHeight = scaledYTop - scaledYBottom; @@ -53,15 +51,16 @@ namespace const RadiansF boxYRotation = static_cast(boxShapeDef.yRotation); const JPH::Quat boxJoltQuat = JPH::Quat::sRotation(JPH::Vec3Arg::sAxisY(), boxYRotation); const JPH::BodyCreationSettings boxSettings(boxShape, boxJoltPos, boxJoltQuat, JPH::EMotionType::Static, PhysicsLayers::NON_MOVING); - const JPH::Body *box = bodyInterface.CreateBody(boxSettings); - if (box == nullptr) + JPH::Body *boxBody = bodyInterface.CreateBody(boxSettings); + if (boxBody == nullptr) { const uint32_t totalBodyCount = physicsSystem.GetNumBodies(); DebugLogError("Couldn't create Jolt body at (" + std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z) + ") in chunk (" + chunkPos.toString() + ") (total: " + std::to_string(totalBodyCount) + ")."); return false; } - *outBodyID = box->GetID(); + boxBody->SetIsSensor(isSensor); + *outBodyID = boxBody->GetID(); return true; } } @@ -89,15 +88,23 @@ void CollisionChunkManager::populateChunk(int index, double ceilingScale, const const VoxelTraitsDefinition &voxelTraitsDef = voxelChunk.getTraitsDef(voxelTraitsDefID); const bool voxelHasCollision = voxelTraitsDef.hasCollision(); // @todo: lore/sound triggers aren't included in this collisionChunk.enabledColliders.set(x, y, z, voxelHasCollision); - - if (voxelHasCollision) + + VoxelChunk::TriggerDefID triggerDefID; + const bool isTriggerVoxel = voxelChunk.tryGetTriggerDefID(x, y, z, &triggerDefID); + + VoxelChunk::TransitionDefID transitionDefID; + const bool isTransitionVoxel = voxelChunk.tryGetTransitionDefID(x, y, z, &transitionDefID); + const bool isSensorCollider = isTriggerVoxel || isTransitionVoxel; + const bool shouldCreateCollider = voxelHasCollision || isSensorCollider; + + if (shouldCreateCollider) { const VoxelShapeDefinition &voxelShapeDef = voxelChunk.getShapeDef(voxelShapeDefID); const CollisionShapeDefinition &collisionShapeDef = collisionChunk.getCollisionShapeDef(collisionShapeDefID); // Generate collider but don't add to simulation. JPH::BodyID bodyID; - if (TryCreatePhysicsCollider(x, y, z, chunkPos, voxelShapeDef, collisionShapeDef, ceilingScale, physicsSystem, &bodyID)) + if (TryCreatePhysicsCollider(x, y, z, chunkPos, collisionShapeDef, voxelShapeDef.scaleType, ceilingScale, isSensorCollider, physicsSystem, &bodyID)) { collisionChunk.physicsBodyIDs.set(x, y, z, bodyID); createdBodyIDs.emplace_back(bodyID); @@ -133,16 +140,19 @@ void CollisionChunkManager::updateDirtyVoxels(const ChunkInt2 &chunkPos, double // @todo: this dirty shapes list might be full of brand new voxels this frame, so we're accidentally destroying + recreating them all (found during the AddBodiesPrepare/Finalize() work) for (const VoxelInt3 &voxelPos : dirtyShapeDefPositions) { - const VoxelChunk::VoxelShapeDefID voxelShapeDefID = voxelChunk.getShapeDefID(voxelPos.x, voxelPos.y, voxelPos.z); + const SNInt x = voxelPos.x; + const int y = voxelPos.y; + const WEInt z = voxelPos.z; + const VoxelChunk::VoxelShapeDefID voxelShapeDefID = voxelChunk.getShapeDefID(x, y, z); const CollisionChunk::CollisionShapeDefID collisionShapeDefID = collisionChunk.getOrAddShapeDefIdMapping(voxelChunk, voxelShapeDefID); - collisionChunk.shapeDefIDs.set(voxelPos.x, voxelPos.y, voxelPos.z, collisionShapeDefID); + collisionChunk.shapeDefIDs.set(x, y, z, collisionShapeDefID); - const VoxelChunk::VoxelTraitsDefID voxelTraitsDefID = voxelChunk.getTraitsDefID(voxelPos.x, voxelPos.y, voxelPos.z); + const VoxelChunk::VoxelTraitsDefID voxelTraitsDefID = voxelChunk.getTraitsDefID(x, y, z); const VoxelTraitsDefinition &voxelTraitsDef = voxelChunk.getTraitsDef(voxelTraitsDefID); const bool voxelHasCollision = voxelTraitsDef.hasCollision(); - collisionChunk.enabledColliders.set(voxelPos.x, voxelPos.y, voxelPos.z, voxelHasCollision); + collisionChunk.enabledColliders.set(x, y, z, voxelHasCollision); - JPH::BodyID existingBodyID = collisionChunk.physicsBodyIDs.get(voxelPos.x, voxelPos.y, voxelPos.z); + JPH::BodyID existingBodyID = collisionChunk.physicsBodyIDs.get(x, y, z); if (!existingBodyID.IsInvalid()) { if (bodyInterface.IsAdded(existingBodyID)) @@ -151,19 +161,27 @@ void CollisionChunkManager::updateDirtyVoxels(const ChunkInt2 &chunkPos, double } bodyIDsToDestroy.emplace_back(existingBodyID); - collisionChunk.physicsBodyIDs.set(voxelPos.x, voxelPos.y, voxelPos.z, Physics::INVALID_BODY_ID); + collisionChunk.physicsBodyIDs.set(x, y, z, Physics::INVALID_BODY_ID); } - if (voxelHasCollision) + VoxelChunk::TriggerDefID triggerDefID; + const bool isTriggerVoxel = voxelChunk.tryGetTriggerDefID(x, y, z, &triggerDefID); + + VoxelChunk::TransitionDefID transitionDefID; + const bool isTransitionVoxel = voxelChunk.tryGetTransitionDefID(x, y, z, &transitionDefID); + const bool isSensorCollider = isTriggerVoxel || isTransitionVoxel; + const bool shouldCreateCollider = voxelHasCollision || isSensorCollider; + + if (shouldCreateCollider) { const VoxelShapeDefinition &voxelShapeDef = voxelChunk.getShapeDef(voxelShapeDefID); const CollisionShapeDefinition &collisionShapeDef = collisionChunk.getCollisionShapeDef(collisionShapeDefID); // Generate collider but don't add to simulation yet. JPH::BodyID bodyID; - if (TryCreatePhysicsCollider(voxelPos.x, voxelPos.y, voxelPos.z, chunkPos, voxelShapeDef, collisionShapeDef, ceilingScale, physicsSystem, &bodyID)) + if (TryCreatePhysicsCollider(x, y, z, chunkPos, collisionShapeDef, voxelShapeDef.scaleType, ceilingScale, isSensorCollider, physicsSystem, &bodyID)) { - collisionChunk.physicsBodyIDs.set(voxelPos.x, voxelPos.y, voxelPos.z, bodyID); + collisionChunk.physicsBodyIDs.set(x, y, z, bodyID); } bodyIDsToAdd.emplace_back(bodyID); diff --git a/OpenTESArena/src/Collision/CollisionShapeDefinition.cpp b/OpenTESArena/src/Collision/CollisionShapeDefinition.cpp index e2df4c13f..6ba866540 100644 --- a/OpenTESArena/src/Collision/CollisionShapeDefinition.cpp +++ b/OpenTESArena/src/Collision/CollisionShapeDefinition.cpp @@ -28,12 +28,7 @@ void CollisionCapsuleShapeDefinition::init(double radius, double middleHeight) CollisionShapeDefinition::CollisionShapeDefinition() { std::memset(this, 0, sizeof(*this)); - this->type = CollisionShapeType::None; -} - -void CollisionShapeDefinition::initNone() -{ - this->type = CollisionShapeType::None; + this->type = static_cast(-1); } void CollisionShapeDefinition::initBox(double width, double height, double depth, double yOffset, Radians yRotation) diff --git a/OpenTESArena/src/Collision/CollisionShapeDefinition.h b/OpenTESArena/src/Collision/CollisionShapeDefinition.h index 7319ed3fa..131df5f89 100644 --- a/OpenTESArena/src/Collision/CollisionShapeDefinition.h +++ b/OpenTESArena/src/Collision/CollisionShapeDefinition.h @@ -5,8 +5,7 @@ enum class CollisionShapeType { - None, // Air - Box, // Voxels + Box, // Voxels inc. air (for sound/lore triggers) Capsule // Entities inc. projectiles }; @@ -39,7 +38,6 @@ struct CollisionShapeDefinition CollisionShapeDefinition(); - void initNone(); void initBox(double width, double height, double depth, double yOffset, Radians yRotation); void initCapsule(double radius, double middleHeight); void initCapsuleAsSphere(double radius); diff --git a/OpenTESArena/src/Rendering/RenderVoxelChunkManager.cpp b/OpenTESArena/src/Rendering/RenderVoxelChunkManager.cpp index 6dc88a54f..b3662fe13 100644 --- a/OpenTESArena/src/Rendering/RenderVoxelChunkManager.cpp +++ b/OpenTESArena/src/Rendering/RenderVoxelChunkManager.cpp @@ -515,15 +515,16 @@ void RenderVoxelChunkManager::loadMeshBuffers(RenderVoxelChunk &renderChunk, con { const VoxelChunk::VoxelShapeDefID voxelShapeDefID = static_cast(shapeDefIndex); const VoxelShapeDefinition &voxelShapeDef = voxelChunk.getShapeDef(voxelShapeDefID); + const VoxelMeshDefinition &voxelMeshDef = voxelShapeDef.mesh; + const bool isRenderMeshValid = !voxelMeshDef.isEmpty(); // Air has a shape for trigger voxels but no mesh RenderVoxelMeshInstance renderVoxelMeshInst; - if (voxelShapeDef.type != VoxelShapeType::None) // Only attempt to create buffers for non-air voxels. + if (isRenderMeshValid) { constexpr int positionComponentsPerVertex = MeshUtils::POSITION_COMPONENTS_PER_VERTEX; constexpr int normalComponentsPerVertex = MeshUtils::NORMAL_COMPONENTS_PER_VERTEX; constexpr int texCoordComponentsPerVertex = MeshUtils::TEX_COORDS_PER_VERTEX; - const VoxelMeshDefinition &voxelMeshDef = voxelShapeDef.mesh; const int vertexCount = voxelMeshDef.rendererVertexCount; if (!renderer.tryCreateVertexBuffer(vertexCount, positionComponentsPerVertex, &renderVoxelMeshInst.vertexBufferID)) { @@ -736,7 +737,8 @@ void RenderVoxelChunkManager::updateChunkDrawCalls(RenderVoxelChunk &renderChunk const VoxelChunk::VoxelShapeDefID voxelShapeDefID = voxelChunk.getShapeDefID(voxel.x, voxel.y, voxel.z); const VoxelShapeDefinition &voxelShapeDef = voxelChunk.getShapeDef(voxelShapeDefID); - if (voxelShapeDef.type == VoxelShapeType::None) + const VoxelMeshDefinition &voxelMeshDef = voxelShapeDef.mesh; + if (voxelMeshDef.isEmpty()) { continue; } diff --git a/OpenTESArena/src/Voxels/VoxelChunkManager.cpp b/OpenTESArena/src/Voxels/VoxelChunkManager.cpp index b14fb29c6..dfc2b2702 100644 --- a/OpenTESArena/src/Voxels/VoxelChunkManager.cpp +++ b/OpenTESArena/src/Voxels/VoxelChunkManager.cpp @@ -685,8 +685,9 @@ void VoxelChunkManager::updateChunkDoorVisibilityInsts(VoxelChunk &chunk, const } const VoxelChunk &voxelChunk = this->getChunkAtIndex(*chunkIndex); - const VoxelShapeDefinition &shapeDef = voxelChunk.getShapeDef(shapeDefID); - return (shapeDef.type == VoxelShapeType::None) || shapeDef.allowsAdjacentDoorFaces; + const VoxelShapeDefinition &voxelShapeDef = voxelChunk.getShapeDef(shapeDefID); + const VoxelMeshDefinition &voxelMeshDef = voxelShapeDef.mesh; + return voxelMeshDef.isEmpty() || voxelShapeDef.allowsAdjacentDoorFaces; }; const bool isNorthValid = isVoxelValidForDoorFace(northChunkIndex, northVoxelShapeDefID); diff --git a/OpenTESArena/src/Voxels/VoxelShapeDefinition.cpp b/OpenTESArena/src/Voxels/VoxelShapeDefinition.cpp index 6763339dc..b4f18b950 100644 --- a/OpenTESArena/src/Voxels/VoxelShapeDefinition.cpp +++ b/OpenTESArena/src/Voxels/VoxelShapeDefinition.cpp @@ -142,24 +142,10 @@ void VoxelMeshDefinition::writeRendererIndexBuffers(BufferView outOpaqu VoxelShapeDefinition::VoxelShapeDefinition() { - // Air by default. - this->initNone(); -} - -void VoxelShapeDefinition::initNone() -{ - this->type = VoxelShapeType::None; - std::memset(&this->box, 0, sizeof(this->box)); - - constexpr ArenaTypes::VoxelType voxelType = ArenaTypes::VoxelType::None; - constexpr VoxelShapeScaleType scaleType = VoxelShapeScaleType::ScaledFromMin; - ArenaMeshUtils::ShapeInitCache shapeInitCache; - this->mesh.initClassic(voxelType, scaleType, shapeInitCache); - this->scaleType = scaleType; - this->allowsBackFaces = ArenaMeshUtils::AllowsBackFacingGeometry(voxelType); - this->allowsAdjacentDoorFaces = ArenaMeshUtils::AllowsAdjacentDoorFaces(voxelType); - this->enablesNeighborGeometry = ArenaMeshUtils::EnablesNeighborVoxelGeometry(voxelType); - this->isContextSensitive = ArenaMeshUtils::HasContextSensitiveGeometry(voxelType); + // Air by default. Needs the shape defined in case of trigger voxels, but doesn't need a render mesh. + ArenaMeshUtils::ShapeInitCache airShapeInitCache; + airShapeInitCache.initDefaultBoxValues(); + this->initBoxFromClassic(ArenaTypes::VoxelType::None, VoxelShapeScaleType::ScaledFromMin, airShapeInitCache); } void VoxelShapeDefinition::initBoxFromClassic(ArenaTypes::VoxelType voxelType, VoxelShapeScaleType scaleType, const ArenaMeshUtils::ShapeInitCache &shapeInitCache) diff --git a/OpenTESArena/src/Voxels/VoxelShapeDefinition.h b/OpenTESArena/src/Voxels/VoxelShapeDefinition.h index f5dba6a2e..81d65ea8d 100644 --- a/OpenTESArena/src/Voxels/VoxelShapeDefinition.h +++ b/OpenTESArena/src/Voxels/VoxelShapeDefinition.h @@ -11,7 +11,6 @@ enum class VoxelShapeType { - None, // Air Box }; @@ -74,7 +73,6 @@ struct VoxelShapeDefinition VoxelShapeDefinition(); - void initNone(); void initBoxFromClassic(ArenaTypes::VoxelType voxelType, VoxelShapeScaleType scaleType, const ArenaMeshUtils::ShapeInitCache &shapeInitCache); };