Committing height field terrain work from tomva1@yahoo.com
This commit is contained in:
@@ -32,6 +32,7 @@ subject to the following restrictions:
|
||||
#include "../ConstraintDemo/ConstraintDemo.h"
|
||||
#include "../Benchmarks/BenchmarkDemo.h"
|
||||
#include "../SoftDemo/SoftDemo.h"
|
||||
#include "../TerrainDemo/TerrainDemo.h"
|
||||
|
||||
|
||||
#include "GlutStuff.h"//OpenGL stuff
|
||||
@@ -145,6 +146,7 @@ btDemoEntry g_demoEntries[] =
|
||||
{"Raytracer Test",Raytracer::Create},
|
||||
{"GjkConvexCast",LinearConvexCastDemo::Create},
|
||||
{"VehicleDemo",VehicleDemo::Create},
|
||||
{"TerrainDemo",btCreateTerrainDemo},
|
||||
{"Benchmark 3000 FALL",BenchmarkDemo1::Create},
|
||||
{"Benchmark 1000 STACK",BenchmarkDemo2::Create},
|
||||
{"Benchmark 136 RAGDOLLS",BenchmarkDemo3::Create},
|
||||
|
||||
@@ -21,4 +21,5 @@ FrameWorkDemo AllBulletDemos :
|
||||
../VehicleDemo/VehicleDemo.cpp
|
||||
../SoftDemo/SoftDemo.cpp
|
||||
../ConstraintDemo/ConstraintDemo.cpp
|
||||
../TerrainDemo/TerrainDemo.cpp
|
||||
;
|
||||
|
||||
@@ -19,6 +19,7 @@ AllBulletDemo_SOURCES=\
|
||||
../GjkConvexCastDemo/LinearConvexCastDemo.cpp \
|
||||
../ConcaveDemo/ConcavePhysicsDemo.cpp \
|
||||
../DynamicControlDemo/MotorDemo.cpp \
|
||||
../TerrainDemo/TerrainDemo.cpp \
|
||||
DemoEntries.cpp \
|
||||
DemoEntries.h\
|
||||
Main.cpp
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
if (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||
SUBDIRS( OpenGL AllBulletDemos ConvexDecompositionDemo Benchmarks HelloWorld CcdPhysicsDemo ConstraintDemo SliderConstraintDemo GenericJointDemo RagdollDemo ForkLiftDemo BasicDemo BspDemo MovingConcaveDemo VehicleDemo ColladaDemo UserCollisionAlgorithm CharacterDemo SoftDemo )
|
||||
SUBDIRS( OpenGL AllBulletDemos ConvexDecompositionDemo Benchmarks HelloWorld
|
||||
CcdPhysicsDemo ConstraintDemo SliderConstraintDemo GenericJointDemo
|
||||
RagdollDemo ForkLiftDemo BasicDemo BspDemo MovingConcaveDemo VehicleDemo
|
||||
ColladaDemo UserCollisionAlgorithm CharacterDemo SoftDemo TerrainDemo )
|
||||
else (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||
SUBDIRS( OpenGL AllBulletDemos ConvexDecompositionDemo Benchmarks HelloWorld MultiThreadedDemo CcdPhysicsDemo ConstraintDemo SliderConstraintDemo GenericJointDemo RagdollDemo ForkLiftDemo BasicDemo BspDemo MovingConcaveDemo VehicleDemo ColladaDemo UserCollisionAlgorithm CharacterDemo SoftDemo )
|
||||
SUBDIRS( OpenGL AllBulletDemos ConvexDecompositionDemo Benchmarks HelloWorld
|
||||
MultiThreadedDemo CcdPhysicsDemo ConstraintDemo SliderConstraintDemo
|
||||
GenericJointDemo RagdollDemo ForkLiftDemo BasicDemo BspDemo MovingConcaveDemo
|
||||
VehicleDemo ColladaDemo UserCollisionAlgorithm CharacterDemo SoftDemo
|
||||
TerrainDemo )
|
||||
endif (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||
|
||||
@@ -101,4 +101,5 @@ SubInclude TOP Demos GjkConvexCastDemo ;
|
||||
SubInclude TOP Demos Raytracer ;
|
||||
SubInclude TOP Demos SimplexDemo ;
|
||||
SubInclude TOP Demos DoublePrecisionDemo ;
|
||||
SubInclude TOP Demos TerrainDemo ;
|
||||
|
||||
|
||||
3
Demos/TerrainDemo/Jamfile
Normal file
3
Demos/TerrainDemo/Jamfile
Normal file
@@ -0,0 +1,3 @@
|
||||
SubDir TOP Demos TerrainDemo ;
|
||||
|
||||
BulletDemo TerrainDemo : [ Wildcard *.h *.cpp ] ;
|
||||
5
Demos/TerrainDemo/Makefile.am
Normal file
5
Demos/TerrainDemo/Makefile.am
Normal file
@@ -0,0 +1,5 @@
|
||||
noinst_PROGRAMS=TerrainDemo
|
||||
|
||||
TerrainDemo_SOURCES=TerrainDemo.cpp TerrainDemo.h main.cpp
|
||||
TerrainDemo_CXXFLAGS=-I@top_builddir@/src -I@top_builddir@/Demos/OpenGL $(CXXFLAGS)
|
||||
TerrainDemo_LDADD=-L../OpenGL -lbulletopenglsupport -L../../src -lbulletdynamics -lbulletcollision -lbulletmath @opengl_LIBS@
|
||||
926
Demos/TerrainDemo/TerrainDemo.cpp
Normal file
926
Demos/TerrainDemo/TerrainDemo.cpp
Normal file
@@ -0,0 +1,926 @@
|
||||
/*
|
||||
Bullet Continuous Collision Detection and Physics Library
|
||||
Copyright (c) 2003-2006,2008 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it freely,
|
||||
subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
|
||||
#include "TerrainDemo.h" // always include our own header first!
|
||||
|
||||
#include "btBulletDynamicsCommon.h"
|
||||
#include "BulletCollision/CollisionShapes/btHeightfieldTerrainShape.h"
|
||||
|
||||
#include "GLDebugDrawer.h"
|
||||
|
||||
#include "GL_ShapeDrawer.h"
|
||||
|
||||
#include "GlutStuff.h"
|
||||
|
||||
#include "BMF_Api.h"
|
||||
|
||||
|
||||
|
||||
// constants -------------------------------------------------------------------
|
||||
static const float s_gravity = 9.8; // 9.8 m/s^2
|
||||
|
||||
static const int s_gridSize = 64 + 1; // must be (2^N) + 1
|
||||
static const float s_gridSpacing = 5.0;
|
||||
|
||||
static const float s_gridHeightScale = 0.2;
|
||||
|
||||
// the singularity at the center of the radial model means we need a lot of
|
||||
// finely-spaced time steps to get the physics right.
|
||||
// These numbers are probably too aggressive for a real game!
|
||||
static const int s_requestedHz = 180;
|
||||
static const float s_engineTimeStep = 1.0 / s_requestedHz;
|
||||
|
||||
// delta phase: radians per second
|
||||
static const float s_deltaPhase = 0.25 * 2.0 * SIMD_PI;
|
||||
|
||||
// what type of terrain is generated?
|
||||
enum eTerrainModel {
|
||||
eRadial = 1, // deterministic
|
||||
eFractal = 2 // random
|
||||
};
|
||||
|
||||
|
||||
typedef unsigned char byte_t;
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// static helper methods
|
||||
//
|
||||
// Only used within this file (helpers and terrain generation, etc)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const char *
|
||||
getTerrainTypeName
|
||||
(
|
||||
eTerrainModel model
|
||||
)
|
||||
{
|
||||
switch (model) {
|
||||
case eRadial:
|
||||
return "Radial";
|
||||
|
||||
case eFractal:
|
||||
return "Fractal";
|
||||
|
||||
default:
|
||||
btAssert(!"bad terrain model type");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char *
|
||||
getDataTypeName
|
||||
(
|
||||
PHY_ScalarType type
|
||||
)
|
||||
{
|
||||
switch (type) {
|
||||
case PHY_UCHAR:
|
||||
return "UnsignedChar";
|
||||
|
||||
case PHY_SHORT:
|
||||
return "Short";
|
||||
|
||||
case PHY_FLOAT:
|
||||
return "Float";
|
||||
|
||||
default:
|
||||
btAssert(!"bad heightfield data type");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static const char *
|
||||
getUpAxisName
|
||||
(
|
||||
int axis
|
||||
)
|
||||
{
|
||||
switch (axis) {
|
||||
case 0:
|
||||
return "X";
|
||||
|
||||
case 1:
|
||||
return "Y";
|
||||
|
||||
case 2:
|
||||
return "Z";
|
||||
|
||||
default:
|
||||
btAssert(!"bad up axis");
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static btVector3
|
||||
getUpVector
|
||||
(
|
||||
int upAxis,
|
||||
btScalar regularValue,
|
||||
btScalar upValue
|
||||
)
|
||||
{
|
||||
btAssert(upAxis >= 0 && upAxis <= 2 && "bad up axis");
|
||||
|
||||
btVector3 v(regularValue, regularValue, regularValue);
|
||||
v[upAxis] = upValue;
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: it would probably cleaner to have a struct per data type, so
|
||||
// you could lookup byte sizes, conversion functions, etc.
|
||||
static int getByteSize
|
||||
(
|
||||
PHY_ScalarType type
|
||||
)
|
||||
{
|
||||
int size = 0;
|
||||
|
||||
switch (type) {
|
||||
case PHY_FLOAT:
|
||||
size = sizeof(float);
|
||||
break;
|
||||
|
||||
case PHY_UCHAR:
|
||||
size = sizeof(unsigned char);
|
||||
break;
|
||||
|
||||
case PHY_SHORT:
|
||||
size = sizeof(short);
|
||||
break;
|
||||
|
||||
default:
|
||||
btAssert(!"Bad heightfield data type");
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static float
|
||||
convertToFloat
|
||||
(
|
||||
const byte_t * p,
|
||||
PHY_ScalarType type
|
||||
)
|
||||
{
|
||||
btAssert(p);
|
||||
|
||||
switch (type) {
|
||||
case PHY_FLOAT:
|
||||
{
|
||||
float * pf = (float *) p;
|
||||
return *pf;
|
||||
}
|
||||
|
||||
case PHY_UCHAR:
|
||||
{
|
||||
unsigned char * pu = (unsigned char *) p;
|
||||
return ((*pu) * s_gridHeightScale);
|
||||
}
|
||||
|
||||
case PHY_SHORT:
|
||||
{
|
||||
short * ps = (short *) p;
|
||||
return ((*ps) * s_gridHeightScale);
|
||||
}
|
||||
|
||||
default:
|
||||
btAssert(!"bad type");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static float
|
||||
getGridHeight
|
||||
(
|
||||
byte_t * grid,
|
||||
int i,
|
||||
int j,
|
||||
PHY_ScalarType type
|
||||
)
|
||||
{
|
||||
btAssert(grid);
|
||||
btAssert(i >= 0 && i < s_gridSize);
|
||||
btAssert(j >= 0 && j < s_gridSize);
|
||||
|
||||
int bpe = getByteSize(type);
|
||||
btAssert(bpe > 0 && "bad bytes per element");
|
||||
|
||||
int idx = (j * s_gridSize) + i;
|
||||
long offset = ((long) bpe) * idx;
|
||||
|
||||
byte_t * p = grid + offset;
|
||||
|
||||
return convertToFloat(p, type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
convertFromFloat
|
||||
(
|
||||
byte_t * p,
|
||||
float value,
|
||||
PHY_ScalarType type
|
||||
)
|
||||
{
|
||||
btAssert(p && "null");
|
||||
|
||||
switch (type) {
|
||||
case PHY_FLOAT:
|
||||
{
|
||||
float * pf = (float *) p;
|
||||
*pf = value;
|
||||
}
|
||||
break;
|
||||
|
||||
case PHY_UCHAR:
|
||||
{
|
||||
unsigned char * pu = (unsigned char *) p;
|
||||
*pu = (unsigned char) (value / s_gridHeightScale);
|
||||
}
|
||||
break;
|
||||
|
||||
case PHY_SHORT:
|
||||
{
|
||||
short * ps = (short *) p;
|
||||
*ps = (short) (value / s_gridHeightScale);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
btAssert(!"bad type");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// creates a radially-varying heightfield
|
||||
static void
|
||||
setRadial
|
||||
(
|
||||
byte_t * grid,
|
||||
int bytesPerElement,
|
||||
PHY_ScalarType type,
|
||||
float phase = 0.0
|
||||
)
|
||||
{
|
||||
btAssert(grid);
|
||||
btAssert(bytesPerElement > 0);
|
||||
|
||||
// min/max
|
||||
float period = 0.5 / s_gridSpacing;
|
||||
float floor = 0.0;
|
||||
float min_r = 3.0 * sqrt(s_gridSpacing);
|
||||
float magnitude = 50.0 * sqrt(s_gridSpacing);
|
||||
|
||||
// pick a base_phase such that phase = 0 results in max height
|
||||
// (this way, if you create a heightfield with phase = 0,
|
||||
// you can rely on the min/max heights that result)
|
||||
float base_phase = (0.5 * SIMD_PI) - (period * min_r);
|
||||
phase += base_phase;
|
||||
|
||||
// center of grid
|
||||
float cx = 0.5 * s_gridSize * s_gridSpacing;
|
||||
float cy = cx; // assume square grid
|
||||
byte_t * p = grid;
|
||||
for (int i = 0; i < s_gridSize; ++i) {
|
||||
float x = i * s_gridSpacing;
|
||||
for (int j = 0; j < s_gridSize; ++j) {
|
||||
float y = j * s_gridSpacing;
|
||||
|
||||
float dx = x - cx;
|
||||
float dy = y - cy;
|
||||
|
||||
float r = sqrt((dx * dx) + (dy * dy));
|
||||
|
||||
float z = period;
|
||||
if (r < min_r) {
|
||||
r = min_r;
|
||||
}
|
||||
z = (1.0 / r) * sin(period * r + phase);
|
||||
if (z > period) {
|
||||
z = period;
|
||||
} else if (z < -period) {
|
||||
z = -period;
|
||||
}
|
||||
z = floor + magnitude * z;
|
||||
|
||||
convertFromFloat(p, z, type);
|
||||
p += bytesPerElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static float
|
||||
randomHeight
|
||||
(
|
||||
int step
|
||||
)
|
||||
{
|
||||
return (0.33 * s_gridSpacing * s_gridSize * step * (rand() - (0.5 * RAND_MAX))) / (1.0 * RAND_MAX * s_gridSize);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
dumpGrid
|
||||
(
|
||||
const byte_t * grid,
|
||||
int bytesPerElement,
|
||||
PHY_ScalarType type,
|
||||
int max
|
||||
)
|
||||
{
|
||||
//std::cerr << "Grid:\n";
|
||||
|
||||
char buffer[32];
|
||||
|
||||
for (int j = 0; j < max; ++j) {
|
||||
for (int i = 0; i < max; ++i) {
|
||||
long offset = j * s_gridSize + i;
|
||||
float z = convertToFloat(grid + offset * bytesPerElement, type);
|
||||
sprintf(buffer, "%6.2f", z);
|
||||
//std::cerr << " " << buffer;
|
||||
}
|
||||
//std::cerr << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
updateHeight
|
||||
(
|
||||
byte_t * p,
|
||||
float new_val,
|
||||
PHY_ScalarType type
|
||||
)
|
||||
{
|
||||
float old_val = convertToFloat(p, type);
|
||||
if (!old_val) {
|
||||
convertFromFloat(p, new_val, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// creates a random, fractal heightfield
|
||||
static void
|
||||
setFractal
|
||||
(
|
||||
byte_t * grid,
|
||||
int bytesPerElement,
|
||||
PHY_ScalarType type,
|
||||
int step
|
||||
)
|
||||
{
|
||||
btAssert(grid);
|
||||
btAssert(bytesPerElement > 0);
|
||||
btAssert(step > 0);
|
||||
btAssert(step < s_gridSize);
|
||||
|
||||
int newStep = step / 2;
|
||||
// std::cerr << "Computing grid with step = " << step << ": before\n";
|
||||
// dumpGrid(grid, bytesPerElement, type, step + 1);
|
||||
|
||||
// special case: starting (must set four corners)
|
||||
if (s_gridSize - 1 == step) {
|
||||
// pick a non-zero (possibly negative) base elevation for testing
|
||||
float base = randomHeight(step / 2);
|
||||
|
||||
convertFromFloat(grid, base, type);
|
||||
convertFromFloat(grid + step * bytesPerElement, base, type);
|
||||
convertFromFloat(grid + step * s_gridSize * bytesPerElement, base, type);
|
||||
convertFromFloat(grid + (step * s_gridSize + step) * bytesPerElement, base, type);
|
||||
}
|
||||
|
||||
// determine elevation of each corner
|
||||
float c00 = convertToFloat(grid, type);
|
||||
float c01 = convertToFloat(grid + step * bytesPerElement, type);
|
||||
float c10 = convertToFloat(grid + (step * s_gridSize) * bytesPerElement, type);
|
||||
float c11 = convertToFloat(grid + (step * s_gridSize + step) * bytesPerElement, type);
|
||||
|
||||
// set top middle
|
||||
updateHeight(grid + newStep * bytesPerElement, 0.5 * (c00 + c01) + randomHeight(step), type);
|
||||
|
||||
// set left middle
|
||||
updateHeight(grid + (newStep * s_gridSize) * bytesPerElement, 0.5 * (c00 + c10) + randomHeight(step), type);
|
||||
|
||||
// set right middle
|
||||
updateHeight(grid + (newStep * s_gridSize + step) * bytesPerElement, 0.5 * (c01 + c11) + randomHeight(step), type);
|
||||
|
||||
// set bottom middle
|
||||
updateHeight(grid + (step * s_gridSize + newStep) * bytesPerElement, 0.5 * (c10 + c11) + randomHeight(step), type);
|
||||
|
||||
// set middle
|
||||
updateHeight(grid + (newStep * s_gridSize + newStep) * bytesPerElement, 0.25 * (c00 + c01 + c10 + c11) + randomHeight(step), type);
|
||||
|
||||
// std::cerr << "Computing grid with step = " << step << ": after\n";
|
||||
// dumpGrid(grid, bytesPerElement, type, step + 1);
|
||||
|
||||
// terminate?
|
||||
if (newStep < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// recurse
|
||||
setFractal(grid, bytesPerElement, type, newStep);
|
||||
setFractal(grid + newStep * bytesPerElement, bytesPerElement, type, newStep);
|
||||
setFractal(grid + (newStep * s_gridSize) * bytesPerElement, bytesPerElement, type, newStep);
|
||||
setFractal(grid + ((newStep * s_gridSize) + newStep) * bytesPerElement, bytesPerElement, type, newStep);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static byte_t *
|
||||
getRawHeightfieldData
|
||||
(
|
||||
eTerrainModel model,
|
||||
PHY_ScalarType type,
|
||||
btScalar& minHeight,
|
||||
btScalar& maxHeight
|
||||
)
|
||||
{
|
||||
// std::cerr << "\nRegenerating terrain\n";
|
||||
// std::cerr << " model = " << model << "\n";
|
||||
// std::cerr << " type = " << type << "\n";
|
||||
|
||||
long nElements = ((long) s_gridSize) * s_gridSize;
|
||||
// std::cerr << " nElements = " << nElements << "\n";
|
||||
|
||||
int bytesPerElement = getByteSize(type);
|
||||
// std::cerr << " bytesPerElement = " << bytesPerElement << "\n";
|
||||
btAssert(bytesPerElement > 0 && "bad bytes per element");
|
||||
|
||||
long nBytes = nElements * bytesPerElement;
|
||||
// std::cerr << " nBytes = " << nBytes << "\n";
|
||||
byte_t * raw = new byte_t[nBytes];
|
||||
btAssert(raw && "out of memory");
|
||||
|
||||
// reseed randomization every 30 seconds
|
||||
srand(time(NULL) / 30);
|
||||
|
||||
// populate based on model
|
||||
switch (model) {
|
||||
case eRadial:
|
||||
setRadial(raw, bytesPerElement, type);
|
||||
break;
|
||||
|
||||
case eFractal:
|
||||
for (int i = 0; i < nBytes; i++)
|
||||
{
|
||||
raw[i] = 0;
|
||||
}
|
||||
setFractal(raw, bytesPerElement, type, s_gridSize - 1);
|
||||
break;
|
||||
|
||||
default:
|
||||
btAssert(!"bad model type");
|
||||
}
|
||||
|
||||
if (0) {
|
||||
// inside if(0) so it keeps compiling but isn't
|
||||
// exercised and doesn't cause warnings
|
||||
// std::cerr << "final grid:\n";
|
||||
dumpGrid(raw, bytesPerElement, type, s_gridSize - 1);
|
||||
}
|
||||
|
||||
// find min/max
|
||||
for (int i = 0; i < s_gridSize; ++i) {
|
||||
for (int j = 0; j < s_gridSize; ++j) {
|
||||
float z = getGridHeight(raw, i, j, type);
|
||||
// std::cerr << "i=" << i << ", j=" << j << ": z=" << z << "\n";
|
||||
|
||||
// update min/max
|
||||
if (!i && !j) {
|
||||
minHeight = z;
|
||||
maxHeight = z;
|
||||
} else {
|
||||
if (z < minHeight) {
|
||||
minHeight = z;
|
||||
}
|
||||
if (z > maxHeight) {
|
||||
maxHeight = z;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (maxHeight < -minHeight) {
|
||||
maxHeight = -minHeight;
|
||||
}
|
||||
if (minHeight > -maxHeight) {
|
||||
minHeight = -maxHeight;
|
||||
}
|
||||
|
||||
// std::cerr << " minHeight = " << minHeight << "\n";
|
||||
// std::cerr << " maxHeight = " << maxHeight << "\n";
|
||||
|
||||
return raw;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TerrainDemo class
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// class that demonstrates the btHeightfieldTerrainShape object
|
||||
class TerrainDemo : public DemoApplication {
|
||||
public:
|
||||
// constructor, destructor ---------------------------------------------
|
||||
TerrainDemo(void);
|
||||
~TerrainDemo(void);
|
||||
|
||||
// public class methods ------------------------------------------------
|
||||
void initialize(void);
|
||||
|
||||
// DemoApplication class interface methods -----------------------------
|
||||
void clientMoveAndDisplay(void);
|
||||
void keyboardCallback(unsigned char key, int x, int y);
|
||||
void renderme(void);
|
||||
|
||||
private:
|
||||
// private helper methods ----------------------------------------------
|
||||
void resetPhysics(void);
|
||||
void clearWorld(void);
|
||||
|
||||
// private data members ------------------------------------------------
|
||||
btDefaultCollisionConfiguration * m_collisionConfiguration;
|
||||
btCollisionDispatcher * m_dispatcher;
|
||||
btAxisSweep3 * m_overlappingPairCache;
|
||||
btSequentialImpulseConstraintSolver * m_constraintSolver;
|
||||
btAlignedObjectArray<btCollisionShape*> m_collisionShapes;
|
||||
int m_upAxis;
|
||||
PHY_ScalarType m_type;
|
||||
eTerrainModel m_model;
|
||||
byte_t * m_rawHeightfieldData;
|
||||
btScalar m_minHeight;
|
||||
btScalar m_maxHeight;
|
||||
float m_phase; // for dynamics
|
||||
bool m_isDynamic;
|
||||
};
|
||||
|
||||
|
||||
|
||||
TerrainDemo::TerrainDemo(void)
|
||||
:
|
||||
m_collisionConfiguration(NULL),
|
||||
m_dispatcher(NULL),
|
||||
m_overlappingPairCache(NULL),
|
||||
m_constraintSolver(NULL),
|
||||
m_upAxis(1),
|
||||
m_type(PHY_FLOAT),
|
||||
m_model(eFractal),
|
||||
m_rawHeightfieldData(NULL),
|
||||
m_phase(0.0),
|
||||
m_isDynamic(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
TerrainDemo::~TerrainDemo(void)
|
||||
{
|
||||
clearWorld();
|
||||
|
||||
//delete dynamics world
|
||||
delete m_dynamicsWorld;
|
||||
|
||||
//delete solver
|
||||
delete m_constraintSolver;
|
||||
|
||||
//delete broadphase
|
||||
delete m_overlappingPairCache;
|
||||
|
||||
//delete dispatcher
|
||||
delete m_dispatcher;
|
||||
|
||||
delete m_collisionConfiguration;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TerrainDemo -- public class methods
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// one-time class and physics initialization
|
||||
void TerrainDemo::initialize(void)
|
||||
{
|
||||
// std::cerr << "initializing...\n";
|
||||
|
||||
// set up basic state
|
||||
m_upAxis = 1; // start with Y-axis as "up"
|
||||
m_type = PHY_FLOAT;
|
||||
m_model = eFractal;
|
||||
m_isDynamic = true;
|
||||
|
||||
// set up the physics world
|
||||
m_collisionConfiguration = new btDefaultCollisionConfiguration();
|
||||
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration);
|
||||
btVector3 worldMin(-1000,-1000,-1000);
|
||||
btVector3 worldMax(1000,1000,1000);
|
||||
m_overlappingPairCache = new btAxisSweep3(worldMin,worldMax);
|
||||
m_constraintSolver = new btSequentialImpulseConstraintSolver();
|
||||
m_dynamicsWorld = new btDiscreteDynamicsWorld(m_dispatcher,m_overlappingPairCache,m_constraintSolver,m_collisionConfiguration);
|
||||
|
||||
// initialize axis- or type-dependent physics from here
|
||||
this->resetPhysics();
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TerrainDemo -- DemoApplication class interface methods
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void TerrainDemo::clientMoveAndDisplay(void)
|
||||
{
|
||||
// elapsed time
|
||||
float us = getDeltaTimeMicroseconds();
|
||||
float seconds = 1.0e-6 * us;
|
||||
|
||||
// we'll carefully iterate through each time step so we can update
|
||||
// the dynamic model if necessary
|
||||
long nStepsPerIteration = 1;
|
||||
while (seconds > 1.0e-6) {
|
||||
float dt = nStepsPerIteration * s_engineTimeStep;
|
||||
if (dt > seconds) {
|
||||
dt = seconds;
|
||||
}
|
||||
seconds -= dt;
|
||||
// std::cerr << " Stepping through " << dt << " seconds\n";
|
||||
|
||||
// if dynamic and radial, go ahead and update the field
|
||||
if (m_rawHeightfieldData && m_isDynamic && eRadial == m_model) {
|
||||
m_phase += s_deltaPhase * dt;
|
||||
if (m_phase > 2.0 * SIMD_PI) {
|
||||
m_phase -= 2.0 * SIMD_PI;
|
||||
}
|
||||
int bpe = getByteSize(m_type);
|
||||
btAssert(bpe > 0 && "Bad bytes per element");
|
||||
setRadial(m_rawHeightfieldData, bpe, m_type, m_phase);
|
||||
}
|
||||
|
||||
if (m_dynamicsWorld) {
|
||||
m_dynamicsWorld->stepSimulation(dt,
|
||||
nStepsPerIteration + 1, s_engineTimeStep);
|
||||
}
|
||||
}
|
||||
|
||||
// okay, render
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
renderme();
|
||||
glFlush();
|
||||
glutSwapBuffers();
|
||||
}
|
||||
|
||||
|
||||
static PHY_ScalarType nextType (PHY_ScalarType type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case PHY_FLOAT:
|
||||
return PHY_SHORT;
|
||||
break;
|
||||
case PHY_SHORT:
|
||||
return PHY_UCHAR;
|
||||
break;
|
||||
case PHY_UCHAR:
|
||||
return PHY_FLOAT;
|
||||
break;
|
||||
}
|
||||
btAssert (0);
|
||||
return PHY_FLOAT;
|
||||
}
|
||||
|
||||
void TerrainDemo::keyboardCallback(unsigned char key, int x, int y) {
|
||||
|
||||
if (',' == key) {
|
||||
// increment model
|
||||
m_model = (eFractal == m_model) ? eRadial : eFractal;
|
||||
this->resetPhysics();
|
||||
}
|
||||
if ('/' == key) {
|
||||
// increment type
|
||||
m_type = nextType(m_type);
|
||||
this->resetPhysics();
|
||||
}
|
||||
if ('\\' == key) {
|
||||
// increment axis
|
||||
m_upAxis++;
|
||||
if (m_upAxis > 2) {
|
||||
m_upAxis = 0;
|
||||
}
|
||||
this->resetPhysics();
|
||||
}
|
||||
if ('[' == key) {
|
||||
// toggle dynamics
|
||||
m_isDynamic = !m_isDynamic;
|
||||
}
|
||||
|
||||
// let demo base class handle!
|
||||
DemoApplication::keyboardCallback(key, x, y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static void
|
||||
doPrint
|
||||
(
|
||||
int x,
|
||||
int& y,
|
||||
int dy,
|
||||
const char * text
|
||||
)
|
||||
{
|
||||
glRasterPos3f(x, y, 0);
|
||||
BMF_DrawString(BMF_GetFont(BMF_kHelvetica10), text);
|
||||
y += dy;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// override the default display just so we can overlay a bit more text
|
||||
void TerrainDemo::renderme(void)
|
||||
{
|
||||
// give base class a shot
|
||||
DemoApplication::renderme();
|
||||
|
||||
// overlay any debug information
|
||||
if (m_dynamicsWorld)
|
||||
m_dynamicsWorld->debugDrawWorld();
|
||||
|
||||
// switch to orthographic
|
||||
setOrthographicProjection();
|
||||
|
||||
// we'll draw on the right top of the screen
|
||||
const int lineWidth = 200;
|
||||
const int lineHeight = 16;
|
||||
char buffer[256];
|
||||
|
||||
int xStart = m_glutScreenWidth - lineWidth;
|
||||
int yStart = lineHeight;
|
||||
|
||||
sprintf(buffer, "Terrain Type: %s", getTerrainTypeName(m_model));
|
||||
doPrint(xStart, yStart, lineHeight, buffer);
|
||||
doPrint(xStart, yStart, lineHeight, "Press ',' to cycle terrain types");
|
||||
doPrint(xStart, yStart, lineHeight, "");
|
||||
|
||||
sprintf(buffer, "Data Type: %s", getDataTypeName(m_type));
|
||||
doPrint(xStart, yStart, lineHeight, buffer);
|
||||
doPrint(xStart, yStart, lineHeight, "Press '/' to cycle data types");
|
||||
doPrint(xStart, yStart, lineHeight, "");
|
||||
|
||||
sprintf(buffer, "'up' axis: %s", getUpAxisName(m_upAxis));
|
||||
doPrint(xStart, yStart, lineHeight, buffer);
|
||||
doPrint(xStart, yStart, lineHeight, "Press '\\' to cycle 'up' axes");
|
||||
doPrint(xStart, yStart, lineHeight, "");
|
||||
|
||||
if (eRadial == m_model) {
|
||||
sprintf(buffer, "Dynamic: %s", m_isDynamic ? "yes" : "no");
|
||||
doPrint(xStart, yStart, lineHeight, buffer);
|
||||
doPrint(xStart, yStart, lineHeight, "Press '[' to toggle dynamics");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TerrainDemo -- private helper methods
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// called whenever key terrain attribute is changed
|
||||
void TerrainDemo::resetPhysics(void)
|
||||
{
|
||||
// remove old heightfield
|
||||
clearWorld();
|
||||
|
||||
// reset gravity to point in appropriate direction
|
||||
m_dynamicsWorld->setGravity(getUpVector(m_upAxis, 0.0, -s_gravity));
|
||||
|
||||
// get new heightfield of appropriate type
|
||||
m_rawHeightfieldData =
|
||||
getRawHeightfieldData(m_model, m_type, m_minHeight, m_maxHeight);
|
||||
btAssert(m_rawHeightfieldData && "failed to create raw heightfield");
|
||||
|
||||
bool flipQuadEdges = false;
|
||||
btHeightfieldTerrainShape * heightfieldShape =
|
||||
new btHeightfieldTerrainShape(s_gridSize, s_gridSize,
|
||||
m_rawHeightfieldData,
|
||||
s_gridHeightScale,
|
||||
m_minHeight, m_maxHeight,
|
||||
m_upAxis, m_type, flipQuadEdges);
|
||||
btAssert(heightfieldShape && "null heightfield");
|
||||
|
||||
// scale the shape
|
||||
btVector3 localScaling = getUpVector(m_upAxis, s_gridSpacing, 1.0);
|
||||
heightfieldShape->setLocalScaling(localScaling);
|
||||
|
||||
// stash this shape away
|
||||
m_collisionShapes.push_back(heightfieldShape);
|
||||
|
||||
// set origin to middle of heightfield
|
||||
btTransform tr;
|
||||
tr.setIdentity();
|
||||
|
||||
// create ground object
|
||||
float mass = 0.0;
|
||||
localCreateRigidBody(mass, tr, heightfieldShape);
|
||||
}
|
||||
|
||||
|
||||
/// removes all objects and shapes from the world
|
||||
void TerrainDemo::clearWorld(void)
|
||||
{
|
||||
//remove the rigidbodies from the dynamics world and delete them
|
||||
int i;
|
||||
for (i=m_dynamicsWorld->getNumCollisionObjects()-1; i>=0 ;i--)
|
||||
{
|
||||
btCollisionObject* obj = m_dynamicsWorld->getCollisionObjectArray()[i];
|
||||
btRigidBody* body = btRigidBody::upcast(obj);
|
||||
if (body && body->getMotionState())
|
||||
{
|
||||
delete body->getMotionState();
|
||||
}
|
||||
m_dynamicsWorld->removeCollisionObject( obj );
|
||||
delete obj;
|
||||
}
|
||||
|
||||
//delete collision shapes
|
||||
for (int j=0;j<m_collisionShapes.size();j++)
|
||||
{
|
||||
btCollisionShape* shape = m_collisionShapes[j];
|
||||
delete shape;
|
||||
}
|
||||
m_collisionShapes.clear();
|
||||
|
||||
// delete raw heightfield data
|
||||
delete m_rawHeightfieldData;
|
||||
m_rawHeightfieldData = NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TerrainDemo -- public API (exposed in header)
|
||||
//
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/// creates an object that demonstrates terrain
|
||||
DemoApplication * btCreateTerrainDemo(void)
|
||||
{
|
||||
TerrainDemo * demo = new TerrainDemo;
|
||||
btAssert(demo && "out of memory");
|
||||
|
||||
demo->initialize();
|
||||
|
||||
return demo;
|
||||
}
|
||||
|
||||
27
Demos/TerrainDemo/TerrainDemo.h
Normal file
27
Demos/TerrainDemo/TerrainDemo.h
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
Bullet Continuous Collision Detection and Physics Library
|
||||
Copyright (c) 2003-2006,2008 Erwin Coumans http://continuousphysics.com/Bullet/
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty.
|
||||
In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute it freely,
|
||||
subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
*/
|
||||
#ifndef TERRAIN_DEMO_H
|
||||
#define TERRAIN_DEMO_H
|
||||
|
||||
|
||||
#include "DemoApplication.h"
|
||||
|
||||
|
||||
// all we need to expose publicly is the factory method!
|
||||
DemoApplication * btCreateTerrainDemo(void);
|
||||
|
||||
|
||||
#endif //TERRAIN_DEMO_H
|
||||
|
||||
14
Demos/TerrainDemo/main.cpp
Normal file
14
Demos/TerrainDemo/main.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
|
||||
#include "TerrainDemo.h"
|
||||
#include "GlutStuff.h"
|
||||
|
||||
int main(int argc,char** argv)
|
||||
{
|
||||
DemoApplication * demo = btCreateTerrainDemo();
|
||||
btAssert(demo && "failed to create terrain demo object");
|
||||
|
||||
return glutmain(argc, argv, 800, 600,
|
||||
"Terrain Demo. http://www.continuousphysics.com/Bullet/phpBB2/",
|
||||
demo);
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
SUBDIRS=src Extras Demos/OpenGL Demos/BasicDemo Demos/VehicleDemo Demos/CcdPhysicsDemo Demos/ColladaDemo Demos/MultiThreadedDemo Demos/SoftDemo Demos/AllBulletDemos
|
||||
SUBDIRS=src Extras Demos/OpenGL Demos/BasicDemo Demos/TerrainDemo Demos/VehicleDemo Demos/CcdPhysicsDemo Demos/ColladaDemo Demos/MultiThreadedDemo Demos/SoftDemo Demos/AllBulletDemos
|
||||
|
||||
@@ -154,7 +154,7 @@ CXXFLAGS="$CXXFLAGS $CFLAGS"
|
||||
# Emit generated files.
|
||||
#----------------------------------------------------------------------------
|
||||
CS_JAMCONFIG_OUTPUT([Jamconfig])
|
||||
AC_CONFIG_FILES([bullet.pc Jamfile Makefile Demos/SoftDemo/Makefile Demos/AllBulletDemos/Makefile Demos/MultiThreadedDemo/Makefile Demos/ColladaDemo/Makefile Demos/OpenGL/Makefile Demos/BasicDemo/Makefile Demos/CcdPhysicsDemo/Makefile Demos/VehicleDemo/Makefile src/Makefile Extras/Makefile])
|
||||
AC_CONFIG_FILES([bullet.pc Jamfile Makefile Demos/SoftDemo/Makefile Demos/AllBulletDemos/Makefile Demos/MultiThreadedDemo/Makefile Demos/ColladaDemo/Makefile Demos/OpenGL/Makefile Demos/BasicDemo/Makefile Demos/CcdPhysicsDemo/Makefile Demos/VehicleDemo/Makefile Demos/TerrainDemo/Makefile src/Makefile Extras/Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
AC_MSG_NOTICE([
|
||||
|
||||
@@ -310,26 +310,7 @@ protected:
|
||||
void walkRecursiveQuantizedTreeAgainstQuantizedTree(const btQuantizedBvhNode* treeNodeA,const btQuantizedBvhNode* treeNodeB,btNodeOverlapCallback* nodeCallback) const;
|
||||
|
||||
|
||||
#define USE_BANCHLESS 1
|
||||
#ifdef USE_BANCHLESS
|
||||
//This block replaces the block below and uses no branches, and replaces the 8 bit return with a 32 bit return for improved performance (~3x on XBox 360)
|
||||
SIMD_FORCE_INLINE unsigned testQuantizedAabbAgainstQuantizedAabb(unsigned short int* aabbMin1,unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2) const
|
||||
{
|
||||
return static_cast<unsigned int>(btSelect((unsigned)((aabbMin1[0] <= aabbMax2[0]) & (aabbMax1[0] >= aabbMin2[0])
|
||||
& (aabbMin1[2] <= aabbMax2[2]) & (aabbMax1[2] >= aabbMin2[2])
|
||||
& (aabbMin1[1] <= aabbMax2[1]) & (aabbMax1[1] >= aabbMin2[1])),
|
||||
1, 0));
|
||||
}
|
||||
#else
|
||||
SIMD_FORCE_INLINE bool testQuantizedAabbAgainstQuantizedAabb(unsigned short int* aabbMin1,unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2) const
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap;
|
||||
overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap;
|
||||
overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
#endif //USE_BANCHLESS
|
||||
|
||||
|
||||
void updateSubtreeHeaders(int leftChildNodexIndex,int rightChildNodexIndex);
|
||||
|
||||
|
||||
@@ -20,6 +20,16 @@ subject to the following restrictions:
|
||||
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types
|
||||
#include "btTriangleCallback.h"
|
||||
|
||||
/// PHY_ScalarType enumerates possible scalar types.
|
||||
/// See the btStridingMeshInterface or btHeightfieldTerrainShape for its use
|
||||
typedef enum PHY_ScalarType {
|
||||
PHY_FLOAT,
|
||||
PHY_DOUBLE,
|
||||
PHY_INTEGER,
|
||||
PHY_SHORT,
|
||||
PHY_FIXEDPOINT88,
|
||||
PHY_UCHAR
|
||||
} PHY_ScalarType;
|
||||
|
||||
///The btConcaveShape class provides an interface for non-moving (static) concave shapes.
|
||||
///It has been implemented by the btStaticPlaneShape, btBvhTriangleMeshShape and btHeightfieldTerrainShape.
|
||||
|
||||
@@ -18,71 +18,107 @@ subject to the following restrictions:
|
||||
#include "LinearMath/btTransformUtil.h"
|
||||
|
||||
|
||||
btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength,void* heightfieldData,btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges)
|
||||
: btConcaveShape (), m_heightStickWidth(heightStickWidth),
|
||||
m_heightStickLength(heightStickLength),
|
||||
m_maxHeight(maxHeight),
|
||||
m_width((btScalar)heightStickWidth-1),
|
||||
m_length((btScalar)heightStickLength-1),
|
||||
m_heightfieldDataUnknown(heightfieldData),
|
||||
m_useFloatData(useFloatData),
|
||||
m_flipQuadEdges(flipQuadEdges),
|
||||
m_useDiamondSubdivision(false),
|
||||
m_upAxis(upAxis),
|
||||
m_localScaling(btScalar(1.),btScalar(1.),btScalar(1.))
|
||||
|
||||
btHeightfieldTerrainShape::btHeightfieldTerrainShape
|
||||
(
|
||||
int heightStickWidth, int heightStickLength, void* heightfieldData,
|
||||
btScalar heightScale, btScalar minHeight, btScalar maxHeight,int upAxis,
|
||||
PHY_ScalarType hdt, bool flipQuadEdges
|
||||
)
|
||||
{
|
||||
initialize(heightStickWidth, heightStickLength, heightfieldData,
|
||||
heightScale, minHeight, maxHeight, upAxis, hdt,
|
||||
flipQuadEdges);
|
||||
}
|
||||
|
||||
|
||||
|
||||
btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength,void* heightfieldData,btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges)
|
||||
{
|
||||
// legacy constructor: support only float or unsigned char,
|
||||
// and min height is zero
|
||||
PHY_ScalarType hdt = (useFloatData) ? PHY_FLOAT : PHY_UCHAR;
|
||||
btScalar minHeight = 0.0;
|
||||
|
||||
// previously, height = uchar * maxHeight / 65535.
|
||||
// So to preserve legacy behavior, heightScale = maxHeight / 65535
|
||||
btScalar heightScale = maxHeight / 65535;
|
||||
|
||||
initialize(heightStickWidth, heightStickLength, heightfieldData,
|
||||
heightScale, minHeight, maxHeight, upAxis, hdt,
|
||||
flipQuadEdges);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void btHeightfieldTerrainShape::initialize
|
||||
(
|
||||
int heightStickWidth, int heightStickLength, void* heightfieldData,
|
||||
btScalar heightScale, btScalar minHeight, btScalar maxHeight, int upAxis,
|
||||
PHY_ScalarType hdt, bool flipQuadEdges
|
||||
)
|
||||
{
|
||||
// validation
|
||||
btAssert(heightStickWidth > 1 && "bad width");
|
||||
btAssert(heightStickLength > 1 && "bad length");
|
||||
btAssert(heightfieldData && "null heightfield data");
|
||||
// btAssert(heightScale) -- do we care? Trust caller here
|
||||
btAssert(minHeight <= maxHeight && "bad min/max height");
|
||||
btAssert(upAxis >= 0 && upAxis < 3 &&
|
||||
"bad upAxis--should be in range [0,2]");
|
||||
btAssert(hdt != PHY_UCHAR || hdt != PHY_FLOAT || hdt != PHY_SHORT &&
|
||||
"Bad height data type enum");
|
||||
|
||||
// initialize member variables
|
||||
m_shapeType = TERRAIN_SHAPE_PROXYTYPE;
|
||||
m_heightStickWidth = heightStickWidth;
|
||||
m_heightStickLength = heightStickLength;
|
||||
m_minHeight = minHeight;
|
||||
m_maxHeight = maxHeight;
|
||||
m_width = (btScalar) (heightStickWidth - 1);
|
||||
m_length = (btScalar) (heightStickLength - 1);
|
||||
m_heightScale = heightScale;
|
||||
m_heightfieldDataUnknown = heightfieldData;
|
||||
m_heightDataType = hdt;
|
||||
m_flipQuadEdges = flipQuadEdges;
|
||||
m_useDiamondSubdivision = false;
|
||||
m_upAxis = upAxis;
|
||||
m_localScaling.setValue(btScalar(1.), btScalar(1.), btScalar(1.));
|
||||
|
||||
btScalar quantizationMargin = 1.f;
|
||||
|
||||
//enlarge the AABB to avoid division by zero when initializing the quantization values
|
||||
btVector3 clampValue(quantizationMargin,quantizationMargin,quantizationMargin);
|
||||
|
||||
btVector3 halfExtents(0,0,0);
|
||||
|
||||
// determine min/max axis-aligned bounding box (aabb) values
|
||||
switch (m_upAxis)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
halfExtents.setValue(
|
||||
btScalar(m_maxHeight),
|
||||
btScalar(m_width), //?? don't know if this should change
|
||||
btScalar(m_length));
|
||||
m_localAabbMin.setValue(m_minHeight, 0, 0);
|
||||
m_localAabbMax.setValue(m_maxHeight, m_width, m_length);
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
halfExtents.setValue(
|
||||
btScalar(m_width),
|
||||
btScalar(m_maxHeight),
|
||||
btScalar(m_length));
|
||||
m_localAabbMin.setValue(0, m_minHeight, 0);
|
||||
m_localAabbMax.setValue(m_width, m_maxHeight, m_length);
|
||||
break;
|
||||
};
|
||||
case 2:
|
||||
{
|
||||
halfExtents.setValue(
|
||||
btScalar(m_width),
|
||||
btScalar(m_length),
|
||||
btScalar(m_maxHeight)
|
||||
);
|
||||
m_localAabbMin.setValue(0, 0, m_minHeight);
|
||||
m_localAabbMax.setValue(m_width, m_length, m_maxHeight);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
//need to get valid m_upAxis
|
||||
btAssert(0);
|
||||
btAssert(0 && "Bad m_upAxis");
|
||||
}
|
||||
}
|
||||
|
||||
halfExtents*= btScalar(0.5);
|
||||
|
||||
m_localAabbMin = -halfExtents - clampValue;
|
||||
m_localAabbMax = halfExtents + clampValue;
|
||||
btVector3 aabbSize = m_localAabbMax - m_localAabbMin;
|
||||
|
||||
// remember origin (defined as exact middle of aabb)
|
||||
m_localOrigin = btScalar(0.5) * (m_localAabbMin + m_localAabbMax);
|
||||
}
|
||||
|
||||
|
||||
|
||||
btHeightfieldTerrainShape::~btHeightfieldTerrainShape()
|
||||
{
|
||||
}
|
||||
@@ -92,33 +128,53 @@ btHeightfieldTerrainShape::~btHeightfieldTerrainShape()
|
||||
void btHeightfieldTerrainShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
|
||||
{
|
||||
btVector3 halfExtents = (m_localAabbMax-m_localAabbMin)* m_localScaling * btScalar(0.5);
|
||||
halfExtents += btVector3(getMargin(),getMargin(),getMargin());
|
||||
|
||||
btVector3 localOrigin(0, 0, 0);
|
||||
localOrigin[m_upAxis] = (m_minHeight + m_maxHeight) * btScalar(0.5);
|
||||
localOrigin *= m_localScaling;
|
||||
|
||||
btMatrix3x3 abs_b = t.getBasis().absolute();
|
||||
btVector3 center = t.getOrigin();
|
||||
btVector3 extent = btVector3(abs_b[0].dot(halfExtents),
|
||||
abs_b[1].dot(halfExtents),
|
||||
abs_b[2].dot(halfExtents));
|
||||
|
||||
extent += btVector3(getMargin(),getMargin(),getMargin());
|
||||
|
||||
aabbMin = center - extent;
|
||||
aabbMax = center + extent;
|
||||
|
||||
|
||||
}
|
||||
|
||||
btScalar btHeightfieldTerrainShape::getHeightFieldValue(int x,int y) const
|
||||
{
|
||||
btScalar val = 0.f;
|
||||
if (m_useFloatData)
|
||||
switch (m_heightDataType)
|
||||
{
|
||||
val = m_heightfieldDataFloat[(y*m_heightStickWidth)+x];
|
||||
} else
|
||||
{
|
||||
//assume unsigned short int
|
||||
unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y*m_heightStickWidth)+x];
|
||||
val = heightFieldValue* (m_maxHeight/btScalar(65535));
|
||||
case PHY_FLOAT:
|
||||
{
|
||||
val = m_heightfieldDataFloat[(y*m_heightStickWidth)+x];
|
||||
break;
|
||||
}
|
||||
|
||||
case PHY_UCHAR:
|
||||
{
|
||||
unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y*m_heightStickWidth)+x];
|
||||
val = heightFieldValue * m_heightScale;
|
||||
break;
|
||||
}
|
||||
|
||||
case PHY_SHORT:
|
||||
{
|
||||
short hfValue = m_heightfieldDataShort[(y * m_heightStickWidth) + x];
|
||||
val = hfValue * m_heightScale;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
btAssert(!"Bad m_heightDataType");
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
@@ -178,41 +234,73 @@ void btHeightfieldTerrainShape::getVertex(int x,int y,btVector3& vertex) const
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline int
|
||||
getQuantized
|
||||
(
|
||||
float x
|
||||
)
|
||||
{
|
||||
if (x < 0.0) {
|
||||
return (int) (x - 0.5);
|
||||
}
|
||||
return (int) (x + 0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// given input vector, return quantized version
|
||||
/**
|
||||
This routine is basically determining the gridpoint indices for a given
|
||||
input vector, answering the question: "which gridpoint is closest to the
|
||||
provided point?".
|
||||
|
||||
"with clamp" means that we restrict the point to be in the heightfield's
|
||||
axis-aligned bounding box.
|
||||
*/
|
||||
void btHeightfieldTerrainShape::quantizeWithClamp(int* out, const btVector3& point,int /*isMax*/) const
|
||||
{
|
||||
btVector3 clampedPoint(point);
|
||||
clampedPoint.setMax(m_localAabbMin);
|
||||
clampedPoint.setMin(m_localAabbMax);
|
||||
|
||||
btVector3 v = (clampedPoint);// - m_bvhAabbMin) * m_bvhQuantization;
|
||||
|
||||
///@todo: optimization: check out how to removed this btFabs
|
||||
out[0] = getQuantized(clampedPoint.getX());
|
||||
out[1] = getQuantized(clampedPoint.getY());
|
||||
out[2] = getQuantized(clampedPoint.getZ());
|
||||
|
||||
out[0] = (int)(v.getX() + v.getX() / btFabs(v.getX())* btScalar(0.5) );
|
||||
out[1] = (int)(v.getY() + v.getY() / btFabs(v.getY())* btScalar(0.5) );
|
||||
out[2] = (int)(v.getZ() + v.getZ() / btFabs(v.getZ())* btScalar(0.5) );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// process all triangles within the provided axis-aligned bounding box
|
||||
/**
|
||||
basic algorithm:
|
||||
- convert input aabb to local coordinates (scale down and shift for local origin)
|
||||
- convert input aabb to a range of heightfield grid points (quantize)
|
||||
- iterate over all triangles in that subset of the grid
|
||||
*/
|
||||
void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const
|
||||
{
|
||||
(void)callback;
|
||||
(void)aabbMax;
|
||||
(void)aabbMin;
|
||||
|
||||
//quantize the aabbMin and aabbMax, and adjust the start/end ranges
|
||||
|
||||
int quantizedAabbMin[3];
|
||||
int quantizedAabbMax[3];
|
||||
|
||||
// scale down the input aabb's so they are in local (non-scaled) coordinates
|
||||
btVector3 localAabbMin = aabbMin*btVector3(1.f/m_localScaling[0],1.f/m_localScaling[1],1.f/m_localScaling[2]);
|
||||
btVector3 localAabbMax = aabbMax*btVector3(1.f/m_localScaling[0],1.f/m_localScaling[1],1.f/m_localScaling[2]);
|
||||
|
||||
|
||||
// account for local origin
|
||||
localAabbMin += m_localOrigin;
|
||||
localAabbMax += m_localOrigin;
|
||||
|
||||
//quantize the aabbMin and aabbMax, and adjust the start/end ranges
|
||||
int quantizedAabbMin[3];
|
||||
int quantizedAabbMax[3];
|
||||
quantizeWithClamp(quantizedAabbMin, localAabbMin,0);
|
||||
quantizeWithClamp(quantizedAabbMax, localAabbMax,1);
|
||||
|
||||
|
||||
// expand the min/max quantized values
|
||||
// this is to catch the case where the input aabb falls between grid points!
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
quantizedAabbMin[i]--;
|
||||
quantizedAabbMax[i]++;
|
||||
}
|
||||
|
||||
int startX=0;
|
||||
int endX=m_heightStickWidth-1;
|
||||
@@ -223,11 +311,6 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
quantizedAabbMin[1]+=m_heightStickWidth/2-1;
|
||||
quantizedAabbMax[1]+=m_heightStickWidth/2+1;
|
||||
quantizedAabbMin[2]+=m_heightStickLength/2-1;
|
||||
quantizedAabbMax[2]+=m_heightStickLength/2+1;
|
||||
|
||||
if (quantizedAabbMin[1]>startX)
|
||||
startX = quantizedAabbMin[1];
|
||||
if (quantizedAabbMax[1]<endX)
|
||||
@@ -240,11 +323,6 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
quantizedAabbMin[0]+=m_heightStickWidth/2-1;
|
||||
quantizedAabbMax[0]+=m_heightStickWidth/2+1;
|
||||
quantizedAabbMin[2]+=m_heightStickLength/2-1;
|
||||
quantizedAabbMax[2]+=m_heightStickLength/2+1;
|
||||
|
||||
if (quantizedAabbMin[0]>startX)
|
||||
startX = quantizedAabbMin[0];
|
||||
if (quantizedAabbMax[0]<endX)
|
||||
@@ -257,11 +335,6 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
|
||||
};
|
||||
case 2:
|
||||
{
|
||||
quantizedAabbMin[0]+=m_heightStickWidth/2-1;
|
||||
quantizedAabbMax[0]+=m_heightStickWidth/2+1;
|
||||
quantizedAabbMin[1]+=m_heightStickLength/2-1;
|
||||
quantizedAabbMax[1]+=m_heightStickLength/2+1;
|
||||
|
||||
if (quantizedAabbMin[0]>startX)
|
||||
startX = quantizedAabbMin[0];
|
||||
if (quantizedAabbMax[0]<endX)
|
||||
|
||||
@@ -18,28 +18,69 @@ subject to the following restrictions:
|
||||
|
||||
#include "btConcaveShape.h"
|
||||
|
||||
///The btHeightfieldTerrainShape simulates a 2D heightfield terrain collision shape. You can also use the more general btBvhTriangleMeshShape instead.
|
||||
///An example implementation of btHeightfieldTerrainShape is provided in Demos/VehicleDemo/VehicleDemo.cpp
|
||||
///btHeightfieldTerrainShape simulates a 2D heightfield terrain
|
||||
/**
|
||||
The caller is responsible for maintaining the heightfield array; this
|
||||
class does not make a copy.
|
||||
|
||||
The heightfield can be dynamic so long as the min/max height values
|
||||
capture the extremes (heights must always be in that range).
|
||||
|
||||
The local origin of the heightfield is assumed to be the exact
|
||||
center (as determined by width and length and height, with each
|
||||
axis multiplied by the localScaling).
|
||||
|
||||
Most (but not all) rendering and heightfield libraries assume upAxis = 1
|
||||
(that is, the y-axis is "up"). This class allows any of the 3 coordinates
|
||||
to be "up". Make sure your choice of axis is consistent with your rendering
|
||||
system.
|
||||
|
||||
The heightfield heights are determined from the data type used for the
|
||||
heightfieldData array.
|
||||
|
||||
- PHY_UCHAR: height at a point is the uchar value at the
|
||||
grid point, multipled by heightScale. uchar isn't recommended
|
||||
because of its inability to deal with negative values, and
|
||||
low resolution (8-bit).
|
||||
|
||||
- PHY_SHORT: height at a point is the short int value at that grid
|
||||
point, multipled by heightScale.
|
||||
|
||||
- PHY_FLOAT: height at a point is the float value at that grid
|
||||
point. heightScale is ignored when using the float heightfield
|
||||
data type.
|
||||
|
||||
Whatever the caller specifies as minHeight and maxHeight will be honored.
|
||||
The class will not inspect the heightfield to discover the actual minimum
|
||||
or maximum heights. These values are used to determine the heightfield's
|
||||
axis-aligned bounding box, multiplied by localScaling.
|
||||
|
||||
For usage and testing see the TerrainDemo.
|
||||
*/
|
||||
class btHeightfieldTerrainShape : public btConcaveShape
|
||||
{
|
||||
protected:
|
||||
btVector3 m_localAabbMin;
|
||||
btVector3 m_localAabbMax;
|
||||
|
||||
btVector3 m_localOrigin;
|
||||
|
||||
///terrain data
|
||||
int m_heightStickWidth;
|
||||
int m_heightStickLength;
|
||||
btScalar m_minHeight;
|
||||
btScalar m_maxHeight;
|
||||
btScalar m_width;
|
||||
btScalar m_length;
|
||||
btScalar m_heightScale;
|
||||
union
|
||||
{
|
||||
unsigned char* m_heightfieldDataUnsignedChar;
|
||||
short* m_heightfieldDataShort;
|
||||
btScalar* m_heightfieldDataFloat;
|
||||
void* m_heightfieldDataUnknown;
|
||||
};
|
||||
|
||||
bool m_useFloatData;
|
||||
|
||||
PHY_ScalarType m_heightDataType;
|
||||
bool m_flipQuadEdges;
|
||||
bool m_useDiamondSubdivision;
|
||||
|
||||
@@ -51,17 +92,39 @@ protected:
|
||||
void quantizeWithClamp(int* out, const btVector3& point,int isMax) const;
|
||||
void getVertex(int x,int y,btVector3& vertex) const;
|
||||
|
||||
inline bool testQuantizedAabbAgainstQuantizedAabb(int* aabbMin1, int* aabbMax1,const int* aabbMin2,const int* aabbMax2) const
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap;
|
||||
overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap;
|
||||
overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
|
||||
|
||||
/// protected initialization
|
||||
/**
|
||||
Handles the work of constructors so that public constructors can be
|
||||
backwards-compatible without a lot of copy/paste.
|
||||
*/
|
||||
void initialize(int heightStickWidth, int heightStickLength,
|
||||
void* heightfieldData, btScalar heightScale,
|
||||
btScalar minHeight, btScalar maxHeight, int upAxis,
|
||||
PHY_ScalarType heightDataType, bool flipQuadEdges);
|
||||
|
||||
public:
|
||||
btHeightfieldTerrainShape(int heightStickWidth,int heightStickHeight,void* heightfieldData, btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges);
|
||||
/// preferred constructor
|
||||
/**
|
||||
This constructor supports a range of heightfield
|
||||
data types, and allows for a non-zero minimum height value.
|
||||
heightScale is needed for any integer-based heightfield data types.
|
||||
*/
|
||||
btHeightfieldTerrainShape(int heightStickWidth,int heightStickLength,
|
||||
void* heightfieldData, btScalar heightScale,
|
||||
btScalar minHeight, btScalar maxHeight,
|
||||
int upAxis, PHY_ScalarType heightDataType,
|
||||
bool flipQuadEdges);
|
||||
|
||||
/// legacy constructor
|
||||
/**
|
||||
The legacy constructor assumes the heightfield has a minimum height
|
||||
of zero. Only unsigned char or floats are supported. For legacy
|
||||
compatibility reasons, heightScale is calculated as maxHeight / 65535
|
||||
(and is only used when useFloatData = false).
|
||||
*/
|
||||
btHeightfieldTerrainShape(int heightStickWidth,int heightStickLength,void* heightfieldData, btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges);
|
||||
|
||||
virtual ~btHeightfieldTerrainShape();
|
||||
|
||||
|
||||
@@ -18,16 +18,9 @@ subject to the following restrictions:
|
||||
|
||||
#include "LinearMath/btVector3.h"
|
||||
#include "btTriangleCallback.h"
|
||||
#include "btConcaveShape.h"
|
||||
|
||||
|
||||
/// PHY_ScalarType enumerates possible scalar types.
|
||||
/// See the btStridingMeshInterface for its use
|
||||
typedef enum PHY_ScalarType {
|
||||
PHY_FLOAT,
|
||||
PHY_DOUBLE,
|
||||
PHY_INTEGER,
|
||||
PHY_SHORT,
|
||||
PHY_FIXEDPOINT88
|
||||
} PHY_ScalarType;
|
||||
|
||||
/// The btStridingMeshInterface is the interface class for high performance generic access to triangle meshes, used in combination with btBvhTriangleMeshShape and some other collision shapes.
|
||||
/// Using index striding of 3*sizeof(integer) it can use triangle arrays, using index striding of 1*sizeof(integer) it can handle triangle strips.
|
||||
|
||||
@@ -210,6 +210,26 @@ SIMD_FORCE_INLINE void btTransformAabb(const btVector3& localAabbMin,const btVec
|
||||
aabbMaxOut = center+extent;
|
||||
}
|
||||
|
||||
#define USE_BANCHLESS 1
|
||||
#ifdef USE_BANCHLESS
|
||||
//This block replaces the block below and uses no branches, and replaces the 8 bit return with a 32 bit return for improved performance (~3x on XBox 360)
|
||||
SIMD_FORCE_INLINE unsigned testQuantizedAabbAgainstQuantizedAabb(const unsigned short int* aabbMin1,const unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2)
|
||||
{
|
||||
return static_cast<unsigned int>(btSelect((unsigned)((aabbMin1[0] <= aabbMax2[0]) & (aabbMax1[0] >= aabbMin2[0])
|
||||
& (aabbMin1[2] <= aabbMax2[2]) & (aabbMax1[2] >= aabbMin2[2])
|
||||
& (aabbMin1[1] <= aabbMax2[1]) & (aabbMax1[1] >= aabbMin2[1])),
|
||||
1, 0));
|
||||
}
|
||||
#else
|
||||
SIMD_FORCE_INLINE bool testQuantizedAabbAgainstQuantizedAabb(const unsigned short int* aabbMin1,const unsigned short int* aabbMax1,const unsigned short int* aabbMin2,const unsigned short int* aabbMax2)
|
||||
{
|
||||
bool overlap = true;
|
||||
overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap;
|
||||
overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap;
|
||||
overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap;
|
||||
return overlap;
|
||||
}
|
||||
#endif //USE_BANCHLESS
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
Reference in New Issue
Block a user