sokol-luajit/projects/box3d/sample_compound.cpp

804 lines
22 KiB
C++

// SPDX-FileCopyrightText: 2025 Erin Catto
// SPDX-License-Identifier: MIT
#include "gfx/debug_adapter.h"
#include "gfx/draw.h"
#include "human.h"
#include "mesh_loader.h"
#include "sample.h"
#include "utils.h"
#include "box3d/box3d.h"
#include <imgui.h>
class SimpleCompound : public Sample
{
public:
explicit SimpleCompound( SampleContext* context )
: Sample( context )
{
if ( m_context->restart == false )
{
m_camera->SetView( 45.0f, 30.0f, 45.0f, b3Pos_zero );
}
{
float a = 4.0f;
b3Vec3 extents = { a, 0.125f * a, a };
b3SurfaceMaterial material = b3DefaultSurfaceMaterial();
b3BoxHull box = b3MakeBoxHull( extents.x, extents.y, extents.z );
b3Transform hullTransform;
hullTransform.p = { 1.0f, -0.125f * a, 0.0f };
hullTransform.q = b3MakeQuatFromAxisAngle( b3Normalize( { 1.0f, 0.0f, 1.0f } ), 0.0f * B3_PI );
b3CompoundHullDef hullDef = {
.hull = &box.base,
.transform = hullTransform,
.material = material,
};
b3CompoundDef def = {};
def.hulls = &hullDef;
def.hullCount = 1;
m_compound = b3CreateCompound( &def );
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.position = { 2.0f, -1.0f, 0.0f };
bodyDef.rotation = b3MakeQuatFromAxisAngle( { 0.0f, 1.0f, 0.0f }, 0.25f * B3_PI );
b3BodyId groundId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
(void)b3CreateBakedCompoundShape( groundId, &shapeDef, m_compound );
}
b3World_SetContactRecycleDistance( m_worldId, 0.0f );
{
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.type = b3_dynamicBody;
bodyDef.position = { 0.0f, 2.0f, 0.0f };
b3BodyId bodyId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
b3Sphere sphere = { b3Vec3_zero, 0.25f };
b3CreateSphereShape( bodyId, &shapeDef, &sphere );
}
//{
// b3BodyDef bodyDef = b3DefaultBodyDef();
// bodyDef.type = b3_dynamicBody;
// bodyDef.position = { 0.0f, 10.0f, 2.0f };
// b3BodyId bodyId = b3CreateBody( m_worldId, &bodyDef );
// b3ShapeDef shapeDef = b3DefaultShapeDef();
// b3BoxHull box = b3MakeBoxHull( 0.2f, 0.3f, 0.4f );
// b3CreateHullShape( bodyId, &shapeDef, &box.base );
//}
m_launchSpeedScale = 1.0f;
}
~SimpleCompound() override
{
b3DestroyCompound( m_compound );
}
void Render() override
{
Sample::Render();
b3Transform transform = { { 0.0f, 0.01f, 0.0f }, b3Quat_identity };
DrawAxes( b3MakeWorldTransform( transform ), 1.0f );
int height = b3DynamicTree_GetHeight( &m_compound->tree );
DrawTextLine( "compound tree height = %d", height );
}
static Sample* Create( SampleContext* context )
{
return new SimpleCompound( context );
}
b3CompoundData* m_compound;
};
static int sampleSimple = RegisterSample( "Compound", "Simple", SimpleCompound::Create );
class CompoundSpheres : public Sample
{
public:
explicit CompoundSpheres( SampleContext* context )
: Sample( context )
{
if ( m_context->restart == false )
{
m_camera->SetView( 45.0f, 30.0f, 45.0f, b3Pos_zero );
}
float h = 10.0f;
b3Vec3 lower = { -h, -h, -h };
b3Vec3 upper = { h, h, h };
b3CompoundSphereDef spheres[m_count];
for ( int i = 0; i < m_count; ++i )
{
spheres[i].sphere.center = RandomVec3( lower, upper );
spheres[i].sphere.radius = RandomFloatRange( 0.01f * h, 0.05f * h );
spheres[i].material = b3DefaultSurfaceMaterial();
}
b3CompoundDef def = {};
def.spheres = spheres;
def.sphereCount = m_count;
m_compound = b3CreateCompound( &def );
b3BodyDef bodyDef = b3DefaultBodyDef();
b3BodyId groundId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
(void)b3CreateBakedCompoundShape( groundId, &shapeDef, m_compound );
}
~CompoundSpheres() override
{
b3DestroyCompound( m_compound );
}
void Render() override
{
Sample::Render();
b3Transform transform = { { 0.0f, 0.01f, 0.0f }, b3Quat_identity };
DrawAxes( b3MakeWorldTransform( transform ), 1.0f );
int height = b3DynamicTree_GetHeight( &m_compound->tree );
DrawTextLine( "compound tree height = %d", height );
}
static Sample* Create( SampleContext* context )
{
return new CompoundSpheres( context );
}
static constexpr int m_count = 20;
b3CompoundData* m_compound;
};
static int sampleCompoundSpheres = RegisterSample( "Compound", "Spheres", CompoundSpheres::Create );
class CompoundHulls : public Sample
{
public:
explicit CompoundHulls( SampleContext* context )
: Sample( context )
{
if ( m_context->restart == false )
{
m_camera->SetView( 45.0f, 30.0f, 45.0f, b3Pos_zero );
}
float h = 10.0f;
b3Vec3 lower = { -h, -h, -h };
b3Vec3 upper = { h, h, h };
b3SurfaceMaterial material = b3DefaultSurfaceMaterial();
b3BoxHull boxHulls[m_count];
b3CompoundHullDef hulls[m_count];
for ( int i = 0; i < m_count; ++i )
{
b3Vec3 extents = {
.x = RandomFloatRange( 0.01f * h, 0.05f * h ),
.y = RandomFloatRange( 0.01f * h, 0.05f * h ),
.z = RandomFloatRange( 0.01f * h, 0.05f * h ),
};
b3Transform transform;
transform.p = RandomVec3( lower, upper );
transform.q = RandomQuat();
boxHulls[i] = b3MakeBoxHull( extents.x, extents.y, extents.z );
hulls[i].hull = &boxHulls[i].base;
hulls[i].transform = transform;
hulls[i].material = material;
}
b3CompoundDef def = {};
def.hulls = hulls;
def.hullCount = m_count;
m_compound = b3CreateCompound( &def );
b3BodyDef bodyDef = b3DefaultBodyDef();
b3BodyId groundId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
(void)b3CreateBakedCompoundShape( groundId, &shapeDef, m_compound );
}
~CompoundHulls() override
{
b3DestroyCompound( m_compound );
}
void Render() override
{
Sample::Render();
b3Transform transform = { { 0.0f, 0.01f, 0.0f }, b3Quat_identity };
DrawAxes( b3MakeWorldTransform( transform ), 1.0f );
int height = b3DynamicTree_GetHeight( &m_compound->tree );
DrawTextLine( "compound tree height = %d", height );
}
static Sample* Create( SampleContext* context )
{
return new CompoundHulls( context );
}
static constexpr int m_count = 20;
b3CompoundData* m_compound;
};
static int sampleCompoundHulls = RegisterSample( "Compound", "Hulls", CompoundHulls::Create );
class TileFloor : public Sample
{
public:
explicit TileFloor( SampleContext* context )
: Sample( context )
{
if ( m_context->restart == false )
{
m_camera->SetView( 45.0f, 30.0f, 45.0f, b3Pos_zero );
}
{
constexpr int gridCount = 50;
constexpr int boxCount = gridCount * gridCount;
float a = 4.0f;
b3Vec3 extents = { a, 0.5f * a, a };
b3SurfaceMaterial material = b3DefaultSurfaceMaterial();
b3BoxHull box = b3MakeBoxHull( extents.x, extents.y, extents.z );
// Must allocate to avoid stack overflow
b3CompoundHullDef* hulls = new b3CompoundHullDef[boxCount];
b3Transform transform = b3Transform_identity;
int index = 0;
for ( int i = 0; i < gridCount; ++i )
{
transform.p.x = ( 2.0f * i - gridCount ) * a;
for ( int j = 0; j < gridCount; ++j )
{
transform.p.z = ( 2.0f * j - gridCount ) * a;
transform.p.y = RandomFloatRange( -0.5f, 0.25f ) * a;
assert( index < boxCount );
hulls[index].hull = &box.base;
hulls[index].transform = transform;
hulls[index].material = material;
index += 1;
}
}
b3CompoundDef def = {};
def.hulls = hulls;
def.hullCount = boxCount;
m_compound = b3CreateCompound( &def );
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.position = { -2.0f, 1.0f, -3.0f };
bodyDef.rotation = b3MakeQuatFromAxisAngle( b3Normalize( { 1.0f, -1.0f, 0.5f } ), 0.0f );
b3BodyId groundId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
(void)b3CreateBakedCompoundShape( groundId, &shapeDef, m_compound );
delete[] hulls;
hulls = nullptr;
}
// b3World_SetContactRecycleDistance( m_worldId, 0.0f );
{
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.type = b3_dynamicBody;
bodyDef.position = { 3.0f, 12.0f, 0.0f };
b3BodyId bodyId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
b3Sphere sphere = { b3Vec3_zero, 0.25f };
b3CreateSphereShape( bodyId, &shapeDef, &sphere );
}
#if 0
{
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.type = b3_dynamicBody;
bodyDef.position = { 0.0f, 10.0f, 2.0f };
b3BodyId bodyId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
b3BoxHull box = b3MakeBoxHull( 0.2f, 0.3f, 0.4f );
b3CreateHullShape( bodyId, &shapeDef, &box.base );
}
#endif
}
~TileFloor() override
{
b3DestroyCompound( m_compound );
}
void Render() override
{
Sample::Render();
b3Transform transform = { { 0.0f, 0.01f, 0.0f }, b3Quat_identity };
DrawAxes( b3MakeWorldTransform( transform ), 1.0f );
DrawTextLine( "compound hull count = %d, mesh count = %d", m_compound->hullCount, m_compound->meshCount );
DrawTextLine( "compound byte count = %d", m_compound->byteCount );
int treeBytes = b3DynamicTree_GetByteCount( &m_compound->tree );
int height = b3DynamicTree_GetHeight( &m_compound->tree );
DrawTextLine( "compound tree byte count = %d, height = %d", treeBytes, height );
}
static Sample* Create( SampleContext* context )
{
return new TileFloor( context );
}
b3CompoundData* m_compound;
};
static int sampleTileFloor = RegisterSample( "Compound", "Tile Floor", TileFloor::Create );
class MeshTile : public Sample
{
public:
explicit MeshTile( SampleContext* context )
: Sample( context )
{
if ( m_context->restart == false )
{
m_camera->SetView( 45.0f, 30.0f, 45.0f, b3Pos_zero );
}
{
constexpr int gridCount = 2;
constexpr int boxCount = gridCount * gridCount;
float a = 4.0f;
b3Vec3 extents = { a, 0.5f * a, a };
b3SurfaceMaterial material = b3DefaultSurfaceMaterial();
b3MeshData* box = b3CreateBoxMesh( { 0.0f, 0.0f, 0.0f }, extents, true );
b3CompoundMeshDef* meshes = new b3CompoundMeshDef[boxCount];
b3Transform transform = b3Transform_identity;
transform.p.y = -0.5f * a;
int index = 0;
for ( int i = 0; i < gridCount; ++i )
{
transform.p.x = ( 2.0f * i - gridCount ) * a;
for ( int j = 0; j < gridCount; ++j )
{
transform.p.z = ( 2.0f * j - gridCount ) * a;
transform.p.y = RandomFloatRange( -0.5f, 0.25f ) * a;
assert( index < boxCount );
meshes[index].meshData = box;
meshes[index].transform = transform;
meshes[index].scale = { 1.0f, 1.0f, 1.0f };
meshes[index].materials = &material;
meshes[index].materialCount = 1;
index += 1;
}
}
b3CompoundDef def = {};
def.meshes = meshes;
def.meshCount = boxCount;
m_compound = b3CreateCompound( &def );
delete[] meshes;
b3DestroyMesh( box );
}
{
b3BodyDef bodyDef = b3DefaultBodyDef();
b3BodyId groundId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
(void)b3CreateBakedCompoundShape( groundId, &shapeDef, m_compound );
}
#if 0
{
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.type = b3_dynamicBody;
bodyDef.position = { 3.0f, 12.0f, 0.0f };
b3BodyId bodyId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
b3Sphere sphere = { b3Vec3_zero, 0.25f };
b3CreateSphereShape( bodyId, &shapeDef, &sphere );
}
#endif
//{
// b3BodyDef bodyDef = b3DefaultBodyDef();
// bodyDef.type = b3_dynamicBody;
// bodyDef.position = { 0.0f, 10.0f, 2.0f };
// b3BodyId bodyId = b3CreateBody( m_worldId, &bodyDef );
// b3ShapeDef shapeDef = b3DefaultShapeDef();
// b3BoxHull box = b3MakeBoxHull( 0.2f, 0.3f, 0.4f );
// b3CreateHullShape( bodyId, &shapeDef, &box.base );
//}
}
~MeshTile() override
{
b3DestroyCompound( m_compound );
}
void Render() override
{
Sample::Render();
b3Transform transform = { { 0.0f, 0.01f, 0.0f }, b3Quat_identity };
DrawAxes( b3MakeWorldTransform( transform ), 1.0f );
DrawTextLine( "compound instance count = %d, byte count = %d", m_compound->meshCount, m_compound->byteCount );
int treeBytes = b3DynamicTree_GetByteCount( &m_compound->tree );
int height = b3DynamicTree_GetHeight( &m_compound->tree );
DrawTextLine( "compound tree byte count = %d, height = %d", treeBytes, height );
}
static Sample* Create( SampleContext* context )
{
return new MeshTile( context );
}
b3CompoundData* m_compound;
};
static int sampleCompoundMesh = RegisterSample( "Compound", "Mesh Tile", MeshTile::Create );
static bool OverlapResultFcn( b3ShapeId shapeId, void* context )
{
(void)shapeId;
bool* overlap = (bool*)context;
*overlap = true;
return false;
}
class Village : public Sample
{
public:
explicit Village( SampleContext* context )
: Sample( context )
{
constexpr int gridCount = m_isDebug ? 8 : 200;
constexpr float a = 4.0f;
m_worldWidth = 2.0f * gridCount * a;
b3Pos position = { 0.0f, 10.0f, 0.0f };
if ( m_context->restart == false )
{
m_camera->SetView( 45.0f, 10.0f, 5.0f, position );
}
m_launchSpeedScale = 2.0f;
m_mover.Initialize( this, position );
{
constexpr int capsuleCapacity = gridCount * gridCount / 8 + 1;
constexpr int hullCount = gridCount * gridCount;
constexpr int sphereCapacity = gridCount * gridCount / 8 + 1;
b3Vec3 extents = { a, 0.5f * a, a };
b3SurfaceMaterial material = b3DefaultSurfaceMaterial();
b3BoxHull box = b3MakeBoxHull( extents.x, extents.y, extents.z );
// Must allocate to avoid stack overflow
b3CompoundCapsuleDef* capsules = new b3CompoundCapsuleDef[capsuleCapacity];
b3CompoundHullDef* hulls = new b3CompoundHullDef[hullCount];
b3CompoundSphereDef* spheres = new b3CompoundSphereDef[sphereCapacity];
b3Transform transform = b3Transform_identity;
int capsuleIndex = 0;
int hullIndex = 0;
int sphereIndex = 0;
for ( int i = 0; i < gridCount; ++i )
{
transform.p.x = ( 2.0f * i - gridCount ) * a;
for ( int j = 0; j < gridCount; ++j )
{
transform.p.z = ( 2.0f * j - gridCount ) * a;
transform.p.y = RandomFloatRange( -0.25f, 0.125f ) * a;
assert( hullIndex < hullCount );
if ( ( i & 1 ) && ( j & 1 ) )
{
b3Vec3 p1 = transform.p + RandomVec3( { -a, a, -a }, { a, 2.0f * a, a } );
b3Vec3 p2 = transform.p + RandomVec3( { -a, a, -a }, { a, 2.0f * a, a } );
float radius = RandomFloatRange( 0.1f, 0.5f );
if ( capsuleIndex < sphereIndex )
{
assert( capsuleIndex < capsuleCapacity );
capsules[capsuleIndex].capsule = { p1, p2, radius };
capsules[capsuleIndex].material = material;
capsuleIndex += 1;
}
else
{
assert( sphereIndex < capsuleCapacity );
spheres[sphereIndex].sphere = { p1, radius };
spheres[sphereIndex].material = material;
sphereIndex += 1;
}
}
hulls[hullIndex].hull = &box.base;
hulls[hullIndex].transform = transform;
hulls[hullIndex].material = material;
hullIndex += 1;
}
}
assert( capsuleIndex <= capsuleCapacity );
assert( hullIndex == hullCount );
assert( sphereIndex <= sphereCapacity );
constexpr int meshGridCount = gridCount / 4;
constexpr int meshCount = meshGridCount * meshGridCount;
constexpr float b = 4.0f * a;
constexpr int materialCapacity = 5;
b3SurfaceMaterial meshMaterials[materialCapacity];
b3MeshData* buildingMesh = CreateMeshData( "data/meshes/building.obj", 1.0f, false, false, true, true );
int materialCount = buildingMesh->materialCount;
assert( materialCount <= materialCapacity );
for ( int i = 0; i < materialCount; ++i )
{
meshMaterials[i] = b3DefaultSurfaceMaterial();
if ( i == 0 )
{
meshMaterials[i].friction = 0.0f;
}
else if ( i == 1 )
{
meshMaterials[i].restitution = 0.5f;
}
meshMaterials[i].userMaterialId = i + 42;
}
b3CompoundMeshDef* meshes = new b3CompoundMeshDef[meshCount];
transform = b3Transform_identity;
int meshIndex = 0;
for ( int i = 0; i < meshGridCount; ++i )
{
transform.p.x = ( 2.0f * i - meshGridCount ) * b + 0.5f * b;
for ( int j = 0; j < meshGridCount; ++j )
{
transform.p.y = 0.5f * a;
transform.p.z = ( 2.0f * j - meshGridCount ) * b + 0.5f * b;
transform.q = b3MakeQuatFromAxisAngle( { 0.0f, 1.0f, 0.0 }, RandomFloatRange( -B3_PI, B3_PI ) );
assert( meshIndex < meshCount );
meshes[meshIndex].meshData = buildingMesh;
meshes[meshIndex].transform = transform;
meshes[meshIndex].scale = RandomVec3( { 0.5f, 0.5f, 0.5f }, { 2.0f, 2.0f, 2.0f } );
if ( meshIndex & 1 )
{
meshes[meshIndex].scale.x = -meshes[meshIndex].scale.x;
}
if ( meshIndex & 3 )
{
meshes[meshIndex].scale.z = -meshes[meshIndex].scale.z;
}
meshes[meshIndex].materials = meshMaterials;
meshes[meshIndex].materialCount = materialCount;
meshIndex += 1;
}
}
assert( meshIndex == meshCount );
b3CompoundDef def = {};
def.capsules = capsules;
def.capsuleCount = capsuleIndex;
def.hullCount = hullCount;
def.hulls = hulls;
def.hullCount = hullCount;
def.meshes = meshes;
def.meshCount = meshCount;
def.spheres = spheres;
def.sphereCount = sphereIndex;
m_compound = b3CreateCompound( &def );
b3BodyDef bodyDef = b3DefaultBodyDef();
bodyDef.position = { -1.0f, -0.5f, 2.0f };
bodyDef.rotation = b3MakeQuatFromAxisAngle( { 0.0f, 1.0f, 0.0f }, -1.15f * B3_PI );
b3BodyId groundId = b3CreateBody( m_worldId, &bodyDef );
b3ShapeDef shapeDef = b3DefaultShapeDef();
(void)b3CreateBakedCompoundShape( groundId, &shapeDef, m_compound );
delete[] capsules;
capsules = nullptr;
delete[] hulls;
hulls = nullptr;
delete[] meshes;
meshes = nullptr;
delete[] spheres;
spheres = nullptr;
b3DestroyMesh( buildingMesh );
}
m_rayOrigin = { -0.45f * m_worldWidth, 20.0f, -0.45f * m_worldWidth };
}
~Village() override
{
b3DestroyCompound( m_compound );
}
void Keyboard( int key, int action, int mods ) override
{
if ( key == 'T' && action == 1 )
{
ToggleThirdPerson();
}
}
void Render() override
{
Sample::Render();
b3Transform transform = { { 0.0f, 0.01f, 0.0f }, b3Quat_identity };
DrawAxes( b3MakeWorldTransform( transform ), 4.0f );
DrawTextLine( "surface type = %d", m_userMaterialId );
DrawTextLine( "compound capsules/hulls/meshes/sphere = %d / %d / %d / %d", m_compound->capsuleCount,
m_compound->hullCount, m_compound->meshCount, m_compound->sphereCount );
DrawTextLine( "compound byte count = %d", m_compound->byteCount );
int treeBytes = b3DynamicTree_GetByteCount( &m_compound->tree );
int height = b3DynamicTree_GetHeight( &m_compound->tree );
DrawTextLine( "compound tree byte count = %d, height = %d", treeBytes, height );
int total = 0;
int drawn = GetLastCompoundDrawStats( &total );
DrawTextLine( "compound children drawn = %d / %d", drawn, total );
}
void Step() override
{
m_mover.Step( nullptr, 0, true );
DrawTextLine( "third person (T) = %d", m_camera->m_thirdPerson );
b3Vec3 translation = { 10.0f, -40.0f, -5.0f };
b3QueryFilter filter = b3DefaultQueryFilter();
{
CastClosestContext context = {};
(void)b3World_CastRay( m_worldId, m_rayOrigin, translation, filter, CastClosestCallback, &context );
DrawLine( m_rayOrigin, b3OffsetPos( m_rayOrigin, translation ), MakeColor( b3_colorAliceBlue ) );
if ( context.hit )
{
b3Pos p1 = context.point;
b3Pos p2 = p1 + 0.5f * context.normal;
DrawLine( p1, p2, MakeColor( b3_colorYellow ) );
DrawPoint( p1, 8.0f, MakeColor( b3_colorLightCoral ) );
DrawTextLine( "ray hit triangle/child/material = %d / %d / %d", context.triangleIndex, context.childIndex,
context.materialId );
}
else
{
DrawTextLine( "ray miss" );
}
}
{
CastClosestContext context = {};
b3Pos origin = m_rayOrigin - b3Vec3{ 1.0f, 0.0f, 1.0f };
b3ShapeProxy proxy = { &b3Vec3_zero, 1, 0.25f };
b3World_CastShape( m_worldId, origin, &proxy, translation, filter, CastClosestCallback, &context );
DrawLine( origin, b3OffsetPos( origin, translation ), MakeColor( b3_colorAliceBlue ) );
if ( context.hit )
{
b3Pos position = b3OffsetPos( origin, context.fraction * translation );
b3Pos p1 = context.point;
b3Pos p2 = p1 + 0.5f * context.normal;
DrawLine( p1, p2, MakeColor( b3_colorYellow ) );
DrawPoint( p1, 8.0f, MakeColor( b3_colorLightCoral ) );
b3Sphere sphere = { b3Vec3_zero, 0.25f };
DrawSolidSphere( { position, b3Quat_identity }, sphere, MakeColor( b3_colorOrchid ) );
DrawTextLine( "shape hit triangle/child/material = %d / %d / %d", context.triangleIndex, context.childIndex,
context.materialId );
}
else
{
DrawTextLine( "shape miss" );
}
}
{
bool overlap = false;
b3Pos origin = { m_rayOrigin.x - 1.0f, 2.0f, m_rayOrigin.z - 1.0f };
b3ShapeProxy proxy = { &b3Vec3_zero, 1, 0.3f };
b3World_OverlapShape( m_worldId, origin, &proxy, filter, OverlapResultFcn, &overlap );
b3HexColor color = overlap ? b3_colorDarkMagenta : b3_colorDarkSeaGreen;
b3Sphere sphere = { b3Vec3_zero, 0.3f };
DrawSolidSphere( { origin, b3Quat_identity }, sphere, MakeColor( color ) );
}
if ( m_rayOrigin.x > 0.45f * m_worldWidth )
{
m_rayOrigin.x = -0.45f * m_worldWidth;
m_rayOrigin.z += 8.0f;
}
if ( m_rayOrigin.z > 0.45f * m_worldWidth )
{
m_rayOrigin.z = -0.45f * m_worldWidth;
}
float timeStep = 0.0f;
if ( m_context->pause == false || m_context->singleStep > 0 )
{
timeStep = m_context->hertz > 0.0f ? 1.0f / m_context->hertz : 0.0f;
}
m_rayOrigin.x += 2.0f * timeStep;
Sample::Step();
}
static Sample* Create( SampleContext* context )
{
return new Village( context );
}
b3CompoundData* m_compound;
CharacterMover m_mover;
float m_worldWidth;
b3Pos m_rayOrigin;
};
static int sampleVillage = RegisterSample( "Compound", "Village", Village::Create );