Skip to content

Commit

Permalink
Generate box shape for air voxels.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
afritz1 committed Jan 1, 2025
1 parent 8f67370 commit 7c9e643
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 57 deletions.
8 changes: 4 additions & 4 deletions OpenTESArena/src/Collision/CollisionChunk.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<int>(voxelShapeDef.type)));
}

collisionShapeDefID = this->addCollisionShapeDef(std::move(collisionShapeDef));
Expand Down
56 changes: 37 additions & 19 deletions OpenTESArena/src/Collision/CollisionChunkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<double>(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;
Expand Down Expand Up @@ -53,15 +51,16 @@ namespace
const RadiansF boxYRotation = static_cast<RadiansF>(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;
}
}
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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))
Expand All @@ -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);
Expand Down
7 changes: 1 addition & 6 deletions OpenTESArena/src/Collision/CollisionShapeDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CollisionShapeType>(-1);
}

void CollisionShapeDefinition::initBox(double width, double height, double depth, double yOffset, Radians yRotation)
Expand Down
4 changes: 1 addition & 3 deletions OpenTESArena/src/Collision/CollisionShapeDefinition.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@

enum class CollisionShapeType
{
None, // Air
Box, // Voxels
Box, // Voxels inc. air (for sound/lore triggers)
Capsule // Entities inc. projectiles
};

Expand Down Expand Up @@ -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);
Expand Down
8 changes: 5 additions & 3 deletions OpenTESArena/src/Rendering/RenderVoxelChunkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -515,15 +515,16 @@ void RenderVoxelChunkManager::loadMeshBuffers(RenderVoxelChunk &renderChunk, con
{
const VoxelChunk::VoxelShapeDefID voxelShapeDefID = static_cast<VoxelChunk::VoxelShapeDefID>(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))
{
Expand Down Expand Up @@ -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;
}
Expand Down
5 changes: 3 additions & 2 deletions OpenTESArena/src/Voxels/VoxelChunkManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
22 changes: 4 additions & 18 deletions OpenTESArena/src/Voxels/VoxelShapeDefinition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -142,24 +142,10 @@ void VoxelMeshDefinition::writeRendererIndexBuffers(BufferView<int32_t> 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)
Expand Down
2 changes: 0 additions & 2 deletions OpenTESArena/src/Voxels/VoxelShapeDefinition.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@

enum class VoxelShapeType
{
None, // Air
Box
};

Expand Down Expand Up @@ -74,7 +73,6 @@ struct VoxelShapeDefinition

VoxelShapeDefinition();

void initNone();
void initBoxFromClassic(ArenaTypes::VoxelType voxelType, VoxelShapeScaleType scaleType, const ArenaMeshUtils::ShapeInitCache &shapeInitCache);
};

Expand Down

0 comments on commit 7c9e643

Please sign in to comment.