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 "../ConstraintDemo/ConstraintDemo.h"
|
||||||
#include "../Benchmarks/BenchmarkDemo.h"
|
#include "../Benchmarks/BenchmarkDemo.h"
|
||||||
#include "../SoftDemo/SoftDemo.h"
|
#include "../SoftDemo/SoftDemo.h"
|
||||||
|
#include "../TerrainDemo/TerrainDemo.h"
|
||||||
|
|
||||||
|
|
||||||
#include "GlutStuff.h"//OpenGL stuff
|
#include "GlutStuff.h"//OpenGL stuff
|
||||||
@@ -145,6 +146,7 @@ btDemoEntry g_demoEntries[] =
|
|||||||
{"Raytracer Test",Raytracer::Create},
|
{"Raytracer Test",Raytracer::Create},
|
||||||
{"GjkConvexCast",LinearConvexCastDemo::Create},
|
{"GjkConvexCast",LinearConvexCastDemo::Create},
|
||||||
{"VehicleDemo",VehicleDemo::Create},
|
{"VehicleDemo",VehicleDemo::Create},
|
||||||
|
{"TerrainDemo",btCreateTerrainDemo},
|
||||||
{"Benchmark 3000 FALL",BenchmarkDemo1::Create},
|
{"Benchmark 3000 FALL",BenchmarkDemo1::Create},
|
||||||
{"Benchmark 1000 STACK",BenchmarkDemo2::Create},
|
{"Benchmark 1000 STACK",BenchmarkDemo2::Create},
|
||||||
{"Benchmark 136 RAGDOLLS",BenchmarkDemo3::Create},
|
{"Benchmark 136 RAGDOLLS",BenchmarkDemo3::Create},
|
||||||
|
|||||||
@@ -21,4 +21,5 @@ FrameWorkDemo AllBulletDemos :
|
|||||||
../VehicleDemo/VehicleDemo.cpp
|
../VehicleDemo/VehicleDemo.cpp
|
||||||
../SoftDemo/SoftDemo.cpp
|
../SoftDemo/SoftDemo.cpp
|
||||||
../ConstraintDemo/ConstraintDemo.cpp
|
../ConstraintDemo/ConstraintDemo.cpp
|
||||||
|
../TerrainDemo/TerrainDemo.cpp
|
||||||
;
|
;
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ AllBulletDemo_SOURCES=\
|
|||||||
../GjkConvexCastDemo/LinearConvexCastDemo.cpp \
|
../GjkConvexCastDemo/LinearConvexCastDemo.cpp \
|
||||||
../ConcaveDemo/ConcavePhysicsDemo.cpp \
|
../ConcaveDemo/ConcavePhysicsDemo.cpp \
|
||||||
../DynamicControlDemo/MotorDemo.cpp \
|
../DynamicControlDemo/MotorDemo.cpp \
|
||||||
|
../TerrainDemo/TerrainDemo.cpp \
|
||||||
DemoEntries.cpp \
|
DemoEntries.cpp \
|
||||||
DemoEntries.h\
|
DemoEntries.h\
|
||||||
Main.cpp
|
Main.cpp
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
if (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
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")
|
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")
|
endif (CMAKE_SIZEOF_VOID_P MATCHES "8")
|
||||||
|
|||||||
@@ -101,4 +101,5 @@ SubInclude TOP Demos GjkConvexCastDemo ;
|
|||||||
SubInclude TOP Demos Raytracer ;
|
SubInclude TOP Demos Raytracer ;
|
||||||
SubInclude TOP Demos SimplexDemo ;
|
SubInclude TOP Demos SimplexDemo ;
|
||||||
SubInclude TOP Demos DoublePrecisionDemo ;
|
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.
|
# Emit generated files.
|
||||||
#----------------------------------------------------------------------------
|
#----------------------------------------------------------------------------
|
||||||
CS_JAMCONFIG_OUTPUT([Jamconfig])
|
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_OUTPUT
|
||||||
|
|
||||||
AC_MSG_NOTICE([
|
AC_MSG_NOTICE([
|
||||||
|
|||||||
@@ -310,26 +310,7 @@ protected:
|
|||||||
void walkRecursiveQuantizedTreeAgainstQuantizedTree(const btQuantizedBvhNode* treeNodeA,const btQuantizedBvhNode* treeNodeB,btNodeOverlapCallback* nodeCallback) const;
|
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);
|
void updateSubtreeHeaders(int leftChildNodexIndex,int rightChildNodexIndex);
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,16 @@ subject to the following restrictions:
|
|||||||
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types
|
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types
|
||||||
#include "btTriangleCallback.h"
|
#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.
|
///The btConcaveShape class provides an interface for non-moving (static) concave shapes.
|
||||||
///It has been implemented by the btStaticPlaneShape, btBvhTriangleMeshShape and btHeightfieldTerrainShape.
|
///It has been implemented by the btStaticPlaneShape, btBvhTriangleMeshShape and btHeightfieldTerrainShape.
|
||||||
|
|||||||
@@ -18,71 +18,107 @@ subject to the following restrictions:
|
|||||||
#include "LinearMath/btTransformUtil.h"
|
#include "LinearMath/btTransformUtil.h"
|
||||||
|
|
||||||
|
|
||||||
btHeightfieldTerrainShape::btHeightfieldTerrainShape(int heightStickWidth, int heightStickLength,void* heightfieldData,btScalar maxHeight,int upAxis,bool useFloatData,bool flipQuadEdges)
|
|
||||||
: btConcaveShape (), m_heightStickWidth(heightStickWidth),
|
btHeightfieldTerrainShape::btHeightfieldTerrainShape
|
||||||
m_heightStickLength(heightStickLength),
|
(
|
||||||
m_maxHeight(maxHeight),
|
int heightStickWidth, int heightStickLength, void* heightfieldData,
|
||||||
m_width((btScalar)heightStickWidth-1),
|
btScalar heightScale, btScalar minHeight, btScalar maxHeight,int upAxis,
|
||||||
m_length((btScalar)heightStickLength-1),
|
PHY_ScalarType hdt, bool flipQuadEdges
|
||||||
m_heightfieldDataUnknown(heightfieldData),
|
)
|
||||||
m_useFloatData(useFloatData),
|
|
||||||
m_flipQuadEdges(flipQuadEdges),
|
|
||||||
m_useDiamondSubdivision(false),
|
|
||||||
m_upAxis(upAxis),
|
|
||||||
m_localScaling(btScalar(1.),btScalar(1.),btScalar(1.))
|
|
||||||
{
|
{
|
||||||
|
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_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;
|
// determine min/max axis-aligned bounding box (aabb) values
|
||||||
|
|
||||||
//enlarge the AABB to avoid division by zero when initializing the quantization values
|
|
||||||
btVector3 clampValue(quantizationMargin,quantizationMargin,quantizationMargin);
|
|
||||||
|
|
||||||
btVector3 halfExtents(0,0,0);
|
|
||||||
|
|
||||||
switch (m_upAxis)
|
switch (m_upAxis)
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
{
|
{
|
||||||
halfExtents.setValue(
|
m_localAabbMin.setValue(m_minHeight, 0, 0);
|
||||||
btScalar(m_maxHeight),
|
m_localAabbMax.setValue(m_maxHeight, m_width, m_length);
|
||||||
btScalar(m_width), //?? don't know if this should change
|
|
||||||
btScalar(m_length));
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 1:
|
case 1:
|
||||||
{
|
{
|
||||||
halfExtents.setValue(
|
m_localAabbMin.setValue(0, m_minHeight, 0);
|
||||||
btScalar(m_width),
|
m_localAabbMax.setValue(m_width, m_maxHeight, m_length);
|
||||||
btScalar(m_maxHeight),
|
|
||||||
btScalar(m_length));
|
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
case 2:
|
case 2:
|
||||||
{
|
{
|
||||||
halfExtents.setValue(
|
m_localAabbMin.setValue(0, 0, m_minHeight);
|
||||||
btScalar(m_width),
|
m_localAabbMax.setValue(m_width, m_length, m_maxHeight);
|
||||||
btScalar(m_length),
|
|
||||||
btScalar(m_maxHeight)
|
|
||||||
);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
//need to get valid m_upAxis
|
//need to get valid m_upAxis
|
||||||
btAssert(0);
|
btAssert(0 && "Bad m_upAxis");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
halfExtents*= btScalar(0.5);
|
// remember origin (defined as exact middle of aabb)
|
||||||
|
m_localOrigin = btScalar(0.5) * (m_localAabbMin + m_localAabbMax);
|
||||||
m_localAabbMin = -halfExtents - clampValue;
|
|
||||||
m_localAabbMax = halfExtents + clampValue;
|
|
||||||
btVector3 aabbSize = m_localAabbMax - m_localAabbMin;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
btHeightfieldTerrainShape::~btHeightfieldTerrainShape()
|
btHeightfieldTerrainShape::~btHeightfieldTerrainShape()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -92,33 +128,53 @@ btHeightfieldTerrainShape::~btHeightfieldTerrainShape()
|
|||||||
void btHeightfieldTerrainShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
|
void btHeightfieldTerrainShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
|
||||||
{
|
{
|
||||||
btVector3 halfExtents = (m_localAabbMax-m_localAabbMin)* m_localScaling * btScalar(0.5);
|
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();
|
btMatrix3x3 abs_b = t.getBasis().absolute();
|
||||||
btVector3 center = t.getOrigin();
|
btVector3 center = t.getOrigin();
|
||||||
btVector3 extent = btVector3(abs_b[0].dot(halfExtents),
|
btVector3 extent = btVector3(abs_b[0].dot(halfExtents),
|
||||||
abs_b[1].dot(halfExtents),
|
abs_b[1].dot(halfExtents),
|
||||||
abs_b[2].dot(halfExtents));
|
abs_b[2].dot(halfExtents));
|
||||||
|
extent += btVector3(getMargin(),getMargin(),getMargin());
|
||||||
|
|
||||||
aabbMin = center - extent;
|
aabbMin = center - extent;
|
||||||
aabbMax = center + extent;
|
aabbMax = center + extent;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
btScalar btHeightfieldTerrainShape::getHeightFieldValue(int x,int y) const
|
btScalar btHeightfieldTerrainShape::getHeightFieldValue(int x,int y) const
|
||||||
{
|
{
|
||||||
btScalar val = 0.f;
|
btScalar val = 0.f;
|
||||||
if (m_useFloatData)
|
switch (m_heightDataType)
|
||||||
{
|
{
|
||||||
val = m_heightfieldDataFloat[(y*m_heightStickWidth)+x];
|
case PHY_FLOAT:
|
||||||
} else
|
{
|
||||||
{
|
val = m_heightfieldDataFloat[(y*m_heightStickWidth)+x];
|
||||||
//assume unsigned short int
|
break;
|
||||||
unsigned char heightFieldValue = m_heightfieldDataUnsignedChar[(y*m_heightStickWidth)+x];
|
}
|
||||||
val = heightFieldValue* (m_maxHeight/btScalar(65535));
|
|
||||||
|
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;
|
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
|
void btHeightfieldTerrainShape::quantizeWithClamp(int* out, const btVector3& point,int /*isMax*/) const
|
||||||
{
|
{
|
||||||
btVector3 clampedPoint(point);
|
btVector3 clampedPoint(point);
|
||||||
clampedPoint.setMax(m_localAabbMin);
|
clampedPoint.setMax(m_localAabbMin);
|
||||||
clampedPoint.setMin(m_localAabbMax);
|
clampedPoint.setMin(m_localAabbMax);
|
||||||
|
|
||||||
btVector3 v = (clampedPoint);// - m_bvhAabbMin) * m_bvhQuantization;
|
out[0] = getQuantized(clampedPoint.getX());
|
||||||
|
out[1] = getQuantized(clampedPoint.getY());
|
||||||
///@todo: optimization: check out how to removed this btFabs
|
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 btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const
|
||||||
{
|
{
|
||||||
(void)callback;
|
// scale down the input aabb's so they are in local (non-scaled) coordinates
|
||||||
(void)aabbMax;
|
|
||||||
(void)aabbMin;
|
|
||||||
|
|
||||||
//quantize the aabbMin and aabbMax, and adjust the start/end ranges
|
|
||||||
|
|
||||||
int quantizedAabbMin[3];
|
|
||||||
int quantizedAabbMax[3];
|
|
||||||
|
|
||||||
btVector3 localAabbMin = aabbMin*btVector3(1.f/m_localScaling[0],1.f/m_localScaling[1],1.f/m_localScaling[2]);
|
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]);
|
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(quantizedAabbMin, localAabbMin,0);
|
||||||
quantizeWithClamp(quantizedAabbMax, localAabbMax,1);
|
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 startX=0;
|
||||||
int endX=m_heightStickWidth-1;
|
int endX=m_heightStickWidth-1;
|
||||||
@@ -223,11 +311,6 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
|
|||||||
{
|
{
|
||||||
case 0:
|
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)
|
if (quantizedAabbMin[1]>startX)
|
||||||
startX = quantizedAabbMin[1];
|
startX = quantizedAabbMin[1];
|
||||||
if (quantizedAabbMax[1]<endX)
|
if (quantizedAabbMax[1]<endX)
|
||||||
@@ -240,11 +323,6 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
|
|||||||
}
|
}
|
||||||
case 1:
|
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)
|
if (quantizedAabbMin[0]>startX)
|
||||||
startX = quantizedAabbMin[0];
|
startX = quantizedAabbMin[0];
|
||||||
if (quantizedAabbMax[0]<endX)
|
if (quantizedAabbMax[0]<endX)
|
||||||
@@ -257,11 +335,6 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback
|
|||||||
};
|
};
|
||||||
case 2:
|
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)
|
if (quantizedAabbMin[0]>startX)
|
||||||
startX = quantizedAabbMin[0];
|
startX = quantizedAabbMin[0];
|
||||||
if (quantizedAabbMax[0]<endX)
|
if (quantizedAabbMax[0]<endX)
|
||||||
|
|||||||
@@ -18,28 +18,69 @@ subject to the following restrictions:
|
|||||||
|
|
||||||
#include "btConcaveShape.h"
|
#include "btConcaveShape.h"
|
||||||
|
|
||||||
///The btHeightfieldTerrainShape simulates a 2D heightfield terrain collision shape. You can also use the more general btBvhTriangleMeshShape instead.
|
///btHeightfieldTerrainShape simulates a 2D heightfield terrain
|
||||||
///An example implementation of btHeightfieldTerrainShape is provided in Demos/VehicleDemo/VehicleDemo.cpp
|
/**
|
||||||
|
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
|
class btHeightfieldTerrainShape : public btConcaveShape
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
btVector3 m_localAabbMin;
|
btVector3 m_localAabbMin;
|
||||||
btVector3 m_localAabbMax;
|
btVector3 m_localAabbMax;
|
||||||
|
btVector3 m_localOrigin;
|
||||||
|
|
||||||
///terrain data
|
///terrain data
|
||||||
int m_heightStickWidth;
|
int m_heightStickWidth;
|
||||||
int m_heightStickLength;
|
int m_heightStickLength;
|
||||||
|
btScalar m_minHeight;
|
||||||
btScalar m_maxHeight;
|
btScalar m_maxHeight;
|
||||||
btScalar m_width;
|
btScalar m_width;
|
||||||
btScalar m_length;
|
btScalar m_length;
|
||||||
|
btScalar m_heightScale;
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
unsigned char* m_heightfieldDataUnsignedChar;
|
unsigned char* m_heightfieldDataUnsignedChar;
|
||||||
|
short* m_heightfieldDataShort;
|
||||||
btScalar* m_heightfieldDataFloat;
|
btScalar* m_heightfieldDataFloat;
|
||||||
void* m_heightfieldDataUnknown;
|
void* m_heightfieldDataUnknown;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool m_useFloatData;
|
PHY_ScalarType m_heightDataType;
|
||||||
bool m_flipQuadEdges;
|
bool m_flipQuadEdges;
|
||||||
bool m_useDiamondSubdivision;
|
bool m_useDiamondSubdivision;
|
||||||
|
|
||||||
@@ -51,17 +92,39 @@ protected:
|
|||||||
void quantizeWithClamp(int* out, const btVector3& point,int isMax) const;
|
void quantizeWithClamp(int* out, const btVector3& point,int isMax) const;
|
||||||
void getVertex(int x,int y,btVector3& vertex) 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;
|
/// protected initialization
|
||||||
overlap = (aabbMin1[0] > aabbMax2[0] || aabbMax1[0] < aabbMin2[0]) ? false : overlap;
|
/**
|
||||||
overlap = (aabbMin1[2] > aabbMax2[2] || aabbMax1[2] < aabbMin2[2]) ? false : overlap;
|
Handles the work of constructors so that public constructors can be
|
||||||
overlap = (aabbMin1[1] > aabbMax2[1] || aabbMax1[1] < aabbMin2[1]) ? false : overlap;
|
backwards-compatible without a lot of copy/paste.
|
||||||
return overlap;
|
*/
|
||||||
}
|
void initialize(int heightStickWidth, int heightStickLength,
|
||||||
|
void* heightfieldData, btScalar heightScale,
|
||||||
|
btScalar minHeight, btScalar maxHeight, int upAxis,
|
||||||
|
PHY_ScalarType heightDataType, bool flipQuadEdges);
|
||||||
|
|
||||||
public:
|
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();
|
virtual ~btHeightfieldTerrainShape();
|
||||||
|
|
||||||
|
|||||||
@@ -18,16 +18,9 @@ subject to the following restrictions:
|
|||||||
|
|
||||||
#include "LinearMath/btVector3.h"
|
#include "LinearMath/btVector3.h"
|
||||||
#include "btTriangleCallback.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.
|
/// 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.
|
/// 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;
|
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
|
#endif
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user