soc-2008-mxcurioni: FRS_glBlendEquation files

This commit is contained in:
Maxime Curioni
2008-09-28 18:43:51 +00:00
parent 5812c494a5
commit bb8477ec57
67 changed files with 18788 additions and 17 deletions

View File

@@ -0,0 +1,1289 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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.
*/
///btDbvt implementation by Nathanael Presson
#include "btDbvt.h"
//
typedef btAlignedObjectArray<btDbvtNode*> tNodeArray;
typedef btAlignedObjectArray<const btDbvtNode*> tConstNodeArray;
//
struct btDbvtNodeEnumerator : btDbvt::ICollide
{
tConstNodeArray nodes;
void Process(const btDbvtNode* n) { nodes.push_back(n); }
};
//
static DBVT_INLINE int indexof(const btDbvtNode* node)
{
return(node->parent->childs[1]==node);
}
//
static DBVT_INLINE btDbvtVolume merge( const btDbvtVolume& a,
const btDbvtVolume& b)
{
#if DBVT_MERGE_IMPL==DBVT_IMPL_SSE
DBVT_ALIGN char locals[sizeof(btDbvtAabbMm)];
btDbvtVolume& res=*(btDbvtVolume*)locals;
#else
btDbvtVolume res;
#endif
Merge(a,b,res);
return(res);
}
// volume+edge lengths
static DBVT_INLINE btScalar size(const btDbvtVolume& a)
{
const btVector3 edges=a.Lengths();
return( edges.x()*edges.y()*edges.z()+
edges.x()+edges.y()+edges.z());
}
//
static void getmaxdepth(const btDbvtNode* node,int depth,int& maxdepth)
{
if(node->isinternal())
{
getmaxdepth(node->childs[0],depth+1,maxdepth);
getmaxdepth(node->childs[0],depth+1,maxdepth);
} else maxdepth=btMax(maxdepth,depth);
}
//
static DBVT_INLINE void deletenode( btDbvt* pdbvt,
btDbvtNode* node)
{
btAlignedFree(pdbvt->m_free);
pdbvt->m_free=node;
}
//
static void recursedeletenode( btDbvt* pdbvt,
btDbvtNode* node)
{
if(!node->isleaf())
{
recursedeletenode(pdbvt,node->childs[0]);
recursedeletenode(pdbvt,node->childs[1]);
}
if(node==pdbvt->m_root) pdbvt->m_root=0;
deletenode(pdbvt,node);
}
//
static DBVT_INLINE btDbvtNode* createnode( btDbvt* pdbvt,
btDbvtNode* parent,
void* data)
{
btDbvtNode* node;
if(pdbvt->m_free)
{ node=pdbvt->m_free;pdbvt->m_free=0; }
else
{ node=new(btAlignedAlloc(sizeof(btDbvtNode),16)) btDbvtNode(); }
node->parent = parent;
node->data = data;
node->childs[1] = 0;
return(node);
}
//
static DBVT_INLINE btDbvtNode* createnode( btDbvt* pdbvt,
btDbvtNode* parent,
const btDbvtVolume& volume,
void* data)
{
btDbvtNode* node=createnode(pdbvt,parent,data);
node->volume=volume;
return(node);
}
//
static DBVT_INLINE btDbvtNode* createnode( btDbvt* pdbvt,
btDbvtNode* parent,
const btDbvtVolume& volume0,
const btDbvtVolume& volume1,
void* data)
{
btDbvtNode* node=createnode(pdbvt,parent,data);
Merge(volume0,volume1,node->volume);
return(node);
}
//
static void insertleaf( btDbvt* pdbvt,
btDbvtNode* root,
btDbvtNode* leaf)
{
if(!pdbvt->m_root)
{
pdbvt->m_root = leaf;
leaf->parent = 0;
}
else
{
if(!root->isleaf())
{
do {
root=root->childs[Select( leaf->volume,
root->childs[0]->volume,
root->childs[1]->volume)];
} while(!root->isleaf());
}
btDbvtNode* prev=root->parent;
btDbvtNode* node=createnode(pdbvt,prev,leaf->volume,root->volume,0);
if(prev)
{
prev->childs[indexof(root)] = node;
node->childs[0] = root;root->parent=node;
node->childs[1] = leaf;leaf->parent=node;
do {
if(!prev->volume.Contain(node->volume))
Merge(prev->childs[0]->volume,prev->childs[1]->volume,prev->volume);
else
break;
node=prev;
} while(0!=(prev=node->parent));
}
else
{
node->childs[0] = root;root->parent=node;
node->childs[1] = leaf;leaf->parent=node;
pdbvt->m_root = node;
}
}
}
//
static btDbvtNode* removeleaf( btDbvt* pdbvt,
btDbvtNode* leaf)
{
if(leaf==pdbvt->m_root)
{
pdbvt->m_root=0;
return(0);
}
else
{
btDbvtNode* parent=leaf->parent;
btDbvtNode* prev=parent->parent;
btDbvtNode* sibling=parent->childs[1-indexof(leaf)];
if(prev)
{
prev->childs[indexof(parent)]=sibling;
sibling->parent=prev;
deletenode(pdbvt,parent);
while(prev)
{
const btDbvtVolume pb=prev->volume;
Merge(prev->childs[0]->volume,prev->childs[1]->volume,prev->volume);
if(NotEqual(pb,prev->volume))
{
prev=prev->parent;
} else break;
}
return(prev?prev:pdbvt->m_root);
}
else
{
pdbvt->m_root=sibling;
sibling->parent=0;
deletenode(pdbvt,parent);
return(pdbvt->m_root);
}
}
}
//
static void fetchleaves(btDbvt* pdbvt,
btDbvtNode* root,
tNodeArray& leaves,
int depth=-1)
{
if(root->isinternal()&&depth)
{
fetchleaves(pdbvt,root->childs[0],leaves,depth-1);
fetchleaves(pdbvt,root->childs[1],leaves,depth-1);
deletenode(pdbvt,root);
}
else
{
leaves.push_back(root);
}
}
//
static void split( const tNodeArray& leaves,
tNodeArray& left,
tNodeArray& right,
const btVector3& org,
const btVector3& axis)
{
left.resize(0);
right.resize(0);
for(int i=0,ni=leaves.size();i<ni;++i)
{
if(dot(axis,leaves[i]->volume.Center()-org)<0)
left.push_back(leaves[i]);
else
right.push_back(leaves[i]);
}
}
//
static btDbvtVolume bounds( const tNodeArray& leaves)
{
#if DBVT_MERGE_IMPL==DBVT_IMPL_SSE
DBVT_ALIGN char locals[sizeof(btDbvtVolume)];
btDbvtVolume& volume=*(btDbvtVolume*)locals;
volume=leaves[0]->volume;
#else
btDbvtVolume volume=leaves[0]->volume;
#endif
for(int i=1,ni=leaves.size();i<ni;++i)
{
Merge(volume,leaves[i]->volume,volume);
}
return(volume);
}
//
static void bottomup( btDbvt* pdbvt,
tNodeArray& leaves)
{
while(leaves.size()>1)
{
btScalar minsize=SIMD_INFINITY;
int minidx[2]={-1,-1};
for(int i=0;i<leaves.size();++i)
{
for(int j=i+1;j<leaves.size();++j)
{
const btScalar sz=size(merge(leaves[i]->volume,leaves[j]->volume));
if(sz<minsize)
{
minsize = sz;
minidx[0] = i;
minidx[1] = j;
}
}
}
btDbvtNode* n[] = {leaves[minidx[0]],leaves[minidx[1]]};
btDbvtNode* p = createnode(pdbvt,0,n[0]->volume,n[1]->volume,0);
p->childs[0] = n[0];
p->childs[1] = n[1];
n[0]->parent = p;
n[1]->parent = p;
leaves[minidx[0]] = p;
leaves.swap(minidx[1],leaves.size()-1);
leaves.pop_back();
}
}
//
static btDbvtNode* topdown(btDbvt* pdbvt,
tNodeArray& leaves,
int bu_treshold)
{
static const btVector3 axis[]={btVector3(1,0,0),
btVector3(0,1,0),
btVector3(0,0,1)};
if(leaves.size()>1)
{
if(leaves.size()>bu_treshold)
{
const btDbvtVolume vol=bounds(leaves);
const btVector3 org=vol.Center();
tNodeArray sets[2];
int bestaxis=-1;
int bestmidp=leaves.size();
int splitcount[3][2]={{0,0},{0,0},{0,0}};
int i;
for( i=0;i<leaves.size();++i)
{
const btVector3 x=leaves[i]->volume.Center()-org;
for(int j=0;j<3;++j)
{
++splitcount[j][dot(x,axis[j])>0?1:0];
}
}
for( i=0;i<3;++i)
{
if((splitcount[i][0]>0)&&(splitcount[i][1]>0))
{
const int midp=(int)btFabs(btScalar(splitcount[i][0]-splitcount[i][1]));
if(midp<bestmidp)
{
bestaxis=i;
bestmidp=midp;
}
}
}
if(bestaxis>=0)
{
sets[0].reserve(splitcount[bestaxis][0]);
sets[1].reserve(splitcount[bestaxis][1]);
split(leaves,sets[0],sets[1],org,axis[bestaxis]);
}
else
{
sets[0].reserve(leaves.size()/2+1);
sets[1].reserve(leaves.size()/2);
for(int i=0,ni=leaves.size();i<ni;++i)
{
sets[i&1].push_back(leaves[i]);
}
}
btDbvtNode* node=createnode(pdbvt,0,vol,0);
node->childs[0]=topdown(pdbvt,sets[0],bu_treshold);
node->childs[1]=topdown(pdbvt,sets[1],bu_treshold);
node->childs[0]->parent=node;
node->childs[1]->parent=node;
return(node);
}
else
{
bottomup(pdbvt,leaves);
return(leaves[0]);
}
}
return(leaves[0]);
}
//
static DBVT_INLINE btDbvtNode* sort(btDbvtNode* n,btDbvtNode*& r)
{
btDbvtNode* p=n->parent;
btAssert(n->isinternal());
if(p>n)
{
const int i=indexof(n);
const int j=1-i;
btDbvtNode* s=p->childs[j];
btDbvtNode* q=p->parent;
btAssert(n==p->childs[i]);
if(q) q->childs[indexof(p)]=n; else r=n;
s->parent=n;
p->parent=n;
n->parent=q;
p->childs[0]=n->childs[0];
p->childs[1]=n->childs[1];
n->childs[0]->parent=p;
n->childs[1]->parent=p;
n->childs[i]=p;
n->childs[j]=s;
btSwap(p->volume,n->volume);
return(p);
}
return(n);
}
//
static DBVT_INLINE btDbvtNode* walkup(btDbvtNode* n,int count)
{
while(n&&(count--)) n=n->parent;
return(n);
}
//
// Api
//
//
btDbvt::btDbvt()
{
m_root = 0;
m_free = 0;
m_lkhd = -1;
m_leaves = 0;
m_opath = 0;
}
//
btDbvt::~btDbvt()
{
clear();
}
//
void btDbvt::clear()
{
if(m_root) recursedeletenode(this,m_root);
btAlignedFree(m_free);
m_free=0;
}
//
void btDbvt::optimizeBottomUp()
{
if(m_root)
{
tNodeArray leaves;
leaves.reserve(m_leaves);
fetchleaves(this,m_root,leaves);
bottomup(this,leaves);
m_root=leaves[0];
}
}
//
void btDbvt::optimizeTopDown(int bu_treshold)
{
if(m_root)
{
tNodeArray leaves;
leaves.reserve(m_leaves);
fetchleaves(this,m_root,leaves);
m_root=topdown(this,leaves,bu_treshold);
}
}
//
void btDbvt::optimizeIncremental(int passes)
{
if(passes<0) passes=m_leaves;
if(m_root&&(passes>0))
{
do {
btDbvtNode* node=m_root;
unsigned bit=0;
while(node->isinternal())
{
node=sort(node,m_root)->childs[(m_opath>>bit)&1];
bit=(bit+1)&(sizeof(unsigned)*8-1);
}
update(node);
++m_opath;
} while(--passes);
}
}
//
btDbvtNode* btDbvt::insert(const btDbvtVolume& volume,void* data)
{
btDbvtNode* leaf=createnode(this,0,volume,data);
insertleaf(this,m_root,leaf);
++m_leaves;
return(leaf);
}
//
void btDbvt::update(btDbvtNode* leaf,int lookahead)
{
btDbvtNode* root=removeleaf(this,leaf);
if(root)
{
if(lookahead>=0)
{
for(int i=0;(i<lookahead)&&root->parent;++i)
{
root=root->parent;
}
} else root=m_root;
}
insertleaf(this,root,leaf);
}
//
void btDbvt::update(btDbvtNode* leaf,const btDbvtVolume& volume)
{
btDbvtNode* root=removeleaf(this,leaf);
if(root)
{
if(m_lkhd>=0)
{
for(int i=0;(i<m_lkhd)&&root->parent;++i)
{
root=root->parent;
}
} else root=m_root;
}
leaf->volume=volume;
insertleaf(this,root,leaf);
}
//
bool btDbvt::update(btDbvtNode* leaf,btDbvtVolume volume,const btVector3& velocity,btScalar margin)
{
if(leaf->volume.Contain(volume)) return(false);
volume.Expand(btVector3(margin,margin,margin));
volume.SignedExpand(velocity);
update(leaf,volume);
return(true);
}
//
bool btDbvt::update(btDbvtNode* leaf,btDbvtVolume volume,const btVector3& velocity)
{
if(leaf->volume.Contain(volume)) return(false);
volume.SignedExpand(velocity);
update(leaf,volume);
return(true);
}
//
bool btDbvt::update(btDbvtNode* leaf,btDbvtVolume volume,btScalar margin)
{
if(leaf->volume.Contain(volume)) return(false);
volume.Expand(btVector3(margin,margin,margin));
update(leaf,volume);
return(true);
}
//
void btDbvt::remove(btDbvtNode* leaf)
{
removeleaf(this,leaf);
deletenode(this,leaf);
--m_leaves;
}
//
void btDbvt::write(IWriter* iwriter) const
{
btDbvtNodeEnumerator nodes;
nodes.nodes.reserve(m_leaves*2);
enumNodes(m_root,nodes);
iwriter->Prepare(m_root,nodes.nodes.size());
for(int i=0;i<nodes.nodes.size();++i)
{
const btDbvtNode* n=nodes.nodes[i];
int p=-1;
if(n->parent) p=nodes.nodes.findLinearSearch(n->parent);
if(n->isinternal())
{
const int c0=nodes.nodes.findLinearSearch(n->childs[0]);
const int c1=nodes.nodes.findLinearSearch(n->childs[1]);
iwriter->WriteNode(n,i,p,c0,c1);
}
else
{
iwriter->WriteLeaf(n,i,p);
}
}
}
//
void btDbvt::clone(btDbvt& dest,IClone* iclone) const
{
dest.clear();
if(m_root!=0)
{
btAlignedObjectArray<sStkCLN> stack;
stack.reserve(m_leaves);
stack.push_back(sStkCLN(m_root,0));
do {
const int i=stack.size()-1;
const sStkCLN e=stack[i];
btDbvtNode* n=createnode(&dest,e.parent,e.node->volume,e.node->data);
stack.pop_back();
if(e.parent!=0)
e.parent->childs[i&1]=n;
else
dest.m_root=n;
if(e.node->isinternal())
{
stack.push_back(sStkCLN(e.node->childs[0],n));
stack.push_back(sStkCLN(e.node->childs[1],n));
}
else
{
iclone->CloneLeaf(n);
}
} while(stack.size()>0);
}
}
//
int btDbvt::maxdepth(const btDbvtNode* node)
{
int depth=0;
if(node) getmaxdepth(node,1,depth);
return(depth);
}
//
int btDbvt::countLeaves(const btDbvtNode* node)
{
if(node->isinternal())
return(countLeaves(node->childs[0])+countLeaves(node->childs[1]));
else
return(1);
}
//
void btDbvt::extractLeaves(const btDbvtNode* node,btAlignedObjectArray<const btDbvtNode*>& leaves)
{
if(node->isinternal())
{
extractLeaves(node->childs[0],leaves);
extractLeaves(node->childs[1],leaves);
}
else
{
leaves.push_back(node);
}
}
//
#if DBVT_ENABLE_BENCHMARK
#include <stdio.h>
#include <stdlib.h>
#include "LinearMath/btQuickProf.h"
/*
q6600,2.4ghz
/Ox /Ob2 /Oi /Ot /I "." /I "..\.." /I "..\..\src" /D "NDEBUG" /D "_LIB" /D "_WINDOWS" /D "_CRT_SECURE_NO_DEPRECATE" /D "_CRT_NONSTDC_NO_DEPRECATE" /D "WIN32"
/GF /FD /MT /GS- /Gy /arch:SSE2 /Zc:wchar_t- /Fp"..\..\out\release8\build\libbulletcollision\libbulletcollision.pch"
/Fo"..\..\out\release8\build\libbulletcollision\\"
/Fd"..\..\out\release8\build\libbulletcollision\bulletcollision.pdb"
/W3 /nologo /c /Wp64 /Zi /errorReport:prompt
Benchmarking dbvt...
World scale: 100.000000
Extents base: 1.000000
Extents range: 4.000000
Leaves: 8192
sizeof(btDbvtVolume): 32 bytes
sizeof(btDbvtNode): 44 bytes
[1] btDbvtVolume intersections: 3499 ms (-1%)
[2] btDbvtVolume merges: 1934 ms (0%)
[3] btDbvt::collideTT: 5485 ms (-21%)
[4] btDbvt::collideTT self: 2814 ms (-20%)
[5] btDbvt::collideTT xform: 7379 ms (-1%)
[6] btDbvt::collideTT xform,self: 7270 ms (-2%)
[7] btDbvt::collideRAY: 6314 ms (0%),(332143 r/s)
[8] insert/remove: 2093 ms (0%),(1001983 ir/s)
[9] updates (teleport): 1879 ms (-3%),(1116100 u/s)
[10] updates (jitter): 1244 ms (-4%),(1685813 u/s)
[11] optimize (incremental): 2514 ms (0%),(1668000 o/s)
[12] btDbvtVolume notequal: 3659 ms (0%)
[13] culling(OCL+fullsort): 2218 ms (0%),(461 t/s)
[14] culling(OCL+qsort): 3688 ms (5%),(2221 t/s)
[15] culling(KDOP+qsort): 1139 ms (-1%),(7192 t/s)
[16] insert/remove batch(256): 5092 ms (0%),(823704 bir/s)
[17] btDbvtVolume select: 3419 ms (0%)
*/
struct btDbvtBenchmark
{
struct NilPolicy : btDbvt::ICollide
{
NilPolicy() : m_pcount(0),m_depth(-SIMD_INFINITY),m_checksort(true) {}
void Process(const btDbvtNode*,const btDbvtNode*) { ++m_pcount; }
void Process(const btDbvtNode*) { ++m_pcount; }
void Process(const btDbvtNode*,btScalar depth)
{
++m_pcount;
if(m_checksort)
{ if(depth>=m_depth) m_depth=depth; else printf("wrong depth: %f (should be >= %f)\r\n",depth,m_depth); }
}
int m_pcount;
btScalar m_depth;
bool m_checksort;
};
struct P14 : btDbvt::ICollide
{
struct Node
{
const btDbvtNode* leaf;
btScalar depth;
};
void Process(const btDbvtNode* leaf,btScalar depth)
{
Node n;
n.leaf = leaf;
n.depth = depth;
}
static int sortfnc(const Node& a,const Node& b)
{
if(a.depth<b.depth) return(+1);
if(a.depth>b.depth) return(-1);
return(0);
}
btAlignedObjectArray<Node> m_nodes;
};
struct P15 : btDbvt::ICollide
{
struct Node
{
const btDbvtNode* leaf;
btScalar depth;
};
void Process(const btDbvtNode* leaf)
{
Node n;
n.leaf = leaf;
n.depth = dot(leaf->volume.Center(),m_axis);
}
static int sortfnc(const Node& a,const Node& b)
{
if(a.depth<b.depth) return(+1);
if(a.depth>b.depth) return(-1);
return(0);
}
btAlignedObjectArray<Node> m_nodes;
btVector3 m_axis;
};
static btScalar RandUnit()
{
return(rand()/(btScalar)RAND_MAX);
}
static btVector3 RandVector3()
{
return(btVector3(RandUnit(),RandUnit(),RandUnit()));
}
static btVector3 RandVector3(btScalar cs)
{
return(RandVector3()*cs-btVector3(cs,cs,cs)/2);
}
static btDbvtVolume RandVolume(btScalar cs,btScalar eb,btScalar es)
{
return(btDbvtVolume::FromCE(RandVector3(cs),btVector3(eb,eb,eb)+RandVector3()*es));
}
static btTransform RandTransform(btScalar cs)
{
btTransform t;
t.setOrigin(RandVector3(cs));
t.setRotation(btQuaternion(RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2,RandUnit()*SIMD_PI*2).normalized());
return(t);
}
static void RandTree(btScalar cs,btScalar eb,btScalar es,int leaves,btDbvt& dbvt)
{
dbvt.clear();
for(int i=0;i<leaves;++i)
{
dbvt.insert(RandVolume(cs,eb,es),0);
}
}
};
void btDbvt::benchmark()
{
static const btScalar cfgVolumeCenterScale = 100;
static const btScalar cfgVolumeExentsBase = 1;
static const btScalar cfgVolumeExentsScale = 4;
static const int cfgLeaves = 8192;
static const bool cfgEnable = true;
//[1] btDbvtVolume intersections
bool cfgBenchmark1_Enable = cfgEnable;
static const int cfgBenchmark1_Iterations = 8;
static const int cfgBenchmark1_Reference = 3499;
//[2] btDbvtVolume merges
bool cfgBenchmark2_Enable = cfgEnable;
static const int cfgBenchmark2_Iterations = 4;
static const int cfgBenchmark2_Reference = 1945;
//[3] btDbvt::collideTT
bool cfgBenchmark3_Enable = cfgEnable;
static const int cfgBenchmark3_Iterations = 512;
static const int cfgBenchmark3_Reference = 5485;
//[4] btDbvt::collideTT self
bool cfgBenchmark4_Enable = cfgEnable;
static const int cfgBenchmark4_Iterations = 512;
static const int cfgBenchmark4_Reference = 2814;
//[5] btDbvt::collideTT xform
bool cfgBenchmark5_Enable = cfgEnable;
static const int cfgBenchmark5_Iterations = 512;
static const btScalar cfgBenchmark5_OffsetScale = 2;
static const int cfgBenchmark5_Reference = 7379;
//[6] btDbvt::collideTT xform,self
bool cfgBenchmark6_Enable = cfgEnable;
static const int cfgBenchmark6_Iterations = 512;
static const btScalar cfgBenchmark6_OffsetScale = 2;
static const int cfgBenchmark6_Reference = 7270;
//[7] btDbvt::collideRAY
bool cfgBenchmark7_Enable = cfgEnable;
static const int cfgBenchmark7_Passes = 32;
static const int cfgBenchmark7_Iterations = 65536;
static const int cfgBenchmark7_Reference = 6307;
//[8] insert/remove
bool cfgBenchmark8_Enable = cfgEnable;
static const int cfgBenchmark8_Passes = 32;
static const int cfgBenchmark8_Iterations = 65536;
static const int cfgBenchmark8_Reference = 2105;
//[9] updates (teleport)
bool cfgBenchmark9_Enable = cfgEnable;
static const int cfgBenchmark9_Passes = 32;
static const int cfgBenchmark9_Iterations = 65536;
static const int cfgBenchmark9_Reference = 1879;
//[10] updates (jitter)
bool cfgBenchmark10_Enable = cfgEnable;
static const btScalar cfgBenchmark10_Scale = cfgVolumeCenterScale/10000;
static const int cfgBenchmark10_Passes = 32;
static const int cfgBenchmark10_Iterations = 65536;
static const int cfgBenchmark10_Reference = 1244;
//[11] optimize (incremental)
bool cfgBenchmark11_Enable = cfgEnable;
static const int cfgBenchmark11_Passes = 64;
static const int cfgBenchmark11_Iterations = 65536;
static const int cfgBenchmark11_Reference = 2510;
//[12] btDbvtVolume notequal
bool cfgBenchmark12_Enable = cfgEnable;
static const int cfgBenchmark12_Iterations = 32;
static const int cfgBenchmark12_Reference = 3677;
//[13] culling(OCL+fullsort)
bool cfgBenchmark13_Enable = cfgEnable;
static const int cfgBenchmark13_Iterations = 1024;
static const int cfgBenchmark13_Reference = 2231;
//[14] culling(OCL+qsort)
bool cfgBenchmark14_Enable = cfgEnable;
static const int cfgBenchmark14_Iterations = 8192;
static const int cfgBenchmark14_Reference = 3500;
//[15] culling(KDOP+qsort)
bool cfgBenchmark15_Enable = cfgEnable;
static const int cfgBenchmark15_Iterations = 8192;
static const int cfgBenchmark15_Reference = 1151;
//[16] insert/remove batch
bool cfgBenchmark16_Enable = cfgEnable;
static const int cfgBenchmark16_BatchCount = 256;
static const int cfgBenchmark16_Passes = 16384;
static const int cfgBenchmark16_Reference = 5138;
//[17] select
bool cfgBenchmark17_Enable = cfgEnable;
static const int cfgBenchmark17_Iterations = 4;
static const int cfgBenchmark17_Reference = 3390;
btClock wallclock;
printf("Benchmarking dbvt...\r\n");
printf("\tWorld scale: %f\r\n",cfgVolumeCenterScale);
printf("\tExtents base: %f\r\n",cfgVolumeExentsBase);
printf("\tExtents range: %f\r\n",cfgVolumeExentsScale);
printf("\tLeaves: %u\r\n",cfgLeaves);
printf("\tsizeof(btDbvtVolume): %u bytes\r\n",sizeof(btDbvtVolume));
printf("\tsizeof(btDbvtNode): %u bytes\r\n",sizeof(btDbvtNode));
if(cfgBenchmark1_Enable)
{// Benchmark 1
srand(380843);
btAlignedObjectArray<btDbvtVolume> volumes;
btAlignedObjectArray<bool> results;
volumes.resize(cfgLeaves);
results.resize(cfgLeaves);
for(int i=0;i<cfgLeaves;++i)
{
volumes[i]=btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale);
}
printf("[1] btDbvtVolume intersections: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark1_Iterations;++i)
{
for(int j=0;j<cfgLeaves;++j)
{
for(int k=0;k<cfgLeaves;++k)
{
results[k]=Intersect(volumes[j],volumes[k]);
}
}
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark1_Reference)*100/time);
}
if(cfgBenchmark2_Enable)
{// Benchmark 2
srand(380843);
btAlignedObjectArray<btDbvtVolume> volumes;
btAlignedObjectArray<btDbvtVolume> results;
volumes.resize(cfgLeaves);
results.resize(cfgLeaves);
for(int i=0;i<cfgLeaves;++i)
{
volumes[i]=btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale);
}
printf("[2] btDbvtVolume merges: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark2_Iterations;++i)
{
for(int j=0;j<cfgLeaves;++j)
{
for(int k=0;k<cfgLeaves;++k)
{
Merge(volumes[j],volumes[k],results[k]);
}
}
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark2_Reference)*100/time);
}
if(cfgBenchmark3_Enable)
{// Benchmark 3
srand(380843);
btDbvt dbvt[2];
btDbvtBenchmark::NilPolicy policy;
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt[0]);
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt[1]);
dbvt[0].optimizeTopDown();
dbvt[1].optimizeTopDown();
printf("[3] btDbvt::collideTT: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark3_Iterations;++i)
{
btDbvt::collideTT(dbvt[0].m_root,dbvt[1].m_root,policy);
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark3_Reference)*100/time);
}
if(cfgBenchmark4_Enable)
{// Benchmark 4
srand(380843);
btDbvt dbvt;
btDbvtBenchmark::NilPolicy policy;
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
printf("[4] btDbvt::collideTT self: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark4_Iterations;++i)
{
btDbvt::collideTT(dbvt.m_root,dbvt.m_root,policy);
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark4_Reference)*100/time);
}
if(cfgBenchmark5_Enable)
{// Benchmark 5
srand(380843);
btDbvt dbvt[2];
btAlignedObjectArray<btTransform> transforms;
btDbvtBenchmark::NilPolicy policy;
transforms.resize(cfgBenchmark5_Iterations);
for(int i=0;i<transforms.size();++i)
{
transforms[i]=btDbvtBenchmark::RandTransform(cfgVolumeCenterScale*cfgBenchmark5_OffsetScale);
}
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt[0]);
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt[1]);
dbvt[0].optimizeTopDown();
dbvt[1].optimizeTopDown();
printf("[5] btDbvt::collideTT xform: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark5_Iterations;++i)
{
btDbvt::collideTT(dbvt[0].m_root,dbvt[1].m_root,transforms[i],policy);
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark5_Reference)*100/time);
}
if(cfgBenchmark6_Enable)
{// Benchmark 6
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<btTransform> transforms;
btDbvtBenchmark::NilPolicy policy;
transforms.resize(cfgBenchmark6_Iterations);
for(int i=0;i<transforms.size();++i)
{
transforms[i]=btDbvtBenchmark::RandTransform(cfgVolumeCenterScale*cfgBenchmark6_OffsetScale);
}
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
printf("[6] btDbvt::collideTT xform,self: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark6_Iterations;++i)
{
btDbvt::collideTT(dbvt.m_root,dbvt.m_root,transforms[i],policy);
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark6_Reference)*100/time);
}
if(cfgBenchmark7_Enable)
{// Benchmark 7
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<btVector3> rayorg;
btAlignedObjectArray<btVector3> raydir;
btDbvtBenchmark::NilPolicy policy;
rayorg.resize(cfgBenchmark7_Iterations);
raydir.resize(cfgBenchmark7_Iterations);
for(int i=0;i<rayorg.size();++i)
{
rayorg[i]=btDbvtBenchmark::RandVector3(cfgVolumeCenterScale*2);
raydir[i]=btDbvtBenchmark::RandVector3(cfgVolumeCenterScale*2);
}
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
printf("[7] btDbvt::collideRAY: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark7_Passes;++i)
{
for(int j=0;j<cfgBenchmark7_Iterations;++j)
{
btDbvt::collideRAY(dbvt.m_root,rayorg[j],raydir[j],policy);
}
}
const int time=(int)wallclock.getTimeMilliseconds();
unsigned rays=cfgBenchmark7_Passes*cfgBenchmark7_Iterations;
printf("%u ms (%i%%),(%u r/s)\r\n",time,(time-cfgBenchmark7_Reference)*100/time,(rays*1000)/time);
}
if(cfgBenchmark8_Enable)
{// Benchmark 8
srand(380843);
btDbvt dbvt;
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
printf("[8] insert/remove: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark8_Passes;++i)
{
for(int j=0;j<cfgBenchmark8_Iterations;++j)
{
dbvt.remove(dbvt.insert(btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale),0));
}
}
const int time=(int)wallclock.getTimeMilliseconds();
const int ir=cfgBenchmark8_Passes*cfgBenchmark8_Iterations;
printf("%u ms (%i%%),(%u ir/s)\r\n",time,(time-cfgBenchmark8_Reference)*100/time,ir*1000/time);
}
if(cfgBenchmark9_Enable)
{// Benchmark 9
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<const btDbvtNode*> leaves;
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
dbvt.extractLeaves(dbvt.m_root,leaves);
printf("[9] updates (teleport): ");
wallclock.reset();
for(int i=0;i<cfgBenchmark9_Passes;++i)
{
for(int j=0;j<cfgBenchmark9_Iterations;++j)
{
dbvt.update(const_cast<btDbvtNode*>(leaves[rand()%cfgLeaves]),
btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale));
}
}
const int time=(int)wallclock.getTimeMilliseconds();
const int up=cfgBenchmark9_Passes*cfgBenchmark9_Iterations;
printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark9_Reference)*100/time,up*1000/time);
}
if(cfgBenchmark10_Enable)
{// Benchmark 10
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<const btDbvtNode*> leaves;
btAlignedObjectArray<btVector3> vectors;
vectors.resize(cfgBenchmark10_Iterations);
for(int i=0;i<vectors.size();++i)
{
vectors[i]=(btDbvtBenchmark::RandVector3()*2-btVector3(1,1,1))*cfgBenchmark10_Scale;
}
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
dbvt.extractLeaves(dbvt.m_root,leaves);
printf("[10] updates (jitter): ");
wallclock.reset();
for(int i=0;i<cfgBenchmark10_Passes;++i)
{
for(int j=0;j<cfgBenchmark10_Iterations;++j)
{
const btVector3& d=vectors[j];
btDbvtNode* l=const_cast<btDbvtNode*>(leaves[rand()%cfgLeaves]);
btDbvtVolume v=btDbvtVolume::FromMM(l->volume.Mins()+d,l->volume.Maxs()+d);
dbvt.update(l,v);
}
}
const int time=(int)wallclock.getTimeMilliseconds();
const int up=cfgBenchmark10_Passes*cfgBenchmark10_Iterations;
printf("%u ms (%i%%),(%u u/s)\r\n",time,(time-cfgBenchmark10_Reference)*100/time,up*1000/time);
}
if(cfgBenchmark11_Enable)
{// Benchmark 11
srand(380843);
btDbvt dbvt;
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
printf("[11] optimize (incremental): ");
wallclock.reset();
for(int i=0;i<cfgBenchmark11_Passes;++i)
{
dbvt.optimizeIncremental(cfgBenchmark11_Iterations);
}
const int time=(int)wallclock.getTimeMilliseconds();
const int op=cfgBenchmark11_Passes*cfgBenchmark11_Iterations;
printf("%u ms (%i%%),(%u o/s)\r\n",time,(time-cfgBenchmark11_Reference)*100/time,op/time*1000);
}
if(cfgBenchmark12_Enable)
{// Benchmark 12
srand(380843);
btAlignedObjectArray<btDbvtVolume> volumes;
btAlignedObjectArray<bool> results;
volumes.resize(cfgLeaves);
results.resize(cfgLeaves);
for(int i=0;i<cfgLeaves;++i)
{
volumes[i]=btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale);
}
printf("[12] btDbvtVolume notequal: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark12_Iterations;++i)
{
for(int j=0;j<cfgLeaves;++j)
{
for(int k=0;k<cfgLeaves;++k)
{
results[k]=NotEqual(volumes[j],volumes[k]);
}
}
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark12_Reference)*100/time);
}
if(cfgBenchmark13_Enable)
{// Benchmark 13
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<btVector3> vectors;
btDbvtBenchmark::NilPolicy policy;
vectors.resize(cfgBenchmark13_Iterations);
for(int i=0;i<vectors.size();++i)
{
vectors[i]=(btDbvtBenchmark::RandVector3()*2-btVector3(1,1,1)).normalized();
}
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
printf("[13] culling(OCL+fullsort): ");
wallclock.reset();
for(int i=0;i<cfgBenchmark13_Iterations;++i)
{
static const btScalar offset=0;
policy.m_depth=-SIMD_INFINITY;
dbvt.collideOCL(dbvt.m_root,&vectors[i],&offset,vectors[i],1,policy);
}
const int time=(int)wallclock.getTimeMilliseconds();
const int t=cfgBenchmark13_Iterations;
printf("%u ms (%i%%),(%u t/s)\r\n",time,(time-cfgBenchmark13_Reference)*100/time,(t*1000)/time);
}
if(cfgBenchmark14_Enable)
{// Benchmark 14
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<btVector3> vectors;
btDbvtBenchmark::P14 policy;
vectors.resize(cfgBenchmark14_Iterations);
for(int i=0;i<vectors.size();++i)
{
vectors[i]=(btDbvtBenchmark::RandVector3()*2-btVector3(1,1,1)).normalized();
}
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
policy.m_nodes.reserve(cfgLeaves);
printf("[14] culling(OCL+qsort): ");
wallclock.reset();
for(int i=0;i<cfgBenchmark14_Iterations;++i)
{
static const btScalar offset=0;
policy.m_nodes.resize(0);
dbvt.collideOCL(dbvt.m_root,&vectors[i],&offset,vectors[i],1,policy,false);
policy.m_nodes.quickSort(btDbvtBenchmark::P14::sortfnc);
}
const int time=(int)wallclock.getTimeMilliseconds();
const int t=cfgBenchmark14_Iterations;
printf("%u ms (%i%%),(%u t/s)\r\n",time,(time-cfgBenchmark14_Reference)*100/time,(t*1000)/time);
}
if(cfgBenchmark15_Enable)
{// Benchmark 15
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<btVector3> vectors;
btDbvtBenchmark::P15 policy;
vectors.resize(cfgBenchmark15_Iterations);
for(int i=0;i<vectors.size();++i)
{
vectors[i]=(btDbvtBenchmark::RandVector3()*2-btVector3(1,1,1)).normalized();
}
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
policy.m_nodes.reserve(cfgLeaves);
printf("[15] culling(KDOP+qsort): ");
wallclock.reset();
for(int i=0;i<cfgBenchmark15_Iterations;++i)
{
static const btScalar offset=0;
policy.m_nodes.resize(0);
policy.m_axis=vectors[i];
dbvt.collideKDOP(dbvt.m_root,&vectors[i],&offset,1,policy);
policy.m_nodes.quickSort(btDbvtBenchmark::P15::sortfnc);
}
const int time=(int)wallclock.getTimeMilliseconds();
const int t=cfgBenchmark15_Iterations;
printf("%u ms (%i%%),(%u t/s)\r\n",time,(time-cfgBenchmark15_Reference)*100/time,(t*1000)/time);
}
if(cfgBenchmark16_Enable)
{// Benchmark 16
srand(380843);
btDbvt dbvt;
btAlignedObjectArray<btDbvtNode*> batch;
btDbvtBenchmark::RandTree(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale,cfgLeaves,dbvt);
dbvt.optimizeTopDown();
batch.reserve(cfgBenchmark16_BatchCount);
printf("[16] insert/remove batch(%u): ",cfgBenchmark16_BatchCount);
wallclock.reset();
for(int i=0;i<cfgBenchmark16_Passes;++i)
{
for(int j=0;j<cfgBenchmark16_BatchCount;++j)
{
batch.push_back(dbvt.insert(btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale),0));
}
for(int j=0;j<cfgBenchmark16_BatchCount;++j)
{
dbvt.remove(batch[j]);
}
batch.resize(0);
}
const int time=(int)wallclock.getTimeMilliseconds();
const int ir=cfgBenchmark16_Passes*cfgBenchmark16_BatchCount;
printf("%u ms (%i%%),(%u bir/s)\r\n",time,(time-cfgBenchmark16_Reference)*100/time,int(ir*1000.0/time));
}
if(cfgBenchmark17_Enable)
{// Benchmark 17
srand(380843);
btAlignedObjectArray<btDbvtVolume> volumes;
btAlignedObjectArray<int> results;
btAlignedObjectArray<int> indices;
volumes.resize(cfgLeaves);
results.resize(cfgLeaves);
indices.resize(cfgLeaves);
for(int i=0;i<cfgLeaves;++i)
{
indices[i]=i;
volumes[i]=btDbvtBenchmark::RandVolume(cfgVolumeCenterScale,cfgVolumeExentsBase,cfgVolumeExentsScale);
}
for(int i=0;i<cfgLeaves;++i)
{
btSwap(indices[i],indices[rand()%cfgLeaves]);
}
printf("[17] btDbvtVolume select: ");
wallclock.reset();
for(int i=0;i<cfgBenchmark17_Iterations;++i)
{
for(int j=0;j<cfgLeaves;++j)
{
for(int k=0;k<cfgLeaves;++k)
{
const int idx=indices[k];
results[idx]=Select(volumes[idx],volumes[j],volumes[k]);
}
}
}
const int time=(int)wallclock.getTimeMilliseconds();
printf("%u ms (%i%%)\r\n",time,(time-cfgBenchmark17_Reference)*100/time);
}
printf("\r\n\r\n");
}
#endif

View File

@@ -0,0 +1,1112 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2007 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.
*/
///btDbvt implementation by Nathanael Presson
#ifndef BT_DYNAMIC_BOUNDING_VOLUME_TREE_H
#define BT_DYNAMIC_BOUNDING_VOLUME_TREE_H
#include "LinearMath/btAlignedObjectArray.h"
#include "LinearMath/btVector3.h"
#include "LinearMath/btTransform.h"
//
// Compile time configuration
//
// Implementation profiles
#define DBVT_IMPL_GENERIC 0 // Generic implementation
#define DBVT_IMPL_SSE 1 // SSE
// Template implementation of ICollide
#ifdef WIN32_AVOID_SSE_WHEN_EMBEDDED_INSIDE_BLENDER //there is always some weird compiler that breaks SSE builds
#if (defined (_MSC_VER) && _MSC_VER >= 1400)
#define DBVT_USE_TEMPLATE 1
#else
#define DBVT_USE_TEMPLATE 0
#endif
#else
#define DBVT_USE_TEMPLATE 0
#endif
// Use only intrinsics instead of inline asm
#define DBVT_USE_INTRINSIC_SSE 1
// Using memmov for collideOCL
#define DBVT_USE_MEMMOVE 1
// Enable benchmarking code
#define DBVT_ENABLE_BENCHMARK 0
// Inlining
#define DBVT_INLINE SIMD_FORCE_INLINE
// Align
#ifdef WIN32
#define DBVT_ALIGN __declspec(align(16))
#else
#define DBVT_ALIGN
#endif
// Specific methods implementation
#ifdef WIN32_AVOID_SSE_WHEN_EMBEDDED_INSIDE_BLENDER //there is always some weird compiler that breaks SSE builds
#define DBVT_SELECT_IMPL DBVT_IMPL_SSE
#define DBVT_MERGE_IMPL DBVT_IMPL_SSE
#define DBVT_INT0_IMPL DBVT_IMPL_SSE
#else
#define DBVT_SELECT_IMPL DBVT_IMPL_GENERIC
#define DBVT_MERGE_IMPL DBVT_IMPL_GENERIC
#define DBVT_INT0_IMPL DBVT_IMPL_GENERIC
#endif
#if (DBVT_SELECT_IMPL==DBVT_IMPL_SSE)|| \
(DBVT_MERGE_IMPL==DBVT_IMPL_SSE)|| \
(DBVT_INT0_IMPL==DBVT_IMPL_SSE)
#include <emmintrin.h>
#endif
//
// Auto config and checks
//
#if DBVT_USE_TEMPLATE
#define DBVT_VIRTUAL
#define DBVT_VIRTUAL_DTOR(a)
#define DBVT_PREFIX template <typename T>
#define DBVT_IPOLICY T& policy
#define DBVT_CHECKTYPE static const ICollide& typechecker=*(T*)0;
#else
#define DBVT_VIRTUAL_DTOR(a) virtual ~a() {}
#define DBVT_VIRTUAL virtual
#define DBVT_PREFIX
#define DBVT_IPOLICY ICollide& policy
#define DBVT_CHECKTYPE
#endif
#if DBVT_USE_MEMMOVE
#ifndef __CELLOS_LV2__
#include <memory.h>
#endif
#include <string.h>
#endif
#ifndef DBVT_USE_TEMPLATE
#error "DBVT_USE_TEMPLATE undefined"
#endif
#ifndef DBVT_USE_MEMMOVE
#error "DBVT_USE_MEMMOVE undefined"
#endif
#ifndef DBVT_ENABLE_BENCHMARK
#error "DBVT_ENABLE_BENCHMARK undefined"
#endif
#ifndef DBVT_SELECT_IMPL
#error "DBVT_SELECT_IMPL undefined"
#endif
#ifndef DBVT_MERGE_IMPL
#error "DBVT_MERGE_IMPL undefined"
#endif
#ifndef DBVT_INT0_IMPL
#error "DBVT_INT0_IMPL undefined"
#endif
//
// Defaults volumes
//
/* btDbvtAabbMm */
struct btDbvtAabbMm
{
DBVT_INLINE btVector3 Center() const { return((mi+mx)/2); }
DBVT_INLINE btVector3 Lengths() const { return(mx-mi); }
DBVT_INLINE btVector3 Extents() const { return((mx-mi)/2); }
DBVT_INLINE const btVector3& Mins() const { return(mi); }
DBVT_INLINE const btVector3& Maxs() const { return(mx); }
static inline btDbvtAabbMm FromCE(const btVector3& c,const btVector3& e);
static inline btDbvtAabbMm FromCR(const btVector3& c,btScalar r);
static inline btDbvtAabbMm FromMM(const btVector3& mi,const btVector3& mx);
static inline btDbvtAabbMm FromPoints(const btVector3* pts,int n);
static inline btDbvtAabbMm FromPoints(const btVector3** ppts,int n);
DBVT_INLINE void Expand(const btVector3& e);
DBVT_INLINE void SignedExpand(const btVector3& e);
DBVT_INLINE bool Contain(const btDbvtAabbMm& a) const;
DBVT_INLINE int Classify(const btVector3& n,btScalar o,int s) const;
DBVT_INLINE btScalar ProjectMinimum(const btVector3& v,unsigned signs) const;
DBVT_INLINE friend bool Intersect( const btDbvtAabbMm& a,
const btDbvtAabbMm& b);
DBVT_INLINE friend bool Intersect( const btDbvtAabbMm& a,
const btDbvtAabbMm& b,
const btTransform& xform);
DBVT_INLINE friend bool Intersect( const btDbvtAabbMm& a,
const btVector3& b);
DBVT_INLINE friend bool Intersect( const btDbvtAabbMm& a,
const btVector3& org,
const btVector3& invdir,
const unsigned* signs);
DBVT_INLINE friend btScalar Proximity( const btDbvtAabbMm& a,
const btDbvtAabbMm& b);
DBVT_INLINE friend int Select( const btDbvtAabbMm& o,
const btDbvtAabbMm& a,
const btDbvtAabbMm& b);
DBVT_INLINE friend void Merge( const btDbvtAabbMm& a,
const btDbvtAabbMm& b,
btDbvtAabbMm& r);
DBVT_INLINE friend bool NotEqual( const btDbvtAabbMm& a,
const btDbvtAabbMm& b);
private:
DBVT_INLINE void AddSpan(const btVector3& d,btScalar& smi,btScalar& smx) const;
private:
btVector3 mi,mx;
};
// Types
typedef btDbvtAabbMm btDbvtVolume;
/* btDbvtNode */
struct btDbvtNode
{
btDbvtVolume volume;
btDbvtNode* parent;
DBVT_INLINE bool isleaf() const { return(childs[1]==0); }
DBVT_INLINE bool isinternal() const { return(!isleaf()); }
union {
btDbvtNode* childs[2];
void* data;
int dataAsInt;
};
};
///The btDbvt class implements a fast dynamic bounding volume tree based on axis aligned bounding boxes (aabb tree).
///This btDbvt is used for soft body collision detection and for the btDbvtBroadphase. It has a fast insert, remove and update of nodes.
///Unlike the btQuantizedBvh, nodes can be dynamically moved around, which allows for change in topology of the underlying data structure.
struct btDbvt
{
/* Stack element */
struct sStkNN
{
const btDbvtNode* a;
const btDbvtNode* b;
sStkNN() {}
sStkNN(const btDbvtNode* na,const btDbvtNode* nb) : a(na),b(nb) {}
};
struct sStkNP
{
const btDbvtNode* node;
int mask;
sStkNP(const btDbvtNode* n,unsigned m) : node(n),mask(m) {}
};
struct sStkNPS
{
const btDbvtNode* node;
int mask;
btScalar value;
sStkNPS() {}
sStkNPS(const btDbvtNode* n,unsigned m,btScalar v) : node(n),mask(m),value(v) {}
};
struct sStkCLN
{
const btDbvtNode* node;
btDbvtNode* parent;
sStkCLN(const btDbvtNode* n,btDbvtNode* p) : node(n),parent(p) {}
};
// Policies/Interfaces
/* ICollide */
struct ICollide
{
DBVT_VIRTUAL_DTOR(ICollide)
DBVT_VIRTUAL void Process(const btDbvtNode*,const btDbvtNode*) {}
DBVT_VIRTUAL void Process(const btDbvtNode*) {}
DBVT_VIRTUAL void Process(const btDbvtNode* n,btScalar) { Process(n); }
DBVT_VIRTUAL bool Descent(const btDbvtNode*) { return(true); }
DBVT_VIRTUAL bool AllLeaves(const btDbvtNode*) { return(true); }
};
/* IWriter */
struct IWriter
{
virtual ~IWriter() {}
virtual void Prepare(const btDbvtNode* root,int numnodes)=0;
virtual void WriteNode(const btDbvtNode*,int index,int parent,int child0,int child1)=0;
virtual void WriteLeaf(const btDbvtNode*,int index,int parent)=0;
};
/* IClone */
struct IClone
{
virtual ~IClone() {}
virtual void CloneLeaf(btDbvtNode*) {}
};
// Constants
enum {
SIMPLE_STACKSIZE = 64,
DOUBLE_STACKSIZE = SIMPLE_STACKSIZE*2
};
// Fields
btDbvtNode* m_root;
btDbvtNode* m_free;
int m_lkhd;
int m_leaves;
unsigned m_opath;
// Methods
btDbvt();
~btDbvt();
void clear();
bool empty() const { return(0==m_root); }
void optimizeBottomUp();
void optimizeTopDown(int bu_treshold=128);
void optimizeIncremental(int passes);
btDbvtNode* insert(const btDbvtVolume& box,void* data);
void update(btDbvtNode* leaf,int lookahead=-1);
void update(btDbvtNode* leaf,const btDbvtVolume& volume);
bool update(btDbvtNode* leaf,btDbvtVolume volume,const btVector3& velocity,btScalar margin);
bool update(btDbvtNode* leaf,btDbvtVolume volume,const btVector3& velocity);
bool update(btDbvtNode* leaf,btDbvtVolume volume,btScalar margin);
void remove(btDbvtNode* leaf);
void write(IWriter* iwriter) const;
void clone(btDbvt& dest,IClone* iclone=0) const;
static int maxdepth(const btDbvtNode* node);
static int countLeaves(const btDbvtNode* node);
static void extractLeaves(const btDbvtNode* node,btAlignedObjectArray<const btDbvtNode*>& leaves);
#if DBVT_ENABLE_BENCHMARK
static void benchmark();
#else
static void benchmark(){}
#endif
// DBVT_IPOLICY must support ICollide policy/interface
DBVT_PREFIX
static void enumNodes( const btDbvtNode* root,
DBVT_IPOLICY);
DBVT_PREFIX
static void enumLeaves( const btDbvtNode* root,
DBVT_IPOLICY);
DBVT_PREFIX
static void collideTT( const btDbvtNode* root0,
const btDbvtNode* root1,
DBVT_IPOLICY);
DBVT_PREFIX
static void collideTT( const btDbvtNode* root0,
const btDbvtNode* root1,
const btTransform& xform,
DBVT_IPOLICY);
DBVT_PREFIX
static void collideTT( const btDbvtNode* root0,
const btTransform& xform0,
const btDbvtNode* root1,
const btTransform& xform1,
DBVT_IPOLICY);
DBVT_PREFIX
static void collideTV( const btDbvtNode* root,
const btDbvtVolume& volume,
DBVT_IPOLICY);
DBVT_PREFIX
static void collideRAY( const btDbvtNode* root,
const btVector3& origin,
const btVector3& direction,
DBVT_IPOLICY);
DBVT_PREFIX
static void collideKDOP(const btDbvtNode* root,
const btVector3* normals,
const btScalar* offsets,
int count,
DBVT_IPOLICY);
DBVT_PREFIX
static void collideOCL( const btDbvtNode* root,
const btVector3* normals,
const btScalar* offsets,
const btVector3& sortaxis,
int count,
DBVT_IPOLICY,
bool fullsort=true);
DBVT_PREFIX
static void collideTU( const btDbvtNode* root,
DBVT_IPOLICY);
// Helpers
static DBVT_INLINE int nearest(const int* i,const btDbvt::sStkNPS* a,btScalar v,int l,int h)
{
int m=0;
while(l<h)
{
m=(l+h)>>1;
if(a[i[m]].value>=v) l=m+1; else h=m;
}
return(h);
}
static DBVT_INLINE int allocate( btAlignedObjectArray<int>& ifree,
btAlignedObjectArray<sStkNPS>& stock,
const sStkNPS& value)
{
int i;
if(ifree.size()>0)
{ i=ifree[ifree.size()-1];ifree.pop_back();stock[i]=value; }
else
{ i=stock.size();stock.push_back(value); }
return(i);
}
//
private:
btDbvt(const btDbvt&) {}
};
//
// Inline's
//
//
inline btDbvtAabbMm btDbvtAabbMm::FromCE(const btVector3& c,const btVector3& e)
{
btDbvtAabbMm box;
box.mi=c-e;box.mx=c+e;
return(box);
}
//
inline btDbvtAabbMm btDbvtAabbMm::FromCR(const btVector3& c,btScalar r)
{
return(FromCE(c,btVector3(r,r,r)));
}
//
inline btDbvtAabbMm btDbvtAabbMm::FromMM(const btVector3& mi,const btVector3& mx)
{
btDbvtAabbMm box;
box.mi=mi;box.mx=mx;
return(box);
}
//
inline btDbvtAabbMm btDbvtAabbMm::FromPoints(const btVector3* pts,int n)
{
btDbvtAabbMm box;
box.mi=box.mx=pts[0];
for(int i=1;i<n;++i)
{
box.mi.setMin(pts[i]);
box.mx.setMax(pts[i]);
}
return(box);
}
//
inline btDbvtAabbMm btDbvtAabbMm::FromPoints(const btVector3** ppts,int n)
{
btDbvtAabbMm box;
box.mi=box.mx=*ppts[0];
for(int i=1;i<n;++i)
{
box.mi.setMin(*ppts[i]);
box.mx.setMax(*ppts[i]);
}
return(box);
}
//
DBVT_INLINE void btDbvtAabbMm::Expand(const btVector3& e)
{
mi-=e;mx+=e;
}
//
DBVT_INLINE void btDbvtAabbMm::SignedExpand(const btVector3& e)
{
if(e.x()>0) mx.setX(mx.x()+e[0]); else mi.setX(mi.x()+e[0]);
if(e.y()>0) mx.setY(mx.y()+e[1]); else mi.setY(mi.y()+e[1]);
if(e.z()>0) mx.setZ(mx.z()+e[2]); else mi.setZ(mi.z()+e[2]);
}
//
DBVT_INLINE bool btDbvtAabbMm::Contain(const btDbvtAabbMm& a) const
{
return( (mi.x()<=a.mi.x())&&
(mi.y()<=a.mi.y())&&
(mi.z()<=a.mi.z())&&
(mx.x()>=a.mx.x())&&
(mx.y()>=a.mx.y())&&
(mx.z()>=a.mx.z()));
}
//
DBVT_INLINE int btDbvtAabbMm::Classify(const btVector3& n,btScalar o,int s) const
{
btVector3 pi,px;
switch(s)
{
case (0+0+0): px=btVector3(mi.x(),mi.y(),mi.z());
pi=btVector3(mx.x(),mx.y(),mx.z());break;
case (1+0+0): px=btVector3(mx.x(),mi.y(),mi.z());
pi=btVector3(mi.x(),mx.y(),mx.z());break;
case (0+2+0): px=btVector3(mi.x(),mx.y(),mi.z());
pi=btVector3(mx.x(),mi.y(),mx.z());break;
case (1+2+0): px=btVector3(mx.x(),mx.y(),mi.z());
pi=btVector3(mi.x(),mi.y(),mx.z());break;
case (0+0+4): px=btVector3(mi.x(),mi.y(),mx.z());
pi=btVector3(mx.x(),mx.y(),mi.z());break;
case (1+0+4): px=btVector3(mx.x(),mi.y(),mx.z());
pi=btVector3(mi.x(),mx.y(),mi.z());break;
case (0+2+4): px=btVector3(mi.x(),mx.y(),mx.z());
pi=btVector3(mx.x(),mi.y(),mi.z());break;
case (1+2+4): px=btVector3(mx.x(),mx.y(),mx.z());
pi=btVector3(mi.x(),mi.y(),mi.z());break;
}
if((dot(n,px)+o)<0) return(-1);
if((dot(n,pi)+o)>=0) return(+1);
return(0);
}
//
DBVT_INLINE btScalar btDbvtAabbMm::ProjectMinimum(const btVector3& v,unsigned signs) const
{
const btVector3* b[]={&mx,&mi};
const btVector3 p( b[(signs>>0)&1]->x(),
b[(signs>>1)&1]->y(),
b[(signs>>2)&1]->z());
return(dot(p,v));
}
//
DBVT_INLINE void btDbvtAabbMm::AddSpan(const btVector3& d,btScalar& smi,btScalar& smx) const
{
for(int i=0;i<3;++i)
{
if(d[i]<0)
{ smi+=mx[i]*d[i];smx+=mi[i]*d[i]; }
else
{ smi+=mi[i]*d[i];smx+=mx[i]*d[i]; }
}
}
//
DBVT_INLINE bool Intersect( const btDbvtAabbMm& a,
const btDbvtAabbMm& b)
{
#if DBVT_INT0_IMPL == DBVT_IMPL_SSE
const __m128 rt(_mm_or_ps( _mm_cmplt_ps(_mm_load_ps(b.mx),_mm_load_ps(a.mi)),
_mm_cmplt_ps(_mm_load_ps(a.mx),_mm_load_ps(b.mi))));
const __int32* pu((const __int32*)&rt);
return((pu[0]|pu[1]|pu[2])==0);
#else
return( (a.mi.x()<=b.mx.x())&&
(a.mx.x()>=b.mi.x())&&
(a.mi.y()<=b.mx.y())&&
(a.mx.y()>=b.mi.y())&&
(a.mi.z()<=b.mx.z())&&
(a.mx.z()>=b.mi.z()));
#endif
}
//
DBVT_INLINE bool Intersect( const btDbvtAabbMm& a,
const btDbvtAabbMm& b,
const btTransform& xform)
{
const btVector3 d0=xform*b.Center()-a.Center();
const btVector3 d1=d0*xform.getBasis();
btScalar s0[2]={0,0};
btScalar s1[2]={dot(xform.getOrigin(),d0),s1[0]};
a.AddSpan(d0,s0[0],s0[1]);
b.AddSpan(d1,s1[0],s1[1]);
if(s0[0]>(s1[1])) return(false);
if(s0[1]<(s1[0])) return(false);
return(true);
}
//
DBVT_INLINE bool Intersect( const btDbvtAabbMm& a,
const btVector3& b)
{
return( (b.x()>=a.mi.x())&&
(b.y()>=a.mi.y())&&
(b.z()>=a.mi.z())&&
(b.x()<=a.mx.x())&&
(b.y()<=a.mx.y())&&
(b.z()<=a.mx.z()));
}
//
DBVT_INLINE bool Intersect( const btDbvtAabbMm& a,
const btVector3& org,
const btVector3& invdir,
const unsigned* signs)
{
#if 0
const btVector3 b0((a.mi-org)*invdir);
const btVector3 b1((a.mx-org)*invdir);
const btVector3 tmin(btMin(b0[0],b1[0]),btMin(b0[1],b1[1]),btMin(b0[2],b1[2]));
const btVector3 tmax(btMax(b0[0],b1[0]),btMax(b0[1],b1[1]),btMax(b0[2],b1[2]));
const btScalar tin=btMax(tmin[0],btMax(tmin[1],tmin[2]));
const btScalar tout=btMin(tmax[0],btMin(tmax[1],tmax[2]));
return(tin<tout);
#else
const btVector3* bounds[2]={&a.mi,&a.mx};
btScalar txmin=(bounds[ signs[0]]->x()-org[0])*invdir[0];
btScalar txmax=(bounds[1-signs[0]]->x()-org[0])*invdir[0];
const btScalar tymin=(bounds[ signs[1]]->y()-org[1])*invdir[1];
const btScalar tymax=(bounds[1-signs[1]]->y()-org[1])*invdir[1];
if((txmin>tymax)||(tymin>txmax)) return(false);
if(tymin>txmin) txmin=tymin;
if(tymax<txmax) txmax=tymax;
const btScalar tzmin=(bounds[ signs[2]]->z()-org[2])*invdir[2];
const btScalar tzmax=(bounds[1-signs[2]]->z()-org[2])*invdir[2];
if((txmin>tzmax)||(tzmin>txmax)) return(false);
if(tzmin>txmin) txmin=tzmin;
if(tzmax<txmax) txmax=tzmax;
return(txmax>0);
#endif
}
//
DBVT_INLINE btScalar Proximity( const btDbvtAabbMm& a,
const btDbvtAabbMm& b)
{
const btVector3 d=(a.mi+a.mx)-(b.mi+b.mx);
return(btFabs(d.x())+btFabs(d.y())+btFabs(d.z()));
}
//
DBVT_INLINE int Select( const btDbvtAabbMm& o,
const btDbvtAabbMm& a,
const btDbvtAabbMm& b)
{
#if DBVT_SELECT_IMPL == DBVT_IMPL_SSE
static DBVT_ALIGN const unsigned __int32 mask[]={0x7fffffff,0x7fffffff,0x7fffffff,0x7fffffff};
// TODO: the intrinsic version is 11% slower
#if DBVT_USE_INTRINSIC_SSE
__m128 omi(_mm_load_ps(o.mi));
omi=_mm_add_ps(omi,_mm_load_ps(o.mx));
__m128 ami(_mm_load_ps(a.mi));
ami=_mm_add_ps(ami,_mm_load_ps(a.mx));
ami=_mm_sub_ps(ami,omi);
ami=_mm_and_ps(ami,_mm_load_ps((const float*)mask));
__m128 bmi(_mm_load_ps(b.mi));
bmi=_mm_add_ps(bmi,_mm_load_ps(b.mx));
bmi=_mm_sub_ps(bmi,omi);
bmi=_mm_and_ps(bmi,_mm_load_ps((const float*)mask));
__m128 t0(_mm_movehl_ps(ami,ami));
ami=_mm_add_ps(ami,t0);
ami=_mm_add_ss(ami,_mm_shuffle_ps(ami,ami,1));
__m128 t1(_mm_movehl_ps(bmi,bmi));
bmi=_mm_add_ps(bmi,t1);
bmi=_mm_add_ss(bmi,_mm_shuffle_ps(bmi,bmi,1));
return(_mm_cmple_ss(bmi,ami).m128_u32[0]&1);
#else
DBVT_ALIGN __int32 r[1];
__asm
{
mov eax,o
mov ecx,a
mov edx,b
movaps xmm0,[eax]
movaps xmm5,mask
addps xmm0,[eax+16]
movaps xmm1,[ecx]
movaps xmm2,[edx]
addps xmm1,[ecx+16]
addps xmm2,[edx+16]
subps xmm1,xmm0
subps xmm2,xmm0
andps xmm1,xmm5
andps xmm2,xmm5
movhlps xmm3,xmm1
movhlps xmm4,xmm2
addps xmm1,xmm3
addps xmm2,xmm4
pshufd xmm3,xmm1,1
pshufd xmm4,xmm2,1
addss xmm1,xmm3
addss xmm2,xmm4
cmpless xmm2,xmm1
movss r,xmm2
}
return(r[0]&1);
#endif
#else
return(Proximity(o,a)<Proximity(o,b)?0:1);
#endif
}
//
DBVT_INLINE void Merge( const btDbvtAabbMm& a,
const btDbvtAabbMm& b,
btDbvtAabbMm& r)
{
#if DBVT_MERGE_IMPL==DBVT_IMPL_SSE
__m128 ami(_mm_load_ps(a.mi));
__m128 amx(_mm_load_ps(a.mx));
__m128 bmi(_mm_load_ps(b.mi));
__m128 bmx(_mm_load_ps(b.mx));
ami=_mm_min_ps(ami,bmi);
amx=_mm_max_ps(amx,bmx);
_mm_store_ps(r.mi,ami);
_mm_store_ps(r.mx,amx);
#else
for(int i=0;i<3;++i)
{
if(a.mi[i]<b.mi[i]) r.mi[i]=a.mi[i]; else r.mi[i]=b.mi[i];
if(a.mx[i]>b.mx[i]) r.mx[i]=a.mx[i]; else r.mx[i]=b.mx[i];
}
#endif
}
//
DBVT_INLINE bool NotEqual( const btDbvtAabbMm& a,
const btDbvtAabbMm& b)
{
return( (a.mi.x()!=b.mi.x())||
(a.mi.y()!=b.mi.y())||
(a.mi.z()!=b.mi.z())||
(a.mx.x()!=b.mx.x())||
(a.mx.y()!=b.mx.y())||
(a.mx.z()!=b.mx.z()));
}
//
// Inline's
//
//
DBVT_PREFIX
inline void btDbvt::enumNodes( const btDbvtNode* root,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
policy.Process(root);
if(root->isinternal())
{
enumNodes(root->childs[0],policy);
enumNodes(root->childs[1],policy);
}
}
//
DBVT_PREFIX
inline void btDbvt::enumLeaves( const btDbvtNode* root,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root->isinternal())
{
enumLeaves(root->childs[0],policy);
enumLeaves(root->childs[1],policy);
}
else
{
policy.Process(root);
}
}
//
DBVT_PREFIX
inline void btDbvt::collideTT( const btDbvtNode* root0,
const btDbvtNode* root1,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root0&&root1)
{
btAlignedObjectArray<sStkNN> stack;
int depth=1;
int treshold=DOUBLE_STACKSIZE-4;
stack.resize(DOUBLE_STACKSIZE);
stack[0]=sStkNN(root0,root1);
do {
sStkNN p=stack[--depth];
if(depth>treshold)
{
stack.resize(stack.size()*2);
treshold=stack.size()-4;
}
if(p.a==p.b)
{
if(p.a->isinternal())
{
stack[depth++]=sStkNN(p.a->childs[0],p.a->childs[0]);
stack[depth++]=sStkNN(p.a->childs[1],p.a->childs[1]);
stack[depth++]=sStkNN(p.a->childs[0],p.a->childs[1]);
}
}
else if(Intersect(p.a->volume,p.b->volume))
{
if(p.a->isinternal())
{
if(p.b->isinternal())
{
stack[depth++]=sStkNN(p.a->childs[0],p.b->childs[0]);
stack[depth++]=sStkNN(p.a->childs[1],p.b->childs[0]);
stack[depth++]=sStkNN(p.a->childs[0],p.b->childs[1]);
stack[depth++]=sStkNN(p.a->childs[1],p.b->childs[1]);
}
else
{
stack[depth++]=sStkNN(p.a->childs[0],p.b);
stack[depth++]=sStkNN(p.a->childs[1],p.b);
}
}
else
{
if(p.b->isinternal())
{
stack[depth++]=sStkNN(p.a,p.b->childs[0]);
stack[depth++]=sStkNN(p.a,p.b->childs[1]);
}
else
{
policy.Process(p.a,p.b);
}
}
}
} while(depth);
}
}
//
DBVT_PREFIX
inline void btDbvt::collideTT( const btDbvtNode* root0,
const btDbvtNode* root1,
const btTransform& xform,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root0&&root1)
{
btAlignedObjectArray<sStkNN> stack;
int depth=1;
int treshold=DOUBLE_STACKSIZE-4;
stack.resize(DOUBLE_STACKSIZE);
stack[0]=sStkNN(root0,root1);
do {
sStkNN p=stack[--depth];
if(Intersect(p.a->volume,p.b->volume,xform))
{
if(depth>treshold)
{
stack.resize(stack.size()*2);
treshold=stack.size()-4;
}
if(p.a->isinternal())
{
if(p.b->isinternal())
{
stack[depth++]=sStkNN(p.a->childs[0],p.b->childs[0]);
stack[depth++]=sStkNN(p.a->childs[1],p.b->childs[0]);
stack[depth++]=sStkNN(p.a->childs[0],p.b->childs[1]);
stack[depth++]=sStkNN(p.a->childs[1],p.b->childs[1]);
}
else
{
stack[depth++]=sStkNN(p.a->childs[0],p.b);
stack[depth++]=sStkNN(p.a->childs[1],p.b);
}
}
else
{
if(p.b->isinternal())
{
stack[depth++]=sStkNN(p.a,p.b->childs[0]);
stack[depth++]=sStkNN(p.a,p.b->childs[1]);
}
else
{
policy.Process(p.a,p.b);
}
}
}
} while(depth);
}
}
//
DBVT_PREFIX
inline void btDbvt::collideTT( const btDbvtNode* root0,
const btTransform& xform0,
const btDbvtNode* root1,
const btTransform& xform1,
DBVT_IPOLICY)
{
const btTransform xform=xform0.inverse()*xform1;
collideTT(root0,root1,xform,policy);
}
//
DBVT_PREFIX
inline void btDbvt::collideTV( const btDbvtNode* root,
const btDbvtVolume& vol,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root)
{
ATTRIBUTE_ALIGNED16(btDbvtVolume) volume(vol);
btAlignedObjectArray<const btDbvtNode*> stack;
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(root);
do {
const btDbvtNode* n=stack[stack.size()-1];
stack.pop_back();
if(Intersect(n->volume,volume))
{
if(n->isinternal())
{
stack.push_back(n->childs[0]);
stack.push_back(n->childs[1]);
}
else
{
policy.Process(n);
}
}
} while(stack.size()>0);
}
}
//
DBVT_PREFIX
inline void btDbvt::collideRAY( const btDbvtNode* root,
const btVector3& origin,
const btVector3& direction,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root)
{
const btVector3 normal=direction.normalized();
const btVector3 invdir( 1/normal.x(),
1/normal.y(),
1/normal.z());
const unsigned signs[]={ direction.x()<0,
direction.y()<0,
direction.z()<0};
btAlignedObjectArray<const btDbvtNode*> stack;
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(root);
do {
const btDbvtNode* node=stack[stack.size()-1];
stack.pop_back();
if(Intersect(node->volume,origin,invdir,signs))
{
if(node->isinternal())
{
stack.push_back(node->childs[0]);
stack.push_back(node->childs[1]);
}
else
{
policy.Process(node);
}
}
} while(stack.size());
}
}
//
DBVT_PREFIX
inline void btDbvt::collideKDOP(const btDbvtNode* root,
const btVector3* normals,
const btScalar* offsets,
int count,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root)
{
const int inside=(1<<count)-1;
btAlignedObjectArray<sStkNP> stack;
int signs[sizeof(unsigned)*8];
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
for(int i=0;i<count;++i)
{
signs[i]= ((normals[i].x()>=0)?1:0)+
((normals[i].y()>=0)?2:0)+
((normals[i].z()>=0)?4:0);
}
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(sStkNP(root,0));
do {
sStkNP se=stack[stack.size()-1];
bool out=false;
stack.pop_back();
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
{
if(0==(se.mask&j))
{
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
switch(side)
{
case -1: out=true;break;
case +1: se.mask|=j;break;
}
}
}
if(!out)
{
if((se.mask!=inside)&&(se.node->isinternal()))
{
stack.push_back(sStkNP(se.node->childs[0],se.mask));
stack.push_back(sStkNP(se.node->childs[1],se.mask));
}
else
{
if(policy.AllLeaves(se.node)) enumLeaves(se.node,policy);
}
}
} while(stack.size());
}
}
//
DBVT_PREFIX
inline void btDbvt::collideOCL( const btDbvtNode* root,
const btVector3* normals,
const btScalar* offsets,
const btVector3& sortaxis,
int count,
DBVT_IPOLICY,
bool fsort)
{
DBVT_CHECKTYPE
if(root)
{
const unsigned srtsgns=(sortaxis[0]>=0?1:0)+
(sortaxis[1]>=0?2:0)+
(sortaxis[2]>=0?4:0);
const int inside=(1<<count)-1;
btAlignedObjectArray<sStkNPS> stock;
btAlignedObjectArray<int> ifree;
btAlignedObjectArray<int> stack;
int signs[sizeof(unsigned)*8];
btAssert(count<int (sizeof(signs)/sizeof(signs[0])));
for(int i=0;i<count;++i)
{
signs[i]= ((normals[i].x()>=0)?1:0)+
((normals[i].y()>=0)?2:0)+
((normals[i].z()>=0)?4:0);
}
stock.reserve(SIMPLE_STACKSIZE);
stack.reserve(SIMPLE_STACKSIZE);
ifree.reserve(SIMPLE_STACKSIZE);
stack.push_back(allocate(ifree,stock,sStkNPS(root,0,root->volume.ProjectMinimum(sortaxis,srtsgns))));
do {
const int id=stack[stack.size()-1];
sStkNPS se=stock[id];
stack.pop_back();ifree.push_back(id);
if(se.mask!=inside)
{
bool out=false;
for(int i=0,j=1;(!out)&&(i<count);++i,j<<=1)
{
if(0==(se.mask&j))
{
const int side=se.node->volume.Classify(normals[i],offsets[i],signs[i]);
switch(side)
{
case -1: out=true;break;
case +1: se.mask|=j;break;
}
}
}
if(out) continue;
}
if(policy.Descent(se.node))
{
if(se.node->isinternal())
{
const btDbvtNode* pns[]={ se.node->childs[0],se.node->childs[1]};
sStkNPS nes[]={ sStkNPS(pns[0],se.mask,pns[0]->volume.ProjectMinimum(sortaxis,srtsgns)),
sStkNPS(pns[1],se.mask,pns[1]->volume.ProjectMinimum(sortaxis,srtsgns))};
const int q=nes[0].value<nes[1].value?1:0;
int j=stack.size();
if(fsort&&(j>0))
{
/* Insert 0 */
j=nearest(&stack[0],&stock[0],nes[q].value,0,stack.size());
stack.push_back(0);
#if DBVT_USE_MEMMOVE
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
#else
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
#endif
stack[j]=allocate(ifree,stock,nes[q]);
/* Insert 1 */
j=nearest(&stack[0],&stock[0],nes[1-q].value,j,stack.size());
stack.push_back(0);
#if DBVT_USE_MEMMOVE
memmove(&stack[j+1],&stack[j],sizeof(int)*(stack.size()-j-1));
#else
for(int k=stack.size()-1;k>j;--k) stack[k]=stack[k-1];
#endif
stack[j]=allocate(ifree,stock,nes[1-q]);
}
else
{
stack.push_back(allocate(ifree,stock,nes[q]));
stack.push_back(allocate(ifree,stock,nes[1-q]));
}
}
else
{
policy.Process(se.node,se.value);
}
}
} while(stack.size());
}
}
//
DBVT_PREFIX
inline void btDbvt::collideTU( const btDbvtNode* root,
DBVT_IPOLICY)
{
DBVT_CHECKTYPE
if(root)
{
btAlignedObjectArray<const btDbvtNode*> stack;
stack.reserve(SIMPLE_STACKSIZE);
stack.push_back(root);
do {
const btDbvtNode* n=stack[stack.size()-1];
stack.pop_back();
if(policy.Descent(n))
{
if(n->isinternal())
{ stack.push_back(n->childs[0]);stack.push_back(n->childs[1]); }
else
{ policy.Process(n); }
}
} while(stack.size()>0);
}
}
//
// PP Cleanup
//
#undef DBVT_USE_MEMMOVE
#undef DBVT_USE_TEMPLATE
#undef DBVT_VIRTUAL_DTOR
#undef DBVT_VIRTUAL
#undef DBVT_PREFIX
#undef DBVT_IPOLICY
#undef DBVT_CHECKTYPE
#undef DBVT_IMPL_GENERIC
#undef DBVT_IMPL_SSE
#undef DBVT_USE_INTRINSIC_SSE
#undef DBVT_SELECT_IMPL
#undef DBVT_MERGE_IMPL
#undef DBVT_INT0_IMPL
#endif

View File

@@ -0,0 +1,548 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2007 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.
*/
///btDbvtBroadphase implementation by Nathanael Presson
#include "btDbvtBroadphase.h"
//
// Profiling
//
#if DBVT_BP_PROFILE||DBVT_BP_ENABLE_BENCHMARK
#include <stdio.h>
#endif
#if DBVT_BP_PROFILE
struct ProfileScope
{
__forceinline ProfileScope(btClock& clock,unsigned long& value) :
m_clock(&clock),m_value(&value),m_base(clock.getTimeMicroseconds())
{
}
__forceinline ~ProfileScope()
{
(*m_value)+=m_clock->getTimeMicroseconds()-m_base;
}
btClock* m_clock;
unsigned long* m_value;
unsigned long m_base;
};
#define SPC(_value_) ProfileScope spc_scope(m_clock,_value_)
#else
#define SPC(_value_)
#endif
//
// Helpers
//
//
template <typename T>
static inline void listappend(T* item,T*& list)
{
item->links[0]=0;
item->links[1]=list;
if(list) list->links[0]=item;
list=item;
}
//
template <typename T>
static inline void listremove(T* item,T*& list)
{
if(item->links[0]) item->links[0]->links[1]=item->links[1]; else list=item->links[1];
if(item->links[1]) item->links[1]->links[0]=item->links[0];
}
//
template <typename T>
static inline int listcount(T* root)
{
int n=0;
while(root) { ++n;root=root->links[1]; }
return(n);
}
//
template <typename T>
static inline void clear(T& value)
{
static const struct ZeroDummy : T {} zerodummy;
value=zerodummy;
}
//
// Colliders
//
/* Tree collider */
struct btDbvtTreeCollider : btDbvt::ICollide
{
btDbvtBroadphase* pbp;
btDbvtProxy* proxy;
btDbvtTreeCollider(btDbvtBroadphase* p) : pbp(p) {}
void Process(const btDbvtNode* na,const btDbvtNode* nb)
{
if(na!=nb)
{
btDbvtProxy* pa=(btDbvtProxy*)na->data;
btDbvtProxy* pb=(btDbvtProxy*)nb->data;
#if DBVT_BP_SORTPAIRS
if(pa>pb) btSwap(pa,pb);
#endif
pbp->m_paircache->addOverlappingPair(pa,pb);
++pbp->m_newpairs;
}
}
void Process(const btDbvtNode* n)
{
Process(n,proxy->leaf);
}
};
//
// btDbvtBroadphase
//
//
btDbvtBroadphase::btDbvtBroadphase(btOverlappingPairCache* paircache)
{
m_deferedcollide = false;
m_needcleanup = true;
m_releasepaircache = (paircache!=0)?false:true;
m_prediction = 1/(btScalar)2;
m_stageCurrent = 0;
m_fixedleft = 0;
m_fupdates = 1;
m_dupdates = 0;
m_cupdates = 10;
m_newpairs = 1;
m_updates_call = 0;
m_updates_done = 0;
m_updates_ratio = 0;
m_paircache = paircache?
paircache :
new(btAlignedAlloc(sizeof(btHashedOverlappingPairCache),16)) btHashedOverlappingPairCache();
m_gid = 0;
m_pid = 0;
m_cid = 0;
for(int i=0;i<=STAGECOUNT;++i)
{
m_stageRoots[i]=0;
}
#if DBVT_BP_PROFILE
clear(m_profiling);
#endif
}
//
btDbvtBroadphase::~btDbvtBroadphase()
{
if(m_releasepaircache)
{
m_paircache->~btOverlappingPairCache();
btAlignedFree(m_paircache);
}
}
//
btBroadphaseProxy* btDbvtBroadphase::createProxy( const btVector3& aabbMin,
const btVector3& aabbMax,
int /*shapeType*/,
void* userPtr,
short int collisionFilterGroup,
short int collisionFilterMask,
btDispatcher* /*dispatcher*/,
void* /*multiSapProxy*/)
{
btDbvtProxy* proxy=new(btAlignedAlloc(sizeof(btDbvtProxy),16)) btDbvtProxy( userPtr,
collisionFilterGroup,
collisionFilterMask);
proxy->aabb = btDbvtVolume::FromMM(aabbMin,aabbMax);
proxy->stage = m_stageCurrent;
proxy->m_uniqueId = ++m_gid;
proxy->leaf = m_sets[0].insert(proxy->aabb,proxy);
listappend(proxy,m_stageRoots[m_stageCurrent]);
if(!m_deferedcollide)
{
btDbvtTreeCollider collider(this);
collider.proxy=proxy;
btDbvt::collideTV(m_sets[0].m_root,proxy->aabb,collider);
btDbvt::collideTV(m_sets[1].m_root,proxy->aabb,collider);
}
return(proxy);
}
//
void btDbvtBroadphase::destroyProxy( btBroadphaseProxy* absproxy,
btDispatcher* dispatcher)
{
btDbvtProxy* proxy=(btDbvtProxy*)absproxy;
if(proxy->stage==STAGECOUNT)
m_sets[1].remove(proxy->leaf);
else
m_sets[0].remove(proxy->leaf);
listremove(proxy,m_stageRoots[proxy->stage]);
m_paircache->removeOverlappingPairsContainingProxy(proxy,dispatcher);
btAlignedFree(proxy);
m_needcleanup=true;
}
//
void btDbvtBroadphase::setAabb( btBroadphaseProxy* absproxy,
const btVector3& aabbMin,
const btVector3& aabbMax,
btDispatcher* /*dispatcher*/)
{
btDbvtProxy* proxy=(btDbvtProxy*)absproxy;
ATTRIBUTE_ALIGNED16(btDbvtVolume) aabb=btDbvtVolume::FromMM(aabbMin,aabbMax);
#if DBVT_BP_PREVENTFALSEUPDATE
if(NotEqual(aabb,proxy->leaf->volume))
#endif
{
bool docollide=false;
if(proxy->stage==STAGECOUNT)
{/* fixed -> dynamic set */
m_sets[1].remove(proxy->leaf);
proxy->leaf=m_sets[0].insert(aabb,proxy);
docollide=true;
}
else
{/* dynamic set */
++m_updates_call;
if(Intersect(proxy->leaf->volume,aabb))
{/* Moving */
const btVector3 delta=aabbMin-proxy->aabb.Mins();
btVector3 velocity(aabb.Extents()*m_prediction);
if(delta[0]<0) velocity[0]=-velocity[0];
if(delta[1]<0) velocity[1]=-velocity[1];
if(delta[2]<0) velocity[2]=-velocity[2];
if (
#ifdef DBVT_BP_MARGIN
m_sets[0].update(proxy->leaf,aabb,velocity,DBVT_BP_MARGIN)
#else
m_sets[0].update(proxy->leaf,aabb,velocity)
#endif
)
{
++m_updates_done;
docollide=true;
}
}
else
{/* Teleporting */
m_sets[0].update(proxy->leaf,aabb);
++m_updates_done;
docollide=true;
}
}
listremove(proxy,m_stageRoots[proxy->stage]);
proxy->aabb = aabb;
proxy->stage = m_stageCurrent;
listappend(proxy,m_stageRoots[m_stageCurrent]);
if(docollide)
{
m_needcleanup=true;
if(!m_deferedcollide)
{
btDbvtTreeCollider collider(this);
btDbvt::collideTT(m_sets[1].m_root,proxy->leaf,collider);
btDbvt::collideTT(m_sets[0].m_root,proxy->leaf,collider);
}
}
}
}
//
void btDbvtBroadphase::calculateOverlappingPairs(btDispatcher* dispatcher)
{
collide(dispatcher);
#if DBVT_BP_PROFILE
if(0==(m_pid%DBVT_BP_PROFILING_RATE))
{
printf("fixed(%u) dynamics(%u) pairs(%u)\r\n",m_sets[1].m_leaves,m_sets[0].m_leaves,m_paircache->getNumOverlappingPairs());
unsigned int total=m_profiling.m_total;
if(total<=0) total=1;
printf("ddcollide: %u%% (%uus)\r\n",(50+m_profiling.m_ddcollide*100)/total,m_profiling.m_ddcollide/DBVT_BP_PROFILING_RATE);
printf("fdcollide: %u%% (%uus)\r\n",(50+m_profiling.m_fdcollide*100)/total,m_profiling.m_fdcollide/DBVT_BP_PROFILING_RATE);
printf("cleanup: %u%% (%uus)\r\n",(50+m_profiling.m_cleanup*100)/total,m_profiling.m_cleanup/DBVT_BP_PROFILING_RATE);
printf("total: %uus\r\n",total/DBVT_BP_PROFILING_RATE);
const unsigned long sum=m_profiling.m_ddcollide+
m_profiling.m_fdcollide+
m_profiling.m_cleanup;
printf("leaked: %u%% (%uus)\r\n",100-((50+sum*100)/total),(total-sum)/DBVT_BP_PROFILING_RATE);
printf("job counts: %u%%\r\n",(m_profiling.m_jobcount*100)/((m_sets[0].m_leaves+m_sets[1].m_leaves)*DBVT_BP_PROFILING_RATE));
clear(m_profiling);
m_clock.reset();
}
#endif
}
//
void btDbvtBroadphase::collide(btDispatcher* dispatcher)
{
SPC(m_profiling.m_total);
/* optimize */
m_sets[0].optimizeIncremental(1+(m_sets[0].m_leaves*m_dupdates)/100);
if(m_fixedleft)
{
const int count=1+(m_sets[1].m_leaves*m_fupdates)/100;
m_sets[1].optimizeIncremental(1+(m_sets[1].m_leaves*m_fupdates)/100);
m_fixedleft=btMax<int>(0,m_fixedleft-count);
}
/* dynamic -> fixed set */
m_stageCurrent=(m_stageCurrent+1)%STAGECOUNT;
btDbvtProxy* current=m_stageRoots[m_stageCurrent];
if(current)
{
btDbvtTreeCollider collider(this);
do {
btDbvtProxy* next=current->links[1];
listremove(current,m_stageRoots[current->stage]);
listappend(current,m_stageRoots[STAGECOUNT]);
#if DBVT_BP_ACCURATESLEEPING
m_paircache->removeOverlappingPairsContainingProxy(current,dispatcher);
collider.proxy=current;
btDbvt::collideTV(m_sets[0].m_root,current->aabb,collider);
btDbvt::collideTV(m_sets[1].m_root,current->aabb,collider);
#endif
m_sets[0].remove(current->leaf);
current->leaf = m_sets[1].insert(current->aabb,current);
current->stage = STAGECOUNT;
current = next;
} while(current);
m_fixedleft=m_sets[1].m_leaves;
m_needcleanup=true;
}
/* collide dynamics */
{
btDbvtTreeCollider collider(this);
if(m_deferedcollide)
{
SPC(m_profiling.m_fdcollide);
btDbvt::collideTT(m_sets[0].m_root,m_sets[1].m_root,collider);
}
if(m_deferedcollide)
{
SPC(m_profiling.m_ddcollide);
btDbvt::collideTT(m_sets[0].m_root,m_sets[0].m_root,collider);
}
}
/* clean up */
if(m_needcleanup)
{
SPC(m_profiling.m_cleanup);
btBroadphasePairArray& pairs=m_paircache->getOverlappingPairArray();
if(pairs.size()>0)
{
const int ci=pairs.size();
int ni=btMin(ci,btMax<int>(m_newpairs,(ci*m_cupdates)/100));
for(int i=0;i<ni;++i)
{
btBroadphasePair& p=pairs[(m_cid+i)%ci];
btDbvtProxy* pa=(btDbvtProxy*)p.m_pProxy0;
btDbvtProxy* pb=(btDbvtProxy*)p.m_pProxy1;
if(!Intersect(pa->leaf->volume,pb->leaf->volume))
{
#if DBVT_BP_SORTPAIRS
if(pa>pb) btSwap(pa,pb);
#endif
m_paircache->removeOverlappingPair(pa,pb,dispatcher);
--ni;--i;
}
}
if(pairs.size()>0) m_cid=(m_cid+ni)%pairs.size(); else m_cid=0;
}
}
++m_pid;
m_newpairs=1;
m_needcleanup=false;
if(m_updates_call>0)
{ m_updates_ratio=m_updates_done/(btScalar)m_updates_call; }
else
{ m_updates_ratio=0; }
m_updates_done/=2;
m_updates_call/=2;
}
//
void btDbvtBroadphase::optimize()
{
m_sets[0].optimizeTopDown();
m_sets[1].optimizeTopDown();
}
//
btOverlappingPairCache* btDbvtBroadphase::getOverlappingPairCache()
{
return(m_paircache);
}
//
const btOverlappingPairCache* btDbvtBroadphase::getOverlappingPairCache() const
{
return(m_paircache);
}
//
void btDbvtBroadphase::getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const
{
ATTRIBUTE_ALIGNED16(btDbvtVolume) bounds;
if(!m_sets[0].empty())
if(!m_sets[1].empty()) Merge( m_sets[0].m_root->volume,
m_sets[1].m_root->volume,bounds);
else
bounds=m_sets[0].m_root->volume;
else if(!m_sets[1].empty()) bounds=m_sets[1].m_root->volume;
else
bounds=btDbvtVolume::FromCR(btVector3(0,0,0),0);
aabbMin=bounds.Mins();
aabbMax=bounds.Maxs();
}
//
void btDbvtBroadphase::printStats()
{}
//
#if DBVT_BP_ENABLE_BENCHMARK
struct btBroadphaseBenchmark
{
struct Experiment
{
const char* name;
int object_count;
int update_count;
int spawn_count;
int iterations;
btScalar speed;
btScalar amplitude;
};
struct Object
{
btVector3 center;
btVector3 extents;
btBroadphaseProxy* proxy;
btScalar time;
void update(btScalar speed,btScalar amplitude,btBroadphaseInterface* pbi)
{
time += speed;
center[0] = btCos(time*(btScalar)2.17)*amplitude+
btSin(time)*amplitude/2;
center[1] = btCos(time*(btScalar)1.38)*amplitude+
btSin(time)*amplitude;
center[2] = btSin(time*(btScalar)0.777)*amplitude;
pbi->setAabb(proxy,center-extents,center+extents,0);
}
};
static int UnsignedRand(int range=RAND_MAX-1) { return(rand()%(range+1)); }
static btScalar UnitRand() { return(UnsignedRand(16384)/(btScalar)16384); }
static void OutputTime(const char* name,btClock& c,unsigned count=0)
{
const unsigned long us=c.getTimeMicroseconds();
const unsigned long ms=(us+500)/1000;
const btScalar sec=us/(btScalar)(1000*1000);
if(count>0)
printf("%s : %u us (%u ms), %.2f/s\r\n",name,us,ms,count/sec);
else
printf("%s : %u us (%u ms)\r\n",name,us,ms);
}
};
void btDbvtBroadphase::benchmark(btBroadphaseInterface* pbi)
{
static const btBroadphaseBenchmark::Experiment experiments[]=
{
{"1024o.10%",1024,10,0,8192,(btScalar)0.005,(btScalar)100},
/*{"4096o.10%",4096,10,0,8192,(btScalar)0.005,(btScalar)100},
{"8192o.10%",8192,10,0,8192,(btScalar)0.005,(btScalar)100},*/
};
static const int nexperiments=sizeof(experiments)/sizeof(experiments[0]);
btAlignedObjectArray<btBroadphaseBenchmark::Object*> objects;
btClock wallclock;
/* Begin */
for(int iexp=0;iexp<nexperiments;++iexp)
{
const btBroadphaseBenchmark::Experiment& experiment=experiments[iexp];
const int object_count=experiment.object_count;
const int update_count=(object_count*experiment.update_count)/100;
const int spawn_count=(object_count*experiment.spawn_count)/100;
const btScalar speed=experiment.speed;
const btScalar amplitude=experiment.amplitude;
printf("Experiment #%u '%s':\r\n",iexp,experiment.name);
printf("\tObjects: %u\r\n",object_count);
printf("\tUpdate: %u\r\n",update_count);
printf("\tSpawn: %u\r\n",spawn_count);
printf("\tSpeed: %f\r\n",speed);
printf("\tAmplitude: %f\r\n",amplitude);
srand(180673);
/* Create objects */
wallclock.reset();
objects.reserve(object_count);
for(int i=0;i<object_count;++i)
{
btBroadphaseBenchmark::Object* po=new btBroadphaseBenchmark::Object();
po->center[0]=btBroadphaseBenchmark::UnitRand()*50;
po->center[1]=btBroadphaseBenchmark::UnitRand()*50;
po->center[2]=btBroadphaseBenchmark::UnitRand()*50;
po->extents[0]=btBroadphaseBenchmark::UnitRand()*2+2;
po->extents[1]=btBroadphaseBenchmark::UnitRand()*2+2;
po->extents[2]=btBroadphaseBenchmark::UnitRand()*2+2;
po->time=btBroadphaseBenchmark::UnitRand()*2000;
po->proxy=pbi->createProxy(po->center-po->extents,po->center+po->extents,0,po,1,1,0,0);
objects.push_back(po);
}
btBroadphaseBenchmark::OutputTime("\tInitialization",wallclock);
/* First update */
wallclock.reset();
for(int i=0;i<objects.size();++i)
{
objects[i]->update(speed,amplitude,pbi);
}
btBroadphaseBenchmark::OutputTime("\tFirst update",wallclock);
/* Updates */
wallclock.reset();
for(int i=0;i<experiment.iterations;++i)
{
for(int j=0;j<update_count;++j)
{
objects[j]->update(speed,amplitude,pbi);
}
pbi->calculateOverlappingPairs(0);
}
btBroadphaseBenchmark::OutputTime("\tUpdate",wallclock,experiment.iterations);
/* Clean up */
wallclock.reset();
for(int i=0;i<objects.size();++i)
{
pbi->destroyProxy(objects[i]->proxy,0);
delete objects[i];
}
objects.resize(0);
btBroadphaseBenchmark::OutputTime("\tRelease",wallclock);
}
}
#else
void btDbvtBroadphase::benchmark(btBroadphaseInterface*)
{}
#endif
#if DBVT_BP_PROFILE
#undef SPC
#endif

View File

@@ -0,0 +1,116 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2007 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.
*/
///btDbvtBroadphase implementation by Nathanael Presson
#ifndef BT_DBVT_BROADPHASE_H
#define BT_DBVT_BROADPHASE_H
#include "BulletCollision/BroadphaseCollision/btDbvt.h"
#include "BulletCollision/BroadphaseCollision/btOverlappingPairCache.h"
//
// Compile time config
//
#define DBVT_BP_PROFILE 0
#define DBVT_BP_SORTPAIRS 1
#define DBVT_BP_PREVENTFALSEUPDATE 0
#define DBVT_BP_ACCURATESLEEPING 0
#define DBVT_BP_ENABLE_BENCHMARK 0
#define DBVT_BP_MARGIN (btScalar)0.05
#if DBVT_BP_PROFILE
#define DBVT_BP_PROFILING_RATE 256
#include "LinearMath/btQuickprof.h"
#endif
//
// btDbvtProxy
//
struct btDbvtProxy : btBroadphaseProxy
{
/* Fields */
btDbvtAabbMm aabb;
btDbvtNode* leaf;
btDbvtProxy* links[2];
int stage;
/* ctor */
btDbvtProxy(void* userPtr,short int collisionFilterGroup, short int collisionFilterMask) :
btBroadphaseProxy(userPtr,collisionFilterGroup,collisionFilterMask)
{
links[0]=links[1]=0;
}
};
typedef btAlignedObjectArray<btDbvtProxy*> btDbvtProxyArray;
///The btDbvtBroadphase implements a broadphase using two dynamic AABB bounding volume hierarchies/trees (see btDbvt).
///One tree is used for static/non-moving objects, and another tree is used for dynamic objects. Objects can move from one tree to the other.
///This is a very fast broadphase, especially for very dynamic worlds where many objects are moving. Its insert/add and remove of objects is generally faster than the sweep and prune broadphases btAxisSweep3 and bt32BitAxisSweep3.
struct btDbvtBroadphase : btBroadphaseInterface
{
/* Config */
enum {
DYNAMIC_SET = 0, /* Dynamic set index */
FIXED_SET = 1, /* Fixed set index */
STAGECOUNT = 2 /* Number of stages */
};
/* Fields */
btDbvt m_sets[2]; // Dbvt sets
btDbvtProxy* m_stageRoots[STAGECOUNT+1]; // Stages list
btOverlappingPairCache* m_paircache; // Pair cache
btScalar m_prediction; // Velocity prediction
int m_stageCurrent; // Current stage
int m_fupdates; // % of fixed updates per frame
int m_dupdates; // % of dynamic updates per frame
int m_cupdates; // % of cleanup updates per frame
int m_newpairs; // Number of pairs created
int m_fixedleft; // Fixed optimization left
unsigned m_updates_call; // Number of updates call
unsigned m_updates_done; // Number of updates done
btScalar m_updates_ratio; // m_updates_done/m_updates_call
int m_pid; // Parse id
int m_cid; // Cleanup index
int m_gid; // Gen id
bool m_releasepaircache; // Release pair cache on delete
bool m_deferedcollide; // Defere dynamic/static collision to collide call
bool m_needcleanup; // Need to run cleanup?
#if DBVT_BP_PROFILE
btClock m_clock;
struct {
unsigned long m_total;
unsigned long m_ddcollide;
unsigned long m_fdcollide;
unsigned long m_cleanup;
unsigned long m_jobcount;
} m_profiling;
#endif
/* Methods */
btDbvtBroadphase(btOverlappingPairCache* paircache=0);
~btDbvtBroadphase();
void collide(btDispatcher* dispatcher);
void optimize();
/* btBroadphaseInterface Implementation */
btBroadphaseProxy* createProxy(const btVector3& aabbMin,const btVector3& aabbMax,int shapeType,void* userPtr,short int collisionFilterGroup,short int collisionFilterMask,btDispatcher* dispatcher,void* multiSapProxy);
void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher);
void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax,btDispatcher* dispatcher);
void calculateOverlappingPairs(btDispatcher* dispatcher);
btOverlappingPairCache* getOverlappingPairCache();
const btOverlappingPairCache* getOverlappingPairCache() const;
void getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const;
void printStats();
static void benchmark(btBroadphaseInterface*);
};
#endif

View File

@@ -0,0 +1,466 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 "btMultiSapBroadphase.h"
#include "btSimpleBroadphase.h"
#include "LinearMath/btAabbUtil2.h"
#include "btQuantizedBvh.h"
/// btSapBroadphaseArray m_sapBroadphases;
/// btOverlappingPairCache* m_overlappingPairs;
extern int gOverlappingPairs;
/*
class btMultiSapSortedOverlappingPairCache : public btSortedOverlappingPairCache
{
public:
virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1)
{
return btSortedOverlappingPairCache::addOverlappingPair((btBroadphaseProxy*)proxy0->m_multiSapParentProxy,(btBroadphaseProxy*)proxy1->m_multiSapParentProxy);
}
};
*/
btMultiSapBroadphase::btMultiSapBroadphase(int /*maxProxies*/,btOverlappingPairCache* pairCache)
:m_overlappingPairs(pairCache),
m_optimizedAabbTree(0),
m_ownsPairCache(false),
m_invalidPair(0)
{
if (!m_overlappingPairs)
{
m_ownsPairCache = true;
void* mem = btAlignedAlloc(sizeof(btSortedOverlappingPairCache),16);
m_overlappingPairs = new (mem)btSortedOverlappingPairCache();
}
struct btMultiSapOverlapFilterCallback : public btOverlapFilterCallback
{
virtual ~btMultiSapOverlapFilterCallback()
{}
// return true when pairs need collision
virtual bool needBroadphaseCollision(btBroadphaseProxy* childProxy0,btBroadphaseProxy* childProxy1) const
{
btBroadphaseProxy* multiProxy0 = (btBroadphaseProxy*)childProxy0->m_multiSapParentProxy;
btBroadphaseProxy* multiProxy1 = (btBroadphaseProxy*)childProxy1->m_multiSapParentProxy;
bool collides = (multiProxy0->m_collisionFilterGroup & multiProxy1->m_collisionFilterMask) != 0;
collides = collides && (multiProxy1->m_collisionFilterGroup & multiProxy0->m_collisionFilterMask);
return collides;
}
};
void* mem = btAlignedAlloc(sizeof(btMultiSapOverlapFilterCallback),16);
m_filterCallback = new (mem)btMultiSapOverlapFilterCallback();
m_overlappingPairs->setOverlapFilterCallback(m_filterCallback);
// mem = btAlignedAlloc(sizeof(btSimpleBroadphase),16);
// m_simpleBroadphase = new (mem) btSimpleBroadphase(maxProxies,m_overlappingPairs);
}
btMultiSapBroadphase::~btMultiSapBroadphase()
{
if (m_ownsPairCache)
{
m_overlappingPairs->~btOverlappingPairCache();
btAlignedFree(m_overlappingPairs);
}
}
void btMultiSapBroadphase::buildTree(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax)
{
m_optimizedAabbTree = new btQuantizedBvh();
m_optimizedAabbTree->setQuantizationValues(bvhAabbMin,bvhAabbMax);
QuantizedNodeArray& nodes = m_optimizedAabbTree->getLeafNodeArray();
for (int i=0;i<m_sapBroadphases.size();i++)
{
btQuantizedBvhNode node;
btVector3 aabbMin,aabbMax;
m_sapBroadphases[i]->getBroadphaseAabb(aabbMin,aabbMax);
m_optimizedAabbTree->quantize(&node.m_quantizedAabbMin[0],aabbMin,0);
m_optimizedAabbTree->quantize(&node.m_quantizedAabbMax[0],aabbMax,1);
int partId = 0;
node.m_escapeIndexOrTriangleIndex = (partId<<(31-MAX_NUM_PARTS_IN_BITS)) | i;
nodes.push_back(node);
}
m_optimizedAabbTree->buildInternal();
}
btBroadphaseProxy* btMultiSapBroadphase::createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* /*ignoreMe*/)
{
//void* ignoreMe -> we could think of recursive multi-sap, if someone is interested
void* mem = btAlignedAlloc(sizeof(btMultiSapProxy),16);
btMultiSapProxy* proxy = new (mem)btMultiSapProxy(aabbMin, aabbMax,shapeType,userPtr, collisionFilterGroup,collisionFilterMask);
m_multiSapProxies.push_back(proxy);
///this should deal with inserting/removal into child broadphases
setAabb(proxy,aabbMin,aabbMax,dispatcher);
return proxy;
}
void btMultiSapBroadphase::destroyProxy(btBroadphaseProxy* /*proxy*/,btDispatcher* /*dispatcher*/)
{
///not yet
btAssert(0);
}
void btMultiSapBroadphase::addToChildBroadphase(btMultiSapProxy* parentMultiSapProxy, btBroadphaseProxy* childProxy, btBroadphaseInterface* childBroadphase)
{
void* mem = btAlignedAlloc(sizeof(btBridgeProxy),16);
btBridgeProxy* bridgeProxyRef = new(mem) btBridgeProxy;
bridgeProxyRef->m_childProxy = childProxy;
bridgeProxyRef->m_childBroadphase = childBroadphase;
parentMultiSapProxy->m_bridgeProxies.push_back(bridgeProxyRef);
}
bool boxIsContainedWithinBox(const btVector3& amin,const btVector3& amax,const btVector3& bmin,const btVector3& bmax);
bool boxIsContainedWithinBox(const btVector3& amin,const btVector3& amax,const btVector3& bmin,const btVector3& bmax)
{
return
amin.getX() >= bmin.getX() && amax.getX() <= bmax.getX() &&
amin.getY() >= bmin.getY() && amax.getY() <= bmax.getY() &&
amin.getZ() >= bmin.getZ() && amax.getZ() <= bmax.getZ();
}
//#include <stdio.h>
void btMultiSapBroadphase::setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax, btDispatcher* dispatcher)
{
btMultiSapProxy* multiProxy = static_cast<btMultiSapProxy*>(proxy);
multiProxy->m_aabbMin = aabbMin;
multiProxy->m_aabbMax = aabbMax;
// bool fullyContained = false;
// bool alreadyInSimple = false;
struct MyNodeOverlapCallback : public btNodeOverlapCallback
{
btMultiSapBroadphase* m_multiSap;
btMultiSapProxy* m_multiProxy;
btDispatcher* m_dispatcher;
MyNodeOverlapCallback(btMultiSapBroadphase* multiSap,btMultiSapProxy* multiProxy,btDispatcher* dispatcher)
:m_multiSap(multiSap),
m_multiProxy(multiProxy),
m_dispatcher(dispatcher)
{
}
virtual void processNode(int /*nodeSubPart*/, int broadphaseIndex)
{
btBroadphaseInterface* childBroadphase = m_multiSap->getBroadphaseArray()[broadphaseIndex];
int containingBroadphaseIndex = -1;
//already found?
for (int i=0;i<m_multiProxy->m_bridgeProxies.size();i++)
{
if (m_multiProxy->m_bridgeProxies[i]->m_childBroadphase == childBroadphase)
{
containingBroadphaseIndex = i;
break;
}
}
if (containingBroadphaseIndex<0)
{
//add it
btBroadphaseProxy* childProxy = childBroadphase->createProxy(m_multiProxy->m_aabbMin,m_multiProxy->m_aabbMax,m_multiProxy->m_shapeType,m_multiProxy->m_clientObject,m_multiProxy->m_collisionFilterGroup,m_multiProxy->m_collisionFilterMask, m_dispatcher,m_multiProxy);
m_multiSap->addToChildBroadphase(m_multiProxy,childProxy,childBroadphase);
}
}
};
MyNodeOverlapCallback myNodeCallback(this,multiProxy,dispatcher);
m_optimizedAabbTree->reportAabbOverlappingNodex(&myNodeCallback,aabbMin,aabbMax);
int i;
for ( i=0;i<multiProxy->m_bridgeProxies.size();i++)
{
btVector3 worldAabbMin,worldAabbMax;
multiProxy->m_bridgeProxies[i]->m_childBroadphase->getBroadphaseAabb(worldAabbMin,worldAabbMax);
bool overlapsBroadphase = TestAabbAgainstAabb2(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax);
if (!overlapsBroadphase)
{
//remove it now
btBridgeProxy* bridgeProxy = multiProxy->m_bridgeProxies[i];
btBroadphaseProxy* childProxy = bridgeProxy->m_childProxy;
bridgeProxy->m_childBroadphase->destroyProxy(childProxy,dispatcher);
multiProxy->m_bridgeProxies.swap( i,multiProxy->m_bridgeProxies.size()-1);
multiProxy->m_bridgeProxies.pop_back();
}
}
/*
if (1)
{
//find broadphase that contain this multiProxy
int numChildBroadphases = getBroadphaseArray().size();
for (int i=0;i<numChildBroadphases;i++)
{
btBroadphaseInterface* childBroadphase = getBroadphaseArray()[i];
btVector3 worldAabbMin,worldAabbMax;
childBroadphase->getBroadphaseAabb(worldAabbMin,worldAabbMax);
bool overlapsBroadphase = TestAabbAgainstAabb2(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax);
// fullyContained = fullyContained || boxIsContainedWithinBox(worldAabbMin,worldAabbMax,multiProxy->m_aabbMin,multiProxy->m_aabbMax);
int containingBroadphaseIndex = -1;
//if already contains this
for (int i=0;i<multiProxy->m_bridgeProxies.size();i++)
{
if (multiProxy->m_bridgeProxies[i]->m_childBroadphase == childBroadphase)
{
containingBroadphaseIndex = i;
}
alreadyInSimple = alreadyInSimple || (multiProxy->m_bridgeProxies[i]->m_childBroadphase == m_simpleBroadphase);
}
if (overlapsBroadphase)
{
if (containingBroadphaseIndex<0)
{
btBroadphaseProxy* childProxy = childBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher);
childProxy->m_multiSapParentProxy = multiProxy;
addToChildBroadphase(multiProxy,childProxy,childBroadphase);
}
} else
{
if (containingBroadphaseIndex>=0)
{
//remove
btBridgeProxy* bridgeProxy = multiProxy->m_bridgeProxies[containingBroadphaseIndex];
btBroadphaseProxy* childProxy = bridgeProxy->m_childProxy;
bridgeProxy->m_childBroadphase->destroyProxy(childProxy,dispatcher);
multiProxy->m_bridgeProxies.swap( containingBroadphaseIndex,multiProxy->m_bridgeProxies.size()-1);
multiProxy->m_bridgeProxies.pop_back();
}
}
}
///If we are in no other child broadphase, stick the proxy in the global 'simple' broadphase (brute force)
///hopefully we don't end up with many entries here (can assert/provide feedback on stats)
if (0)//!multiProxy->m_bridgeProxies.size())
{
///we don't pass the userPtr but our multisap proxy. We need to patch this, before processing an actual collision
///this is needed to be able to calculate the aabb overlap
btBroadphaseProxy* childProxy = m_simpleBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher);
childProxy->m_multiSapParentProxy = multiProxy;
addToChildBroadphase(multiProxy,childProxy,m_simpleBroadphase);
}
}
if (!multiProxy->m_bridgeProxies.size())
{
///we don't pass the userPtr but our multisap proxy. We need to patch this, before processing an actual collision
///this is needed to be able to calculate the aabb overlap
btBroadphaseProxy* childProxy = m_simpleBroadphase->createProxy(aabbMin,aabbMax,multiProxy->m_shapeType,multiProxy->m_clientObject,multiProxy->m_collisionFilterGroup,multiProxy->m_collisionFilterMask, dispatcher);
childProxy->m_multiSapParentProxy = multiProxy;
addToChildBroadphase(multiProxy,childProxy,m_simpleBroadphase);
}
*/
//update
for ( i=0;i<multiProxy->m_bridgeProxies.size();i++)
{
btBridgeProxy* bridgeProxyRef = multiProxy->m_bridgeProxies[i];
bridgeProxyRef->m_childBroadphase->setAabb(bridgeProxyRef->m_childProxy,aabbMin,aabbMax,dispatcher);
}
}
bool stopUpdating=false;
class btMultiSapBroadphasePairSortPredicate
{
public:
bool operator() ( const btBroadphasePair& a1, const btBroadphasePair& b1 )
{
btMultiSapBroadphase::btMultiSapProxy* aProxy0 = a1.m_pProxy0 ? (btMultiSapBroadphase::btMultiSapProxy*)a1.m_pProxy0->m_multiSapParentProxy : 0;
btMultiSapBroadphase::btMultiSapProxy* aProxy1 = a1.m_pProxy1 ? (btMultiSapBroadphase::btMultiSapProxy*)a1.m_pProxy1->m_multiSapParentProxy : 0;
btMultiSapBroadphase::btMultiSapProxy* bProxy0 = b1.m_pProxy0 ? (btMultiSapBroadphase::btMultiSapProxy*)b1.m_pProxy0->m_multiSapParentProxy : 0;
btMultiSapBroadphase::btMultiSapProxy* bProxy1 = b1.m_pProxy1 ? (btMultiSapBroadphase::btMultiSapProxy*)b1.m_pProxy1->m_multiSapParentProxy : 0;
return aProxy0 > bProxy0 ||
(aProxy0 == bProxy0 && aProxy1 > bProxy1) ||
(aProxy0 == bProxy0 && aProxy1 == bProxy1 && a1.m_algorithm > b1.m_algorithm);
}
};
///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb
void btMultiSapBroadphase::calculateOverlappingPairs(btDispatcher* dispatcher)
{
// m_simpleBroadphase->calculateOverlappingPairs(dispatcher);
if (!stopUpdating && getOverlappingPairCache()->hasDeferredRemoval())
{
btBroadphasePairArray& overlappingPairArray = getOverlappingPairCache()->getOverlappingPairArray();
// quicksort(overlappingPairArray,0,overlappingPairArray.size());
overlappingPairArray.quickSort(btMultiSapBroadphasePairSortPredicate());
//perform a sort, to find duplicates and to sort 'invalid' pairs to the end
// overlappingPairArray.heapSort(btMultiSapBroadphasePairSortPredicate());
overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair);
m_invalidPair = 0;
int i;
btBroadphasePair previousPair;
previousPair.m_pProxy0 = 0;
previousPair.m_pProxy1 = 0;
previousPair.m_algorithm = 0;
for (i=0;i<overlappingPairArray.size();i++)
{
btBroadphasePair& pair = overlappingPairArray[i];
btMultiSapProxy* aProxy0 = pair.m_pProxy0 ? (btMultiSapProxy*)pair.m_pProxy0->m_multiSapParentProxy : 0;
btMultiSapProxy* aProxy1 = pair.m_pProxy1 ? (btMultiSapProxy*)pair.m_pProxy1->m_multiSapParentProxy : 0;
btMultiSapProxy* bProxy0 = previousPair.m_pProxy0 ? (btMultiSapProxy*)previousPair.m_pProxy0->m_multiSapParentProxy : 0;
btMultiSapProxy* bProxy1 = previousPair.m_pProxy1 ? (btMultiSapProxy*)previousPair.m_pProxy1->m_multiSapParentProxy : 0;
bool isDuplicate = (aProxy0 == bProxy0) && (aProxy1 == bProxy1);
previousPair = pair;
bool needsRemoval = false;
if (!isDuplicate)
{
bool hasOverlap = testAabbOverlap(pair.m_pProxy0,pair.m_pProxy1);
if (hasOverlap)
{
needsRemoval = false;//callback->processOverlap(pair);
} else
{
needsRemoval = true;
}
} else
{
//remove duplicate
needsRemoval = true;
//should have no algorithm
btAssert(!pair.m_algorithm);
}
if (needsRemoval)
{
getOverlappingPairCache()->cleanOverlappingPair(pair,dispatcher);
// m_overlappingPairArray.swap(i,m_overlappingPairArray.size()-1);
// m_overlappingPairArray.pop_back();
pair.m_pProxy0 = 0;
pair.m_pProxy1 = 0;
m_invalidPair++;
gOverlappingPairs--;
}
}
///if you don't like to skip the invalid pairs in the array, execute following code:
#define CLEAN_INVALID_PAIRS 1
#ifdef CLEAN_INVALID_PAIRS
//perform a sort, to sort 'invalid' pairs to the end
//overlappingPairArray.heapSort(btMultiSapBroadphasePairSortPredicate());
overlappingPairArray.quickSort(btMultiSapBroadphasePairSortPredicate());
overlappingPairArray.resize(overlappingPairArray.size() - m_invalidPair);
m_invalidPair = 0;
#endif//CLEAN_INVALID_PAIRS
//printf("overlappingPairArray.size()=%d\n",overlappingPairArray.size());
}
}
bool btMultiSapBroadphase::testAabbOverlap(btBroadphaseProxy* childProxy0,btBroadphaseProxy* childProxy1)
{
btMultiSapProxy* multiSapProxy0 = (btMultiSapProxy*)childProxy0->m_multiSapParentProxy;
btMultiSapProxy* multiSapProxy1 = (btMultiSapProxy*)childProxy1->m_multiSapParentProxy;
return TestAabbAgainstAabb2(multiSapProxy0->m_aabbMin,multiSapProxy0->m_aabbMax,
multiSapProxy1->m_aabbMin,multiSapProxy1->m_aabbMax);
}
void btMultiSapBroadphase::printStats()
{
/* printf("---------------------------------\n");
printf("btMultiSapBroadphase.h\n");
printf("numHandles = %d\n",m_multiSapProxies.size());
//find broadphase that contain this multiProxy
int numChildBroadphases = getBroadphaseArray().size();
for (int i=0;i<numChildBroadphases;i++)
{
btBroadphaseInterface* childBroadphase = getBroadphaseArray()[i];
childBroadphase->printStats();
}
*/
}

View File

@@ -0,0 +1,144 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 BT_MULTI_SAP_BROADPHASE
#define BT_MULTI_SAP_BROADPHASE
#include "btBroadphaseInterface.h"
#include "LinearMath/btAlignedObjectArray.h"
#include "btOverlappingPairCache.h"
class btBroadphaseInterface;
class btSimpleBroadphase;
typedef btAlignedObjectArray<btBroadphaseInterface*> btSapBroadphaseArray;
///The btMultiSapBroadphase is a broadphase that contains multiple SAP broadphases.
///The user can add SAP broadphases that cover the world. A btBroadphaseProxy can be in multiple child broadphases at the same time.
///A btQuantizedBvh acceleration structures finds overlapping SAPs for each btBroadphaseProxy.
///See http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=328
///and http://www.continuousphysics.com/Bullet/phpBB2/viewtopic.php?t=1329
class btMultiSapBroadphase :public btBroadphaseInterface
{
btSapBroadphaseArray m_sapBroadphases;
btSimpleBroadphase* m_simpleBroadphase;
btOverlappingPairCache* m_overlappingPairs;
class btQuantizedBvh* m_optimizedAabbTree;
bool m_ownsPairCache;
btOverlapFilterCallback* m_filterCallback;
int m_invalidPair;
struct btBridgeProxy
{
btBroadphaseProxy* m_childProxy;
btBroadphaseInterface* m_childBroadphase;
};
public:
struct btMultiSapProxy : public btBroadphaseProxy
{
///array with all the entries that this proxy belongs to
btAlignedObjectArray<btBridgeProxy*> m_bridgeProxies;
btVector3 m_aabbMin;
btVector3 m_aabbMax;
int m_shapeType;
/* void* m_userPtr;
short int m_collisionFilterGroup;
short int m_collisionFilterMask;
*/
btMultiSapProxy(const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask)
:btBroadphaseProxy(userPtr,collisionFilterGroup,collisionFilterMask),
m_aabbMin(aabbMin),
m_aabbMax(aabbMax),
m_shapeType(shapeType)
{
m_multiSapParentProxy =this;
}
};
protected:
btAlignedObjectArray<btMultiSapProxy*> m_multiSapProxies;
public:
btMultiSapBroadphase(int maxProxies = 16384,btOverlappingPairCache* pairCache=0);
btSapBroadphaseArray& getBroadphaseArray()
{
return m_sapBroadphases;
}
const btSapBroadphaseArray& getBroadphaseArray() const
{
return m_sapBroadphases;
}
virtual ~btMultiSapBroadphase();
virtual btBroadphaseProxy* createProxy( const btVector3& aabbMin, const btVector3& aabbMax,int shapeType,void* userPtr, short int collisionFilterGroup,short int collisionFilterMask, btDispatcher* dispatcher,void* multiSapProxy);
virtual void destroyProxy(btBroadphaseProxy* proxy,btDispatcher* dispatcher);
virtual void setAabb(btBroadphaseProxy* proxy,const btVector3& aabbMin,const btVector3& aabbMax, btDispatcher* dispatcher);
void addToChildBroadphase(btMultiSapProxy* parentMultiSapProxy, btBroadphaseProxy* childProxy, btBroadphaseInterface* childBroadphase);
///calculateOverlappingPairs is optional: incremental algorithms (sweep and prune) might do it during the set aabb
virtual void calculateOverlappingPairs(btDispatcher* dispatcher);
bool testAabbOverlap(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1);
virtual btOverlappingPairCache* getOverlappingPairCache()
{
return m_overlappingPairs;
}
virtual const btOverlappingPairCache* getOverlappingPairCache() const
{
return m_overlappingPairs;
}
///getAabb returns the axis aligned bounding box in the 'global' coordinate frame
///will add some transform later
virtual void getBroadphaseAabb(btVector3& aabbMin,btVector3& aabbMax) const
{
aabbMin.setValue(-1e30f,-1e30f,-1e30f);
aabbMax.setValue(1e30f,1e30f,1e30f);
}
void buildTree(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax);
virtual void printStats();
void quicksort (btBroadphasePairArray& a, int lo, int hi);
};
#endif //BT_MULTI_SAP_BROADPHASE

View File

@@ -0,0 +1,40 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 OVERLAPPING_PAIR_CALLBACK_H
#define OVERLAPPING_PAIR_CALLBACK_H
class btDispatcher;
struct btBroadphasePair;
///The btOverlappingPairCallback class is an additional optional broadphase user callback for adding/removing overlapping pairs, similar interface to btOverlappingPairCache.
class btOverlappingPairCallback
{
public:
virtual ~btOverlappingPairCallback()
{
}
virtual btBroadphasePair* addOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) = 0;
virtual void* removeOverlappingPair(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1,btDispatcher* dispatcher) = 0;
virtual void removeOverlappingPairsContainingProxy(btBroadphaseProxy* proxy0,btDispatcher* dispatcher) = 0;
};
#endif //OVERLAPPING_PAIR_CALLBACK_H

View File

@@ -0,0 +1,1025 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 "btQuantizedBvh.h"
#include "LinearMath/btAabbUtil2.h"
#include "LinearMath/btIDebugDraw.h"
btQuantizedBvh::btQuantizedBvh() : m_useQuantization(false),
//m_traversalMode(TRAVERSAL_STACKLESS_CACHE_FRIENDLY)
m_traversalMode(TRAVERSAL_STACKLESS)
//m_traversalMode(TRAVERSAL_RECURSIVE)
,m_subtreeHeaderCount(0) //PCK: add this line
{
}
void btQuantizedBvh::buildInternal()
{
///assumes that caller filled in the m_quantizedLeafNodes
m_useQuantization = true;
int numLeafNodes = 0;
if (m_useQuantization)
{
//now we have an array of leafnodes in m_leafNodes
numLeafNodes = m_quantizedLeafNodes.size();
m_quantizedContiguousNodes.resize(2*numLeafNodes);
}
m_curNodeIndex = 0;
buildTree(0,numLeafNodes);
///if the entire tree is small then subtree size, we need to create a header info for the tree
if(m_useQuantization && !m_SubtreeHeaders.size())
{
btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand();
subtree.setAabbFromQuantizeNode(m_quantizedContiguousNodes[0]);
subtree.m_rootNodeIndex = 0;
subtree.m_subtreeSize = m_quantizedContiguousNodes[0].isLeafNode() ? 1 : m_quantizedContiguousNodes[0].getEscapeIndex();
}
//PCK: update the copy of the size
m_subtreeHeaderCount = m_SubtreeHeaders.size();
//PCK: clear m_quantizedLeafNodes and m_leafNodes, they are temporary
m_quantizedLeafNodes.clear();
m_leafNodes.clear();
}
///just for debugging, to visualize the individual patches/subtrees
#ifdef DEBUG_PATCH_COLORS
btVector3 color[4]=
{
btVector3(255,0,0),
btVector3(0,255,0),
btVector3(0,0,255),
btVector3(0,255,255)
};
#endif //DEBUG_PATCH_COLORS
void btQuantizedBvh::setQuantizationValues(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax,btScalar quantizationMargin)
{
//enlarge the AABB to avoid division by zero when initializing the quantization values
btVector3 clampValue(quantizationMargin,quantizationMargin,quantizationMargin);
m_bvhAabbMin = bvhAabbMin - clampValue;
m_bvhAabbMax = bvhAabbMax + clampValue;
btVector3 aabbSize = m_bvhAabbMax - m_bvhAabbMin;
m_bvhQuantization = btVector3(btScalar(65533.0),btScalar(65533.0),btScalar(65533.0)) / aabbSize;
m_useQuantization = true;
}
btQuantizedBvh::~btQuantizedBvh()
{
}
#ifdef DEBUG_TREE_BUILDING
int gStackDepth = 0;
int gMaxStackDepth = 0;
#endif //DEBUG_TREE_BUILDING
void btQuantizedBvh::buildTree (int startIndex,int endIndex)
{
#ifdef DEBUG_TREE_BUILDING
gStackDepth++;
if (gStackDepth > gMaxStackDepth)
gMaxStackDepth = gStackDepth;
#endif //DEBUG_TREE_BUILDING
int splitAxis, splitIndex, i;
int numIndices =endIndex-startIndex;
int curIndex = m_curNodeIndex;
assert(numIndices>0);
if (numIndices==1)
{
#ifdef DEBUG_TREE_BUILDING
gStackDepth--;
#endif //DEBUG_TREE_BUILDING
assignInternalNodeFromLeafNode(m_curNodeIndex,startIndex);
m_curNodeIndex++;
return;
}
//calculate Best Splitting Axis and where to split it. Sort the incoming 'leafNodes' array within range 'startIndex/endIndex'.
splitAxis = calcSplittingAxis(startIndex,endIndex);
splitIndex = sortAndCalcSplittingIndex(startIndex,endIndex,splitAxis);
int internalNodeIndex = m_curNodeIndex;
setInternalNodeAabbMax(m_curNodeIndex,m_bvhAabbMin);
setInternalNodeAabbMin(m_curNodeIndex,m_bvhAabbMax);
for (i=startIndex;i<endIndex;i++)
{
mergeInternalNodeAabb(m_curNodeIndex,getAabbMin(i),getAabbMax(i));
}
m_curNodeIndex++;
//internalNode->m_escapeIndex;
int leftChildNodexIndex = m_curNodeIndex;
//build left child tree
buildTree(startIndex,splitIndex);
int rightChildNodexIndex = m_curNodeIndex;
//build right child tree
buildTree(splitIndex,endIndex);
#ifdef DEBUG_TREE_BUILDING
gStackDepth--;
#endif //DEBUG_TREE_BUILDING
int escapeIndex = m_curNodeIndex - curIndex;
if (m_useQuantization)
{
//escapeIndex is the number of nodes of this subtree
const int sizeQuantizedNode =sizeof(btQuantizedBvhNode);
const int treeSizeInBytes = escapeIndex * sizeQuantizedNode;
if (treeSizeInBytes > MAX_SUBTREE_SIZE_IN_BYTES)
{
updateSubtreeHeaders(leftChildNodexIndex,rightChildNodexIndex);
}
}
setInternalNodeEscapeIndex(internalNodeIndex,escapeIndex);
}
void btQuantizedBvh::updateSubtreeHeaders(int leftChildNodexIndex,int rightChildNodexIndex)
{
btAssert(m_useQuantization);
btQuantizedBvhNode& leftChildNode = m_quantizedContiguousNodes[leftChildNodexIndex];
int leftSubTreeSize = leftChildNode.isLeafNode() ? 1 : leftChildNode.getEscapeIndex();
int leftSubTreeSizeInBytes = leftSubTreeSize * static_cast<int>(sizeof(btQuantizedBvhNode));
btQuantizedBvhNode& rightChildNode = m_quantizedContiguousNodes[rightChildNodexIndex];
int rightSubTreeSize = rightChildNode.isLeafNode() ? 1 : rightChildNode.getEscapeIndex();
int rightSubTreeSizeInBytes = rightSubTreeSize * static_cast<int>(sizeof(btQuantizedBvhNode));
if(leftSubTreeSizeInBytes <= MAX_SUBTREE_SIZE_IN_BYTES)
{
btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand();
subtree.setAabbFromQuantizeNode(leftChildNode);
subtree.m_rootNodeIndex = leftChildNodexIndex;
subtree.m_subtreeSize = leftSubTreeSize;
}
if(rightSubTreeSizeInBytes <= MAX_SUBTREE_SIZE_IN_BYTES)
{
btBvhSubtreeInfo& subtree = m_SubtreeHeaders.expand();
subtree.setAabbFromQuantizeNode(rightChildNode);
subtree.m_rootNodeIndex = rightChildNodexIndex;
subtree.m_subtreeSize = rightSubTreeSize;
}
//PCK: update the copy of the size
m_subtreeHeaderCount = m_SubtreeHeaders.size();
}
int btQuantizedBvh::sortAndCalcSplittingIndex(int startIndex,int endIndex,int splitAxis)
{
int i;
int splitIndex =startIndex;
int numIndices = endIndex - startIndex;
btScalar splitValue;
btVector3 means(btScalar(0.),btScalar(0.),btScalar(0.));
for (i=startIndex;i<endIndex;i++)
{
btVector3 center = btScalar(0.5)*(getAabbMax(i)+getAabbMin(i));
means+=center;
}
means *= (btScalar(1.)/(btScalar)numIndices);
splitValue = means[splitAxis];
//sort leafNodes so all values larger then splitValue comes first, and smaller values start from 'splitIndex'.
for (i=startIndex;i<endIndex;i++)
{
btVector3 center = btScalar(0.5)*(getAabbMax(i)+getAabbMin(i));
if (center[splitAxis] > splitValue)
{
//swap
swapLeafNodes(i,splitIndex);
splitIndex++;
}
}
//if the splitIndex causes unbalanced trees, fix this by using the center in between startIndex and endIndex
//otherwise the tree-building might fail due to stack-overflows in certain cases.
//unbalanced1 is unsafe: it can cause stack overflows
//bool unbalanced1 = ((splitIndex==startIndex) || (splitIndex == (endIndex-1)));
//unbalanced2 should work too: always use center (perfect balanced trees)
//bool unbalanced2 = true;
//this should be safe too:
int rangeBalancedIndices = numIndices/3;
bool unbalanced = ((splitIndex<=(startIndex+rangeBalancedIndices)) || (splitIndex >=(endIndex-1-rangeBalancedIndices)));
if (unbalanced)
{
splitIndex = startIndex+ (numIndices>>1);
}
bool unbal = (splitIndex==startIndex) || (splitIndex == (endIndex));
(void)unbal;
btAssert(!unbal);
return splitIndex;
}
int btQuantizedBvh::calcSplittingAxis(int startIndex,int endIndex)
{
int i;
btVector3 means(btScalar(0.),btScalar(0.),btScalar(0.));
btVector3 variance(btScalar(0.),btScalar(0.),btScalar(0.));
int numIndices = endIndex-startIndex;
for (i=startIndex;i<endIndex;i++)
{
btVector3 center = btScalar(0.5)*(getAabbMax(i)+getAabbMin(i));
means+=center;
}
means *= (btScalar(1.)/(btScalar)numIndices);
for (i=startIndex;i<endIndex;i++)
{
btVector3 center = btScalar(0.5)*(getAabbMax(i)+getAabbMin(i));
btVector3 diff2 = center-means;
diff2 = diff2 * diff2;
variance += diff2;
}
variance *= (btScalar(1.)/ ((btScalar)numIndices-1) );
return variance.maxAxis();
}
void btQuantizedBvh::reportAabbOverlappingNodex(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const
{
//either choose recursive traversal (walkTree) or stackless (walkStacklessTree)
if (m_useQuantization)
{
///quantize query AABB
unsigned short int quantizedQueryAabbMin[3];
unsigned short int quantizedQueryAabbMax[3];
quantizeWithClamp(quantizedQueryAabbMin,aabbMin,0);
quantizeWithClamp(quantizedQueryAabbMax,aabbMax,1);
switch (m_traversalMode)
{
case TRAVERSAL_STACKLESS:
walkStacklessQuantizedTree(nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax,0,m_curNodeIndex);
break;
case TRAVERSAL_STACKLESS_CACHE_FRIENDLY:
walkStacklessQuantizedTreeCacheFriendly(nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax);
break;
case TRAVERSAL_RECURSIVE:
{
const btQuantizedBvhNode* rootNode = &m_quantizedContiguousNodes[0];
walkRecursiveQuantizedTreeAgainstQueryAabb(rootNode,nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax);
}
break;
default:
//unsupported
btAssert(0);
}
} else
{
walkStacklessTree(nodeCallback,aabbMin,aabbMax);
}
}
int maxIterations = 0;
void btQuantizedBvh::walkStacklessTree(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const
{
btAssert(!m_useQuantization);
const btOptimizedBvhNode* rootNode = &m_contiguousNodes[0];
int escapeIndex, curIndex = 0;
int walkIterations = 0;
bool isLeafNode;
//PCK: unsigned instead of bool
unsigned aabbOverlap;
while (curIndex < m_curNodeIndex)
{
//catch bugs in tree data
assert (walkIterations < m_curNodeIndex);
walkIterations++;
aabbOverlap = TestAabbAgainstAabb2(aabbMin,aabbMax,rootNode->m_aabbMinOrg,rootNode->m_aabbMaxOrg);
isLeafNode = rootNode->m_escapeIndex == -1;
//PCK: unsigned instead of bool
if (isLeafNode && (aabbOverlap != 0))
{
nodeCallback->processNode(rootNode->m_subPart,rootNode->m_triangleIndex);
}
//PCK: unsigned instead of bool
if ((aabbOverlap != 0) || isLeafNode)
{
rootNode++;
curIndex++;
} else
{
escapeIndex = rootNode->m_escapeIndex;
rootNode += escapeIndex;
curIndex += escapeIndex;
}
}
if (maxIterations < walkIterations)
maxIterations = walkIterations;
}
/*
///this was the original recursive traversal, before we optimized towards stackless traversal
void btQuantizedBvh::walkTree(btOptimizedBvhNode* rootNode,btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const
{
bool isLeafNode, aabbOverlap = TestAabbAgainstAabb2(aabbMin,aabbMax,rootNode->m_aabbMin,rootNode->m_aabbMax);
if (aabbOverlap)
{
isLeafNode = (!rootNode->m_leftChild && !rootNode->m_rightChild);
if (isLeafNode)
{
nodeCallback->processNode(rootNode);
} else
{
walkTree(rootNode->m_leftChild,nodeCallback,aabbMin,aabbMax);
walkTree(rootNode->m_rightChild,nodeCallback,aabbMin,aabbMax);
}
}
}
*/
void btQuantizedBvh::walkRecursiveQuantizedTreeAgainstQueryAabb(const btQuantizedBvhNode* currentNode,btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const
{
btAssert(m_useQuantization);
bool isLeafNode;
//PCK: unsigned instead of bool
unsigned aabbOverlap;
//PCK: unsigned instead of bool
aabbOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,currentNode->m_quantizedAabbMin,currentNode->m_quantizedAabbMax);
isLeafNode = currentNode->isLeafNode();
//PCK: unsigned instead of bool
if (aabbOverlap != 0)
{
if (isLeafNode)
{
nodeCallback->processNode(currentNode->getPartId(),currentNode->getTriangleIndex());
} else
{
//process left and right children
const btQuantizedBvhNode* leftChildNode = currentNode+1;
walkRecursiveQuantizedTreeAgainstQueryAabb(leftChildNode,nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax);
const btQuantizedBvhNode* rightChildNode = leftChildNode->isLeafNode() ? leftChildNode+1:leftChildNode+leftChildNode->getEscapeIndex();
walkRecursiveQuantizedTreeAgainstQueryAabb(rightChildNode,nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax);
}
}
}
void btQuantizedBvh::walkStacklessQuantizedTreeAgainstRay(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin, const btVector3& aabbMax, int startNodeIndex,int endNodeIndex) const
{
btAssert(m_useQuantization);
int curIndex = startNodeIndex;
int walkIterations = 0;
int subTreeSize = endNodeIndex - startNodeIndex;
(void)subTreeSize;
const btQuantizedBvhNode* rootNode = &m_quantizedContiguousNodes[startNodeIndex];
int escapeIndex;
bool isLeafNode;
//PCK: unsigned instead of bool
unsigned boxBoxOverlap = 0;
unsigned rayBoxOverlap = 0;
btScalar lambda_max = 1.0;
#define RAYAABB2
#ifdef RAYAABB2
btVector3 rayFrom = raySource;
btVector3 rayDirection = (rayTarget-raySource);
rayDirection.normalize ();
lambda_max = rayDirection.dot(rayTarget-raySource);
///what about division by zero? --> just set rayDirection[i] to 1.0
rayDirection[0] = rayDirection[0] == btScalar(0.0) ? btScalar(1e30) : btScalar(1.0) / rayDirection[0];
rayDirection[1] = rayDirection[1] == btScalar(0.0) ? btScalar(1e30) : btScalar(1.0) / rayDirection[1];
rayDirection[2] = rayDirection[2] == btScalar(0.0) ? btScalar(1e30) : btScalar(1.0) / rayDirection[2];
unsigned int sign[3] = { rayDirection[0] < 0.0, rayDirection[1] < 0.0, rayDirection[2] < 0.0};
#endif
/* Quick pruning by quantized box */
btVector3 rayAabbMin = raySource;
btVector3 rayAabbMax = raySource;
rayAabbMin.setMin(rayTarget);
rayAabbMax.setMax(rayTarget);
/* Add box cast extents to bounding box */
rayAabbMin += aabbMin;
rayAabbMax += aabbMax;
unsigned short int quantizedQueryAabbMin[3];
unsigned short int quantizedQueryAabbMax[3];
quantizeWithClamp(quantizedQueryAabbMin,rayAabbMin,0);
quantizeWithClamp(quantizedQueryAabbMax,rayAabbMax,1);
while (curIndex < endNodeIndex)
{
//#define VISUALLY_ANALYZE_BVH 1
#ifdef VISUALLY_ANALYZE_BVH
//some code snippet to debugDraw aabb, to visually analyze bvh structure
static int drawPatch = 0;
//need some global access to a debugDrawer
extern btIDebugDraw* debugDrawerPtr;
if (curIndex==drawPatch)
{
btVector3 aabbMin,aabbMax;
aabbMin = unQuantize(rootNode->m_quantizedAabbMin);
aabbMax = unQuantize(rootNode->m_quantizedAabbMax);
btVector3 color(1,0,0);
debugDrawerPtr->drawAabb(aabbMin,aabbMax,color);
}
#endif//VISUALLY_ANALYZE_BVH
//catch bugs in tree data
assert (walkIterations < subTreeSize);
walkIterations++;
//PCK: unsigned instead of bool
// only interested if this is closer than any previous hit
btScalar param = 1.0;
rayBoxOverlap = 0;
boxBoxOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,rootNode->m_quantizedAabbMin,rootNode->m_quantizedAabbMax);
isLeafNode = rootNode->isLeafNode();
if (boxBoxOverlap)
{
btVector3 bounds[2];
bounds[0] = unQuantize(rootNode->m_quantizedAabbMin);
bounds[1] = unQuantize(rootNode->m_quantizedAabbMax);
/* Add box cast extents */
bounds[0] += aabbMin;
bounds[1] += aabbMax;
btVector3 normal;
#if 0
bool ra2 = btRayAabb2 (raySource, rayDirection, sign, bounds, param, 0.0, lambda_max);
bool ra = btRayAabb (raySource, rayTarget, bounds[0], bounds[1], param, normal);
if (ra2 != ra)
{
printf("functions don't match\n");
}
#endif
#ifdef RAYAABB2
///careful with this check: need to check division by zero (above) and fix the unQuantize method
///thanks Joerg/hiker for the reproduction case!
///http://www.bulletphysics.com/Bullet/phpBB3/viewtopic.php?f=9&t=1858
rayBoxOverlap = btRayAabb2 (raySource, rayDirection, sign, bounds, param, 0.0f, lambda_max);
#else
rayBoxOverlap = true;//btRayAabb(raySource, rayTarget, bounds[0], bounds[1], param, normal);
#endif
}
if (isLeafNode && rayBoxOverlap)
{
nodeCallback->processNode(rootNode->getPartId(),rootNode->getTriangleIndex());
}
//PCK: unsigned instead of bool
if ((rayBoxOverlap != 0) || isLeafNode)
{
rootNode++;
curIndex++;
} else
{
escapeIndex = rootNode->getEscapeIndex();
rootNode += escapeIndex;
curIndex += escapeIndex;
}
}
if (maxIterations < walkIterations)
maxIterations = walkIterations;
}
void btQuantizedBvh::walkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,int startNodeIndex,int endNodeIndex) const
{
btAssert(m_useQuantization);
int curIndex = startNodeIndex;
int walkIterations = 0;
int subTreeSize = endNodeIndex - startNodeIndex;
(void)subTreeSize;
const btQuantizedBvhNode* rootNode = &m_quantizedContiguousNodes[startNodeIndex];
int escapeIndex;
bool isLeafNode;
//PCK: unsigned instead of bool
unsigned aabbOverlap;
while (curIndex < endNodeIndex)
{
//#define VISUALLY_ANALYZE_BVH 1
#ifdef VISUALLY_ANALYZE_BVH
//some code snippet to debugDraw aabb, to visually analyze bvh structure
static int drawPatch = 0;
//need some global access to a debugDrawer
extern btIDebugDraw* debugDrawerPtr;
if (curIndex==drawPatch)
{
btVector3 aabbMin,aabbMax;
aabbMin = unQuantize(rootNode->m_quantizedAabbMin);
aabbMax = unQuantize(rootNode->m_quantizedAabbMax);
btVector3 color(1,0,0);
debugDrawerPtr->drawAabb(aabbMin,aabbMax,color);
}
#endif//VISUALLY_ANALYZE_BVH
//catch bugs in tree data
assert (walkIterations < subTreeSize);
walkIterations++;
//PCK: unsigned instead of bool
aabbOverlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,rootNode->m_quantizedAabbMin,rootNode->m_quantizedAabbMax);
isLeafNode = rootNode->isLeafNode();
if (isLeafNode && aabbOverlap)
{
nodeCallback->processNode(rootNode->getPartId(),rootNode->getTriangleIndex());
}
//PCK: unsigned instead of bool
if ((aabbOverlap != 0) || isLeafNode)
{
rootNode++;
curIndex++;
} else
{
escapeIndex = rootNode->getEscapeIndex();
rootNode += escapeIndex;
curIndex += escapeIndex;
}
}
if (maxIterations < walkIterations)
maxIterations = walkIterations;
}
//This traversal can be called from Playstation 3 SPU
void btQuantizedBvh::walkStacklessQuantizedTreeCacheFriendly(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const
{
btAssert(m_useQuantization);
int i;
for (i=0;i<this->m_SubtreeHeaders.size();i++)
{
const btBvhSubtreeInfo& subtree = m_SubtreeHeaders[i];
//PCK: unsigned instead of bool
unsigned overlap = testQuantizedAabbAgainstQuantizedAabb(quantizedQueryAabbMin,quantizedQueryAabbMax,subtree.m_quantizedAabbMin,subtree.m_quantizedAabbMax);
if (overlap != 0)
{
walkStacklessQuantizedTree(nodeCallback,quantizedQueryAabbMin,quantizedQueryAabbMax,
subtree.m_rootNodeIndex,
subtree.m_rootNodeIndex+subtree.m_subtreeSize);
}
}
}
void btQuantizedBvh::reportRayOverlappingNodex (btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget) const
{
bool fast_path = m_useQuantization && m_traversalMode == TRAVERSAL_STACKLESS;
if (fast_path)
{
walkStacklessQuantizedTreeAgainstRay(nodeCallback, raySource, rayTarget, btVector3(0, 0, 0), btVector3(0, 0, 0), 0, m_curNodeIndex);
} else {
/* Otherwise fallback to AABB overlap test */
btVector3 aabbMin = raySource;
btVector3 aabbMax = raySource;
aabbMin.setMin(rayTarget);
aabbMax.setMax(rayTarget);
reportAabbOverlappingNodex(nodeCallback,aabbMin,aabbMax);
}
}
void btQuantizedBvh::reportBoxCastOverlappingNodex(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin,const btVector3& aabbMax) const
{
bool fast_path = m_useQuantization && m_traversalMode == TRAVERSAL_STACKLESS;
if (fast_path)
{
walkStacklessQuantizedTreeAgainstRay(nodeCallback, raySource, rayTarget, aabbMin, aabbMax, 0, m_curNodeIndex);
} else {
/* Slow path:
Construct the bounding box for the entire box cast and send that down the tree */
btVector3 qaabbMin = raySource;
btVector3 qaabbMax = raySource;
qaabbMin.setMin(rayTarget);
qaabbMax.setMax(rayTarget);
qaabbMin += aabbMin;
qaabbMax += aabbMax;
reportAabbOverlappingNodex(nodeCallback,qaabbMin,qaabbMax);
}
}
void btQuantizedBvh::swapLeafNodes(int i,int splitIndex)
{
if (m_useQuantization)
{
btQuantizedBvhNode tmp = m_quantizedLeafNodes[i];
m_quantizedLeafNodes[i] = m_quantizedLeafNodes[splitIndex];
m_quantizedLeafNodes[splitIndex] = tmp;
} else
{
btOptimizedBvhNode tmp = m_leafNodes[i];
m_leafNodes[i] = m_leafNodes[splitIndex];
m_leafNodes[splitIndex] = tmp;
}
}
void btQuantizedBvh::assignInternalNodeFromLeafNode(int internalNode,int leafNodeIndex)
{
if (m_useQuantization)
{
m_quantizedContiguousNodes[internalNode] = m_quantizedLeafNodes[leafNodeIndex];
} else
{
m_contiguousNodes[internalNode] = m_leafNodes[leafNodeIndex];
}
}
//PCK: include
#include <new>
//PCK: consts
static const unsigned BVH_ALIGNMENT = 16;
static const unsigned BVH_ALIGNMENT_MASK = BVH_ALIGNMENT-1;
static const unsigned BVH_ALIGNMENT_BLOCKS = 2;
unsigned int btQuantizedBvh::getAlignmentSerializationPadding()
{
return BVH_ALIGNMENT_BLOCKS * BVH_ALIGNMENT;
}
unsigned btQuantizedBvh::calculateSerializeBufferSize()
{
unsigned baseSize = sizeof(btQuantizedBvh) + getAlignmentSerializationPadding();
baseSize += sizeof(btBvhSubtreeInfo) * m_subtreeHeaderCount;
if (m_useQuantization)
{
return baseSize + m_curNodeIndex * sizeof(btQuantizedBvhNode);
}
return baseSize + m_curNodeIndex * sizeof(btOptimizedBvhNode);
}
bool btQuantizedBvh::serialize(void *o_alignedDataBuffer, unsigned /*i_dataBufferSize */, bool i_swapEndian)
{
assert(m_subtreeHeaderCount == m_SubtreeHeaders.size());
m_subtreeHeaderCount = m_SubtreeHeaders.size();
/* if (i_dataBufferSize < calculateSerializeBufferSize() || o_alignedDataBuffer == NULL || (((unsigned)o_alignedDataBuffer & BVH_ALIGNMENT_MASK) != 0))
{
///check alignedment for buffer?
btAssert(0);
return false;
}
*/
btQuantizedBvh *targetBvh = (btQuantizedBvh *)o_alignedDataBuffer;
// construct the class so the virtual function table, etc will be set up
// Also, m_leafNodes and m_quantizedLeafNodes will be initialized to default values by the constructor
new (targetBvh) btQuantizedBvh;
if (i_swapEndian)
{
targetBvh->m_curNodeIndex = static_cast<int>(btSwapEndian(m_curNodeIndex));
btSwapVector3Endian(m_bvhAabbMin,targetBvh->m_bvhAabbMin);
btSwapVector3Endian(m_bvhAabbMax,targetBvh->m_bvhAabbMax);
btSwapVector3Endian(m_bvhQuantization,targetBvh->m_bvhQuantization);
targetBvh->m_traversalMode = (btTraversalMode)btSwapEndian(m_traversalMode);
targetBvh->m_subtreeHeaderCount = static_cast<int>(btSwapEndian(m_subtreeHeaderCount));
}
else
{
targetBvh->m_curNodeIndex = m_curNodeIndex;
targetBvh->m_bvhAabbMin = m_bvhAabbMin;
targetBvh->m_bvhAabbMax = m_bvhAabbMax;
targetBvh->m_bvhQuantization = m_bvhQuantization;
targetBvh->m_traversalMode = m_traversalMode;
targetBvh->m_subtreeHeaderCount = m_subtreeHeaderCount;
}
targetBvh->m_useQuantization = m_useQuantization;
unsigned char *nodeData = (unsigned char *)targetBvh;
nodeData += sizeof(btQuantizedBvh);
unsigned sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK;
nodeData += sizeToAdd;
int nodeCount = m_curNodeIndex;
if (m_useQuantization)
{
targetBvh->m_quantizedContiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount);
if (i_swapEndian)
{
for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0]);
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1]);
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2]);
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0]);
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1]);
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2] = btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2]);
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = static_cast<int>(btSwapEndian(m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex));
}
}
else
{
for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0];
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1];
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2];
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0];
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1];
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2] = m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2];
targetBvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex;
}
}
nodeData += sizeof(btQuantizedBvhNode) * nodeCount;
}
else
{
targetBvh->m_contiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount);
if (i_swapEndian)
{
for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
btSwapVector3Endian(m_contiguousNodes[nodeIndex].m_aabbMinOrg, targetBvh->m_contiguousNodes[nodeIndex].m_aabbMinOrg);
btSwapVector3Endian(m_contiguousNodes[nodeIndex].m_aabbMaxOrg, targetBvh->m_contiguousNodes[nodeIndex].m_aabbMaxOrg);
targetBvh->m_contiguousNodes[nodeIndex].m_escapeIndex = static_cast<int>(btSwapEndian(m_contiguousNodes[nodeIndex].m_escapeIndex));
targetBvh->m_contiguousNodes[nodeIndex].m_subPart = static_cast<int>(btSwapEndian(m_contiguousNodes[nodeIndex].m_subPart));
targetBvh->m_contiguousNodes[nodeIndex].m_triangleIndex = static_cast<int>(btSwapEndian(m_contiguousNodes[nodeIndex].m_triangleIndex));
}
}
else
{
for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
targetBvh->m_contiguousNodes[nodeIndex].m_aabbMinOrg = m_contiguousNodes[nodeIndex].m_aabbMinOrg;
targetBvh->m_contiguousNodes[nodeIndex].m_aabbMaxOrg = m_contiguousNodes[nodeIndex].m_aabbMaxOrg;
targetBvh->m_contiguousNodes[nodeIndex].m_escapeIndex = m_contiguousNodes[nodeIndex].m_escapeIndex;
targetBvh->m_contiguousNodes[nodeIndex].m_subPart = m_contiguousNodes[nodeIndex].m_subPart;
targetBvh->m_contiguousNodes[nodeIndex].m_triangleIndex = m_contiguousNodes[nodeIndex].m_triangleIndex;
}
}
nodeData += sizeof(btOptimizedBvhNode) * nodeCount;
}
sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK;
nodeData += sizeToAdd;
// Now serialize the subtree headers
targetBvh->m_SubtreeHeaders.initializeFromBuffer(nodeData, m_subtreeHeaderCount, m_subtreeHeaderCount);
if (i_swapEndian)
{
for (int i = 0; i < m_subtreeHeaderCount; i++)
{
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMin[0]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMin[1]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMin[2]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMax[0]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMax[1]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2] = btSwapEndian(m_SubtreeHeaders[i].m_quantizedAabbMax[2]);
targetBvh->m_SubtreeHeaders[i].m_rootNodeIndex = static_cast<int>(btSwapEndian(m_SubtreeHeaders[i].m_rootNodeIndex));
targetBvh->m_SubtreeHeaders[i].m_subtreeSize = static_cast<int>(btSwapEndian(m_SubtreeHeaders[i].m_subtreeSize));
}
}
else
{
for (int i = 0; i < m_subtreeHeaderCount; i++)
{
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0] = (m_SubtreeHeaders[i].m_quantizedAabbMin[0]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1] = (m_SubtreeHeaders[i].m_quantizedAabbMin[1]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2] = (m_SubtreeHeaders[i].m_quantizedAabbMin[2]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0] = (m_SubtreeHeaders[i].m_quantizedAabbMax[0]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1] = (m_SubtreeHeaders[i].m_quantizedAabbMax[1]);
targetBvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2] = (m_SubtreeHeaders[i].m_quantizedAabbMax[2]);
targetBvh->m_SubtreeHeaders[i].m_rootNodeIndex = (m_SubtreeHeaders[i].m_rootNodeIndex);
targetBvh->m_SubtreeHeaders[i].m_subtreeSize = (m_SubtreeHeaders[i].m_subtreeSize);
targetBvh->m_SubtreeHeaders[i] = m_SubtreeHeaders[i];
}
}
nodeData += sizeof(btBvhSubtreeInfo) * m_subtreeHeaderCount;
return true;
}
btQuantizedBvh *btQuantizedBvh::deSerializeInPlace(void *i_alignedDataBuffer, unsigned int i_dataBufferSize, bool i_swapEndian)
{
if (i_alignedDataBuffer == NULL)// || (((unsigned)i_alignedDataBuffer & BVH_ALIGNMENT_MASK) != 0))
{
return NULL;
}
btQuantizedBvh *bvh = (btQuantizedBvh *)i_alignedDataBuffer;
if (i_swapEndian)
{
bvh->m_curNodeIndex = static_cast<int>(btSwapEndian(bvh->m_curNodeIndex));
btUnSwapVector3Endian(bvh->m_bvhAabbMin);
btUnSwapVector3Endian(bvh->m_bvhAabbMax);
btUnSwapVector3Endian(bvh->m_bvhQuantization);
bvh->m_traversalMode = (btTraversalMode)btSwapEndian(bvh->m_traversalMode);
bvh->m_subtreeHeaderCount = static_cast<int>(btSwapEndian(bvh->m_subtreeHeaderCount));
}
unsigned int calculatedBufSize = bvh->calculateSerializeBufferSize();
btAssert(calculatedBufSize <= i_dataBufferSize);
if (calculatedBufSize > i_dataBufferSize)
{
return NULL;
}
unsigned char *nodeData = (unsigned char *)bvh;
nodeData += sizeof(btQuantizedBvh);
unsigned sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK;
nodeData += sizeToAdd;
int nodeCount = bvh->m_curNodeIndex;
// Must call placement new to fill in virtual function table, etc, but we don't want to overwrite most data, so call a special version of the constructor
// Also, m_leafNodes and m_quantizedLeafNodes will be initialized to default values by the constructor
new (bvh) btQuantizedBvh(*bvh, false);
if (bvh->m_useQuantization)
{
bvh->m_quantizedContiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount);
if (i_swapEndian)
{
for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0]);
bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[1]);
bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[2]);
bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0]);
bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[1]);
bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2] = btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[2]);
bvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = static_cast<int>(btSwapEndian(bvh->m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex));
}
}
nodeData += sizeof(btQuantizedBvhNode) * nodeCount;
}
else
{
bvh->m_contiguousNodes.initializeFromBuffer(nodeData, nodeCount, nodeCount);
if (i_swapEndian)
{
for (int nodeIndex = 0; nodeIndex < nodeCount; nodeIndex++)
{
btUnSwapVector3Endian(bvh->m_contiguousNodes[nodeIndex].m_aabbMinOrg);
btUnSwapVector3Endian(bvh->m_contiguousNodes[nodeIndex].m_aabbMaxOrg);
bvh->m_contiguousNodes[nodeIndex].m_escapeIndex = static_cast<int>(btSwapEndian(bvh->m_contiguousNodes[nodeIndex].m_escapeIndex));
bvh->m_contiguousNodes[nodeIndex].m_subPart = static_cast<int>(btSwapEndian(bvh->m_contiguousNodes[nodeIndex].m_subPart));
bvh->m_contiguousNodes[nodeIndex].m_triangleIndex = static_cast<int>(btSwapEndian(bvh->m_contiguousNodes[nodeIndex].m_triangleIndex));
}
}
nodeData += sizeof(btOptimizedBvhNode) * nodeCount;
}
sizeToAdd = 0;//(BVH_ALIGNMENT-((unsigned)nodeData & BVH_ALIGNMENT_MASK))&BVH_ALIGNMENT_MASK;
nodeData += sizeToAdd;
// Now serialize the subtree headers
bvh->m_SubtreeHeaders.initializeFromBuffer(nodeData, bvh->m_subtreeHeaderCount, bvh->m_subtreeHeaderCount);
if (i_swapEndian)
{
for (int i = 0; i < bvh->m_subtreeHeaderCount; i++)
{
bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[0]);
bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[1]);
bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMin[2]);
bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[0]);
bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[1]);
bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2] = btSwapEndian(bvh->m_SubtreeHeaders[i].m_quantizedAabbMax[2]);
bvh->m_SubtreeHeaders[i].m_rootNodeIndex = static_cast<int>(btSwapEndian(bvh->m_SubtreeHeaders[i].m_rootNodeIndex));
bvh->m_SubtreeHeaders[i].m_subtreeSize = static_cast<int>(btSwapEndian(bvh->m_SubtreeHeaders[i].m_subtreeSize));
}
}
return bvh;
}
// Constructor that prevents btVector3's default constructor from being called
btQuantizedBvh::btQuantizedBvh(btQuantizedBvh &self, bool /* ownsMemory */) :
m_bvhAabbMin(self.m_bvhAabbMin),
m_bvhAabbMax(self.m_bvhAabbMax),
m_bvhQuantization(self.m_bvhQuantization)
{
}

View File

@@ -0,0 +1,486 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 QUANTIZED_BVH_H
#define QUANTIZED_BVH_H
//#define DEBUG_CHECK_DEQUANTIZATION 1
#ifdef DEBUG_CHECK_DEQUANTIZATION
#ifdef __SPU__
#define printf spu_printf
#endif //__SPU__
#include <stdio.h>
#include <stdlib.h>
#endif //DEBUG_CHECK_DEQUANTIZATION
#include "LinearMath/btVector3.h"
#include "LinearMath/btAlignedAllocator.h"
//http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/vclrf__m128.asp
//Note: currently we have 16 bytes per quantized node
#define MAX_SUBTREE_SIZE_IN_BYTES 2048
// 10 gives the potential for 1024 parts, with at most 2^21 (2097152) (minus one
// actually) triangles each (since the sign bit is reserved
#define MAX_NUM_PARTS_IN_BITS 10
///btQuantizedBvhNode is a compressed aabb node, 16 bytes.
///Node can be used for leafnode or internal node. Leafnodes can point to 32-bit triangle index (non-negative range).
ATTRIBUTE_ALIGNED16 (struct) btQuantizedBvhNode
{
BT_DECLARE_ALIGNED_ALLOCATOR();
//12 bytes
unsigned short int m_quantizedAabbMin[3];
unsigned short int m_quantizedAabbMax[3];
//4 bytes
int m_escapeIndexOrTriangleIndex;
bool isLeafNode() const
{
//skipindex is negative (internal node), triangleindex >=0 (leafnode)
return (m_escapeIndexOrTriangleIndex >= 0);
}
int getEscapeIndex() const
{
btAssert(!isLeafNode());
return -m_escapeIndexOrTriangleIndex;
}
int getTriangleIndex() const
{
btAssert(isLeafNode());
// Get only the lower bits where the triangle index is stored
return (m_escapeIndexOrTriangleIndex&~((~0)<<(31-MAX_NUM_PARTS_IN_BITS)));
}
int getPartId() const
{
btAssert(isLeafNode());
// Get only the highest bits where the part index is stored
return (m_escapeIndexOrTriangleIndex>>(31-MAX_NUM_PARTS_IN_BITS));
}
}
;
/// btOptimizedBvhNode contains both internal and leaf node information.
/// Total node size is 44 bytes / node. You can use the compressed version of 16 bytes.
ATTRIBUTE_ALIGNED16 (struct) btOptimizedBvhNode
{
BT_DECLARE_ALIGNED_ALLOCATOR();
//32 bytes
btVector3 m_aabbMinOrg;
btVector3 m_aabbMaxOrg;
//4
int m_escapeIndex;
//8
//for child nodes
int m_subPart;
int m_triangleIndex;
int m_padding[5];//bad, due to alignment
};
///btBvhSubtreeInfo provides info to gather a subtree of limited size
ATTRIBUTE_ALIGNED16(class) btBvhSubtreeInfo
{
public:
BT_DECLARE_ALIGNED_ALLOCATOR();
//12 bytes
unsigned short int m_quantizedAabbMin[3];
unsigned short int m_quantizedAabbMax[3];
//4 bytes, points to the root of the subtree
int m_rootNodeIndex;
//4 bytes
int m_subtreeSize;
int m_padding[3];
btBvhSubtreeInfo()
{
//memset(&m_padding[0], 0, sizeof(m_padding));
}
void setAabbFromQuantizeNode(const btQuantizedBvhNode& quantizedNode)
{
m_quantizedAabbMin[0] = quantizedNode.m_quantizedAabbMin[0];
m_quantizedAabbMin[1] = quantizedNode.m_quantizedAabbMin[1];
m_quantizedAabbMin[2] = quantizedNode.m_quantizedAabbMin[2];
m_quantizedAabbMax[0] = quantizedNode.m_quantizedAabbMax[0];
m_quantizedAabbMax[1] = quantizedNode.m_quantizedAabbMax[1];
m_quantizedAabbMax[2] = quantizedNode.m_quantizedAabbMax[2];
}
}
;
class btNodeOverlapCallback
{
public:
virtual ~btNodeOverlapCallback() {};
virtual void processNode(int subPart, int triangleIndex) = 0;
};
#include "LinearMath/btAlignedAllocator.h"
#include "LinearMath/btAlignedObjectArray.h"
///for code readability:
typedef btAlignedObjectArray<btOptimizedBvhNode> NodeArray;
typedef btAlignedObjectArray<btQuantizedBvhNode> QuantizedNodeArray;
typedef btAlignedObjectArray<btBvhSubtreeInfo> BvhSubtreeInfoArray;
///The btQuantizedBvh class stores an AABB tree that can be quickly traversed on CPU and Cell SPU.
///It is used by the btBvhTriangleMeshShape as midphase, and by the btMultiSapBroadphase.
///It is recommended to use quantization for better performance and lower memory requirements.
ATTRIBUTE_ALIGNED16(class) btQuantizedBvh
{
protected:
NodeArray m_leafNodes;
NodeArray m_contiguousNodes;
QuantizedNodeArray m_quantizedLeafNodes;
QuantizedNodeArray m_quantizedContiguousNodes;
int m_curNodeIndex;
//quantization data
bool m_useQuantization;
btVector3 m_bvhAabbMin;
btVector3 m_bvhAabbMax;
btVector3 m_bvhQuantization;
public:
BT_DECLARE_ALIGNED_ALLOCATOR();
enum btTraversalMode
{
TRAVERSAL_STACKLESS = 0,
TRAVERSAL_STACKLESS_CACHE_FRIENDLY,
TRAVERSAL_RECURSIVE
};
protected:
btTraversalMode m_traversalMode;
BvhSubtreeInfoArray m_SubtreeHeaders;
//This is only used for serialization so we don't have to add serialization directly to btAlignedObjectArray
int m_subtreeHeaderCount;
///two versions, one for quantized and normal nodes. This allows code-reuse while maintaining readability (no template/macro!)
///this might be refactored into a virtual, it is usually not calculated at run-time
void setInternalNodeAabbMin(int nodeIndex, const btVector3& aabbMin)
{
if (m_useQuantization)
{
quantize(&m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[0] ,aabbMin,0);
} else
{
m_contiguousNodes[nodeIndex].m_aabbMinOrg = aabbMin;
}
}
void setInternalNodeAabbMax(int nodeIndex,const btVector3& aabbMax)
{
if (m_useQuantization)
{
quantize(&m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[0],aabbMax,1);
} else
{
m_contiguousNodes[nodeIndex].m_aabbMaxOrg = aabbMax;
}
}
btVector3 getAabbMin(int nodeIndex) const
{
if (m_useQuantization)
{
return unQuantize(&m_quantizedLeafNodes[nodeIndex].m_quantizedAabbMin[0]);
}
//non-quantized
return m_leafNodes[nodeIndex].m_aabbMinOrg;
}
btVector3 getAabbMax(int nodeIndex) const
{
if (m_useQuantization)
{
return unQuantize(&m_quantizedLeafNodes[nodeIndex].m_quantizedAabbMax[0]);
}
//non-quantized
return m_leafNodes[nodeIndex].m_aabbMaxOrg;
}
void setInternalNodeEscapeIndex(int nodeIndex, int escapeIndex)
{
if (m_useQuantization)
{
m_quantizedContiguousNodes[nodeIndex].m_escapeIndexOrTriangleIndex = -escapeIndex;
}
else
{
m_contiguousNodes[nodeIndex].m_escapeIndex = escapeIndex;
}
}
void mergeInternalNodeAabb(int nodeIndex,const btVector3& newAabbMin,const btVector3& newAabbMax)
{
if (m_useQuantization)
{
unsigned short int quantizedAabbMin[3];
unsigned short int quantizedAabbMax[3];
quantize(quantizedAabbMin,newAabbMin,0);
quantize(quantizedAabbMax,newAabbMax,1);
for (int i=0;i<3;i++)
{
if (m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[i] > quantizedAabbMin[i])
m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMin[i] = quantizedAabbMin[i];
if (m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[i] < quantizedAabbMax[i])
m_quantizedContiguousNodes[nodeIndex].m_quantizedAabbMax[i] = quantizedAabbMax[i];
}
} else
{
//non-quantized
m_contiguousNodes[nodeIndex].m_aabbMinOrg.setMin(newAabbMin);
m_contiguousNodes[nodeIndex].m_aabbMaxOrg.setMax(newAabbMax);
}
}
void swapLeafNodes(int firstIndex,int secondIndex);
void assignInternalNodeFromLeafNode(int internalNode,int leafNodeIndex);
protected:
void buildTree (int startIndex,int endIndex);
int calcSplittingAxis(int startIndex,int endIndex);
int sortAndCalcSplittingIndex(int startIndex,int endIndex,int splitAxis);
void walkStacklessTree(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const;
void walkStacklessQuantizedTreeAgainstRay(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin, const btVector3& aabbMax, int startNodeIndex,int endNodeIndex) const;
void walkStacklessQuantizedTree(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax,int startNodeIndex,int endNodeIndex) const;
///tree traversal designed for small-memory processors like PS3 SPU
void walkStacklessQuantizedTreeCacheFriendly(btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const;
///use the 16-byte stackless 'skipindex' node tree to do a recursive traversal
void walkRecursiveQuantizedTreeAgainstQueryAabb(const btQuantizedBvhNode* currentNode,btNodeOverlapCallback* nodeCallback,unsigned short int* quantizedQueryAabbMin,unsigned short int* quantizedQueryAabbMax) const;
///use the 16-byte stackless 'skipindex' node tree to do a recursive traversal
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);
public:
btQuantizedBvh();
virtual ~btQuantizedBvh();
///***************************************** expert/internal use only *************************
void setQuantizationValues(const btVector3& bvhAabbMin,const btVector3& bvhAabbMax,btScalar quantizationMargin=btScalar(1.0));
QuantizedNodeArray& getLeafNodeArray() { return m_quantizedLeafNodes; }
///buildInternal is expert use only: assumes that setQuantizationValues and LeafNodeArray are initialized
void buildInternal();
///***************************************** expert/internal use only *************************
void reportAabbOverlappingNodex(btNodeOverlapCallback* nodeCallback,const btVector3& aabbMin,const btVector3& aabbMax) const;
void reportRayOverlappingNodex (btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget) const;
void reportBoxCastOverlappingNodex(btNodeOverlapCallback* nodeCallback, const btVector3& raySource, const btVector3& rayTarget, const btVector3& aabbMin,const btVector3& aabbMax) const;
SIMD_FORCE_INLINE void quantize(unsigned short* out, const btVector3& point,int isMax) const
{
btAssert(m_useQuantization);
btAssert(point.getX() <= m_bvhAabbMax.getX());
btAssert(point.getY() <= m_bvhAabbMax.getY());
btAssert(point.getZ() <= m_bvhAabbMax.getZ());
btAssert(point.getX() >= m_bvhAabbMin.getX());
btAssert(point.getY() >= m_bvhAabbMin.getY());
btAssert(point.getZ() >= m_bvhAabbMin.getZ());
btVector3 v = (point - m_bvhAabbMin) * m_bvhQuantization;
///Make sure rounding is done in a way that unQuantize(quantizeWithClamp(...)) is conservative
///end-points always set the first bit, so that they are sorted properly (so that neighbouring AABBs overlap properly)
///todo: double-check this
if (isMax)
{
out[0] = (unsigned short) (((unsigned short)(v.getX()+btScalar(1.)) | 1));
out[1] = (unsigned short) (((unsigned short)(v.getY()+btScalar(1.)) | 1));
out[2] = (unsigned short) (((unsigned short)(v.getZ()+btScalar(1.)) | 1));
} else
{
out[0] = (unsigned short) (((unsigned short)(v.getX()) & 0xfffe));
out[1] = (unsigned short) (((unsigned short)(v.getY()) & 0xfffe));
out[2] = (unsigned short) (((unsigned short)(v.getZ()) & 0xfffe));
}
#ifdef DEBUG_CHECK_DEQUANTIZATION
btVector3 newPoint = unQuantize(out);
if (isMax)
{
if (newPoint.getX() < point.getX())
{
printf("unconservative X, diffX = %f, oldX=%f,newX=%f\n",newPoint.getX()-point.getX(), newPoint.getX(),point.getX());
}
if (newPoint.getY() < point.getY())
{
printf("unconservative Y, diffY = %f, oldY=%f,newY=%f\n",newPoint.getY()-point.getY(), newPoint.getY(),point.getY());
}
if (newPoint.getZ() < point.getZ())
{
printf("unconservative Z, diffZ = %f, oldZ=%f,newZ=%f\n",newPoint.getZ()-point.getZ(), newPoint.getZ(),point.getZ());
}
} else
{
if (newPoint.getX() > point.getX())
{
printf("unconservative X, diffX = %f, oldX=%f,newX=%f\n",newPoint.getX()-point.getX(), newPoint.getX(),point.getX());
}
if (newPoint.getY() > point.getY())
{
printf("unconservative Y, diffY = %f, oldY=%f,newY=%f\n",newPoint.getY()-point.getY(), newPoint.getY(),point.getY());
}
if (newPoint.getZ() > point.getZ())
{
printf("unconservative Z, diffZ = %f, oldZ=%f,newZ=%f\n",newPoint.getZ()-point.getZ(), newPoint.getZ(),point.getZ());
}
}
#endif //DEBUG_CHECK_DEQUANTIZATION
}
SIMD_FORCE_INLINE void quantizeWithClamp(unsigned short* out, const btVector3& point2,int isMax) const
{
btAssert(m_useQuantization);
btVector3 clampedPoint(point2);
clampedPoint.setMax(m_bvhAabbMin);
clampedPoint.setMin(m_bvhAabbMax);
quantize(out,clampedPoint,isMax);
}
SIMD_FORCE_INLINE btVector3 unQuantize(const unsigned short* vecIn) const
{
btVector3 vecOut;
vecOut.setValue(
(btScalar)(vecIn[0]) / (m_bvhQuantization.getX()),
(btScalar)(vecIn[1]) / (m_bvhQuantization.getY()),
(btScalar)(vecIn[2]) / (m_bvhQuantization.getZ()));
vecOut += m_bvhAabbMin;
return vecOut;
}
///setTraversalMode let's you choose between stackless, recursive or stackless cache friendly tree traversal. Note this is only implemented for quantized trees.
void setTraversalMode(btTraversalMode traversalMode)
{
m_traversalMode = traversalMode;
}
SIMD_FORCE_INLINE QuantizedNodeArray& getQuantizedNodeArray()
{
return m_quantizedContiguousNodes;
}
SIMD_FORCE_INLINE BvhSubtreeInfoArray& getSubtreeInfoArray()
{
return m_SubtreeHeaders;
}
/////Calculate space needed to store BVH for serialization
unsigned calculateSerializeBufferSize();
/// Data buffer MUST be 16 byte aligned
virtual bool serialize(void *o_alignedDataBuffer, unsigned i_dataBufferSize, bool i_swapEndian);
///deSerializeInPlace loads and initializes a BVH from a buffer in memory 'in place'
static btQuantizedBvh *deSerializeInPlace(void *i_alignedDataBuffer, unsigned int i_dataBufferSize, bool i_swapEndian);
static unsigned int getAlignmentSerializationPadding();
SIMD_FORCE_INLINE bool isQuantized()
{
return m_useQuantization;
}
private:
// Special "copy" constructor that allows for in-place deserialization
// Prevents btVector3's default constructor from being called, but doesn't inialize much else
// ownsMemory should most likely be false if deserializing, and if you are not, don't call this (it also changes the function signature, which we need)
btQuantizedBvh(btQuantizedBvh &other, bool ownsMemory);
}
;
#endif //QUANTIZED_BVH_H

View File

@@ -0,0 +1,85 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 "btBoxBoxCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
#include "BulletCollision/CollisionShapes/btBoxShape.h"
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
#include "btBoxBoxDetector.h"
#define USE_PERSISTENT_CONTACTS 1
btBoxBoxCollisionAlgorithm::btBoxBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* obj0,btCollisionObject* obj1)
: btCollisionAlgorithm(ci),
m_ownManifold(false),
m_manifoldPtr(mf)
{
if (!m_manifoldPtr && m_dispatcher->needsCollision(obj0,obj1))
{
m_manifoldPtr = m_dispatcher->getNewManifold(obj0,obj1);
m_ownManifold = true;
}
}
btBoxBoxCollisionAlgorithm::~btBoxBoxCollisionAlgorithm()
{
if (m_ownManifold)
{
if (m_manifoldPtr)
m_dispatcher->releaseManifold(m_manifoldPtr);
}
}
void btBoxBoxCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut)
{
if (!m_manifoldPtr)
return;
btCollisionObject* col0 = body0;
btCollisionObject* col1 = body1;
btBoxShape* box0 = (btBoxShape*)col0->getCollisionShape();
btBoxShape* box1 = (btBoxShape*)col1->getCollisionShape();
/// report a contact. internally this will be kept persistent, and contact reduction is done
resultOut->setPersistentManifold(m_manifoldPtr);
#ifndef USE_PERSISTENT_CONTACTS
m_manifoldPtr->clearManifold();
#endif //USE_PERSISTENT_CONTACTS
btDiscreteCollisionDetectorInterface::ClosestPointInput input;
input.m_maximumDistanceSquared = 1e30f;
input.m_transformA = body0->getWorldTransform();
input.m_transformB = body1->getWorldTransform();
btBoxBoxDetector detector(box0,box1);
detector.getClosestPoints(input,*resultOut,dispatchInfo.m_debugDraw);
#ifdef USE_PERSISTENT_CONTACTS
// refreshContactPoints is only necessary when using persistent contact points. otherwise all points are newly added
if (m_ownManifold)
{
resultOut->refreshContactPoints();
}
#endif //USE_PERSISTENT_CONTACTS
}
btScalar btBoxBoxCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* /*body0*/,btCollisionObject* /*body1*/,const btDispatcherInfo& /*dispatchInfo*/,btManifoldResult* /*resultOut*/)
{
//not yet
return 1.f;
}

View File

@@ -0,0 +1,66 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 BOX_BOX__COLLISION_ALGORITHM_H
#define BOX_BOX__COLLISION_ALGORITHM_H
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
#include "BulletCollision/BroadphaseCollision/btDispatcher.h"
#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h"
class btPersistentManifold;
///box-box collision detection
class btBoxBoxCollisionAlgorithm : public btCollisionAlgorithm
{
bool m_ownManifold;
btPersistentManifold* m_manifoldPtr;
public:
btBoxBoxCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci)
: btCollisionAlgorithm(ci) {}
virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut);
virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut);
btBoxBoxCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* body0,btCollisionObject* body1);
virtual ~btBoxBoxCollisionAlgorithm();
virtual void getAllContactManifolds(btManifoldArray& manifoldArray)
{
if (m_manifoldPtr && m_ownManifold)
{
manifoldArray.push_back(m_manifoldPtr);
}
}
struct CreateFunc :public btCollisionAlgorithmCreateFunc
{
virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1)
{
int bbsize = sizeof(btBoxBoxCollisionAlgorithm);
void* ptr = ci.m_dispatcher1->allocateCollisionAlgorithm(bbsize);
return new(ptr) btBoxBoxCollisionAlgorithm(0,ci,body0,body1);
}
};
};
#endif //BOX_BOX__COLLISION_ALGORITHM_H

View File

@@ -0,0 +1,683 @@
/*
* Box-Box collision detection re-distributed under the ZLib license with permission from Russell L. Smith
* Original version is from Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.
* All rights reserved. Email: russ@q12.org Web: www.q12.org
Bullet Continuous Collision Detection and Physics Library
Bullet is Copyright (c) 2003-2006 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.
*/
///ODE box-box collision detection is adapted to work with Bullet
#include "btBoxBoxDetector.h"
#include "BulletCollision/CollisionShapes/btBoxShape.h"
#include <float.h>
#include <string.h>
btBoxBoxDetector::btBoxBoxDetector(btBoxShape* box1,btBoxShape* box2)
: m_box1(box1),
m_box2(box2)
{
}
// given two boxes (p1,R1,side1) and (p2,R2,side2), collide them together and
// generate contact points. this returns 0 if there is no contact otherwise
// it returns the number of contacts generated.
// `normal' returns the contact normal.
// `depth' returns the maximum penetration depth along that normal.
// `return_code' returns a number indicating the type of contact that was
// detected:
// 1,2,3 = box 2 intersects with a face of box 1
// 4,5,6 = box 1 intersects with a face of box 2
// 7..15 = edge-edge contact
// `maxc' is the maximum number of contacts allowed to be generated, i.e.
// the size of the `contact' array.
// `contact' and `skip' are the contact array information provided to the
// collision functions. this function only fills in the position and depth
// fields.
struct dContactGeom;
#define dDOTpq(a,b,p,q) ((a)[0]*(b)[0] + (a)[p]*(b)[q] + (a)[2*(p)]*(b)[2*(q)])
#define dInfinity FLT_MAX
/*PURE_INLINE btScalar dDOT (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,1); }
PURE_INLINE btScalar dDOT13 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,3); }
PURE_INLINE btScalar dDOT31 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,3,1); }
PURE_INLINE btScalar dDOT33 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,3,3); }
*/
static btScalar dDOT (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,1); }
static btScalar dDOT44 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,4,4); }
static btScalar dDOT41 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,4,1); }
static btScalar dDOT14 (const btScalar *a, const btScalar *b) { return dDOTpq(a,b,1,4); }
#define dMULTIPLYOP1_331(A,op,B,C) \
{\
(A)[0] op dDOT41((B),(C)); \
(A)[1] op dDOT41((B+1),(C)); \
(A)[2] op dDOT41((B+2),(C)); \
}
#define dMULTIPLYOP0_331(A,op,B,C) \
{ \
(A)[0] op dDOT((B),(C)); \
(A)[1] op dDOT((B+4),(C)); \
(A)[2] op dDOT((B+8),(C)); \
}
#define dMULTIPLY1_331(A,B,C) dMULTIPLYOP1_331(A,=,B,C)
#define dMULTIPLY0_331(A,B,C) dMULTIPLYOP0_331(A,=,B,C)
typedef btScalar dMatrix3[4*3];
void dLineClosestApproach (const btVector3& pa, const btVector3& ua,
const btVector3& pb, const btVector3& ub,
btScalar *alpha, btScalar *beta);
void dLineClosestApproach (const btVector3& pa, const btVector3& ua,
const btVector3& pb, const btVector3& ub,
btScalar *alpha, btScalar *beta)
{
btVector3 p;
p[0] = pb[0] - pa[0];
p[1] = pb[1] - pa[1];
p[2] = pb[2] - pa[2];
btScalar uaub = dDOT(ua,ub);
btScalar q1 = dDOT(ua,p);
btScalar q2 = -dDOT(ub,p);
btScalar d = 1-uaub*uaub;
if (d <= btScalar(0.0001f)) {
// @@@ this needs to be made more robust
*alpha = 0;
*beta = 0;
}
else {
d = 1.f/d;
*alpha = (q1 + uaub*q2)*d;
*beta = (uaub*q1 + q2)*d;
}
}
// find all the intersection points between the 2D rectangle with vertices
// at (+/-h[0],+/-h[1]) and the 2D quadrilateral with vertices (p[0],p[1]),
// (p[2],p[3]),(p[4],p[5]),(p[6],p[7]).
//
// the intersection points are returned as x,y pairs in the 'ret' array.
// the number of intersection points is returned by the function (this will
// be in the range 0 to 8).
static int intersectRectQuad2 (btScalar h[2], btScalar p[8], btScalar ret[16])
{
// q (and r) contain nq (and nr) coordinate points for the current (and
// chopped) polygons
int nq=4,nr=0;
btScalar buffer[16];
btScalar *q = p;
btScalar *r = ret;
for (int dir=0; dir <= 1; dir++) {
// direction notation: xy[0] = x axis, xy[1] = y axis
for (int sign=-1; sign <= 1; sign += 2) {
// chop q along the line xy[dir] = sign*h[dir]
btScalar *pq = q;
btScalar *pr = r;
nr = 0;
for (int i=nq; i > 0; i--) {
// go through all points in q and all lines between adjacent points
if (sign*pq[dir] < h[dir]) {
// this point is inside the chopping line
pr[0] = pq[0];
pr[1] = pq[1];
pr += 2;
nr++;
if (nr & 8) {
q = r;
goto done;
}
}
btScalar *nextq = (i > 1) ? pq+2 : q;
if ((sign*pq[dir] < h[dir]) ^ (sign*nextq[dir] < h[dir])) {
// this line crosses the chopping line
pr[1-dir] = pq[1-dir] + (nextq[1-dir]-pq[1-dir]) /
(nextq[dir]-pq[dir]) * (sign*h[dir]-pq[dir]);
pr[dir] = sign*h[dir];
pr += 2;
nr++;
if (nr & 8) {
q = r;
goto done;
}
}
pq += 2;
}
q = r;
r = (q==ret) ? buffer : ret;
nq = nr;
}
}
done:
if (q != ret) memcpy (ret,q,nr*2*sizeof(btScalar));
return nr;
}
#define M__PI 3.14159265f
// given n points in the plane (array p, of size 2*n), generate m points that
// best represent the whole set. the definition of 'best' here is not
// predetermined - the idea is to select points that give good box-box
// collision detection behavior. the chosen point indexes are returned in the
// array iret (of size m). 'i0' is always the first entry in the array.
// n must be in the range [1..8]. m must be in the range [1..n]. i0 must be
// in the range [0..n-1].
void cullPoints2 (int n, btScalar p[], int m, int i0, int iret[]);
void cullPoints2 (int n, btScalar p[], int m, int i0, int iret[])
{
// compute the centroid of the polygon in cx,cy
int i,j;
btScalar a,cx,cy,q;
if (n==1) {
cx = p[0];
cy = p[1];
}
else if (n==2) {
cx = btScalar(0.5)*(p[0] + p[2]);
cy = btScalar(0.5)*(p[1] + p[3]);
}
else {
a = 0;
cx = 0;
cy = 0;
for (i=0; i<(n-1); i++) {
q = p[i*2]*p[i*2+3] - p[i*2+2]*p[i*2+1];
a += q;
cx += q*(p[i*2]+p[i*2+2]);
cy += q*(p[i*2+1]+p[i*2+3]);
}
q = p[n*2-2]*p[1] - p[0]*p[n*2-1];
a = 1.f/(btScalar(3.0)*(a+q));
cx = a*(cx + q*(p[n*2-2]+p[0]));
cy = a*(cy + q*(p[n*2-1]+p[1]));
}
// compute the angle of each point w.r.t. the centroid
btScalar A[8];
for (i=0; i<n; i++) A[i] = btAtan2(p[i*2+1]-cy,p[i*2]-cx);
// search for points that have angles closest to A[i0] + i*(2*pi/m).
int avail[8];
for (i=0; i<n; i++) avail[i] = 1;
avail[i0] = 0;
iret[0] = i0;
iret++;
for (j=1; j<m; j++) {
a = btScalar(j)*(2*M__PI/m) + A[i0];
if (a > M__PI) a -= 2*M__PI;
btScalar maxdiff=1e9,diff;
#if defined(DEBUG) || defined (_DEBUG)
*iret = i0; // iret is not allowed to keep this value
#endif
for (i=0; i<n; i++) {
if (avail[i]) {
diff = btFabs (A[i]-a);
if (diff > M__PI) diff = 2*M__PI - diff;
if (diff < maxdiff) {
maxdiff = diff;
*iret = i;
}
}
}
#if defined(DEBUG) || defined (_DEBUG)
btAssert (*iret != i0); // ensure iret got set
#endif
avail[*iret] = 0;
iret++;
}
}
int dBoxBox2 (const btVector3& p1, const dMatrix3 R1,
const btVector3& side1, const btVector3& p2,
const dMatrix3 R2, const btVector3& side2,
btVector3& normal, btScalar *depth, int *return_code,
int maxc, dContactGeom * /*contact*/, int /*skip*/,btDiscreteCollisionDetectorInterface::Result& output);
int dBoxBox2 (const btVector3& p1, const dMatrix3 R1,
const btVector3& side1, const btVector3& p2,
const dMatrix3 R2, const btVector3& side2,
btVector3& normal, btScalar *depth, int *return_code,
int maxc, dContactGeom * /*contact*/, int /*skip*/,btDiscreteCollisionDetectorInterface::Result& output)
{
const btScalar fudge_factor = btScalar(1.05);
btVector3 p,pp,normalC;
const btScalar *normalR = 0;
btScalar A[3],B[3],R11,R12,R13,R21,R22,R23,R31,R32,R33,
Q11,Q12,Q13,Q21,Q22,Q23,Q31,Q32,Q33,s,s2,l;
int i,j,invert_normal,code;
// get vector from centers of box 1 to box 2, relative to box 1
p = p2 - p1;
dMULTIPLY1_331 (pp,R1,p); // get pp = p relative to body 1
// get side lengths / 2
A[0] = side1[0]*btScalar(0.5);
A[1] = side1[1]*btScalar(0.5);
A[2] = side1[2]*btScalar(0.5);
B[0] = side2[0]*btScalar(0.5);
B[1] = side2[1]*btScalar(0.5);
B[2] = side2[2]*btScalar(0.5);
// Rij is R1'*R2, i.e. the relative rotation between R1 and R2
R11 = dDOT44(R1+0,R2+0); R12 = dDOT44(R1+0,R2+1); R13 = dDOT44(R1+0,R2+2);
R21 = dDOT44(R1+1,R2+0); R22 = dDOT44(R1+1,R2+1); R23 = dDOT44(R1+1,R2+2);
R31 = dDOT44(R1+2,R2+0); R32 = dDOT44(R1+2,R2+1); R33 = dDOT44(R1+2,R2+2);
Q11 = btFabs(R11); Q12 = btFabs(R12); Q13 = btFabs(R13);
Q21 = btFabs(R21); Q22 = btFabs(R22); Q23 = btFabs(R23);
Q31 = btFabs(R31); Q32 = btFabs(R32); Q33 = btFabs(R33);
// for all 15 possible separating axes:
// * see if the axis separates the boxes. if so, return 0.
// * find the depth of the penetration along the separating axis (s2)
// * if this is the largest depth so far, record it.
// the normal vector will be set to the separating axis with the smallest
// depth. note: normalR is set to point to a column of R1 or R2 if that is
// the smallest depth normal so far. otherwise normalR is 0 and normalC is
// set to a vector relative to body 1. invert_normal is 1 if the sign of
// the normal should be flipped.
#define TST(expr1,expr2,norm,cc) \
s2 = btFabs(expr1) - (expr2); \
if (s2 > 0) return 0; \
if (s2 > s) { \
s = s2; \
normalR = norm; \
invert_normal = ((expr1) < 0); \
code = (cc); \
}
s = -dInfinity;
invert_normal = 0;
code = 0;
// separating axis = u1,u2,u3
TST (pp[0],(A[0] + B[0]*Q11 + B[1]*Q12 + B[2]*Q13),R1+0,1);
TST (pp[1],(A[1] + B[0]*Q21 + B[1]*Q22 + B[2]*Q23),R1+1,2);
TST (pp[2],(A[2] + B[0]*Q31 + B[1]*Q32 + B[2]*Q33),R1+2,3);
// separating axis = v1,v2,v3
TST (dDOT41(R2+0,p),(A[0]*Q11 + A[1]*Q21 + A[2]*Q31 + B[0]),R2+0,4);
TST (dDOT41(R2+1,p),(A[0]*Q12 + A[1]*Q22 + A[2]*Q32 + B[1]),R2+1,5);
TST (dDOT41(R2+2,p),(A[0]*Q13 + A[1]*Q23 + A[2]*Q33 + B[2]),R2+2,6);
// note: cross product axes need to be scaled when s is computed.
// normal (n1,n2,n3) is relative to box 1.
#undef TST
#define TST(expr1,expr2,n1,n2,n3,cc) \
s2 = btFabs(expr1) - (expr2); \
if (s2 > 0) return 0; \
l = btSqrt((n1)*(n1) + (n2)*(n2) + (n3)*(n3)); \
if (l > 0) { \
s2 /= l; \
if (s2*fudge_factor > s) { \
s = s2; \
normalR = 0; \
normalC[0] = (n1)/l; normalC[1] = (n2)/l; normalC[2] = (n3)/l; \
invert_normal = ((expr1) < 0); \
code = (cc); \
} \
}
// separating axis = u1 x (v1,v2,v3)
TST(pp[2]*R21-pp[1]*R31,(A[1]*Q31+A[2]*Q21+B[1]*Q13+B[2]*Q12),0,-R31,R21,7);
TST(pp[2]*R22-pp[1]*R32,(A[1]*Q32+A[2]*Q22+B[0]*Q13+B[2]*Q11),0,-R32,R22,8);
TST(pp[2]*R23-pp[1]*R33,(A[1]*Q33+A[2]*Q23+B[0]*Q12+B[1]*Q11),0,-R33,R23,9);
// separating axis = u2 x (v1,v2,v3)
TST(pp[0]*R31-pp[2]*R11,(A[0]*Q31+A[2]*Q11+B[1]*Q23+B[2]*Q22),R31,0,-R11,10);
TST(pp[0]*R32-pp[2]*R12,(A[0]*Q32+A[2]*Q12+B[0]*Q23+B[2]*Q21),R32,0,-R12,11);
TST(pp[0]*R33-pp[2]*R13,(A[0]*Q33+A[2]*Q13+B[0]*Q22+B[1]*Q21),R33,0,-R13,12);
// separating axis = u3 x (v1,v2,v3)
TST(pp[1]*R11-pp[0]*R21,(A[0]*Q21+A[1]*Q11+B[1]*Q33+B[2]*Q32),-R21,R11,0,13);
TST(pp[1]*R12-pp[0]*R22,(A[0]*Q22+A[1]*Q12+B[0]*Q33+B[2]*Q31),-R22,R12,0,14);
TST(pp[1]*R13-pp[0]*R23,(A[0]*Q23+A[1]*Q13+B[0]*Q32+B[1]*Q31),-R23,R13,0,15);
#undef TST
if (!code) return 0;
// if we get to this point, the boxes interpenetrate. compute the normal
// in global coordinates.
if (normalR) {
normal[0] = normalR[0];
normal[1] = normalR[4];
normal[2] = normalR[8];
}
else {
dMULTIPLY0_331 (normal,R1,normalC);
}
if (invert_normal) {
normal[0] = -normal[0];
normal[1] = -normal[1];
normal[2] = -normal[2];
}
*depth = -s;
// compute contact point(s)
if (code > 6) {
// an edge from box 1 touches an edge from box 2.
// find a point pa on the intersecting edge of box 1
btVector3 pa;
btScalar sign;
for (i=0; i<3; i++) pa[i] = p1[i];
for (j=0; j<3; j++) {
sign = (dDOT14(normal,R1+j) > 0) ? btScalar(1.0) : btScalar(-1.0);
for (i=0; i<3; i++) pa[i] += sign * A[j] * R1[i*4+j];
}
// find a point pb on the intersecting edge of box 2
btVector3 pb;
for (i=0; i<3; i++) pb[i] = p2[i];
for (j=0; j<3; j++) {
sign = (dDOT14(normal,R2+j) > 0) ? btScalar(-1.0) : btScalar(1.0);
for (i=0; i<3; i++) pb[i] += sign * B[j] * R2[i*4+j];
}
btScalar alpha,beta;
btVector3 ua,ub;
for (i=0; i<3; i++) ua[i] = R1[((code)-7)/3 + i*4];
for (i=0; i<3; i++) ub[i] = R2[((code)-7)%3 + i*4];
dLineClosestApproach (pa,ua,pb,ub,&alpha,&beta);
for (i=0; i<3; i++) pa[i] += ua[i]*alpha;
for (i=0; i<3; i++) pb[i] += ub[i]*beta;
{
//contact[0].pos[i] = btScalar(0.5)*(pa[i]+pb[i]);
//contact[0].depth = *depth;
btVector3 pointInWorld;
#ifdef USE_CENTER_POINT
for (i=0; i<3; i++)
pointInWorld[i] = (pa[i]+pb[i])*btScalar(0.5);
output.addContactPoint(-normal,pointInWorld,-*depth);
#else
output.addContactPoint(-normal,pb,-*depth);
#endif //
*return_code = code;
}
return 1;
}
// okay, we have a face-something intersection (because the separating
// axis is perpendicular to a face). define face 'a' to be the reference
// face (i.e. the normal vector is perpendicular to this) and face 'b' to be
// the incident face (the closest face of the other box).
const btScalar *Ra,*Rb,*pa,*pb,*Sa,*Sb;
if (code <= 3) {
Ra = R1;
Rb = R2;
pa = p1;
pb = p2;
Sa = A;
Sb = B;
}
else {
Ra = R2;
Rb = R1;
pa = p2;
pb = p1;
Sa = B;
Sb = A;
}
// nr = normal vector of reference face dotted with axes of incident box.
// anr = absolute values of nr.
btVector3 normal2,nr,anr;
if (code <= 3) {
normal2[0] = normal[0];
normal2[1] = normal[1];
normal2[2] = normal[2];
}
else {
normal2[0] = -normal[0];
normal2[1] = -normal[1];
normal2[2] = -normal[2];
}
dMULTIPLY1_331 (nr,Rb,normal2);
anr[0] = btFabs (nr[0]);
anr[1] = btFabs (nr[1]);
anr[2] = btFabs (nr[2]);
// find the largest compontent of anr: this corresponds to the normal
// for the indident face. the other axis numbers of the indicent face
// are stored in a1,a2.
int lanr,a1,a2;
if (anr[1] > anr[0]) {
if (anr[1] > anr[2]) {
a1 = 0;
lanr = 1;
a2 = 2;
}
else {
a1 = 0;
a2 = 1;
lanr = 2;
}
}
else {
if (anr[0] > anr[2]) {
lanr = 0;
a1 = 1;
a2 = 2;
}
else {
a1 = 0;
a2 = 1;
lanr = 2;
}
}
// compute center point of incident face, in reference-face coordinates
btVector3 center;
if (nr[lanr] < 0) {
for (i=0; i<3; i++) center[i] = pb[i] - pa[i] + Sb[lanr] * Rb[i*4+lanr];
}
else {
for (i=0; i<3; i++) center[i] = pb[i] - pa[i] - Sb[lanr] * Rb[i*4+lanr];
}
// find the normal and non-normal axis numbers of the reference box
int codeN,code1,code2;
if (code <= 3) codeN = code-1; else codeN = code-4;
if (codeN==0) {
code1 = 1;
code2 = 2;
}
else if (codeN==1) {
code1 = 0;
code2 = 2;
}
else {
code1 = 0;
code2 = 1;
}
// find the four corners of the incident face, in reference-face coordinates
btScalar quad[8]; // 2D coordinate of incident face (x,y pairs)
btScalar c1,c2,m11,m12,m21,m22;
c1 = dDOT14 (center,Ra+code1);
c2 = dDOT14 (center,Ra+code2);
// optimize this? - we have already computed this data above, but it is not
// stored in an easy-to-index format. for now it's quicker just to recompute
// the four dot products.
m11 = dDOT44 (Ra+code1,Rb+a1);
m12 = dDOT44 (Ra+code1,Rb+a2);
m21 = dDOT44 (Ra+code2,Rb+a1);
m22 = dDOT44 (Ra+code2,Rb+a2);
{
btScalar k1 = m11*Sb[a1];
btScalar k2 = m21*Sb[a1];
btScalar k3 = m12*Sb[a2];
btScalar k4 = m22*Sb[a2];
quad[0] = c1 - k1 - k3;
quad[1] = c2 - k2 - k4;
quad[2] = c1 - k1 + k3;
quad[3] = c2 - k2 + k4;
quad[4] = c1 + k1 + k3;
quad[5] = c2 + k2 + k4;
quad[6] = c1 + k1 - k3;
quad[7] = c2 + k2 - k4;
}
// find the size of the reference face
btScalar rect[2];
rect[0] = Sa[code1];
rect[1] = Sa[code2];
// intersect the incident and reference faces
btScalar ret[16];
int n = intersectRectQuad2 (rect,quad,ret);
if (n < 1) return 0; // this should never happen
// convert the intersection points into reference-face coordinates,
// and compute the contact position and depth for each point. only keep
// those points that have a positive (penetrating) depth. delete points in
// the 'ret' array as necessary so that 'point' and 'ret' correspond.
btScalar point[3*8]; // penetrating contact points
btScalar dep[8]; // depths for those points
btScalar det1 = 1.f/(m11*m22 - m12*m21);
m11 *= det1;
m12 *= det1;
m21 *= det1;
m22 *= det1;
int cnum = 0; // number of penetrating contact points found
for (j=0; j < n; j++) {
btScalar k1 = m22*(ret[j*2]-c1) - m12*(ret[j*2+1]-c2);
btScalar k2 = -m21*(ret[j*2]-c1) + m11*(ret[j*2+1]-c2);
for (i=0; i<3; i++) point[cnum*3+i] =
center[i] + k1*Rb[i*4+a1] + k2*Rb[i*4+a2];
dep[cnum] = Sa[codeN] - dDOT(normal2,point+cnum*3);
if (dep[cnum] >= 0) {
ret[cnum*2] = ret[j*2];
ret[cnum*2+1] = ret[j*2+1];
cnum++;
}
}
if (cnum < 1) return 0; // this should never happen
// we can't generate more contacts than we actually have
if (maxc > cnum) maxc = cnum;
if (maxc < 1) maxc = 1;
if (cnum <= maxc) {
// we have less contacts than we need, so we use them all
for (j=0; j < cnum; j++) {
//AddContactPoint...
//dContactGeom *con = CONTACT(contact,skip*j);
//for (i=0; i<3; i++) con->pos[i] = point[j*3+i] + pa[i];
//con->depth = dep[j];
btVector3 pointInWorld;
for (i=0; i<3; i++)
pointInWorld[i] = point[j*3+i] + pa[i];
output.addContactPoint(-normal,pointInWorld,-dep[j]);
}
}
else {
// we have more contacts than are wanted, some of them must be culled.
// find the deepest point, it is always the first contact.
int i1 = 0;
btScalar maxdepth = dep[0];
for (i=1; i<cnum; i++) {
if (dep[i] > maxdepth) {
maxdepth = dep[i];
i1 = i;
}
}
int iret[8];
cullPoints2 (cnum,ret,maxc,i1,iret);
for (j=0; j < maxc; j++) {
// dContactGeom *con = CONTACT(contact,skip*j);
// for (i=0; i<3; i++) con->pos[i] = point[iret[j]*3+i] + pa[i];
// con->depth = dep[iret[j]];
btVector3 posInWorld;
for (i=0; i<3; i++)
posInWorld[i] = point[iret[j]*3+i] + pa[i];
output.addContactPoint(-normal,posInWorld,-dep[iret[j]]);
}
cnum = maxc;
}
*return_code = code;
return cnum;
}
void btBoxBoxDetector::getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* /*debugDraw*/,bool /*swapResults*/)
{
const btTransform& transformA = input.m_transformA;
const btTransform& transformB = input.m_transformB;
int skip = 0;
dContactGeom *contact = 0;
dMatrix3 R1;
dMatrix3 R2;
for (int j=0;j<3;j++)
{
R1[0+4*j] = transformA.getBasis()[j].x();
R2[0+4*j] = transformB.getBasis()[j].x();
R1[1+4*j] = transformA.getBasis()[j].y();
R2[1+4*j] = transformB.getBasis()[j].y();
R1[2+4*j] = transformA.getBasis()[j].z();
R2[2+4*j] = transformB.getBasis()[j].z();
}
btVector3 normal;
btScalar depth;
int return_code;
int maxc = 4;
dBoxBox2 (transformA.getOrigin(),
R1,
2.f*m_box1->getHalfExtentsWithMargin(),
transformB.getOrigin(),
R2,
2.f*m_box2->getHalfExtentsWithMargin(),
normal, &depth, &return_code,
maxc, contact, skip,
output
);
}

View File

@@ -0,0 +1,44 @@
/*
* Box-Box collision detection re-distributed under the ZLib license with permission from Russell L. Smith
* Original version is from Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.
* All rights reserved. Email: russ@q12.org Web: www.q12.org
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 BOX_BOX_DETECTOR_H
#define BOX_BOX_DETECTOR_H
class btBoxShape;
#include "BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h"
/// btBoxBoxDetector wraps the ODE box-box collision detector
/// re-distributed under the Zlib license with permission from Russell L. Smith
struct btBoxBoxDetector : public btDiscreteCollisionDetectorInterface
{
btBoxShape* m_box1;
btBoxShape* m_box2;
public:
btBoxBoxDetector(btBoxShape* box1,btBoxShape* box2);
virtual ~btBoxBoxDetector() {};
virtual void getClosestPoints(const ClosestPointInput& input,Result& output,class btIDebugDraw* debugDraw,bool swapResults=false);
};
#endif //BT_BOX_BOX_DETECTOR_H

View File

@@ -0,0 +1,47 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 BT_COLLISION_CONFIGURATION
#define BT_COLLISION_CONFIGURATION
struct btCollisionAlgorithmCreateFunc;
class btStackAlloc;
class btPoolAllocator;
///btCollisionConfiguration allows to configure Bullet collision detection
///stack allocator size, default collision algorithms and persistent manifold pool size
///todo: describe the meaning
class btCollisionConfiguration
{
public:
virtual ~btCollisionConfiguration()
{
}
///memory pools
virtual btPoolAllocator* getPersistentManifoldPool() = 0;
virtual btPoolAllocator* getCollisionAlgorithmPool() = 0;
virtual btStackAlloc* getStackAllocator() = 0;
virtual btCollisionAlgorithmCreateFunc* getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1) =0;
};
#endif //BT_COLLISION_CONFIGURATION

View File

@@ -0,0 +1,108 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 "btConvexPlaneCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
#include "BulletCollision/CollisionDispatch/btCollisionObject.h"
#include "BulletCollision/CollisionShapes/btConvexShape.h"
#include "BulletCollision/CollisionShapes/btStaticPlaneShape.h"
//#include <stdio.h>
btConvexPlaneCollisionAlgorithm::btConvexPlaneCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped)
: btCollisionAlgorithm(ci),
m_ownManifold(false),
m_manifoldPtr(mf),
m_isSwapped(isSwapped)
{
btCollisionObject* convexObj = m_isSwapped? col1 : col0;
btCollisionObject* planeObj = m_isSwapped? col0 : col1;
if (!m_manifoldPtr && m_dispatcher->needsCollision(convexObj,planeObj))
{
m_manifoldPtr = m_dispatcher->getNewManifold(convexObj,planeObj);
m_ownManifold = true;
}
}
btConvexPlaneCollisionAlgorithm::~btConvexPlaneCollisionAlgorithm()
{
if (m_ownManifold)
{
if (m_manifoldPtr)
m_dispatcher->releaseManifold(m_manifoldPtr);
}
}
void btConvexPlaneCollisionAlgorithm::processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut)
{
(void)dispatchInfo;
(void)resultOut;
if (!m_manifoldPtr)
return;
btCollisionObject* convexObj = m_isSwapped? body1 : body0;
btCollisionObject* planeObj = m_isSwapped? body0: body1;
btConvexShape* convexShape = (btConvexShape*) convexObj->getCollisionShape();
btStaticPlaneShape* planeShape = (btStaticPlaneShape*) planeObj->getCollisionShape();
bool hasCollision = false;
const btVector3& planeNormal = planeShape->getPlaneNormal();
const btScalar& planeConstant = planeShape->getPlaneConstant();
btTransform planeInConvex;
planeInConvex= convexObj->getWorldTransform().inverse() * planeObj->getWorldTransform();
btTransform convexInPlaneTrans;
convexInPlaneTrans= planeObj->getWorldTransform().inverse() * convexObj->getWorldTransform();
btVector3 vtx = convexShape->localGetSupportingVertex(planeInConvex.getBasis()*-planeNormal);
btVector3 vtxInPlane = convexInPlaneTrans(vtx);
btScalar distance = (planeNormal.dot(vtxInPlane) - planeConstant);
btVector3 vtxInPlaneProjected = vtxInPlane - distance*planeNormal;
btVector3 vtxInPlaneWorld = planeObj->getWorldTransform() * vtxInPlaneProjected;
hasCollision = distance < m_manifoldPtr->getContactBreakingThreshold();
resultOut->setPersistentManifold(m_manifoldPtr);
if (hasCollision)
{
/// report a contact. internally this will be kept persistent, and contact reduction is done
btVector3 normalOnSurfaceB = planeObj->getWorldTransform().getBasis() * planeNormal;
btVector3 pOnB = vtxInPlaneWorld;
resultOut->addContactPoint(normalOnSurfaceB,pOnB,distance);
}
if (m_ownManifold)
{
if (m_manifoldPtr->getNumContacts())
{
resultOut->refreshContactPoints();
}
}
}
btScalar btConvexPlaneCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* col0,btCollisionObject* col1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut)
{
(void)resultOut;
(void)dispatchInfo;
(void)col0;
(void)col1;
//not yet
return btScalar(1.);
}

View File

@@ -0,0 +1,71 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 CONVEX_PLANE_COLLISION_ALGORITHM_H
#define CONVEX_PLANE_COLLISION_ALGORITHM_H
#include "BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h"
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h"
#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h"
class btPersistentManifold;
#include "btCollisionDispatcher.h"
#include "LinearMath/btVector3.h"
/// btSphereBoxCollisionAlgorithm provides sphere-box collision detection.
/// Other features are frame-coherency (persistent data) and collision response.
class btConvexPlaneCollisionAlgorithm : public btCollisionAlgorithm
{
bool m_ownManifold;
btPersistentManifold* m_manifoldPtr;
bool m_isSwapped;
public:
btConvexPlaneCollisionAlgorithm(btPersistentManifold* mf,const btCollisionAlgorithmConstructionInfo& ci,btCollisionObject* col0,btCollisionObject* col1, bool isSwapped);
virtual ~btConvexPlaneCollisionAlgorithm();
virtual void processCollision (btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut);
virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut);
virtual void getAllContactManifolds(btManifoldArray& manifoldArray)
{
if (m_manifoldPtr && m_ownManifold)
{
manifoldArray.push_back(m_manifoldPtr);
}
}
struct CreateFunc :public btCollisionAlgorithmCreateFunc
{
virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, btCollisionObject* body0,btCollisionObject* body1)
{
void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btConvexPlaneCollisionAlgorithm));
if (!m_swapped)
{
return new(mem) btConvexPlaneCollisionAlgorithm(0,ci,body0,body1,false);
} else
{
return new(mem) btConvexPlaneCollisionAlgorithm(0,ci,body0,body1,true);
}
}
};
};
#endif //CONVEX_PLANE_COLLISION_ALGORITHM_H

View File

@@ -0,0 +1,291 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 "btDefaultCollisionConfiguration.h"
#include "BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h"
#include "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h"
#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM
#include "BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h"
#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM
#include "BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h"
#include "BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h"
#include "BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h"
#include "BulletCollision/NarrowPhaseCollision/btVoronoiSimplexSolver.h"
#include "LinearMath/btStackAlloc.h"
#include "LinearMath/btPoolAllocator.h"
btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(const btDefaultCollisionConstructionInfo& constructionInfo)
//btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(btStackAlloc* stackAlloc,btPoolAllocator* persistentManifoldPool,btPoolAllocator* collisionAlgorithmPool)
{
void* mem = btAlignedAlloc(sizeof(btVoronoiSimplexSolver),16);
m_simplexSolver = new (mem)btVoronoiSimplexSolver();
#define USE_EPA 1
#ifdef USE_EPA
mem = btAlignedAlloc(sizeof(btGjkEpaPenetrationDepthSolver),16);
m_pdSolver = new (mem)btGjkEpaPenetrationDepthSolver;
#else
mem = btAlignedAlloc(sizeof(btMinkowskiPenetrationDepthSolver),16);
m_pdSolver = new (mem)btMinkowskiPenetrationDepthSolver;
#endif//USE_EPA
//default CreationFunctions, filling the m_doubleDispatch table
mem = btAlignedAlloc(sizeof(btConvexConvexAlgorithm::CreateFunc),16);
m_convexConvexCreateFunc = new(mem) btConvexConvexAlgorithm::CreateFunc(m_simplexSolver,m_pdSolver);
mem = btAlignedAlloc(sizeof(btConvexConcaveCollisionAlgorithm::CreateFunc),16);
m_convexConcaveCreateFunc = new (mem)btConvexConcaveCollisionAlgorithm::CreateFunc;
mem = btAlignedAlloc(sizeof(btConvexConcaveCollisionAlgorithm::CreateFunc),16);
m_swappedConvexConcaveCreateFunc = new (mem)btConvexConcaveCollisionAlgorithm::SwappedCreateFunc;
mem = btAlignedAlloc(sizeof(btCompoundCollisionAlgorithm::CreateFunc),16);
m_compoundCreateFunc = new (mem)btCompoundCollisionAlgorithm::CreateFunc;
mem = btAlignedAlloc(sizeof(btCompoundCollisionAlgorithm::SwappedCreateFunc),16);
m_swappedCompoundCreateFunc = new (mem)btCompoundCollisionAlgorithm::SwappedCreateFunc;
mem = btAlignedAlloc(sizeof(btEmptyAlgorithm::CreateFunc),16);
m_emptyCreateFunc = new(mem) btEmptyAlgorithm::CreateFunc;
mem = btAlignedAlloc(sizeof(btSphereSphereCollisionAlgorithm::CreateFunc),16);
m_sphereSphereCF = new(mem) btSphereSphereCollisionAlgorithm::CreateFunc;
#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM
mem = btAlignedAlloc(sizeof(btSphereBoxCollisionAlgorithm::CreateFunc),16);
m_sphereBoxCF = new(mem) btSphereBoxCollisionAlgorithm::CreateFunc;
mem = btAlignedAlloc(sizeof(btSphereBoxCollisionAlgorithm::CreateFunc),16);
m_boxSphereCF = new (mem)btSphereBoxCollisionAlgorithm::CreateFunc;
m_boxSphereCF->m_swapped = true;
#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM
mem = btAlignedAlloc(sizeof(btSphereTriangleCollisionAlgorithm::CreateFunc),16);
m_sphereTriangleCF = new (mem)btSphereTriangleCollisionAlgorithm::CreateFunc;
mem = btAlignedAlloc(sizeof(btSphereTriangleCollisionAlgorithm::CreateFunc),16);
m_triangleSphereCF = new (mem)btSphereTriangleCollisionAlgorithm::CreateFunc;
m_triangleSphereCF->m_swapped = true;
mem = btAlignedAlloc(sizeof(btBoxBoxCollisionAlgorithm::CreateFunc),16);
m_boxBoxCF = new(mem)btBoxBoxCollisionAlgorithm::CreateFunc;
//convex versus plane
mem = btAlignedAlloc (sizeof(btConvexPlaneCollisionAlgorithm::CreateFunc),16);
m_convexPlaneCF = new (mem) btConvexPlaneCollisionAlgorithm::CreateFunc;
mem = btAlignedAlloc (sizeof(btConvexPlaneCollisionAlgorithm::CreateFunc),16);
m_planeConvexCF = new (mem) btConvexPlaneCollisionAlgorithm::CreateFunc;
m_planeConvexCF->m_swapped = true;
///calculate maximum element size, big enough to fit any collision algorithm in the memory pool
int maxSize = sizeof(btConvexConvexAlgorithm);
int maxSize2 = sizeof(btConvexConcaveCollisionAlgorithm);
int maxSize3 = sizeof(btCompoundCollisionAlgorithm);
int maxSize4 = sizeof(btEmptyAlgorithm);
int collisionAlgorithmMaxElementSize = btMax(maxSize,maxSize2);
collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize3);
collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize4);
if (constructionInfo.m_stackAlloc)
{
m_ownsStackAllocator = false;
this->m_stackAlloc = constructionInfo.m_stackAlloc;
} else
{
m_ownsStackAllocator = true;
void* mem = btAlignedAlloc(sizeof(btStackAlloc),16);
m_stackAlloc = new(mem)btStackAlloc(constructionInfo.m_defaultStackAllocatorSize);
}
if (constructionInfo.m_persistentManifoldPool)
{
m_ownsPersistentManifoldPool = false;
m_persistentManifoldPool = constructionInfo.m_persistentManifoldPool;
} else
{
m_ownsPersistentManifoldPool = true;
void* mem = btAlignedAlloc(sizeof(btPoolAllocator),16);
m_persistentManifoldPool = new (mem) btPoolAllocator(sizeof(btPersistentManifold),constructionInfo.m_defaultMaxPersistentManifoldPoolSize);
}
if (constructionInfo.m_collisionAlgorithmPool)
{
m_ownsCollisionAlgorithmPool = false;
m_collisionAlgorithmPool = constructionInfo.m_collisionAlgorithmPool;
} else
{
m_ownsCollisionAlgorithmPool = true;
void* mem = btAlignedAlloc(sizeof(btPoolAllocator),16);
m_collisionAlgorithmPool = new(mem) btPoolAllocator(collisionAlgorithmMaxElementSize,constructionInfo.m_defaultMaxCollisionAlgorithmPoolSize);
}
}
btDefaultCollisionConfiguration::~btDefaultCollisionConfiguration()
{
if (m_ownsStackAllocator)
{
m_stackAlloc->destroy();
m_stackAlloc->~btStackAlloc();
btAlignedFree(m_stackAlloc);
}
if (m_ownsCollisionAlgorithmPool)
{
m_collisionAlgorithmPool->~btPoolAllocator();
btAlignedFree(m_collisionAlgorithmPool);
}
if (m_ownsPersistentManifoldPool)
{
m_persistentManifoldPool->~btPoolAllocator();
btAlignedFree(m_persistentManifoldPool);
}
m_convexConvexCreateFunc->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_convexConvexCreateFunc);
m_convexConcaveCreateFunc->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_convexConcaveCreateFunc);
m_swappedConvexConcaveCreateFunc->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_swappedConvexConcaveCreateFunc);
m_compoundCreateFunc->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_compoundCreateFunc);
m_swappedCompoundCreateFunc->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_swappedCompoundCreateFunc);
m_emptyCreateFunc->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_emptyCreateFunc);
m_sphereSphereCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_sphereSphereCF);
#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM
m_sphereBoxCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_sphereBoxCF);
m_boxSphereCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_boxSphereCF);
#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM
m_sphereTriangleCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_sphereTriangleCF);
m_triangleSphereCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_triangleSphereCF);
m_boxBoxCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_boxBoxCF);
m_convexPlaneCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_convexPlaneCF);
m_planeConvexCF->~btCollisionAlgorithmCreateFunc();
btAlignedFree( m_planeConvexCF);
m_simplexSolver->~btVoronoiSimplexSolver();
btAlignedFree(m_simplexSolver);
m_pdSolver->~btConvexPenetrationDepthSolver();
btAlignedFree(m_pdSolver);
}
btCollisionAlgorithmCreateFunc* btDefaultCollisionConfiguration::getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1)
{
if ((proxyType0 == SPHERE_SHAPE_PROXYTYPE) && (proxyType1==SPHERE_SHAPE_PROXYTYPE))
{
return m_sphereSphereCF;
}
#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM
if ((proxyType0 == SPHERE_SHAPE_PROXYTYPE) && (proxyType1==BOX_SHAPE_PROXYTYPE))
{
return m_sphereBoxCF;
}
if ((proxyType0 == BOX_SHAPE_PROXYTYPE ) && (proxyType1==SPHERE_SHAPE_PROXYTYPE))
{
return m_boxSphereCF;
}
#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM
if ((proxyType0 == SPHERE_SHAPE_PROXYTYPE ) && (proxyType1==TRIANGLE_SHAPE_PROXYTYPE))
{
return m_sphereTriangleCF;
}
if ((proxyType0 == TRIANGLE_SHAPE_PROXYTYPE ) && (proxyType1==SPHERE_SHAPE_PROXYTYPE))
{
return m_triangleSphereCF;
}
if ((proxyType0 == BOX_SHAPE_PROXYTYPE) && (proxyType1 == BOX_SHAPE_PROXYTYPE))
{
return m_boxBoxCF;
}
if (btBroadphaseProxy::isConvex(proxyType0) && (proxyType1 == STATIC_PLANE_PROXYTYPE))
{
return m_convexPlaneCF;
}
if (btBroadphaseProxy::isConvex(proxyType1) && (proxyType0 == STATIC_PLANE_PROXYTYPE))
{
return m_planeConvexCF;
}
if (btBroadphaseProxy::isConvex(proxyType0) && btBroadphaseProxy::isConvex(proxyType1))
{
return m_convexConvexCreateFunc;
}
if (btBroadphaseProxy::isConvex(proxyType0) && btBroadphaseProxy::isConcave(proxyType1))
{
return m_convexConcaveCreateFunc;
}
if (btBroadphaseProxy::isConvex(proxyType1) && btBroadphaseProxy::isConcave(proxyType0))
{
return m_swappedConvexConcaveCreateFunc;
}
if (btBroadphaseProxy::isCompound(proxyType0))
{
return m_compoundCreateFunc;
} else
{
if (btBroadphaseProxy::isCompound(proxyType1))
{
return m_swappedCompoundCreateFunc;
}
}
//failed to find an algorithm
return m_emptyCreateFunc;
}

View File

@@ -0,0 +1,118 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 BT_DEFAULT_COLLISION_CONFIGURATION
#define BT_DEFAULT_COLLISION_CONFIGURATION
#include "btCollisionConfiguration.h"
class btVoronoiSimplexSolver;
class btConvexPenetrationDepthSolver;
struct btDefaultCollisionConstructionInfo
{
btStackAlloc* m_stackAlloc;
btPoolAllocator* m_persistentManifoldPool;
btPoolAllocator* m_collisionAlgorithmPool;
int m_defaultMaxPersistentManifoldPoolSize;
int m_defaultMaxCollisionAlgorithmPoolSize;
int m_defaultStackAllocatorSize;
btDefaultCollisionConstructionInfo()
:m_stackAlloc(0),
m_persistentManifoldPool(0),
m_collisionAlgorithmPool(0),
m_defaultMaxPersistentManifoldPoolSize(65535),
m_defaultMaxCollisionAlgorithmPoolSize(65535),
m_defaultStackAllocatorSize(5*1024*1024)
{
}
};
///btCollisionConfiguration allows to configure Bullet collision detection
///stack allocator, pool memory allocators
///todo: describe the meaning
class btDefaultCollisionConfiguration : public btCollisionConfiguration
{
protected:
int m_persistentManifoldPoolSize;
btStackAlloc* m_stackAlloc;
bool m_ownsStackAllocator;
btPoolAllocator* m_persistentManifoldPool;
bool m_ownsPersistentManifoldPool;
btPoolAllocator* m_collisionAlgorithmPool;
bool m_ownsCollisionAlgorithmPool;
//default simplex/penetration depth solvers
btVoronoiSimplexSolver* m_simplexSolver;
btConvexPenetrationDepthSolver* m_pdSolver;
//default CreationFunctions, filling the m_doubleDispatch table
btCollisionAlgorithmCreateFunc* m_convexConvexCreateFunc;
btCollisionAlgorithmCreateFunc* m_convexConcaveCreateFunc;
btCollisionAlgorithmCreateFunc* m_swappedConvexConcaveCreateFunc;
btCollisionAlgorithmCreateFunc* m_compoundCreateFunc;
btCollisionAlgorithmCreateFunc* m_swappedCompoundCreateFunc;
btCollisionAlgorithmCreateFunc* m_emptyCreateFunc;
btCollisionAlgorithmCreateFunc* m_sphereSphereCF;
#ifdef USE_BUGGY_SPHERE_BOX_ALGORITHM
btCollisionAlgorithmCreateFunc* m_sphereBoxCF;
btCollisionAlgorithmCreateFunc* m_boxSphereCF;
#endif //USE_BUGGY_SPHERE_BOX_ALGORITHM
btCollisionAlgorithmCreateFunc* m_boxBoxCF;
btCollisionAlgorithmCreateFunc* m_sphereTriangleCF;
btCollisionAlgorithmCreateFunc* m_triangleSphereCF;
btCollisionAlgorithmCreateFunc* m_planeConvexCF;
btCollisionAlgorithmCreateFunc* m_convexPlaneCF;
public:
btDefaultCollisionConfiguration(const btDefaultCollisionConstructionInfo& constructionInfo = btDefaultCollisionConstructionInfo());
virtual ~btDefaultCollisionConfiguration();
///memory pools
virtual btPoolAllocator* getPersistentManifoldPool()
{
return m_persistentManifoldPool;
}
virtual btPoolAllocator* getCollisionAlgorithmPool()
{
return m_collisionAlgorithmPool;
}
virtual btStackAlloc* getStackAllocator()
{
return m_stackAlloc;
}
virtual btCollisionAlgorithmCreateFunc* getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1);
};
#endif //BT_DEFAULT_COLLISION_CONFIGURATION

View File

@@ -0,0 +1,78 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 "btConvexInternalShape.h"
btConvexInternalShape::btConvexInternalShape()
: m_localScaling(btScalar(1.),btScalar(1.),btScalar(1.)),
m_collisionMargin(CONVEX_DISTANCE_MARGIN)
{
}
void btConvexInternalShape::setLocalScaling(const btVector3& scaling)
{
m_localScaling = scaling.absolute();
}
void btConvexInternalShape::getAabbSlow(const btTransform& trans,btVector3&minAabb,btVector3&maxAabb) const
{
btScalar margin = getMargin();
for (int i=0;i<3;i++)
{
btVector3 vec(btScalar(0.),btScalar(0.),btScalar(0.));
vec[i] = btScalar(1.);
btVector3 sv = localGetSupportingVertex(vec*trans.getBasis());
btVector3 tmp = trans(sv);
maxAabb[i] = tmp[i]+margin;
vec[i] = btScalar(-1.);
tmp = trans(localGetSupportingVertex(vec*trans.getBasis()));
minAabb[i] = tmp[i]-margin;
}
};
btVector3 btConvexInternalShape::localGetSupportingVertex(const btVector3& vec)const
{
#ifndef __SPU__
btVector3 supVertex = localGetSupportingVertexWithoutMargin(vec);
if ( getMargin()!=btScalar(0.) )
{
btVector3 vecnorm = vec;
if (vecnorm .length2() < (SIMD_EPSILON*SIMD_EPSILON))
{
vecnorm.setValue(btScalar(-1.),btScalar(-1.),btScalar(-1.));
}
vecnorm.normalize();
supVertex+= getMargin() * vecnorm;
}
return supVertex;
#else
return btVector3(0,0,0);
#endif //__SPU__
}

View File

@@ -0,0 +1,98 @@
#ifndef BT_CONVEX_INTERNAL_SHAPE_H
#define BT_CONVEX_INTERNAL_SHAPE_H
#include "btConvexShape.h"
///The btConvexInternalShape is an internal base class, shared by most convex shape implementations.
class btConvexInternalShape : public btConvexShape
{
protected:
//local scaling. collisionMargin is not scaled !
btVector3 m_localScaling;
btVector3 m_implicitShapeDimensions;
btScalar m_collisionMargin;
btScalar m_padding;
public:
btConvexInternalShape();
virtual ~btConvexInternalShape()
{
}
virtual btVector3 localGetSupportingVertex(const btVector3& vec)const;
#ifndef __SPU__
virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec) const= 0;
//notice that the vectors should be unit length
virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const= 0;
#endif //#ifndef __SPU__
const btVector3& getImplicitShapeDimensions() const
{
return m_implicitShapeDimensions;
}
///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version
void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
{
getAabbSlow(t,aabbMin,aabbMax);
}
virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const;
virtual void setLocalScaling(const btVector3& scaling);
virtual const btVector3& getLocalScaling() const
{
return m_localScaling;
}
const btVector3& getLocalScalingNV() const
{
return m_localScaling;
}
virtual void setMargin(btScalar margin)
{
m_collisionMargin = margin;
}
virtual btScalar getMargin() const
{
return m_collisionMargin;
}
btScalar getMarginNV() const
{
return m_collisionMargin;
}
virtual int getNumPreferredPenetrationDirections() const
{
return 0;
}
virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const
{
(void)penetrationVector;
(void)index;
btAssert(0);
}
};
#endif //BT_CONVEX_INTERNAL_SHAPE_H

View File

@@ -0,0 +1,34 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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.
*/
/// This file was created by Alex Silverman
#ifndef MATERIAL_H
#define MATERIAL_H
// Material class to be used by btMultimaterialTriangleMeshShape to store triangle properties
class btMaterial
{
// public members so that materials can change due to world events
public:
btScalar m_friction;
btScalar m_restitution;
int pad[2];
btMaterial(){}
btMaterial(btScalar fric, btScalar rest) { m_friction = fric; m_restitution = rest; }
};
#endif // MATERIAL_H

View File

@@ -0,0 +1,45 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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.
*/
/// This file was created by Alex Silverman
#include "BulletCollision/CollisionShapes/btMultimaterialTriangleMeshShape.h"
#include "BulletCollision/CollisionShapes/btTriangleIndexVertexMaterialArray.h"
//#include "BulletCollision/CollisionShapes/btOptimizedBvh.h"
///Obtains the material for a specific triangle
const btMaterial * btMultimaterialTriangleMeshShape::getMaterialProperties(int partID, int triIndex)
{
const unsigned char * materialBase = 0;
int numMaterials;
PHY_ScalarType materialType;
int materialStride;
const unsigned char * triangleMaterialBase = 0;
int numTriangles;
int triangleMaterialStride;
PHY_ScalarType triangleType;
((btTriangleIndexVertexMaterialArray*)m_meshInterface)->getLockedReadOnlyMaterialBase(&materialBase, numMaterials, materialType, materialStride,
&triangleMaterialBase, numTriangles, triangleMaterialStride, triangleType, partID);
// return the pointer to the place with the friction for the triangle
// TODO: This depends on whether it's a moving mesh or not
// BUG IN GIMPACT
//return (btScalar*)(&materialBase[triangleMaterialBase[(triIndex-1) * triangleMaterialStride] * materialStride]);
int * matInd = (int *)(&(triangleMaterialBase[(triIndex * triangleMaterialStride)]));
btMaterial *matVal = (btMaterial *)(&(materialBase[*matInd * materialStride]));
return (matVal);
}

View File

@@ -0,0 +1,124 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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.
*/
/// This file was created by Alex Silverman
#ifndef BVH_TRIANGLE_MATERIAL_MESH_SHAPE_H
#define BVH_TRIANGLE_MATERIAL_MESH_SHAPE_H
#include "btBvhTriangleMeshShape.h"
#include "btMaterial.h"
///The BvhTriangleMaterialMeshShape extends the btBvhTriangleMeshShape. Its main contribution is the interface into a material array, which allows per-triangle friction and restitution.
ATTRIBUTE_ALIGNED16(class) btMultimaterialTriangleMeshShape : public btBvhTriangleMeshShape
{
btAlignedObjectArray <btMaterial*> m_materialList;
int ** m_triangleMaterials;
public:
BT_DECLARE_ALIGNED_ALLOCATOR();
btMultimaterialTriangleMeshShape(): btBvhTriangleMeshShape() {}
btMultimaterialTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression, bool buildBvh = true):
btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression, buildBvh)
{
btVector3 m_triangle[3];
const unsigned char *vertexbase;
int numverts;
PHY_ScalarType type;
int stride;
const unsigned char *indexbase;
int indexstride;
int numfaces;
PHY_ScalarType indicestype;
//m_materialLookup = (int**)(btAlignedAlloc(sizeof(int*) * meshInterface->getNumSubParts(), 16));
for(int i = 0; i < meshInterface->getNumSubParts(); i++)
{
m_meshInterface->getLockedReadOnlyVertexIndexBase(
&vertexbase,
numverts,
type,
stride,
&indexbase,
indexstride,
numfaces,
indicestype,
i);
//m_materialLookup[i] = (int*)(btAlignedAlloc(sizeof(int) * numfaces, 16));
}
}
///optionally pass in a larger bvh aabb, used for quantization. This allows for deformations within this aabb
btMultimaterialTriangleMeshShape(btStridingMeshInterface* meshInterface, bool useQuantizedAabbCompression,const btVector3& bvhAabbMin,const btVector3& bvhAabbMax, bool buildBvh = true):
btBvhTriangleMeshShape(meshInterface, useQuantizedAabbCompression, bvhAabbMin, bvhAabbMax, buildBvh)
{
btVector3 m_triangle[3];
const unsigned char *vertexbase;
int numverts;
PHY_ScalarType type;
int stride;
const unsigned char *indexbase;
int indexstride;
int numfaces;
PHY_ScalarType indicestype;
//m_materialLookup = (int**)(btAlignedAlloc(sizeof(int*) * meshInterface->getNumSubParts(), 16));
for(int i = 0; i < meshInterface->getNumSubParts(); i++)
{
m_meshInterface->getLockedReadOnlyVertexIndexBase(
&vertexbase,
numverts,
type,
stride,
&indexbase,
indexstride,
numfaces,
indicestype,
i);
//m_materialLookup[i] = (int*)(btAlignedAlloc(sizeof(int) * numfaces * 2, 16));
}
}
virtual ~btMultimaterialTriangleMeshShape()
{
/*
for(int i = 0; i < m_meshInterface->getNumSubParts(); i++)
{
btAlignedFree(m_materialValues[i]);
m_materialLookup[i] = NULL;
}
btAlignedFree(m_materialValues);
m_materialLookup = NULL;
*/
}
virtual int getShapeType() const
{
return MULTIMATERIAL_TRIANGLE_MESH_PROXYTYPE;
}
//debugging
virtual const char* getName()const {return "MULTIMATERIALTRIANGLEMESH";}
///Obtains the material for a specific triangle
const btMaterial * getMaterialProperties(int partID, int triIndex);
}
;
#endif //BVH_TRIANGLE_MATERIAL_MESH_SHAPE_H

View File

@@ -0,0 +1,121 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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 "btScaledBvhTriangleMeshShape.h"
btScaledBvhTriangleMeshShape::btScaledBvhTriangleMeshShape(btBvhTriangleMeshShape* childShape,btVector3 localScaling)
:m_bvhTriMeshShape(childShape),
m_localScaling(localScaling)
{
}
btScaledBvhTriangleMeshShape::~btScaledBvhTriangleMeshShape()
{
}
class btScaledTriangleCallback : public btTriangleCallback
{
btTriangleCallback* m_originalCallback;
btVector3 m_localScaling;
public:
btScaledTriangleCallback(btTriangleCallback* originalCallback,btVector3 localScaling)
:m_originalCallback(originalCallback),
m_localScaling(localScaling)
{
}
virtual void processTriangle(btVector3* triangle, int partId, int triangleIndex)
{
btVector3 newTriangle[3];
newTriangle[0] = triangle[0]*m_localScaling;
newTriangle[1] = triangle[1]*m_localScaling;
newTriangle[2] = triangle[2]*m_localScaling;
m_originalCallback->processTriangle(&newTriangle[0],partId,triangleIndex);
}
};
void btScaledBvhTriangleMeshShape::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const
{
btScaledTriangleCallback scaledCallback(callback,m_localScaling);
btVector3 invLocalScaling(1.f/m_localScaling.getX(),1.f/m_localScaling.getY(),1.f/m_localScaling.getZ());
btVector3 scaledAabbMin,scaledAabbMax;
///support negative scaling
scaledAabbMin[0] = m_localScaling.getX() >= 0. ? aabbMin[0] * invLocalScaling[0] : aabbMax[0] * invLocalScaling[0];
scaledAabbMin[1] = m_localScaling.getY() >= 0. ? aabbMin[1] * invLocalScaling[1] : aabbMax[1] * invLocalScaling[1];
scaledAabbMin[2] = m_localScaling.getZ() >= 0. ? aabbMin[2] * invLocalScaling[2] : aabbMax[2] * invLocalScaling[2];
scaledAabbMax[0] = m_localScaling.getX() <= 0. ? aabbMin[0] * invLocalScaling[0] : aabbMax[0] * invLocalScaling[0];
scaledAabbMax[1] = m_localScaling.getY() <= 0. ? aabbMin[1] * invLocalScaling[1] : aabbMax[1] * invLocalScaling[1];
scaledAabbMax[2] = m_localScaling.getZ() <= 0. ? aabbMin[2] * invLocalScaling[2] : aabbMax[2] * invLocalScaling[2];
m_bvhTriMeshShape->processAllTriangles(&scaledCallback,scaledAabbMin,scaledAabbMax);
}
void btScaledBvhTriangleMeshShape::getAabb(const btTransform& trans,btVector3& aabbMin,btVector3& aabbMax) const
{
btVector3 localAabbMin = m_bvhTriMeshShape->getLocalAabbMin();
btVector3 localAabbMax = m_bvhTriMeshShape->getLocalAabbMax();
btVector3 tmpLocalAabbMin = localAabbMin * m_localScaling;
btVector3 tmpLocalAabbMax = localAabbMax * m_localScaling;
localAabbMin[0] = (m_localScaling.getX() >= 0.) ? tmpLocalAabbMin[0] : tmpLocalAabbMax[0];
localAabbMin[1] = (m_localScaling.getY() >= 0.) ? tmpLocalAabbMin[1] : tmpLocalAabbMax[1];
localAabbMin[2] = (m_localScaling.getZ() >= 0.) ? tmpLocalAabbMin[2] : tmpLocalAabbMax[2];
localAabbMax[0] = (m_localScaling.getX() <= 0.) ? tmpLocalAabbMin[0] : tmpLocalAabbMax[0];
localAabbMax[1] = (m_localScaling.getY() <= 0.) ? tmpLocalAabbMin[1] : tmpLocalAabbMax[1];
localAabbMax[2] = (m_localScaling.getZ() <= 0.) ? tmpLocalAabbMin[2] : tmpLocalAabbMax[2];
btVector3 localHalfExtents = btScalar(0.5)*(localAabbMax-localAabbMin);
btScalar margin = m_bvhTriMeshShape->getMargin();
localHalfExtents += btVector3(margin,margin,margin);
btVector3 localCenter = btScalar(0.5)*(localAabbMax+localAabbMin);
btMatrix3x3 abs_b = trans.getBasis().absolute();
btPoint3 center = trans(localCenter);
btVector3 extent = btVector3(abs_b[0].dot(localHalfExtents),
abs_b[1].dot(localHalfExtents),
abs_b[2].dot(localHalfExtents));
aabbMin = center - extent;
aabbMax = center + extent;
}
void btScaledBvhTriangleMeshShape::setLocalScaling(const btVector3& scaling)
{
m_localScaling = scaling;
}
const btVector3& btScaledBvhTriangleMeshShape::getLocalScaling() const
{
return m_localScaling;
}
void btScaledBvhTriangleMeshShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const
{
///don't make this a movable object!
// btAssert(0);
}

View File

@@ -0,0 +1,67 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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 SCALED_BVH_TRIANGLE_MESH_SHAPE_H
#define SCALED_BVH_TRIANGLE_MESH_SHAPE_H
#include "BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h"
///The btScaledBvhTriangleMeshShape allows to instance a scaled version of an existing btBvhTriangleMeshShape.
///Note that each btBvhTriangleMeshShape still can have its own local scaling, independent from this btScaledBvhTriangleMeshShape 'localScaling'
ATTRIBUTE_ALIGNED16(class) btScaledBvhTriangleMeshShape : public btConcaveShape
{
btVector3 m_localScaling;
btBvhTriangleMeshShape* m_bvhTriMeshShape;
public:
btScaledBvhTriangleMeshShape(btBvhTriangleMeshShape* childShape,btVector3 localScaling);
virtual ~btScaledBvhTriangleMeshShape();
virtual int getShapeType() const
{
//use un-used 'FAST_CONCAVE_MESH_PROXYTYPE' for now, later add SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE to btBroadphaseProxy.h
return SCALED_TRIANGLE_MESH_SHAPE_PROXYTYPE;
}
virtual void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const;
virtual void setLocalScaling(const btVector3& scaling);
virtual const btVector3& getLocalScaling() const;
virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const;
virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const;
btBvhTriangleMeshShape* getChildShape()
{
return m_bvhTriMeshShape;
}
const btBvhTriangleMeshShape* getChildShape() const
{
return m_bvhTriMeshShape;
}
//debugging
virtual const char* getName()const {return "SCALEDBVHTRIANGLEMESH";}
};
#endif //BVH_TRIANGLE_MESH_SHAPE_H

View File

@@ -0,0 +1,164 @@
/*
btbtShapeHull implemented by John McCutchan.
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2008 Erwin Coumans http://bulletphysics.com
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 "btShapeHull.h"
#include "LinearMath/btConvexHull.h"
#define NUM_UNITSPHERE_POINTS 42
static btVector3 btUnitSpherePoints[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2] =
{
btVector3(btScalar(0.000000) , btScalar(-0.000000),btScalar(-1.000000)),
btVector3(btScalar(0.723608) , btScalar(-0.525725),btScalar(-0.447219)),
btVector3(btScalar(-0.276388) , btScalar(-0.850649),btScalar(-0.447219)),
btVector3(btScalar(-0.894426) , btScalar(-0.000000),btScalar(-0.447216)),
btVector3(btScalar(-0.276388) , btScalar(0.850649),btScalar(-0.447220)),
btVector3(btScalar(0.723608) , btScalar(0.525725),btScalar(-0.447219)),
btVector3(btScalar(0.276388) , btScalar(-0.850649),btScalar(0.447220)),
btVector3(btScalar(-0.723608) , btScalar(-0.525725),btScalar(0.447219)),
btVector3(btScalar(-0.723608) , btScalar(0.525725),btScalar(0.447219)),
btVector3(btScalar(0.276388) , btScalar(0.850649),btScalar(0.447219)),
btVector3(btScalar(0.894426) , btScalar(0.000000),btScalar(0.447216)),
btVector3(btScalar(-0.000000) , btScalar(0.000000),btScalar(1.000000)),
btVector3(btScalar(0.425323) , btScalar(-0.309011),btScalar(-0.850654)),
btVector3(btScalar(-0.162456) , btScalar(-0.499995),btScalar(-0.850654)),
btVector3(btScalar(0.262869) , btScalar(-0.809012),btScalar(-0.525738)),
btVector3(btScalar(0.425323) , btScalar(0.309011),btScalar(-0.850654)),
btVector3(btScalar(0.850648) , btScalar(-0.000000),btScalar(-0.525736)),
btVector3(btScalar(-0.525730) , btScalar(-0.000000),btScalar(-0.850652)),
btVector3(btScalar(-0.688190) , btScalar(-0.499997),btScalar(-0.525736)),
btVector3(btScalar(-0.162456) , btScalar(0.499995),btScalar(-0.850654)),
btVector3(btScalar(-0.688190) , btScalar(0.499997),btScalar(-0.525736)),
btVector3(btScalar(0.262869) , btScalar(0.809012),btScalar(-0.525738)),
btVector3(btScalar(0.951058) , btScalar(0.309013),btScalar(0.000000)),
btVector3(btScalar(0.951058) , btScalar(-0.309013),btScalar(0.000000)),
btVector3(btScalar(0.587786) , btScalar(-0.809017),btScalar(0.000000)),
btVector3(btScalar(0.000000) , btScalar(-1.000000),btScalar(0.000000)),
btVector3(btScalar(-0.587786) , btScalar(-0.809017),btScalar(0.000000)),
btVector3(btScalar(-0.951058) , btScalar(-0.309013),btScalar(-0.000000)),
btVector3(btScalar(-0.951058) , btScalar(0.309013),btScalar(-0.000000)),
btVector3(btScalar(-0.587786) , btScalar(0.809017),btScalar(-0.000000)),
btVector3(btScalar(-0.000000) , btScalar(1.000000),btScalar(-0.000000)),
btVector3(btScalar(0.587786) , btScalar(0.809017),btScalar(-0.000000)),
btVector3(btScalar(0.688190) , btScalar(-0.499997),btScalar(0.525736)),
btVector3(btScalar(-0.262869) , btScalar(-0.809012),btScalar(0.525738)),
btVector3(btScalar(-0.850648) , btScalar(0.000000),btScalar(0.525736)),
btVector3(btScalar(-0.262869) , btScalar(0.809012),btScalar(0.525738)),
btVector3(btScalar(0.688190) , btScalar(0.499997),btScalar(0.525736)),
btVector3(btScalar(0.525730) , btScalar(0.000000),btScalar(0.850652)),
btVector3(btScalar(0.162456) , btScalar(-0.499995),btScalar(0.850654)),
btVector3(btScalar(-0.425323) , btScalar(-0.309011),btScalar(0.850654)),
btVector3(btScalar(-0.425323) , btScalar(0.309011),btScalar(0.850654)),
btVector3(btScalar(0.162456) , btScalar(0.499995),btScalar(0.850654))
};
btShapeHull::btShapeHull (const btConvexShape* shape)
{
m_shape = shape;
m_vertices.clear ();
m_indices.clear();
m_numIndices = 0;
}
btShapeHull::~btShapeHull ()
{
m_indices.clear();
m_vertices.clear ();
}
bool
btShapeHull::buildHull (btScalar /*margin*/)
{
int numSampleDirections = NUM_UNITSPHERE_POINTS;
{
int numPDA = m_shape->getNumPreferredPenetrationDirections();
if (numPDA)
{
for (int i=0;i<numPDA;i++)
{
btVector3 norm;
m_shape->getPreferredPenetrationDirection(i,norm);
btUnitSpherePoints[numSampleDirections] = norm;
numSampleDirections++;
}
}
}
btVector3 supportPoints[NUM_UNITSPHERE_POINTS+MAX_PREFERRED_PENETRATION_DIRECTIONS*2];
int i;
for (i = 0; i < numSampleDirections; i++)
{
supportPoints[i] = m_shape->localGetSupportingVertex(btUnitSpherePoints[i]);
}
HullDesc hd;
hd.mFlags = QF_TRIANGLES;
hd.mVcount = static_cast<unsigned int>(numSampleDirections);
#ifdef BT_USE_DOUBLE_PRECISION
hd.mVertices = &supportPoints[0];
hd.mVertexStride = sizeof(btVector3);
#else
hd.mVertices = &supportPoints[0];
hd.mVertexStride = sizeof (btVector3);
#endif
HullLibrary hl;
HullResult hr;
if (hl.CreateConvexHull (hd, hr) == QE_FAIL)
{
return false;
}
m_vertices.resize (static_cast<int>(hr.mNumOutputVertices));
for (i = 0; i < static_cast<int>(hr.mNumOutputVertices); i++)
{
m_vertices[i] = hr.m_OutputVertices[i];
}
m_numIndices = hr.mNumIndices;
m_indices.resize(static_cast<int>(m_numIndices));
for (i = 0; i < static_cast<int>(m_numIndices); i++)
{
m_indices[i] = hr.m_Indices[i];
}
// free temporary hull result that we just copied
hl.ReleaseResult (hr);
return true;
}
int
btShapeHull::numTriangles () const
{
return static_cast<int>(m_numIndices / 3);
}
int
btShapeHull::numVertices () const
{
return m_vertices.size ();
}
int
btShapeHull::numIndices () const
{
return static_cast<int>(m_numIndices);
}

View File

@@ -0,0 +1,56 @@
/*
btShapeHull implemented by John McCutchan.
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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 _SHAPE_HULL_H
#define _SHAPE_HULL_H
#include "LinearMath/btAlignedObjectArray.h"
#include "BulletCollision/CollisionShapes/btConvexShape.h"
///The btShapeHull class takes a btConvexShape, builds a simplified convex hull using btConvexHull and provides triangle indices and vertices.
///It can be useful for to simplify a complex convex object and for visualization of a non-polyhedral convex object.
///It approximates the convex hull using the supporting vertex of 42 directions.
class btShapeHull
{
public:
btShapeHull (const btConvexShape* shape);
~btShapeHull ();
bool buildHull (btScalar margin);
int numTriangles () const;
int numVertices () const;
int numIndices () const;
const btVector3* getVertexPointer() const
{
return &m_vertices[0];
}
const unsigned int* getIndexPointer() const
{
return &m_indices[0];
}
protected:
btAlignedObjectArray<btVector3> m_vertices;
btAlignedObjectArray<unsigned int> m_indices;
unsigned int m_numIndices;
const btConvexShape* m_shape;
};
#endif //_SHAPE_HULL_H

View File

@@ -0,0 +1,86 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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.
*/
///This file was created by Alex Silverman
#include "btTriangleIndexVertexMaterialArray.h"
btTriangleIndexVertexMaterialArray::btTriangleIndexVertexMaterialArray(int numTriangles,int* triangleIndexBase,int triangleIndexStride,
int numVertices,btScalar* vertexBase,int vertexStride,
int numMaterials, unsigned char* materialBase, int materialStride,
int* triangleMaterialsBase, int materialIndexStride) :
btTriangleIndexVertexArray(numTriangles, triangleIndexBase, triangleIndexStride, numVertices, vertexBase, vertexStride)
{
btMaterialProperties mat;
mat.m_numMaterials = numMaterials;
mat.m_materialBase = materialBase;
mat.m_materialStride = materialStride;
#ifdef BT_USE_DOUBLE_PRECISION
mat.m_materialType = PHY_DOUBLE;
#else
mat.m_materialType = PHY_FLOAT;
#endif
mat.m_numTriangles = numTriangles;
mat.m_triangleMaterialsBase = (unsigned char *)triangleMaterialsBase;
mat.m_triangleMaterialStride = materialIndexStride;
mat.m_triangleType = PHY_INTEGER;
addMaterialProperties(mat);
}
void btTriangleIndexVertexMaterialArray::getLockedMaterialBase(unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride,
unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType, int subpart)
{
btAssert(subpart< getNumSubParts() );
btMaterialProperties& mats = m_materials[subpart];
numMaterials = mats.m_numMaterials;
(*materialBase) = (unsigned char *) mats.m_materialBase;
#ifdef BT_USE_DOUBLE_PRECISION
materialType = PHY_DOUBLE;
#else
materialType = PHY_FLOAT;
#endif
materialStride = mats.m_materialStride;
numTriangles = mats.m_numTriangles;
(*triangleMaterialBase) = (unsigned char *)mats.m_triangleMaterialsBase;
triangleMaterialStride = mats.m_triangleMaterialStride;
triangleType = mats.m_triangleType;
}
void btTriangleIndexVertexMaterialArray::getLockedReadOnlyMaterialBase(const unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride,
const unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType, int subpart)
{
btMaterialProperties& mats = m_materials[subpart];
numMaterials = mats.m_numMaterials;
(*materialBase) = (const unsigned char *) mats.m_materialBase;
#ifdef BT_USE_DOUBLE_PRECISION
materialType = PHY_DOUBLE;
#else
materialType = PHY_FLOAT;
#endif
materialStride = mats.m_materialStride;
numTriangles = mats.m_numTriangles;
(*triangleMaterialBase) = (const unsigned char *)mats.m_triangleMaterialsBase;
triangleMaterialStride = mats.m_triangleMaterialStride;
triangleType = mats.m_triangleType;
}

View File

@@ -0,0 +1,84 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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.
*/
///This file was created by Alex Silverman
#ifndef BT_MULTIMATERIAL_TRIANGLE_INDEX_VERTEX_ARRAY_H
#define BT_MULTIMATERIAL_TRIANGLE_INDEX_VERTEX_ARRAY_H
#include "btTriangleIndexVertexArray.h"
ATTRIBUTE_ALIGNED16( struct) btMaterialProperties
{
///m_materialBase ==========> 2 btScalar values make up one material, friction then restitution
int m_numMaterials;
const unsigned char * m_materialBase;
int m_materialStride;
PHY_ScalarType m_materialType;
///m_numTriangles <=========== This exists in the btIndexedMesh object for the same subpart, but since we're
/// padding the structure, it can be reproduced at no real cost
///m_triangleMaterials =====> 1 integer value makes up one entry
/// eg: m_triangleMaterials[1] = 5; // This will set triangle 2 to use material 5
int m_numTriangles;
const unsigned char * m_triangleMaterialsBase;
int m_triangleMaterialStride;
///m_triangleType <========== Automatically set in addMaterialProperties
PHY_ScalarType m_triangleType;
};
typedef btAlignedObjectArray<btMaterialProperties> MaterialArray;
///Teh btTriangleIndexVertexMaterialArray is built on TriangleIndexVertexArray
///The addition of a material array allows for the utilization of the partID and
///triangleIndex that are returned in the ContactAddedCallback. As with
///TriangleIndexVertexArray, no duplicate is made of the material data, so it
///is the users responsibility to maintain the array during the lifetime of the
///TriangleIndexVertexMaterialArray.
ATTRIBUTE_ALIGNED16(class) btTriangleIndexVertexMaterialArray : public btTriangleIndexVertexArray
{
protected:
MaterialArray m_materials;
public:
BT_DECLARE_ALIGNED_ALLOCATOR();
btTriangleIndexVertexMaterialArray()
{
}
btTriangleIndexVertexMaterialArray(int numTriangles,int* triangleIndexBase,int triangleIndexStride,
int numVertices,btScalar* vertexBase,int vertexStride,
int numMaterials, unsigned char* materialBase, int materialStride,
int* triangleMaterialsBase, int materialIndexStride);
virtual ~btTriangleIndexVertexMaterialArray() {}
void addMaterialProperties(const btMaterialProperties& mat, PHY_ScalarType triangleType = PHY_INTEGER)
{
m_materials.push_back(mat);
m_materials[m_materials.size()-1].m_triangleType = triangleType;
}
virtual void getLockedMaterialBase(unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride,
unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType ,int subpart = 0);
virtual void getLockedReadOnlyMaterialBase(const unsigned char **materialBase, int& numMaterials, PHY_ScalarType& materialType, int& materialStride,
const unsigned char ** triangleMaterialBase, int& numTriangles, int& triangleMaterialStride, PHY_ScalarType& triangleType, int subpart = 0);
}
;
#endif //BT_MULTIMATERIAL_TRIANGLE_INDEX_VERTEX_ARRAY_H

View File

@@ -0,0 +1,114 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2007 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 "btUniformScalingShape.h"
btUniformScalingShape::btUniformScalingShape( btConvexShape* convexChildShape,btScalar uniformScalingFactor):
m_childConvexShape(convexChildShape),
m_uniformScalingFactor(uniformScalingFactor)
{
}
btUniformScalingShape::~btUniformScalingShape()
{
}
btVector3 btUniformScalingShape::localGetSupportingVertexWithoutMargin(const btVector3& vec)const
{
btVector3 tmpVertex;
tmpVertex = m_childConvexShape->localGetSupportingVertexWithoutMargin(vec);
return tmpVertex*m_uniformScalingFactor;
}
void btUniformScalingShape::batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const
{
m_childConvexShape->batchedUnitVectorGetSupportingVertexWithoutMargin(vectors,supportVerticesOut,numVectors);
int i;
for (i=0;i<numVectors;i++)
{
supportVerticesOut[i] = supportVerticesOut[i] * m_uniformScalingFactor;
}
}
btVector3 btUniformScalingShape::localGetSupportingVertex(const btVector3& vec)const
{
btVector3 tmpVertex;
tmpVertex = m_childConvexShape->localGetSupportingVertex(vec);
return tmpVertex*m_uniformScalingFactor;
}
void btUniformScalingShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const
{
///this linear upscaling is not realistic, but we don't deal with large mass ratios...
btVector3 tmpInertia;
m_childConvexShape->calculateLocalInertia(mass,tmpInertia);
inertia = tmpInertia * m_uniformScalingFactor;
}
///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version
void btUniformScalingShape::getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
{
m_childConvexShape->getAabb(t,aabbMin,aabbMax);
btVector3 aabbCenter = (aabbMax+aabbMin)*btScalar(0.5);
btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*btScalar(0.5)*m_uniformScalingFactor;
aabbMin = aabbCenter - scaledAabbHalfExtends;
aabbMax = aabbCenter + scaledAabbHalfExtends;
}
void btUniformScalingShape::getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const
{
m_childConvexShape->getAabbSlow(t,aabbMin,aabbMax);
btVector3 aabbCenter = (aabbMax+aabbMin)*btScalar(0.5);
btVector3 scaledAabbHalfExtends = (aabbMax-aabbMin)*btScalar(0.5)*m_uniformScalingFactor;
aabbMin = aabbCenter - scaledAabbHalfExtends;
aabbMax = aabbCenter + scaledAabbHalfExtends;
}
void btUniformScalingShape::setLocalScaling(const btVector3& scaling)
{
m_childConvexShape->setLocalScaling(scaling);
}
const btVector3& btUniformScalingShape::getLocalScaling() const
{
return m_childConvexShape->getLocalScaling();
}
void btUniformScalingShape::setMargin(btScalar margin)
{
m_childConvexShape->setMargin(margin);
}
btScalar btUniformScalingShape::getMargin() const
{
return m_childConvexShape->getMargin() * m_uniformScalingFactor;
}
int btUniformScalingShape::getNumPreferredPenetrationDirections() const
{
return m_childConvexShape->getNumPreferredPenetrationDirections();
}
void btUniformScalingShape::getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const
{
m_childConvexShape->getPreferredPenetrationDirection(index,penetrationVector);
}

View File

@@ -0,0 +1,88 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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 BT_UNIFORM_SCALING_SHAPE_H
#define BT_UNIFORM_SCALING_SHAPE_H
#include "btConvexShape.h"
#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" // for the types
///The btUniformScalingShape allows to re-use uniform scaled instances of btConvexShape in a memory efficient way.
///Istead of using btUniformScalingShape, it is better to use the non-uniform setLocalScaling method on convex shapes that implement it.
class btUniformScalingShape : public btConvexShape
{
btConvexShape* m_childConvexShape;
btScalar m_uniformScalingFactor;
public:
btUniformScalingShape( btConvexShape* convexChildShape, btScalar uniformScalingFactor);
virtual ~btUniformScalingShape();
virtual btVector3 localGetSupportingVertexWithoutMargin(const btVector3& vec)const;
virtual btVector3 localGetSupportingVertex(const btVector3& vec)const;
virtual void batchedUnitVectorGetSupportingVertexWithoutMargin(const btVector3* vectors,btVector3* supportVerticesOut,int numVectors) const;
virtual void calculateLocalInertia(btScalar mass,btVector3& inertia) const;
btScalar getUniformScalingFactor() const
{
return m_uniformScalingFactor;
}
btConvexShape* getChildShape()
{
return m_childConvexShape;
}
const btConvexShape* getChildShape() const
{
return m_childConvexShape;
}
virtual const char* getName()const
{
return "UniformScalingShape";
}
virtual int getShapeType() const { return UNIFORM_SCALING_SHAPE_PROXYTYPE; }
///////////////////////////
///getAabb's default implementation is brute force, expected derived classes to implement a fast dedicated version
void getAabb(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const;
virtual void getAabbSlow(const btTransform& t,btVector3& aabbMin,btVector3& aabbMax) const;
virtual void setLocalScaling(const btVector3& scaling) ;
virtual const btVector3& getLocalScaling() const ;
virtual void setMargin(btScalar margin);
virtual btScalar getMargin() const;
virtual int getNumPreferredPenetrationDirections() const;
virtual void getPreferredPenetrationDirection(int index, btVector3& penetrationVector) const;
};
#endif //BT_UNIFORM_SCALING_SHAPE_H

View File

@@ -0,0 +1,943 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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.
*/
/*
GJK-EPA collision solver by Nathanael Presson, 2008
*/
#include "BulletCollision/CollisionShapes/btConvexInternalShape.h"
#include "BulletCollision/CollisionShapes/btSphereShape.h"
#include "btGjkEpa2.h"
#if defined(DEBUG) || defined (_DEBUG)
#include <stdio.h> //for debug printf
#ifdef __SPU__
#include <spu_printf.h>
#define printf spu_printf
#endif //__SPU__
#endif
namespace gjkepa2_impl
{
// Config
/* GJK */
#define GJK_MAX_ITERATIONS 128
#define GJK_ACCURARY ((btScalar)0.0001)
#define GJK_MIN_DISTANCE ((btScalar)0.0001)
#define GJK_DUPLICATED_EPS ((btScalar)0.0001)
#define GJK_SIMPLEX2_EPS ((btScalar)0.0)
#define GJK_SIMPLEX3_EPS ((btScalar)0.0)
#define GJK_SIMPLEX4_EPS ((btScalar)0.0)
/* EPA */
#define EPA_MAX_VERTICES 64
#define EPA_MAX_FACES (EPA_MAX_VERTICES*2)
#define EPA_MAX_ITERATIONS 255
#define EPA_ACCURACY ((btScalar)0.0001)
#define EPA_FALLBACK (10*EPA_ACCURACY)
#define EPA_PLANE_EPS ((btScalar)0.00001)
#define EPA_INSIDE_EPS ((btScalar)0.01)
// Shorthands
typedef unsigned int U;
typedef unsigned char U1;
// MinkowskiDiff
struct MinkowskiDiff
{
const btConvexShape* m_shapes[2];
btMatrix3x3 m_toshape1;
btTransform m_toshape0;
btVector3 (btConvexShape::*Ls)(const btVector3&) const;
void EnableMargin(bool enable)
{
if(enable)
Ls=&btConvexShape::localGetSupportingVertex;
else
Ls=&btConvexShape::localGetSupportingVertexWithoutMargin;
}
inline btVector3 Support0(const btVector3& d) const
{
return(((m_shapes[0])->*(Ls))(d));
}
inline btVector3 Support1(const btVector3& d) const
{
return(m_toshape0*((m_shapes[1])->*(Ls))(m_toshape1*d));
}
inline btVector3 Support(const btVector3& d) const
{
return(Support0(d)-Support1(-d));
}
btVector3 Support(const btVector3& d,U index) const
{
if(index)
return(Support1(d));
else
return(Support0(d));
}
};
typedef MinkowskiDiff tShape;
// GJK
struct GJK
{
/* Types */
struct sSV
{
btVector3 d,w;
};
struct sSimplex
{
sSV* c[4];
btScalar p[4];
U rank;
};
struct eStatus { enum _ {
Valid,
Inside,
Failed };};
/* Fields */
tShape m_shape;
btVector3 m_ray;
btScalar m_distance;
sSimplex m_simplices[2];
sSV m_store[4];
sSV* m_free[4];
U m_nfree;
U m_current;
sSimplex* m_simplex;
eStatus::_ m_status;
/* Methods */
GJK()
{
Initialize();
}
void Initialize()
{
m_ray = btVector3(0,0,0);
m_nfree = 0;
m_status = eStatus::Failed;
m_current = 0;
m_distance = 0;
}
eStatus::_ Evaluate(const tShape& shapearg,const btVector3& guess)
{
U iterations=0;
btScalar sqdist=0;
btScalar alpha=0;
btVector3 lastw[4];
U clastw=0;
/* Initialize solver */
m_free[0] = &m_store[0];
m_free[1] = &m_store[1];
m_free[2] = &m_store[2];
m_free[3] = &m_store[3];
m_nfree = 4;
m_current = 0;
m_status = eStatus::Valid;
m_shape = shapearg;
m_distance = 0;
/* Initialize simplex */
m_simplices[0].rank = 0;
m_ray = guess;
const btScalar sqrl= m_ray.length2();
appendvertice(m_simplices[0],sqrl>0?-m_ray:btVector3(1,0,0));
m_simplices[0].p[0] = 1;
m_ray = m_simplices[0].c[0]->w;
sqdist = sqrl;
lastw[0] =
lastw[1] =
lastw[2] =
lastw[3] = m_ray;
/* Loop */
do {
const U next=1-m_current;
sSimplex& cs=m_simplices[m_current];
sSimplex& ns=m_simplices[next];
/* Check zero */
const btScalar rl=m_ray.length();
if(rl<GJK_MIN_DISTANCE)
{/* Touching or inside */
m_status=eStatus::Inside;
break;
}
/* Append new vertice in -'v' direction */
appendvertice(cs,-m_ray);
const btVector3& w=cs.c[cs.rank-1]->w;
bool found=false;
for(U i=0;i<4;++i)
{
if((w-lastw[i]).length2()<GJK_DUPLICATED_EPS)
{ found=true;break; }
}
if(found)
{/* Return old simplex */
removevertice(m_simplices[m_current]);
break;
}
else
{/* Update lastw */
lastw[clastw=(clastw+1)&3]=w;
}
/* Check for termination */
const btScalar omega=dot(m_ray,w)/rl;
alpha=btMax(omega,alpha);
if(((rl-alpha)-(GJK_ACCURARY*rl))<=0)
{/* Return old simplex */
removevertice(m_simplices[m_current]);
break;
}
/* Reduce simplex */
btScalar weights[4];
U mask=0;
switch(cs.rank)
{
case 2: sqdist=projectorigin( cs.c[0]->w,
cs.c[1]->w,
weights,mask);break;
case 3: sqdist=projectorigin( cs.c[0]->w,
cs.c[1]->w,
cs.c[2]->w,
weights,mask);break;
case 4: sqdist=projectorigin( cs.c[0]->w,
cs.c[1]->w,
cs.c[2]->w,
cs.c[3]->w,
weights,mask);break;
}
if(sqdist>=0)
{/* Valid */
ns.rank = 0;
m_ray = btVector3(0,0,0);
m_current = next;
for(U i=0,ni=cs.rank;i<ni;++i)
{
if(mask&(1<<i))
{
ns.c[ns.rank] = cs.c[i];
ns.p[ns.rank++] = weights[i];
m_ray += cs.c[i]->w*weights[i];
}
else
{
m_free[m_nfree++] = cs.c[i];
}
}
if(mask==15) m_status=eStatus::Inside;
}
else
{/* Return old simplex */
removevertice(m_simplices[m_current]);
break;
}
m_status=((++iterations)<GJK_MAX_ITERATIONS)?m_status:eStatus::Failed;
} while(m_status==eStatus::Valid);
m_simplex=&m_simplices[m_current];
switch(m_status)
{
case eStatus::Valid: m_distance=m_ray.length();break;
case eStatus::Inside: m_distance=0;break;
}
return(m_status);
}
bool EncloseOrigin()
{
switch(m_simplex->rank)
{
case 1:
{
for(U i=0;i<3;++i)
{
btVector3 axis=btVector3(0,0,0);
axis[i]=1;
appendvertice(*m_simplex, axis);
if(EncloseOrigin()) return(true);
removevertice(*m_simplex);
appendvertice(*m_simplex,-axis);
if(EncloseOrigin()) return(true);
removevertice(*m_simplex);
}
}
break;
case 2:
{
const btVector3 d=m_simplex->c[1]->w-m_simplex->c[0]->w;
for(U i=0;i<3;++i)
{
btVector3 axis=btVector3(0,0,0);
axis[i]=1;
const btVector3 p=cross(d,axis);
if(p.length2()>0)
{
appendvertice(*m_simplex, p);
if(EncloseOrigin()) return(true);
removevertice(*m_simplex);
appendvertice(*m_simplex,-p);
if(EncloseOrigin()) return(true);
removevertice(*m_simplex);
}
}
}
break;
case 3:
{
const btVector3 n=cross(m_simplex->c[1]->w-m_simplex->c[0]->w,
m_simplex->c[2]->w-m_simplex->c[0]->w);
if(n.length2()>0)
{
appendvertice(*m_simplex,n);
if(EncloseOrigin()) return(true);
removevertice(*m_simplex);
appendvertice(*m_simplex,-n);
if(EncloseOrigin()) return(true);
removevertice(*m_simplex);
}
}
break;
case 4:
{
if(btFabs(det( m_simplex->c[0]->w-m_simplex->c[3]->w,
m_simplex->c[1]->w-m_simplex->c[3]->w,
m_simplex->c[2]->w-m_simplex->c[3]->w))>0)
return(true);
}
break;
}
return(false);
}
/* Internals */
void getsupport(const btVector3& d,sSV& sv) const
{
sv.d = d/d.length();
sv.w = m_shape.Support(sv.d);
}
void removevertice(sSimplex& simplex)
{
m_free[m_nfree++]=simplex.c[--simplex.rank];
}
void appendvertice(sSimplex& simplex,const btVector3& v)
{
simplex.p[simplex.rank]=0;
simplex.c[simplex.rank]=m_free[--m_nfree];
getsupport(v,*simplex.c[simplex.rank++]);
}
static btScalar det(const btVector3& a,const btVector3& b,const btVector3& c)
{
return( a.y()*b.z()*c.x()+a.z()*b.x()*c.y()-
a.x()*b.z()*c.y()-a.y()*b.x()*c.z()+
a.x()*b.y()*c.z()-a.z()*b.y()*c.x());
}
static btScalar projectorigin( const btVector3& a,
const btVector3& b,
btScalar* w,U& m)
{
const btVector3 d=b-a;
const btScalar l=d.length2();
if(l>GJK_SIMPLEX2_EPS)
{
const btScalar t(l>0?-dot(a,d)/l:0);
if(t>=1) { w[0]=0;w[1]=1;m=2;return(b.length2()); }
else if(t<=0) { w[0]=1;w[1]=0;m=1;return(a.length2()); }
else { w[0]=1-(w[1]=t);m=3;return((a+d*t).length2()); }
}
return(-1);
}
static btScalar projectorigin( const btVector3& a,
const btVector3& b,
const btVector3& c,
btScalar* w,U& m)
{
static const U imd3[]={1,2,0};
const btVector3* vt[]={&a,&b,&c};
const btVector3 dl[]={a-b,b-c,c-a};
const btVector3 n=cross(dl[0],dl[1]);
const btScalar l=n.length2();
if(l>GJK_SIMPLEX3_EPS)
{
btScalar mindist=-1;
btScalar subw[2];
U subm;
for(U i=0;i<3;++i)
{
if(dot(*vt[i],cross(dl[i],n))>0)
{
const U j=imd3[i];
const btScalar subd(projectorigin(*vt[i],*vt[j],subw,subm));
if((mindist<0)||(subd<mindist))
{
mindist = subd;
m = static_cast<U>(((subm&1)?1<<i:0)+((subm&2)?1<<j:0));
w[i] = subw[0];
w[j] = subw[1];
w[imd3[j]] = 0;
}
}
}
if(mindist<0)
{
const btScalar d=dot(a,n);
const btScalar s=btSqrt(l);
const btVector3 p=n*(d/l);
mindist = p.length2();
m = 7;
w[0] = (cross(dl[1],b-p)).length()/s;
w[1] = (cross(dl[2],c-p)).length()/s;
w[2] = 1-(w[0]+w[1]);
}
return(mindist);
}
return(-1);
}
static btScalar projectorigin( const btVector3& a,
const btVector3& b,
const btVector3& c,
const btVector3& d,
btScalar* w,U& m)
{
static const U imd3[]={1,2,0};
const btVector3* vt[]={&a,&b,&c,&d};
const btVector3 dl[]={a-d,b-d,c-d};
const btScalar vl=det(dl[0],dl[1],dl[2]);
const bool ng=(vl*dot(a,cross(b-c,a-b)))<=0;
if(ng&&(btFabs(vl)>GJK_SIMPLEX4_EPS))
{
btScalar mindist=-1;
btScalar subw[3];
U subm;
for(U i=0;i<3;++i)
{
const U j=imd3[i];
const btScalar s=vl*dot(d,cross(dl[i],dl[j]));
if(s>0)
{
const btScalar subd=projectorigin(*vt[i],*vt[j],d,subw,subm);
if((mindist<0)||(subd<mindist))
{
mindist = subd;
m = static_cast<U>((subm&1?1<<i:0)+
(subm&2?1<<j:0)+
(subm&4?8:0));
w[i] = subw[0];
w[j] = subw[1];
w[imd3[j]] = 0;
w[3] = subw[2];
}
}
}
if(mindist<0)
{
mindist = 0;
m = 15;
w[0] = det(c,b,d)/vl;
w[1] = det(a,c,d)/vl;
w[2] = det(b,a,d)/vl;
w[3] = 1-(w[0]+w[1]+w[2]);
}
return(mindist);
}
return(-1);
}
};
// EPA
struct EPA
{
/* Types */
typedef GJK::sSV sSV;
struct sFace
{
btVector3 n;
btScalar d;
btScalar p;
sSV* c[3];
sFace* f[3];
sFace* l[2];
U1 e[3];
U1 pass;
};
struct sList
{
sFace* root;
U count;
sList() : root(0),count(0) {}
};
struct sHorizon
{
sFace* cf;
sFace* ff;
U nf;
sHorizon() : cf(0),ff(0),nf(0) {}
};
struct eStatus { enum _ {
Valid,
Touching,
Degenerated,
NonConvex,
InvalidHull,
OutOfFaces,
OutOfVertices,
AccuraryReached,
FallBack,
Failed };};
/* Fields */
eStatus::_ m_status;
GJK::sSimplex m_result;
btVector3 m_normal;
btScalar m_depth;
sSV m_sv_store[EPA_MAX_VERTICES];
sFace m_fc_store[EPA_MAX_FACES];
U m_nextsv;
sList m_hull;
sList m_stock;
/* Methods */
EPA()
{
Initialize();
}
static inline void bind(sFace* fa,U ea,sFace* fb,U eb)
{
fa->e[ea]=(U1)eb;fa->f[ea]=fb;
fb->e[eb]=(U1)ea;fb->f[eb]=fa;
}
static inline void append(sList& list,sFace* face)
{
face->l[0] = 0;
face->l[1] = list.root;
if(list.root) list.root->l[0]=face;
list.root = face;
++list.count;
}
static inline void remove(sList& list,sFace* face)
{
if(face->l[1]) face->l[1]->l[0]=face->l[0];
if(face->l[0]) face->l[0]->l[1]=face->l[1];
if(face==list.root) list.root=face->l[1];
--list.count;
}
void Initialize()
{
m_status = eStatus::Failed;
m_normal = btVector3(0,0,0);
m_depth = 0;
m_nextsv = 0;
for(U i=0;i<EPA_MAX_FACES;++i)
{
append(m_stock,&m_fc_store[EPA_MAX_FACES-i-1]);
}
}
eStatus::_ Evaluate(GJK& gjk,const btVector3& guess)
{
GJK::sSimplex& simplex=*gjk.m_simplex;
if((simplex.rank>1)&&gjk.EncloseOrigin())
{
/* Clean up */
while(m_hull.root)
{
sFace* f = m_hull.root;
remove(m_hull,f);
append(m_stock,f);
}
m_status = eStatus::Valid;
m_nextsv = 0;
/* Orient simplex */
if(gjk.det( simplex.c[0]->w-simplex.c[3]->w,
simplex.c[1]->w-simplex.c[3]->w,
simplex.c[2]->w-simplex.c[3]->w)<0)
{
btSwap(simplex.c[0],simplex.c[1]);
btSwap(simplex.p[0],simplex.p[1]);
}
/* Build initial hull */
sFace* tetra[]={newface(simplex.c[0],simplex.c[1],simplex.c[2],true),
newface(simplex.c[1],simplex.c[0],simplex.c[3],true),
newface(simplex.c[2],simplex.c[1],simplex.c[3],true),
newface(simplex.c[0],simplex.c[2],simplex.c[3],true)};
if(m_hull.count==4)
{
sFace* best=findbest();
sFace outer=*best;
U pass=0;
U iterations=0;
bind(tetra[0],0,tetra[1],0);
bind(tetra[0],1,tetra[2],0);
bind(tetra[0],2,tetra[3],0);
bind(tetra[1],1,tetra[3],2);
bind(tetra[1],2,tetra[2],1);
bind(tetra[2],2,tetra[3],1);
m_status=eStatus::Valid;
for(;iterations<EPA_MAX_ITERATIONS;++iterations)
{
if(m_nextsv<EPA_MAX_VERTICES)
{
sHorizon horizon;
sSV* w=&m_sv_store[m_nextsv++];
bool valid=true;
best->pass = (U1)(++pass);
gjk.getsupport(best->n,*w);
const btScalar wdist=dot(best->n,w->w)-best->d;
if(wdist>EPA_ACCURACY)
{
for(U j=0;(j<3)&&valid;++j)
{
valid&=expand( pass,w,
best->f[j],best->e[j],
horizon);
}
if(valid&&(horizon.nf>=3))
{
bind(horizon.cf,1,horizon.ff,2);
remove(m_hull,best);
append(m_stock,best);
best=findbest();
if(best->p>=outer.p) outer=*best;
} else { m_status=eStatus::InvalidHull;break; }
} else { m_status=eStatus::AccuraryReached;break; }
} else { m_status=eStatus::OutOfVertices;break; }
}
const btVector3 projection=outer.n*outer.d;
m_normal = outer.n;
m_depth = outer.d;
m_result.rank = 3;
m_result.c[0] = outer.c[0];
m_result.c[1] = outer.c[1];
m_result.c[2] = outer.c[2];
m_result.p[0] = cross( outer.c[1]->w-projection,
outer.c[2]->w-projection).length();
m_result.p[1] = cross( outer.c[2]->w-projection,
outer.c[0]->w-projection).length();
m_result.p[2] = cross( outer.c[0]->w-projection,
outer.c[1]->w-projection).length();
const btScalar sum=m_result.p[0]+m_result.p[1]+m_result.p[2];
m_result.p[0] /= sum;
m_result.p[1] /= sum;
m_result.p[2] /= sum;
return(m_status);
}
}
/* Fallback */
m_status = eStatus::FallBack;
m_normal = -guess;
const btScalar nl=m_normal.length();
if(nl>0)
m_normal = m_normal/nl;
else
m_normal = btVector3(1,0,0);
m_depth = 0;
m_result.rank=1;
m_result.c[0]=simplex.c[0];
m_result.p[0]=1;
return(m_status);
}
sFace* newface(sSV* a,sSV* b,sSV* c,bool forced)
{
if(m_stock.root)
{
sFace* face=m_stock.root;
remove(m_stock,face);
append(m_hull,face);
face->pass = 0;
face->c[0] = a;
face->c[1] = b;
face->c[2] = c;
face->n = cross(b->w-a->w,c->w-a->w);
const btScalar l=face->n.length();
const bool v=l>EPA_ACCURACY;
face->p = btMin(btMin(
dot(a->w,cross(face->n,a->w-b->w)),
dot(b->w,cross(face->n,b->w-c->w))),
dot(c->w,cross(face->n,c->w-a->w))) /
(v?l:1);
face->p = face->p>=-EPA_INSIDE_EPS?0:face->p;
if(v)
{
face->d = dot(a->w,face->n)/l;
face->n /= l;
if(forced||(face->d>=-EPA_PLANE_EPS))
{
return(face);
} else m_status=eStatus::NonConvex;
} else m_status=eStatus::Degenerated;
remove(m_hull,face);
append(m_stock,face);
return(0);
}
m_status=m_stock.root?eStatus::OutOfVertices:eStatus::OutOfFaces;
return(0);
}
sFace* findbest()
{
sFace* minf=m_hull.root;
btScalar mind=minf->d*minf->d;
btScalar maxp=minf->p;
for(sFace* f=minf->l[1];f;f=f->l[1])
{
const btScalar sqd=f->d*f->d;
if((f->p>=maxp)&&(sqd<mind))
{
minf=f;
mind=sqd;
maxp=f->p;
}
}
return(minf);
}
bool expand(U pass,sSV* w,sFace* f,U e,sHorizon& horizon)
{
static const U i1m3[]={1,2,0};
static const U i2m3[]={2,0,1};
if(f->pass!=pass)
{
const U e1=i1m3[e];
if((dot(f->n,w->w)-f->d)<-EPA_PLANE_EPS)
{
sFace* nf=newface(f->c[e1],f->c[e],w,false);
if(nf)
{
bind(nf,0,f,e);
if(horizon.cf) bind(horizon.cf,1,nf,2); else horizon.ff=nf;
horizon.cf=nf;
++horizon.nf;
return(true);
}
}
else
{
const U e2=i2m3[e];
f->pass = (U1)pass;
if( expand(pass,w,f->f[e1],f->e[e1],horizon)&&
expand(pass,w,f->f[e2],f->e[e2],horizon))
{
remove(m_hull,f);
append(m_stock,f);
return(true);
}
}
}
return(false);
}
};
//
static void Initialize( const btConvexShape* shape0,const btTransform& wtrs0,
const btConvexShape* shape1,const btTransform& wtrs1,
btGjkEpaSolver2::sResults& results,
tShape& shape,
bool withmargins)
{
/* Results */
results.witnesses[0] =
results.witnesses[1] = btVector3(0,0,0);
results.status = btGjkEpaSolver2::sResults::Separated;
/* Shape */
shape.m_shapes[0] = shape0;
shape.m_shapes[1] = shape1;
shape.m_toshape1 = wtrs1.getBasis().transposeTimes(wtrs0.getBasis());
shape.m_toshape0 = wtrs0.inverseTimes(wtrs1);
shape.EnableMargin(withmargins);
}
}
//
// Api
//
using namespace gjkepa2_impl;
//
int btGjkEpaSolver2::StackSizeRequirement()
{
return(sizeof(GJK)+sizeof(EPA));
}
//
bool btGjkEpaSolver2::Distance( const btConvexShape* shape0,
const btTransform& wtrs0,
const btConvexShape* shape1,
const btTransform& wtrs1,
const btVector3& guess,
sResults& results)
{
tShape shape;
Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,false);
GJK gjk;
GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,guess);
if(gjk_status==GJK::eStatus::Valid)
{
btVector3 w0=btVector3(0,0,0);
btVector3 w1=btVector3(0,0,0);
for(U i=0;i<gjk.m_simplex->rank;++i)
{
const btScalar p=gjk.m_simplex->p[i];
w0+=shape.Support( gjk.m_simplex->c[i]->d,0)*p;
w1+=shape.Support(-gjk.m_simplex->c[i]->d,1)*p;
}
results.witnesses[0] = wtrs0*w0;
results.witnesses[1] = wtrs0*w1;
results.normal = w0-w1;
results.distance = results.normal.length();
results.normal /= results.distance>GJK_MIN_DISTANCE?results.distance:1;
return(true);
}
else
{
results.status = gjk_status==GJK::eStatus::Inside?
sResults::Penetrating :
sResults::GJK_Failed ;
return(false);
}
}
//
bool btGjkEpaSolver2::Penetration( const btConvexShape* shape0,
const btTransform& wtrs0,
const btConvexShape* shape1,
const btTransform& wtrs1,
const btVector3& guess,
sResults& results,
bool usemargins)
{
tShape shape;
Initialize(shape0,wtrs0,shape1,wtrs1,results,shape,usemargins);
GJK gjk;
GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,-guess);
switch(gjk_status)
{
case GJK::eStatus::Inside:
{
EPA epa;
EPA::eStatus::_ epa_status=epa.Evaluate(gjk,-guess);
if(epa_status!=EPA::eStatus::Failed)
{
btVector3 w0=btVector3(0,0,0);
for(U i=0;i<epa.m_result.rank;++i)
{
w0+=shape.Support(epa.m_result.c[i]->d,0)*epa.m_result.p[i];
}
results.status = sResults::Penetrating;
results.witnesses[0] = wtrs0*w0;
results.witnesses[1] = wtrs0*(w0-epa.m_normal*epa.m_depth);
results.normal = -epa.m_normal;
results.distance = -epa.m_depth;
return(true);
} else results.status=sResults::EPA_Failed;
}
break;
case GJK::eStatus::Failed:
results.status=sResults::GJK_Failed;
break;
}
return(false);
}
//
btScalar btGjkEpaSolver2::SignedDistance(const btVector3& position,
btScalar margin,
const btConvexShape* shape0,
const btTransform& wtrs0,
sResults& results)
{
tShape shape;
btSphereShape shape1(margin);
btTransform wtrs1(btQuaternion(0,0,0,1),position);
Initialize(shape0,wtrs0,&shape1,wtrs1,results,shape,false);
GJK gjk;
GJK::eStatus::_ gjk_status=gjk.Evaluate(shape,btVector3(1,1,1));
if(gjk_status==GJK::eStatus::Valid)
{
btVector3 w0=btVector3(0,0,0);
btVector3 w1=btVector3(0,0,0);
for(U i=0;i<gjk.m_simplex->rank;++i)
{
const btScalar p=gjk.m_simplex->p[i];
w0+=shape.Support( gjk.m_simplex->c[i]->d,0)*p;
w1+=shape.Support(-gjk.m_simplex->c[i]->d,1)*p;
}
results.witnesses[0] = wtrs0*w0;
results.witnesses[1] = wtrs0*w1;
const btVector3 delta= results.witnesses[1]-
results.witnesses[0];
const btScalar margin= shape0->getMargin()+
shape1.getMargin();
const btScalar length= delta.length();
results.normal = delta/length;
results.witnesses[0] += results.normal*margin;
return(length-margin);
}
else
{
if(gjk_status==GJK::eStatus::Inside)
{
if(Penetration(shape0,wtrs0,&shape1,wtrs1,gjk.m_ray,results))
{
const btVector3 delta= results.witnesses[0]-
results.witnesses[1];
const btScalar length= delta.length();
if (length >= SIMD_EPSILON)
results.normal = delta/length;
return(-length);
}
}
}
return(SIMD_INFINITY);
}
//
bool btGjkEpaSolver2::SignedDistance(const btConvexShape* shape0,
const btTransform& wtrs0,
const btConvexShape* shape1,
const btTransform& wtrs1,
const btVector3& guess,
sResults& results)
{
if(!Distance(shape0,wtrs0,shape1,wtrs1,guess,results))
return(Penetration(shape0,wtrs0,shape1,wtrs1,guess,results,false));
else
return(true);
}
/* Symbols cleanup */
#undef GJK_MAX_ITERATIONS
#undef GJK_ACCURARY
#undef GJK_MIN_DISTANCE
#undef GJK_DUPLICATED_EPS
#undef GJK_SIMPLEX2_EPS
#undef GJK_SIMPLEX3_EPS
#undef GJK_SIMPLEX4_EPS
#undef EPA_MAX_VERTICES
#undef EPA_MAX_FACES
#undef EPA_MAX_ITERATIONS
#undef EPA_ACCURACY
#undef EPA_FALLBACK
#undef EPA_PLANE_EPS
#undef EPA_INSIDE_EPS

View File

@@ -0,0 +1,71 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-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.
*/
/*
GJK-EPA collision solver by Nathanael Presson, 2008
*/
#ifndef _68DA1F85_90B7_4bb0_A705_83B4040A75C6_
#define _68DA1F85_90B7_4bb0_A705_83B4040A75C6_
#include "BulletCollision/CollisionShapes/btConvexShape.h"
///btGjkEpaSolver contributed under zlib by Nathanael Presson
struct btGjkEpaSolver2
{
struct sResults
{
enum eStatus
{
Separated, /* Shapes doesnt penetrate */
Penetrating, /* Shapes are penetrating */
GJK_Failed, /* GJK phase fail, no big issue, shapes are probably just 'touching' */
EPA_Failed /* EPA phase fail, bigger problem, need to save parameters, and debug */
} status;
btVector3 witnesses[2];
btVector3 normal;
btScalar distance;
};
static int StackSizeRequirement();
static bool Distance( const btConvexShape* shape0,const btTransform& wtrs0,
const btConvexShape* shape1,const btTransform& wtrs1,
const btVector3& guess,
sResults& results);
static bool Penetration(const btConvexShape* shape0,const btTransform& wtrs0,
const btConvexShape* shape1,const btTransform& wtrs1,
const btVector3& guess,
sResults& results,
bool usemargins=true);
static btScalar SignedDistance( const btVector3& position,
btScalar margin,
const btConvexShape* shape,
const btTransform& wtrs,
sResults& results);
static bool SignedDistance( const btConvexShape* shape0,const btTransform& wtrs0,
const btConvexShape* shape1,const btTransform& wtrs1,
const btVector3& guess,
sResults& results);
};
#endif

View File

@@ -0,0 +1,415 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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.
*/
/*
Added by Roman Ponomarev (rponom@gmail.com)
April 04, 2008
*/
//-----------------------------------------------------------------------------
#include "btSliderConstraint.h"
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "LinearMath/btTransformUtil.h"
#include <new>
//-----------------------------------------------------------------------------
void btSliderConstraint::initParams()
{
m_lowerLinLimit = btScalar(1.0);
m_upperLinLimit = btScalar(-1.0);
m_lowerAngLimit = btScalar(0.);
m_upperAngLimit = btScalar(0.);
m_softnessDirLin = SLIDER_CONSTRAINT_DEF_SOFTNESS;
m_restitutionDirLin = SLIDER_CONSTRAINT_DEF_RESTITUTION;
m_dampingDirLin = btScalar(0.);
m_softnessDirAng = SLIDER_CONSTRAINT_DEF_SOFTNESS;
m_restitutionDirAng = SLIDER_CONSTRAINT_DEF_RESTITUTION;
m_dampingDirAng = btScalar(0.);
m_softnessOrthoLin = SLIDER_CONSTRAINT_DEF_SOFTNESS;
m_restitutionOrthoLin = SLIDER_CONSTRAINT_DEF_RESTITUTION;
m_dampingOrthoLin = SLIDER_CONSTRAINT_DEF_DAMPING;
m_softnessOrthoAng = SLIDER_CONSTRAINT_DEF_SOFTNESS;
m_restitutionOrthoAng = SLIDER_CONSTRAINT_DEF_RESTITUTION;
m_dampingOrthoAng = SLIDER_CONSTRAINT_DEF_DAMPING;
m_softnessLimLin = SLIDER_CONSTRAINT_DEF_SOFTNESS;
m_restitutionLimLin = SLIDER_CONSTRAINT_DEF_RESTITUTION;
m_dampingLimLin = SLIDER_CONSTRAINT_DEF_DAMPING;
m_softnessLimAng = SLIDER_CONSTRAINT_DEF_SOFTNESS;
m_restitutionLimAng = SLIDER_CONSTRAINT_DEF_RESTITUTION;
m_dampingLimAng = SLIDER_CONSTRAINT_DEF_DAMPING;
m_poweredLinMotor = false;
m_targetLinMotorVelocity = btScalar(0.);
m_maxLinMotorForce = btScalar(0.);
m_accumulatedLinMotorImpulse = btScalar(0.0);
m_poweredAngMotor = false;
m_targetAngMotorVelocity = btScalar(0.);
m_maxAngMotorForce = btScalar(0.);
m_accumulatedAngMotorImpulse = btScalar(0.0);
} // btSliderConstraint::initParams()
//-----------------------------------------------------------------------------
btSliderConstraint::btSliderConstraint()
:btTypedConstraint(SLIDER_CONSTRAINT_TYPE),
m_useLinearReferenceFrameA(true)
{
initParams();
} // btSliderConstraint::btSliderConstraint()
//-----------------------------------------------------------------------------
btSliderConstraint::btSliderConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB, bool useLinearReferenceFrameA)
: btTypedConstraint(SLIDER_CONSTRAINT_TYPE, rbA, rbB)
, m_frameInA(frameInA)
, m_frameInB(frameInB),
m_useLinearReferenceFrameA(useLinearReferenceFrameA)
{
initParams();
} // btSliderConstraint::btSliderConstraint()
//-----------------------------------------------------------------------------
void btSliderConstraint::buildJacobian()
{
if(m_useLinearReferenceFrameA)
{
buildJacobianInt(m_rbA, m_rbB, m_frameInA, m_frameInB);
}
else
{
buildJacobianInt(m_rbB, m_rbA, m_frameInB, m_frameInA);
}
} // btSliderConstraint::buildJacobian()
//-----------------------------------------------------------------------------
void btSliderConstraint::buildJacobianInt(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB)
{
//calculate transforms
m_calculatedTransformA = rbA.getCenterOfMassTransform() * frameInA;
m_calculatedTransformB = rbB.getCenterOfMassTransform() * frameInB;
m_realPivotAInW = m_calculatedTransformA.getOrigin();
m_realPivotBInW = m_calculatedTransformB.getOrigin();
m_sliderAxis = m_calculatedTransformA.getBasis().getColumn(0); // along X
m_delta = m_realPivotBInW - m_realPivotAInW;
m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis;
m_relPosA = m_projPivotInW - rbA.getCenterOfMassPosition();
m_relPosB = m_realPivotBInW - rbB.getCenterOfMassPosition();
btVector3 normalWorld;
int i;
//linear part
for(i = 0; i < 3; i++)
{
normalWorld = m_calculatedTransformA.getBasis().getColumn(i);
new (&m_jacLin[i]) btJacobianEntry(
rbA.getCenterOfMassTransform().getBasis().transpose(),
rbB.getCenterOfMassTransform().getBasis().transpose(),
m_relPosA,
m_relPosB,
normalWorld,
rbA.getInvInertiaDiagLocal(),
rbA.getInvMass(),
rbB.getInvInertiaDiagLocal(),
rbB.getInvMass()
);
m_jacLinDiagABInv[i] = btScalar(1.) / m_jacLin[i].getDiagonal();
m_depth[i] = m_delta.dot(normalWorld);
}
testLinLimits();
// angular part
for(i = 0; i < 3; i++)
{
normalWorld = m_calculatedTransformA.getBasis().getColumn(i);
new (&m_jacAng[i]) btJacobianEntry(
normalWorld,
rbA.getCenterOfMassTransform().getBasis().transpose(),
rbB.getCenterOfMassTransform().getBasis().transpose(),
rbA.getInvInertiaDiagLocal(),
rbB.getInvInertiaDiagLocal()
);
}
testAngLimits();
btVector3 axisA = m_calculatedTransformA.getBasis().getColumn(0);
m_kAngle = btScalar(1.0 )/ (rbA.computeAngularImpulseDenominator(axisA) + rbB.computeAngularImpulseDenominator(axisA));
// clear accumulator for motors
m_accumulatedLinMotorImpulse = btScalar(0.0);
m_accumulatedAngMotorImpulse = btScalar(0.0);
} // btSliderConstraint::buildJacobianInt()
//-----------------------------------------------------------------------------
void btSliderConstraint::solveConstraint(btScalar timeStep)
{
m_timeStep = timeStep;
if(m_useLinearReferenceFrameA)
{
solveConstraintInt(m_rbA, m_rbB);
}
else
{
solveConstraintInt(m_rbB, m_rbA);
}
} // btSliderConstraint::solveConstraint()
//-----------------------------------------------------------------------------
void btSliderConstraint::solveConstraintInt(btRigidBody& rbA, btRigidBody& rbB)
{
int i;
// linear
btVector3 velA = rbA.getVelocityInLocalPoint(m_relPosA);
btVector3 velB = rbB.getVelocityInLocalPoint(m_relPosB);
btVector3 vel = velA - velB;
for(i = 0; i < 3; i++)
{
const btVector3& normal = m_jacLin[i].m_linearJointAxis;
btScalar rel_vel = normal.dot(vel);
// calculate positional error
btScalar depth = m_depth[i];
// get parameters
btScalar softness = (i) ? m_softnessOrthoLin : (m_solveLinLim ? m_softnessLimLin : m_softnessDirLin);
btScalar restitution = (i) ? m_restitutionOrthoLin : (m_solveLinLim ? m_restitutionLimLin : m_restitutionDirLin);
btScalar damping = (i) ? m_dampingOrthoLin : (m_solveLinLim ? m_dampingLimLin : m_dampingDirLin);
// calcutate and apply impulse
btScalar normalImpulse = softness * (restitution * depth / m_timeStep - damping * rel_vel) * m_jacLinDiagABInv[i];
btVector3 impulse_vector = normal * normalImpulse;
rbA.applyImpulse( impulse_vector, m_relPosA);
rbB.applyImpulse(-impulse_vector, m_relPosB);
if(m_poweredLinMotor && (!i))
{ // apply linear motor
if(m_accumulatedLinMotorImpulse < m_maxLinMotorForce)
{
btScalar desiredMotorVel = m_targetLinMotorVelocity;
btScalar motor_relvel = desiredMotorVel + rel_vel;
normalImpulse = -motor_relvel * m_jacLinDiagABInv[i];
// clamp accumulated impulse
btScalar new_acc = m_accumulatedLinMotorImpulse + btFabs(normalImpulse);
if(new_acc > m_maxLinMotorForce)
{
new_acc = m_maxLinMotorForce;
}
btScalar del = new_acc - m_accumulatedLinMotorImpulse;
if(normalImpulse < btScalar(0.0))
{
normalImpulse = -del;
}
else
{
normalImpulse = del;
}
m_accumulatedLinMotorImpulse = new_acc;
// apply clamped impulse
impulse_vector = normal * normalImpulse;
rbA.applyImpulse( impulse_vector, m_relPosA);
rbB.applyImpulse(-impulse_vector, m_relPosB);
}
}
}
// angular
// get axes in world space
btVector3 axisA = m_calculatedTransformA.getBasis().getColumn(0);
btVector3 axisB = m_calculatedTransformB.getBasis().getColumn(0);
const btVector3& angVelA = rbA.getAngularVelocity();
const btVector3& angVelB = rbB.getAngularVelocity();
btVector3 angVelAroundAxisA = axisA * axisA.dot(angVelA);
btVector3 angVelAroundAxisB = axisB * axisB.dot(angVelB);
btVector3 angAorthog = angVelA - angVelAroundAxisA;
btVector3 angBorthog = angVelB - angVelAroundAxisB;
btVector3 velrelOrthog = angAorthog-angBorthog;
//solve orthogonal angular velocity correction
btScalar len = velrelOrthog.length();
if (len > btScalar(0.00001))
{
btVector3 normal = velrelOrthog.normalized();
btScalar denom = rbA.computeAngularImpulseDenominator(normal) + rbB.computeAngularImpulseDenominator(normal);
velrelOrthog *= (btScalar(1.)/denom) * m_dampingOrthoAng * m_softnessOrthoAng;
}
//solve angular positional correction
btVector3 angularError = axisA.cross(axisB) *(btScalar(1.)/m_timeStep);
btScalar len2 = angularError.length();
if (len2>btScalar(0.00001))
{
btVector3 normal2 = angularError.normalized();
btScalar denom2 = rbA.computeAngularImpulseDenominator(normal2) + rbB.computeAngularImpulseDenominator(normal2);
angularError *= (btScalar(1.)/denom2) * m_restitutionOrthoAng * m_softnessOrthoAng;
}
// apply impulse
rbA.applyTorqueImpulse(-velrelOrthog+angularError);
rbB.applyTorqueImpulse(velrelOrthog-angularError);
btScalar impulseMag;
//solve angular limits
if(m_solveAngLim)
{
impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingLimAng + m_angDepth * m_restitutionLimAng / m_timeStep;
impulseMag *= m_kAngle * m_softnessLimAng;
}
else
{
impulseMag = (angVelB - angVelA).dot(axisA) * m_dampingDirAng + m_angDepth * m_restitutionDirAng / m_timeStep;
impulseMag *= m_kAngle * m_softnessDirAng;
}
btVector3 impulse = axisA * impulseMag;
rbA.applyTorqueImpulse(impulse);
rbB.applyTorqueImpulse(-impulse);
//apply angular motor
if(m_poweredAngMotor)
{
if(m_accumulatedAngMotorImpulse < m_maxAngMotorForce)
{
btVector3 velrel = angVelAroundAxisA - angVelAroundAxisB;
btScalar projRelVel = velrel.dot(axisA);
btScalar desiredMotorVel = m_targetAngMotorVelocity;
btScalar motor_relvel = desiredMotorVel - projRelVel;
btScalar angImpulse = m_kAngle * motor_relvel;
// clamp accumulated impulse
btScalar new_acc = m_accumulatedAngMotorImpulse + btFabs(angImpulse);
if(new_acc > m_maxAngMotorForce)
{
new_acc = m_maxAngMotorForce;
}
btScalar del = new_acc - m_accumulatedAngMotorImpulse;
if(angImpulse < btScalar(0.0))
{
angImpulse = -del;
}
else
{
angImpulse = del;
}
m_accumulatedAngMotorImpulse = new_acc;
// apply clamped impulse
btVector3 motorImp = angImpulse * axisA;
m_rbA.applyTorqueImpulse(motorImp);
m_rbB.applyTorqueImpulse(-motorImp);
}
}
} // btSliderConstraint::solveConstraint()
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void btSliderConstraint::calculateTransforms(void){
if(m_useLinearReferenceFrameA)
{
m_calculatedTransformA = m_rbA.getCenterOfMassTransform() * m_frameInA;
m_calculatedTransformB = m_rbB.getCenterOfMassTransform() * m_frameInB;
}
else
{
m_calculatedTransformA = m_rbB.getCenterOfMassTransform() * m_frameInB;
m_calculatedTransformB = m_rbA.getCenterOfMassTransform() * m_frameInA;
}
m_realPivotAInW = m_calculatedTransformA.getOrigin();
m_realPivotBInW = m_calculatedTransformB.getOrigin();
m_sliderAxis = m_calculatedTransformA.getBasis().getColumn(0); // along X
m_delta = m_realPivotBInW - m_realPivotAInW;
m_projPivotInW = m_realPivotAInW + m_sliderAxis.dot(m_delta) * m_sliderAxis;
btVector3 normalWorld;
int i;
//linear part
for(i = 0; i < 3; i++)
{
normalWorld = m_calculatedTransformA.getBasis().getColumn(i);
m_depth[i] = m_delta.dot(normalWorld);
}
} // btSliderConstraint::calculateTransforms()
//-----------------------------------------------------------------------------
void btSliderConstraint::testLinLimits(void)
{
m_solveLinLim = false;
m_linPos = m_depth[0];
if(m_lowerLinLimit <= m_upperLinLimit)
{
if(m_depth[0] > m_upperLinLimit)
{
m_depth[0] -= m_upperLinLimit;
m_solveLinLim = true;
}
else if(m_depth[0] < m_lowerLinLimit)
{
m_depth[0] -= m_lowerLinLimit;
m_solveLinLim = true;
}
else
{
m_depth[0] = btScalar(0.);
}
}
else
{
m_depth[0] = btScalar(0.);
}
} // btSliderConstraint::testLinLimits()
//-----------------------------------------------------------------------------
void btSliderConstraint::testAngLimits(void)
{
m_angDepth = btScalar(0.);
m_solveAngLim = false;
if(m_lowerAngLimit <= m_upperAngLimit)
{
const btVector3 axisA0 = m_calculatedTransformA.getBasis().getColumn(1);
const btVector3 axisA1 = m_calculatedTransformA.getBasis().getColumn(2);
const btVector3 axisB0 = m_calculatedTransformB.getBasis().getColumn(1);
btScalar rot = btAtan2Fast(axisB0.dot(axisA1), axisB0.dot(axisA0));
if(rot < m_lowerAngLimit)
{
m_angDepth = rot - m_lowerAngLimit;
m_solveAngLim = true;
}
else if(rot > m_upperAngLimit)
{
m_angDepth = rot - m_upperAngLimit;
m_solveAngLim = true;
}
}
} // btSliderConstraint::testAngLimits()
//-----------------------------------------------------------------------------
btVector3 btSliderConstraint::getAncorInA(void)
{
btVector3 ancorInA;
ancorInA = m_realPivotAInW + (m_lowerLinLimit + m_upperLinLimit) * btScalar(0.5) * m_sliderAxis;
ancorInA = m_rbA.getCenterOfMassTransform().inverse() * ancorInA;
return ancorInA;
} // btSliderConstraint::getAncorInA()
//-----------------------------------------------------------------------------
btVector3 btSliderConstraint::getAncorInB(void)
{
btVector3 ancorInB;
ancorInB = m_frameInB.getOrigin();
return ancorInB;
} // btSliderConstraint::getAncorInB();

View File

@@ -0,0 +1,218 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2006 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.
*/
/*
Added by Roman Ponomarev (rponom@gmail.com)
April 04, 2008
TODO:
- add clamping od accumulated impulse to improve stability
- add conversion for ODE constraint solver
*/
#ifndef SLIDER_CONSTRAINT_H
#define SLIDER_CONSTRAINT_H
//-----------------------------------------------------------------------------
#include "LinearMath/btVector3.h"
#include "btJacobianEntry.h"
#include "btTypedConstraint.h"
//-----------------------------------------------------------------------------
class btRigidBody;
//-----------------------------------------------------------------------------
#define SLIDER_CONSTRAINT_DEF_SOFTNESS (btScalar(1.0))
#define SLIDER_CONSTRAINT_DEF_DAMPING (btScalar(1.0))
#define SLIDER_CONSTRAINT_DEF_RESTITUTION (btScalar(0.7))
//-----------------------------------------------------------------------------
class btSliderConstraint : public btTypedConstraint
{
protected:
btTransform m_frameInA;
btTransform m_frameInB;
// use frameA fo define limits, if true
bool m_useLinearReferenceFrameA;
// linear limits
btScalar m_lowerLinLimit;
btScalar m_upperLinLimit;
// angular limits
btScalar m_lowerAngLimit;
btScalar m_upperAngLimit;
// softness, restitution and damping for different cases
// DirLin - moving inside linear limits
// LimLin - hitting linear limit
// DirAng - moving inside angular limits
// LimAng - hitting angular limit
// OrthoLin, OrthoAng - against constraint axis
btScalar m_softnessDirLin;
btScalar m_restitutionDirLin;
btScalar m_dampingDirLin;
btScalar m_softnessDirAng;
btScalar m_restitutionDirAng;
btScalar m_dampingDirAng;
btScalar m_softnessLimLin;
btScalar m_restitutionLimLin;
btScalar m_dampingLimLin;
btScalar m_softnessLimAng;
btScalar m_restitutionLimAng;
btScalar m_dampingLimAng;
btScalar m_softnessOrthoLin;
btScalar m_restitutionOrthoLin;
btScalar m_dampingOrthoLin;
btScalar m_softnessOrthoAng;
btScalar m_restitutionOrthoAng;
btScalar m_dampingOrthoAng;
// for interlal use
bool m_solveLinLim;
bool m_solveAngLim;
btJacobianEntry m_jacLin[3];
btScalar m_jacLinDiagABInv[3];
btJacobianEntry m_jacAng[3];
btScalar m_timeStep;
btTransform m_calculatedTransformA;
btTransform m_calculatedTransformB;
btVector3 m_sliderAxis;
btVector3 m_realPivotAInW;
btVector3 m_realPivotBInW;
btVector3 m_projPivotInW;
btVector3 m_delta;
btVector3 m_depth;
btVector3 m_relPosA;
btVector3 m_relPosB;
btScalar m_linPos;
btScalar m_angDepth;
btScalar m_kAngle;
bool m_poweredLinMotor;
btScalar m_targetLinMotorVelocity;
btScalar m_maxLinMotorForce;
btScalar m_accumulatedLinMotorImpulse;
bool m_poweredAngMotor;
btScalar m_targetAngMotorVelocity;
btScalar m_maxAngMotorForce;
btScalar m_accumulatedAngMotorImpulse;
//------------------------
void initParams();
public:
// constructors
btSliderConstraint(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB ,bool useLinearReferenceFrameA);
btSliderConstraint();
// overrides
virtual void buildJacobian();
virtual void solveConstraint(btScalar timeStep);
// access
const btRigidBody& getRigidBodyA() const { return m_rbA; }
const btRigidBody& getRigidBodyB() const { return m_rbB; }
const btTransform & getCalculatedTransformA() const { return m_calculatedTransformA; }
const btTransform & getCalculatedTransformB() const { return m_calculatedTransformB; }
const btTransform & getFrameOffsetA() const { return m_frameInA; }
const btTransform & getFrameOffsetB() const { return m_frameInB; }
btTransform & getFrameOffsetA() { return m_frameInA; }
btTransform & getFrameOffsetB() { return m_frameInB; }
btScalar getLowerLinLimit() { return m_lowerLinLimit; }
void setLowerLinLimit(btScalar lowerLimit) { m_lowerLinLimit = lowerLimit; }
btScalar getUpperLinLimit() { return m_upperLinLimit; }
void setUpperLinLimit(btScalar upperLimit) { m_upperLinLimit = upperLimit; }
btScalar getLowerAngLimit() { return m_lowerAngLimit; }
void setLowerAngLimit(btScalar lowerLimit) { m_lowerAngLimit = lowerLimit; }
btScalar getUpperAngLimit() { return m_upperAngLimit; }
void setUpperAngLimit(btScalar upperLimit) { m_upperAngLimit = upperLimit; }
bool getUseLinearReferenceFrameA() { return m_useLinearReferenceFrameA; }
btScalar getSoftnessDirLin() { return m_softnessDirLin; }
btScalar getRestitutionDirLin() { return m_restitutionDirLin; }
btScalar getDampingDirLin() { return m_dampingDirLin ; }
btScalar getSoftnessDirAng() { return m_softnessDirAng; }
btScalar getRestitutionDirAng() { return m_restitutionDirAng; }
btScalar getDampingDirAng() { return m_dampingDirAng; }
btScalar getSoftnessLimLin() { return m_softnessLimLin; }
btScalar getRestitutionLimLin() { return m_restitutionLimLin; }
btScalar getDampingLimLin() { return m_dampingLimLin; }
btScalar getSoftnessLimAng() { return m_softnessLimAng; }
btScalar getRestitutionLimAng() { return m_restitutionLimAng; }
btScalar getDampingLimAng() { return m_dampingLimAng; }
btScalar getSoftnessOrthoLin() { return m_softnessOrthoLin; }
btScalar getRestitutionOrthoLin() { return m_restitutionOrthoLin; }
btScalar getDampingOrthoLin() { return m_dampingOrthoLin; }
btScalar getSoftnessOrthoAng() { return m_softnessOrthoAng; }
btScalar getRestitutionOrthoAng() { return m_restitutionOrthoAng; }
btScalar getDampingOrthoAng() { return m_dampingOrthoAng; }
void setSoftnessDirLin(btScalar softnessDirLin) { m_softnessDirLin = softnessDirLin; }
void setRestitutionDirLin(btScalar restitutionDirLin) { m_restitutionDirLin = restitutionDirLin; }
void setDampingDirLin(btScalar dampingDirLin) { m_dampingDirLin = dampingDirLin; }
void setSoftnessDirAng(btScalar softnessDirAng) { m_softnessDirAng = softnessDirAng; }
void setRestitutionDirAng(btScalar restitutionDirAng) { m_restitutionDirAng = restitutionDirAng; }
void setDampingDirAng(btScalar dampingDirAng) { m_dampingDirAng = dampingDirAng; }
void setSoftnessLimLin(btScalar softnessLimLin) { m_softnessLimLin = softnessLimLin; }
void setRestitutionLimLin(btScalar restitutionLimLin) { m_restitutionLimLin = restitutionLimLin; }
void setDampingLimLin(btScalar dampingLimLin) { m_dampingLimLin = dampingLimLin; }
void setSoftnessLimAng(btScalar softnessLimAng) { m_softnessLimAng = softnessLimAng; }
void setRestitutionLimAng(btScalar restitutionLimAng) { m_restitutionLimAng = restitutionLimAng; }
void setDampingLimAng(btScalar dampingLimAng) { m_dampingLimAng = dampingLimAng; }
void setSoftnessOrthoLin(btScalar softnessOrthoLin) { m_softnessOrthoLin = softnessOrthoLin; }
void setRestitutionOrthoLin(btScalar restitutionOrthoLin) { m_restitutionOrthoLin = restitutionOrthoLin; }
void setDampingOrthoLin(btScalar dampingOrthoLin) { m_dampingOrthoLin = dampingOrthoLin; }
void setSoftnessOrthoAng(btScalar softnessOrthoAng) { m_softnessOrthoAng = softnessOrthoAng; }
void setRestitutionOrthoAng(btScalar restitutionOrthoAng) { m_restitutionOrthoAng = restitutionOrthoAng; }
void setDampingOrthoAng(btScalar dampingOrthoAng) { m_dampingOrthoAng = dampingOrthoAng; }
void setPoweredLinMotor(bool onOff) { m_poweredLinMotor = onOff; }
bool getPoweredLinMotor() { return m_poweredLinMotor; }
void setTargetLinMotorVelocity(btScalar targetLinMotorVelocity) { m_targetLinMotorVelocity = targetLinMotorVelocity; }
btScalar getTargetLinMotorVelocity() { return m_targetLinMotorVelocity; }
void setMaxLinMotorForce(btScalar maxLinMotorForce) { m_maxLinMotorForce = maxLinMotorForce; }
btScalar getMaxLinMotorForce() { return m_maxLinMotorForce; }
void setPoweredAngMotor(bool onOff) { m_poweredAngMotor = onOff; }
bool getPoweredAngMotor() { return m_poweredAngMotor; }
void setTargetAngMotorVelocity(btScalar targetAngMotorVelocity) { m_targetAngMotorVelocity = targetAngMotorVelocity; }
btScalar getTargetAngMotorVelocity() { return m_targetAngMotorVelocity; }
void setMaxAngMotorForce(btScalar maxAngMotorForce) { m_maxAngMotorForce = maxAngMotorForce; }
btScalar getMaxAngMotorForce() { return m_maxAngMotorForce; }
btScalar getLinearPos() { return m_linPos; }
// access for ODE solver
bool getSolveLinLimit() { return m_solveLinLim; }
btScalar getLinDepth() { return m_depth[0]; }
bool getSolveAngLimit() { return m_solveAngLim; }
btScalar getAngDepth() { return m_angDepth; }
// internal
void buildJacobianInt(btRigidBody& rbA, btRigidBody& rbB, const btTransform& frameInA, const btTransform& frameInB);
void solveConstraintInt(btRigidBody& rbA, btRigidBody& rbB);
// shared code used by ODE solver
void calculateTransforms(void);
void testLinLimits(void);
void testAngLimits(void);
// access for PE Solver
btVector3 getAncorInA(void);
btVector3 getAncorInB(void);
};
//-----------------------------------------------------------------------------
#endif //SLIDER_CONSTRAINT_H

View File

@@ -0,0 +1,193 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2007 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 "btContinuousDynamicsWorld.h"
#include "LinearMath/btQuickprof.h"
//collision detection
#include "BulletCollision/CollisionDispatch/btCollisionDispatcher.h"
#include "BulletCollision/BroadphaseCollision/btSimpleBroadphase.h"
#include "BulletCollision/CollisionShapes/btCollisionShape.h"
#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h"
//rigidbody & constraints
#include "BulletDynamics/Dynamics/btRigidBody.h"
#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h"
#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h"
#include "BulletDynamics/ConstraintSolver/btTypedConstraint.h"
#include <stdio.h>
btContinuousDynamicsWorld::btContinuousDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration)
:btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration)
{
}
btContinuousDynamicsWorld::~btContinuousDynamicsWorld()
{
}
void btContinuousDynamicsWorld::internalSingleStepSimulation( btScalar timeStep)
{
startProfiling(timeStep);
///update aabbs information
updateAabbs();
//static int frame=0;
// printf("frame %d\n",frame++);
///apply gravity, predict motion
predictUnconstraintMotion(timeStep);
btDispatcherInfo& dispatchInfo = getDispatchInfo();
dispatchInfo.m_timeStep = timeStep;
dispatchInfo.m_stepCount = 0;
dispatchInfo.m_debugDraw = getDebugDrawer();
///perform collision detection
performDiscreteCollisionDetection();
calculateSimulationIslands();
getSolverInfo().m_timeStep = timeStep;
///solve contact and other joint constraints
solveConstraints(getSolverInfo());
///CallbackTriggers();
calculateTimeOfImpacts(timeStep);
btScalar toi = dispatchInfo.m_timeOfImpact;
// if (toi < 1.f)
// printf("toi = %f\n",toi);
if (toi < 0.f)
printf("toi = %f\n",toi);
///integrate transforms
integrateTransforms(timeStep * toi);
///update vehicle simulation
updateVehicles(timeStep);
updateActivationState( timeStep );
if(0 != m_internalTickCallback) {
(*m_internalTickCallback)(this, timeStep);
}
}
void btContinuousDynamicsWorld::calculateTimeOfImpacts(btScalar timeStep)
{
///these should be 'temporal' aabbs!
updateTemporalAabbs(timeStep);
///'toi' is the global smallest time of impact. However, we just calculate the time of impact for each object individually.
///so we handle the case moving versus static properly, and we cheat for moving versus moving
btScalar toi = 1.f;
btDispatcherInfo& dispatchInfo = getDispatchInfo();
dispatchInfo.m_timeStep = timeStep;
dispatchInfo.m_timeOfImpact = 1.f;
dispatchInfo.m_stepCount = 0;
dispatchInfo.m_dispatchFunc = btDispatcherInfo::DISPATCH_CONTINUOUS;
///calculate time of impact for overlapping pairs
btDispatcher* dispatcher = getDispatcher();
if (dispatcher)
dispatcher->dispatchAllCollisionPairs(m_broadphasePairCache->getOverlappingPairCache(),dispatchInfo,m_dispatcher1);
toi = dispatchInfo.m_timeOfImpact;
dispatchInfo.m_dispatchFunc = btDispatcherInfo::DISPATCH_DISCRETE;
}
void btContinuousDynamicsWorld::updateTemporalAabbs(btScalar timeStep)
{
btVector3 temporalAabbMin,temporalAabbMax;
for ( int i=0;i<m_collisionObjects.size();i++)
{
btCollisionObject* colObj = m_collisionObjects[i];
btRigidBody* body = btRigidBody::upcast(colObj);
if (body)
{
body->getCollisionShape()->getAabb(m_collisionObjects[i]->getWorldTransform(),temporalAabbMin,temporalAabbMax);
const btVector3& linvel = body->getLinearVelocity();
//make the AABB temporal
btScalar temporalAabbMaxx = temporalAabbMax.getX();
btScalar temporalAabbMaxy = temporalAabbMax.getY();
btScalar temporalAabbMaxz = temporalAabbMax.getZ();
btScalar temporalAabbMinx = temporalAabbMin.getX();
btScalar temporalAabbMiny = temporalAabbMin.getY();
btScalar temporalAabbMinz = temporalAabbMin.getZ();
// add linear motion
btVector3 linMotion = linvel*timeStep;
if (linMotion.x() > 0.f)
temporalAabbMaxx += linMotion.x();
else
temporalAabbMinx += linMotion.x();
if (linMotion.y() > 0.f)
temporalAabbMaxy += linMotion.y();
else
temporalAabbMiny += linMotion.y();
if (linMotion.z() > 0.f)
temporalAabbMaxz += linMotion.z();
else
temporalAabbMinz += linMotion.z();
//add conservative angular motion
btScalar angularMotion(0);// = angvel.length() * GetAngularMotionDisc() * timeStep;
btVector3 angularMotion3d(angularMotion,angularMotion,angularMotion);
temporalAabbMin = btVector3(temporalAabbMinx,temporalAabbMiny,temporalAabbMinz);
temporalAabbMax = btVector3(temporalAabbMaxx,temporalAabbMaxy,temporalAabbMaxz);
temporalAabbMin -= angularMotion3d;
temporalAabbMax += angularMotion3d;
m_broadphasePairCache->setAabb(body->getBroadphaseHandle(),temporalAabbMin,temporalAabbMax,m_dispatcher1);
}
}
//update aabb (of all moved objects)
m_broadphasePairCache->calculateOverlappingPairs(m_dispatcher1);
}

View File

@@ -0,0 +1,46 @@
/*
Bullet Continuous Collision Detection and Physics Library
Copyright (c) 2003-2007 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 BT_CONTINUOUS_DYNAMICS_WORLD_H
#define BT_CONTINUOUS_DYNAMICS_WORLD_H
#include "btDiscreteDynamicsWorld.h"
///btContinuousDynamicsWorld adds optional (per object) continuous collision detection for fast moving objects to the btDiscreteDynamicsWorld.
///This copes with fast moving objects that otherwise would tunnel/miss collisions.
///Under construction, don't use yet! Please use btDiscreteDynamicsWorld instead.
class btContinuousDynamicsWorld : public btDiscreteDynamicsWorld
{
void updateTemporalAabbs(btScalar timeStep);
public:
btContinuousDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration);
virtual ~btContinuousDynamicsWorld();
///time stepping with calculation of time of impact for selected fast moving objects
virtual void internalSingleStepSimulation( btScalar timeStep);
virtual void calculateTimeOfImpacts(btScalar timeStep);
virtual btDynamicsWorldType getWorldType() const
{
return BT_CONTINUOUS_DYNAMICS_WORLD;
}
};
#endif //BT_CONTINUOUS_DYNAMICS_WORLD_H

View File

@@ -0,0 +1,1174 @@
/*
Stan Melax Convex Hull Computation
Copyright (c) 2003-2006 Stan Melax http://www.melax.com/
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 <string.h>
#include "btConvexHull.h"
#include "LinearMath/btAlignedObjectArray.h"
#include "LinearMath/btMinMax.h"
#include "LinearMath/btVector3.h"
template <class T>
void Swap(T &a,T &b)
{
T tmp = a;
a=b;
b=tmp;
}
//----------------------------------
class int3
{
public:
int x,y,z;
int3(){};
int3(int _x,int _y, int _z){x=_x;y=_y;z=_z;}
const int& operator[](int i) const {return (&x)[i];}
int& operator[](int i) {return (&x)[i];}
};
//------- btPlane ----------
inline btPlane PlaneFlip(const btPlane &plane){return btPlane(-plane.normal,-plane.dist);}
inline int operator==( const btPlane &a, const btPlane &b ) { return (a.normal==b.normal && a.dist==b.dist); }
inline int coplanar( const btPlane &a, const btPlane &b ) { return (a==b || a==PlaneFlip(b)); }
//--------- Utility Functions ------
btVector3 PlaneLineIntersection(const btPlane &plane, const btVector3 &p0, const btVector3 &p1);
btVector3 PlaneProject(const btPlane &plane, const btVector3 &point);
btVector3 ThreePlaneIntersection(const btPlane &p0,const btPlane &p1, const btPlane &p2);
btVector3 ThreePlaneIntersection(const btPlane &p0,const btPlane &p1, const btPlane &p2)
{
btVector3 N1 = p0.normal;
btVector3 N2 = p1.normal;
btVector3 N3 = p2.normal;
btVector3 n2n3; n2n3 = N2.cross(N3);
btVector3 n3n1; n3n1 = N3.cross(N1);
btVector3 n1n2; n1n2 = N1.cross(N2);
btScalar quotient = (N1.dot(n2n3));
btAssert(btFabs(quotient) > btScalar(0.000001));
quotient = btScalar(-1.) / quotient;
n2n3 *= p0.dist;
n3n1 *= p1.dist;
n1n2 *= p2.dist;
btVector3 potentialVertex = n2n3;
potentialVertex += n3n1;
potentialVertex += n1n2;
potentialVertex *= quotient;
btVector3 result(potentialVertex.getX(),potentialVertex.getY(),potentialVertex.getZ());
return result;
}
btScalar DistanceBetweenLines(const btVector3 &ustart, const btVector3 &udir, const btVector3 &vstart, const btVector3 &vdir, btVector3 *upoint=NULL, btVector3 *vpoint=NULL);
btVector3 TriNormal(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2);
btVector3 NormalOf(const btVector3 *vert, const int n);
btVector3 PlaneLineIntersection(const btPlane &plane, const btVector3 &p0, const btVector3 &p1)
{
// returns the point where the line p0-p1 intersects the plane n&d
static btVector3 dif;
dif = p1-p0;
btScalar dn= dot(plane.normal,dif);
btScalar t = -(plane.dist+dot(plane.normal,p0) )/dn;
return p0 + (dif*t);
}
btVector3 PlaneProject(const btPlane &plane, const btVector3 &point)
{
return point - plane.normal * (dot(point,plane.normal)+plane.dist);
}
btVector3 TriNormal(const btVector3 &v0, const btVector3 &v1, const btVector3 &v2)
{
// return the normal of the triangle
// inscribed by v0, v1, and v2
btVector3 cp=cross(v1-v0,v2-v1);
btScalar m=cp.length();
if(m==0) return btVector3(1,0,0);
return cp*(btScalar(1.0)/m);
}
btScalar DistanceBetweenLines(const btVector3 &ustart, const btVector3 &udir, const btVector3 &vstart, const btVector3 &vdir, btVector3 *upoint, btVector3 *vpoint)
{
static btVector3 cp;
cp = cross(udir,vdir).normalized();
btScalar distu = -dot(cp,ustart);
btScalar distv = -dot(cp,vstart);
btScalar dist = (btScalar)fabs(distu-distv);
if(upoint)
{
btPlane plane;
plane.normal = cross(vdir,cp).normalized();
plane.dist = -dot(plane.normal,vstart);
*upoint = PlaneLineIntersection(plane,ustart,ustart+udir);
}
if(vpoint)
{
btPlane plane;
plane.normal = cross(udir,cp).normalized();
plane.dist = -dot(plane.normal,ustart);
*vpoint = PlaneLineIntersection(plane,vstart,vstart+vdir);
}
return dist;
}
#define COPLANAR (0)
#define UNDER (1)
#define OVER (2)
#define SPLIT (OVER|UNDER)
#define PAPERWIDTH (btScalar(0.001))
btScalar planetestepsilon = PAPERWIDTH;
typedef ConvexH::HalfEdge HalfEdge;
ConvexH::ConvexH(int vertices_size,int edges_size,int facets_size)
{
vertices.resize(vertices_size);
edges.resize(edges_size);
facets.resize(facets_size);
}
int PlaneTest(const btPlane &p, const btVector3 &v);
int PlaneTest(const btPlane &p, const btVector3 &v) {
btScalar a = dot(v,p.normal)+p.dist;
int flag = (a>planetestepsilon)?OVER:((a<-planetestepsilon)?UNDER:COPLANAR);
return flag;
}
int SplitTest(ConvexH &convex,const btPlane &plane);
int SplitTest(ConvexH &convex,const btPlane &plane) {
int flag=0;
for(int i=0;i<convex.vertices.size();i++) {
flag |= PlaneTest(plane,convex.vertices[i]);
}
return flag;
}
class VertFlag
{
public:
unsigned char planetest;
unsigned char junk;
unsigned char undermap;
unsigned char overmap;
};
class EdgeFlag
{
public:
unsigned char planetest;
unsigned char fixes;
short undermap;
short overmap;
};
class PlaneFlag
{
public:
unsigned char undermap;
unsigned char overmap;
};
class Coplanar{
public:
unsigned short ea;
unsigned char v0;
unsigned char v1;
};
template<class T>
int maxdirfiltered(const T *p,int count,const T &dir,btAlignedObjectArray<int> &allow)
{
btAssert(count);
int m=-1;
for(int i=0;i<count;i++)
if(allow[i])
{
if(m==-1 || dot(p[i],dir)>dot(p[m],dir))
m=i;
}
btAssert(m!=-1);
return m;
}
btVector3 orth(const btVector3 &v);
btVector3 orth(const btVector3 &v)
{
btVector3 a=cross(v,btVector3(0,0,1));
btVector3 b=cross(v,btVector3(0,1,0));
if (a.length() > b.length())
{
return a.normalized();
} else {
return b.normalized();
}
}
template<class T>
int maxdirsterid(const T *p,int count,const T &dir,btAlignedObjectArray<int> &allow)
{
int m=-1;
while(m==-1)
{
m = maxdirfiltered(p,count,dir,allow);
if(allow[m]==3) return m;
T u = orth(dir);
T v = cross(u,dir);
int ma=-1;
for(btScalar x = btScalar(0.0) ; x<= btScalar(360.0) ; x+= btScalar(45.0))
{
btScalar s = sinf(SIMD_RADS_PER_DEG*(x));
btScalar c = cosf(SIMD_RADS_PER_DEG*(x));
int mb = maxdirfiltered(p,count,dir+(u*s+v*c)*btScalar(0.025),allow);
if(ma==m && mb==m)
{
allow[m]=3;
return m;
}
if(ma!=-1 && ma!=mb) // Yuck - this is really ugly
{
int mc = ma;
for(btScalar xx = x-btScalar(40.0) ; xx <= x ; xx+= btScalar(5.0))
{
btScalar s = sinf(SIMD_RADS_PER_DEG*(xx));
btScalar c = cosf(SIMD_RADS_PER_DEG*(xx));
int md = maxdirfiltered(p,count,dir+(u*s+v*c)*btScalar(0.025),allow);
if(mc==m && md==m)
{
allow[m]=3;
return m;
}
mc=md;
}
}
ma=mb;
}
allow[m]=0;
m=-1;
}
btAssert(0);
return m;
}
int operator ==(const int3 &a,const int3 &b);
int operator ==(const int3 &a,const int3 &b)
{
for(int i=0;i<3;i++)
{
if(a[i]!=b[i]) return 0;
}
return 1;
}
int above(btVector3* vertices,const int3& t, const btVector3 &p, btScalar epsilon);
int above(btVector3* vertices,const int3& t, const btVector3 &p, btScalar epsilon)
{
btVector3 n=TriNormal(vertices[t[0]],vertices[t[1]],vertices[t[2]]);
return (dot(n,p-vertices[t[0]]) > epsilon); // EPSILON???
}
int hasedge(const int3 &t, int a,int b);
int hasedge(const int3 &t, int a,int b)
{
for(int i=0;i<3;i++)
{
int i1= (i+1)%3;
if(t[i]==a && t[i1]==b) return 1;
}
return 0;
}
int hasvert(const int3 &t, int v);
int hasvert(const int3 &t, int v)
{
return (t[0]==v || t[1]==v || t[2]==v) ;
}
int shareedge(const int3 &a,const int3 &b);
int shareedge(const int3 &a,const int3 &b)
{
int i;
for(i=0;i<3;i++)
{
int i1= (i+1)%3;
if(hasedge(a,b[i1],b[i])) return 1;
}
return 0;
}
class Tri;
class Tri : public int3
{
public:
int3 n;
int id;
int vmax;
btScalar rise;
Tri(int a,int b,int c):int3(a,b,c),n(-1,-1,-1)
{
vmax=-1;
rise = btScalar(0.0);
}
~Tri()
{
}
int &neib(int a,int b);
};
int &Tri::neib(int a,int b)
{
static int er=-1;
int i;
for(i=0;i<3;i++)
{
int i1=(i+1)%3;
int i2=(i+2)%3;
if((*this)[i]==a && (*this)[i1]==b) return n[i2];
if((*this)[i]==b && (*this)[i1]==a) return n[i2];
}
btAssert(0);
return er;
}
void HullLibrary::b2bfix(Tri* s,Tri*t)
{
int i;
for(i=0;i<3;i++)
{
int i1=(i+1)%3;
int i2=(i+2)%3;
int a = (*s)[i1];
int b = (*s)[i2];
btAssert(m_tris[s->neib(a,b)]->neib(b,a) == s->id);
btAssert(m_tris[t->neib(a,b)]->neib(b,a) == t->id);
m_tris[s->neib(a,b)]->neib(b,a) = t->neib(b,a);
m_tris[t->neib(b,a)]->neib(a,b) = s->neib(a,b);
}
}
void HullLibrary::removeb2b(Tri* s,Tri*t)
{
b2bfix(s,t);
deAllocateTriangle(s);
deAllocateTriangle(t);
}
void HullLibrary::checkit(Tri *t)
{
(void)t;
int i;
btAssert(m_tris[t->id]==t);
for(i=0;i<3;i++)
{
int i1=(i+1)%3;
int i2=(i+2)%3;
int a = (*t)[i1];
int b = (*t)[i2];
// release compile fix
(void)i1;
(void)i2;
(void)a;
(void)b;
btAssert(a!=b);
btAssert( m_tris[t->n[i]]->neib(b,a) == t->id);
}
}
Tri* HullLibrary::allocateTriangle(int a,int b,int c)
{
void* mem = btAlignedAlloc(sizeof(Tri),16);
Tri* tr = new (mem)Tri(a,b,c);
tr->id = m_tris.size();
m_tris.push_back(tr);
return tr;
}
void HullLibrary::deAllocateTriangle(Tri* tri)
{
btAssert(m_tris[tri->id]==tri);
m_tris[tri->id]=NULL;
tri->~Tri();
btAlignedFree(tri);
}
void HullLibrary::extrude(Tri *t0,int v)
{
int3 t= *t0;
int n = m_tris.size();
Tri* ta = allocateTriangle(v,t[1],t[2]);
ta->n = int3(t0->n[0],n+1,n+2);
m_tris[t0->n[0]]->neib(t[1],t[2]) = n+0;
Tri* tb = allocateTriangle(v,t[2],t[0]);
tb->n = int3(t0->n[1],n+2,n+0);
m_tris[t0->n[1]]->neib(t[2],t[0]) = n+1;
Tri* tc = allocateTriangle(v,t[0],t[1]);
tc->n = int3(t0->n[2],n+0,n+1);
m_tris[t0->n[2]]->neib(t[0],t[1]) = n+2;
checkit(ta);
checkit(tb);
checkit(tc);
if(hasvert(*m_tris[ta->n[0]],v)) removeb2b(ta,m_tris[ta->n[0]]);
if(hasvert(*m_tris[tb->n[0]],v)) removeb2b(tb,m_tris[tb->n[0]]);
if(hasvert(*m_tris[tc->n[0]],v)) removeb2b(tc,m_tris[tc->n[0]]);
deAllocateTriangle(t0);
}
Tri* HullLibrary::extrudable(btScalar epsilon)
{
int i;
Tri *t=NULL;
for(i=0;i<m_tris.size();i++)
{
if(!t || (m_tris[i] && t->rise<m_tris[i]->rise))
{
t = m_tris[i];
}
}
return (t->rise >epsilon)?t:NULL ;
}
int4 HullLibrary::FindSimplex(btVector3 *verts,int verts_count,btAlignedObjectArray<int> &allow)
{
btVector3 basis[3];
basis[0] = btVector3( btScalar(0.01), btScalar(0.02), btScalar(1.0) );
int p0 = maxdirsterid(verts,verts_count, basis[0],allow);
int p1 = maxdirsterid(verts,verts_count,-basis[0],allow);
basis[0] = verts[p0]-verts[p1];
if(p0==p1 || basis[0]==btVector3(0,0,0))
return int4(-1,-1,-1,-1);
basis[1] = cross(btVector3( btScalar(1),btScalar(0.02), btScalar(0)),basis[0]);
basis[2] = cross(btVector3(btScalar(-0.02), btScalar(1), btScalar(0)),basis[0]);
if (basis[1].length() > basis[2].length())
{
basis[1].normalize();
} else {
basis[1] = basis[2];
basis[1].normalize ();
}
int p2 = maxdirsterid(verts,verts_count,basis[1],allow);
if(p2 == p0 || p2 == p1)
{
p2 = maxdirsterid(verts,verts_count,-basis[1],allow);
}
if(p2 == p0 || p2 == p1)
return int4(-1,-1,-1,-1);
basis[1] = verts[p2] - verts[p0];
basis[2] = cross(basis[1],basis[0]).normalized();
int p3 = maxdirsterid(verts,verts_count,basis[2],allow);
if(p3==p0||p3==p1||p3==p2) p3 = maxdirsterid(verts,verts_count,-basis[2],allow);
if(p3==p0||p3==p1||p3==p2)
return int4(-1,-1,-1,-1);
btAssert(!(p0==p1||p0==p2||p0==p3||p1==p2||p1==p3||p2==p3));
if(dot(verts[p3]-verts[p0],cross(verts[p1]-verts[p0],verts[p2]-verts[p0])) <0) {Swap(p2,p3);}
return int4(p0,p1,p2,p3);
}
int HullLibrary::calchullgen(btVector3 *verts,int verts_count, int vlimit)
{
if(verts_count <4) return 0;
if(vlimit==0) vlimit=1000000000;
int j;
btVector3 bmin(*verts),bmax(*verts);
btAlignedObjectArray<int> isextreme;
isextreme.reserve(verts_count);
btAlignedObjectArray<int> allow;
allow.reserve(verts_count);
for(j=0;j<verts_count;j++)
{
allow.push_back(1);
isextreme.push_back(0);
bmin.setMin (verts[j]);
bmax.setMax (verts[j]);
}
btScalar epsilon = (bmax-bmin).length() * btScalar(0.001);
btAssert (epsilon != 0.0);
int4 p = FindSimplex(verts,verts_count,allow);
if(p.x==-1) return 0; // simplex failed
btVector3 center = (verts[p[0]]+verts[p[1]]+verts[p[2]]+verts[p[3]]) / btScalar(4.0); // a valid interior point
Tri *t0 = allocateTriangle(p[2],p[3],p[1]); t0->n=int3(2,3,1);
Tri *t1 = allocateTriangle(p[3],p[2],p[0]); t1->n=int3(3,2,0);
Tri *t2 = allocateTriangle(p[0],p[1],p[3]); t2->n=int3(0,1,3);
Tri *t3 = allocateTriangle(p[1],p[0],p[2]); t3->n=int3(1,0,2);
isextreme[p[0]]=isextreme[p[1]]=isextreme[p[2]]=isextreme[p[3]]=1;
checkit(t0);checkit(t1);checkit(t2);checkit(t3);
for(j=0;j<m_tris.size();j++)
{
Tri *t=m_tris[j];
btAssert(t);
btAssert(t->vmax<0);
btVector3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
t->vmax = maxdirsterid(verts,verts_count,n,allow);
t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]);
}
Tri *te;
vlimit-=4;
while(vlimit >0 && ((te=extrudable(epsilon)) != 0))
{
int3 ti=*te;
int v=te->vmax;
btAssert(v != -1);
btAssert(!isextreme[v]); // wtf we've already done this vertex
isextreme[v]=1;
//if(v==p0 || v==p1 || v==p2 || v==p3) continue; // done these already
j=m_tris.size();
while(j--) {
if(!m_tris[j]) continue;
int3 t=*m_tris[j];
if(above(verts,t,verts[v],btScalar(0.01)*epsilon))
{
extrude(m_tris[j],v);
}
}
// now check for those degenerate cases where we have a flipped triangle or a really skinny triangle
j=m_tris.size();
while(j--)
{
if(!m_tris[j]) continue;
if(!hasvert(*m_tris[j],v)) break;
int3 nt=*m_tris[j];
if(above(verts,nt,center,btScalar(0.01)*epsilon) || cross(verts[nt[1]]-verts[nt[0]],verts[nt[2]]-verts[nt[1]]).length()< epsilon*epsilon*btScalar(0.1) )
{
Tri *nb = m_tris[m_tris[j]->n[0]];
btAssert(nb);btAssert(!hasvert(*nb,v));btAssert(nb->id<j);
extrude(nb,v);
j=m_tris.size();
}
}
j=m_tris.size();
while(j--)
{
Tri *t=m_tris[j];
if(!t) continue;
if(t->vmax>=0) break;
btVector3 n=TriNormal(verts[(*t)[0]],verts[(*t)[1]],verts[(*t)[2]]);
t->vmax = maxdirsterid(verts,verts_count,n,allow);
if(isextreme[t->vmax])
{
t->vmax=-1; // already done that vertex - algorithm needs to be able to terminate.
}
else
{
t->rise = dot(n,verts[t->vmax]-verts[(*t)[0]]);
}
}
vlimit --;
}
return 1;
}
int HullLibrary::calchull(btVector3 *verts,int verts_count, TUIntArray& tris_out, int &tris_count,int vlimit)
{
int rc=calchullgen(verts,verts_count, vlimit) ;
if(!rc) return 0;
btAlignedObjectArray<int> ts;
int i;
for(i=0;i<m_tris.size();i++)
{
if(m_tris[i])
{
for(int j=0;j<3;j++)
ts.push_back((*m_tris[i])[j]);
deAllocateTriangle(m_tris[i]);
}
}
tris_count = ts.size()/3;
tris_out.resize(ts.size());
for (i=0;i<ts.size();i++)
{
tris_out[i] = static_cast<unsigned int>(ts[i]);
}
m_tris.resize(0);
return 1;
}
bool HullLibrary::ComputeHull(unsigned int vcount,const btVector3 *vertices,PHullResult &result,unsigned int vlimit)
{
int tris_count;
int ret = calchull( (btVector3 *) vertices, (int) vcount, result.m_Indices, tris_count, static_cast<int>(vlimit) );
if(!ret) return false;
result.mIndexCount = (unsigned int) (tris_count*3);
result.mFaceCount = (unsigned int) tris_count;
result.mVertices = (btVector3*) vertices;
result.mVcount = (unsigned int) vcount;
return true;
}
void ReleaseHull(PHullResult &result);
void ReleaseHull(PHullResult &result)
{
if ( result.m_Indices.size() )
{
result.m_Indices.clear();
}
result.mVcount = 0;
result.mIndexCount = 0;
result.mVertices = 0;
}
//*********************************************************************
//*********************************************************************
//******** HullLib header
//*********************************************************************
//*********************************************************************
//*********************************************************************
//*********************************************************************
//******** HullLib implementation
//*********************************************************************
//*********************************************************************
HullError HullLibrary::CreateConvexHull(const HullDesc &desc, // describes the input request
HullResult &result) // contains the resulst
{
HullError ret = QE_FAIL;
PHullResult hr;
unsigned int vcount = desc.mVcount;
if ( vcount < 8 ) vcount = 8;
btAlignedObjectArray<btVector3> vertexSource;
vertexSource.resize(static_cast<int>(vcount));
btVector3 scale;
unsigned int ovcount;
bool ok = CleanupVertices(desc.mVcount,desc.mVertices, desc.mVertexStride, ovcount, &vertexSource[0], desc.mNormalEpsilon, scale ); // normalize point cloud, remove duplicates!
if ( ok )
{
// if ( 1 ) // scale vertices back to their original size.
{
for (unsigned int i=0; i<ovcount; i++)
{
btVector3& v = vertexSource[static_cast<int>(i)];
v[0]*=scale[0];
v[1]*=scale[1];
v[2]*=scale[2];
}
}
ok = ComputeHull(ovcount,&vertexSource[0],hr,desc.mMaxVertices);
if ( ok )
{
// re-index triangle mesh so it refers to only used vertices, rebuild a new vertex table.
btAlignedObjectArray<btVector3> vertexScratch;
vertexScratch.resize(static_cast<int>(hr.mVcount));
BringOutYourDead(hr.mVertices,hr.mVcount, &vertexScratch[0], ovcount, &hr.m_Indices[0], hr.mIndexCount );
ret = QE_OK;
if ( desc.HasHullFlag(QF_TRIANGLES) ) // if he wants the results as triangle!
{
result.mPolygons = false;
result.mNumOutputVertices = ovcount;
result.m_OutputVertices.resize(static_cast<int>(ovcount));
result.mNumFaces = hr.mFaceCount;
result.mNumIndices = hr.mIndexCount;
result.m_Indices.resize(static_cast<int>(hr.mIndexCount));
memcpy(&result.m_OutputVertices[0], &vertexScratch[0], sizeof(btVector3)*ovcount );
if ( desc.HasHullFlag(QF_REVERSE_ORDER) )
{
const unsigned int *source = &hr.m_Indices[0];
unsigned int *dest = &result.m_Indices[0];
for (unsigned int i=0; i<hr.mFaceCount; i++)
{
dest[0] = source[2];
dest[1] = source[1];
dest[2] = source[0];
dest+=3;
source+=3;
}
}
else
{
memcpy(&result.m_Indices[0], &hr.m_Indices[0], sizeof(unsigned int)*hr.mIndexCount);
}
}
else
{
result.mPolygons = true;
result.mNumOutputVertices = ovcount;
result.m_OutputVertices.resize(static_cast<int>(ovcount));
result.mNumFaces = hr.mFaceCount;
result.mNumIndices = hr.mIndexCount+hr.mFaceCount;
result.m_Indices.resize(static_cast<int>(result.mNumIndices));
memcpy(&result.m_OutputVertices[0], &vertexScratch[0], sizeof(btVector3)*ovcount );
// if ( 1 )
{
const unsigned int *source = &hr.m_Indices[0];
unsigned int *dest = &result.m_Indices[0];
for (unsigned int i=0; i<hr.mFaceCount; i++)
{
dest[0] = 3;
if ( desc.HasHullFlag(QF_REVERSE_ORDER) )
{
dest[1] = source[2];
dest[2] = source[1];
dest[3] = source[0];
}
else
{
dest[1] = source[0];
dest[2] = source[1];
dest[3] = source[2];
}
dest+=4;
source+=3;
}
}
}
ReleaseHull(hr);
}
}
return ret;
}
HullError HullLibrary::ReleaseResult(HullResult &result) // release memory allocated for this result, we are done with it.
{
if ( result.m_OutputVertices.size())
{
result.mNumOutputVertices=0;
result.m_OutputVertices.clear();
}
if ( result.m_Indices.size() )
{
result.mNumIndices=0;
result.m_Indices.clear();
}
return QE_OK;
}
static void addPoint(unsigned int &vcount,btVector3 *p,btScalar x,btScalar y,btScalar z)
{
// XXX, might be broken
btVector3& dest = p[vcount];
dest[0] = x;
dest[1] = y;
dest[2] = z;
vcount++;
}
btScalar GetDist(btScalar px,btScalar py,btScalar pz,const btScalar *p2);
btScalar GetDist(btScalar px,btScalar py,btScalar pz,const btScalar *p2)
{
btScalar dx = px - p2[0];
btScalar dy = py - p2[1];
btScalar dz = pz - p2[2];
return dx*dx+dy*dy+dz*dz;
}
bool HullLibrary::CleanupVertices(unsigned int svcount,
const btVector3 *svertices,
unsigned int stride,
unsigned int &vcount, // output number of vertices
btVector3 *vertices, // location to store the results.
btScalar normalepsilon,
btVector3& scale)
{
if ( svcount == 0 ) return false;
m_vertexIndexMapping.resize(0);
#define EPSILON btScalar(0.000001) /* close enough to consider two btScalaring point numbers to be 'the same'. */
vcount = 0;
btScalar recip[3];
if ( scale )
{
scale[0] = 1;
scale[1] = 1;
scale[2] = 1;
}
btScalar bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
btScalar bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
const char *vtx = (const char *) svertices;
// if ( 1 )
{
for (unsigned int i=0; i<svcount; i++)
{
const btScalar *p = (const btScalar *) vtx;
vtx+=stride;
for (int j=0; j<3; j++)
{
if ( p[j] < bmin[j] ) bmin[j] = p[j];
if ( p[j] > bmax[j] ) bmax[j] = p[j];
}
}
}
btScalar dx = bmax[0] - bmin[0];
btScalar dy = bmax[1] - bmin[1];
btScalar dz = bmax[2] - bmin[2];
btVector3 center;
center[0] = dx*btScalar(0.5) + bmin[0];
center[1] = dy*btScalar(0.5) + bmin[1];
center[2] = dz*btScalar(0.5) + bmin[2];
if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || svcount < 3 )
{
btScalar len = FLT_MAX;
if ( dx > EPSILON && dx < len ) len = dx;
if ( dy > EPSILON && dy < len ) len = dy;
if ( dz > EPSILON && dz < len ) len = dz;
if ( len == FLT_MAX )
{
dx = dy = dz = btScalar(0.01); // one centimeter
}
else
{
if ( dx < EPSILON ) dx = len * btScalar(0.05); // 1/5th the shortest non-zero edge.
if ( dy < EPSILON ) dy = len * btScalar(0.05);
if ( dz < EPSILON ) dz = len * btScalar(0.05);
}
btScalar x1 = center[0] - dx;
btScalar x2 = center[0] + dx;
btScalar y1 = center[1] - dy;
btScalar y2 = center[1] + dy;
btScalar z1 = center[2] - dz;
btScalar z2 = center[2] + dz;
addPoint(vcount,vertices,x1,y1,z1);
addPoint(vcount,vertices,x2,y1,z1);
addPoint(vcount,vertices,x2,y2,z1);
addPoint(vcount,vertices,x1,y2,z1);
addPoint(vcount,vertices,x1,y1,z2);
addPoint(vcount,vertices,x2,y1,z2);
addPoint(vcount,vertices,x2,y2,z2);
addPoint(vcount,vertices,x1,y2,z2);
return true; // return cube
}
else
{
if ( scale )
{
scale[0] = dx;
scale[1] = dy;
scale[2] = dz;
recip[0] = 1 / dx;
recip[1] = 1 / dy;
recip[2] = 1 / dz;
center[0]*=recip[0];
center[1]*=recip[1];
center[2]*=recip[2];
}
}
vtx = (const char *) svertices;
for (unsigned int i=0; i<svcount; i++)
{
const btVector3 *p = (const btVector3 *)vtx;
vtx+=stride;
btScalar px = p->getX();
btScalar py = p->getY();
btScalar pz = p->getZ();
if ( scale )
{
px = px*recip[0]; // normalize
py = py*recip[1]; // normalize
pz = pz*recip[2]; // normalize
}
// if ( 1 )
{
unsigned int j;
for (j=0; j<vcount; j++)
{
/// XXX might be broken
btVector3& v = vertices[j];
btScalar x = v[0];
btScalar y = v[1];
btScalar z = v[2];
btScalar dx = fabsf(x - px );
btScalar dy = fabsf(y - py );
btScalar dz = fabsf(z - pz );
if ( dx < normalepsilon && dy < normalepsilon && dz < normalepsilon )
{
// ok, it is close enough to the old one
// now let us see if it is further from the center of the point cloud than the one we already recorded.
// in which case we keep this one instead.
btScalar dist1 = GetDist(px,py,pz,center);
btScalar dist2 = GetDist(v[0],v[1],v[2],center);
if ( dist1 > dist2 )
{
v[0] = px;
v[1] = py;
v[2] = pz;
}
break;
}
}
if ( j == vcount )
{
btVector3& dest = vertices[vcount];
dest[0] = px;
dest[1] = py;
dest[2] = pz;
vcount++;
}
m_vertexIndexMapping.push_back(j);
}
}
// ok..now make sure we didn't prune so many vertices it is now invalid.
// if ( 1 )
{
btScalar bmin[3] = { FLT_MAX, FLT_MAX, FLT_MAX };
btScalar bmax[3] = { -FLT_MAX, -FLT_MAX, -FLT_MAX };
for (unsigned int i=0; i<vcount; i++)
{
const btVector3& p = vertices[i];
for (int j=0; j<3; j++)
{
if ( p[j] < bmin[j] ) bmin[j] = p[j];
if ( p[j] > bmax[j] ) bmax[j] = p[j];
}
}
btScalar dx = bmax[0] - bmin[0];
btScalar dy = bmax[1] - bmin[1];
btScalar dz = bmax[2] - bmin[2];
if ( dx < EPSILON || dy < EPSILON || dz < EPSILON || vcount < 3)
{
btScalar cx = dx*btScalar(0.5) + bmin[0];
btScalar cy = dy*btScalar(0.5) + bmin[1];
btScalar cz = dz*btScalar(0.5) + bmin[2];
btScalar len = FLT_MAX;
if ( dx >= EPSILON && dx < len ) len = dx;
if ( dy >= EPSILON && dy < len ) len = dy;
if ( dz >= EPSILON && dz < len ) len = dz;
if ( len == FLT_MAX )
{
dx = dy = dz = btScalar(0.01); // one centimeter
}
else
{
if ( dx < EPSILON ) dx = len * btScalar(0.05); // 1/5th the shortest non-zero edge.
if ( dy < EPSILON ) dy = len * btScalar(0.05);
if ( dz < EPSILON ) dz = len * btScalar(0.05);
}
btScalar x1 = cx - dx;
btScalar x2 = cx + dx;
btScalar y1 = cy - dy;
btScalar y2 = cy + dy;
btScalar z1 = cz - dz;
btScalar z2 = cz + dz;
vcount = 0; // add box
addPoint(vcount,vertices,x1,y1,z1);
addPoint(vcount,vertices,x2,y1,z1);
addPoint(vcount,vertices,x2,y2,z1);
addPoint(vcount,vertices,x1,y2,z1);
addPoint(vcount,vertices,x1,y1,z2);
addPoint(vcount,vertices,x2,y1,z2);
addPoint(vcount,vertices,x2,y2,z2);
addPoint(vcount,vertices,x1,y2,z2);
return true;
}
}
return true;
}
void HullLibrary::BringOutYourDead(const btVector3* verts,unsigned int vcount, btVector3* overts,unsigned int &ocount,unsigned int *indices,unsigned indexcount)
{
btAlignedObjectArray<int>tmpIndices;
tmpIndices.resize(m_vertexIndexMapping.size());
int i;
for (i=0;i<m_vertexIndexMapping.size();i++)
{
tmpIndices[i] = m_vertexIndexMapping[i];
}
TUIntArray usedIndices;
usedIndices.resize(static_cast<int>(vcount));
memset(&usedIndices[0],0,sizeof(unsigned int)*vcount);
ocount = 0;
for (i=0; i<indexcount; i++)
{
unsigned int v = indices[i]; // original array index
btAssert( v >= 0 && v < vcount );
if ( usedIndices[static_cast<int>(v)] ) // if already remapped
{
indices[i] = usedIndices[static_cast<int>(v)]-1; // index to new array
}
else
{
indices[i] = ocount; // new index mapping
overts[ocount][0] = verts[v][0]; // copy old vert to new vert array
overts[ocount][1] = verts[v][1];
overts[ocount][2] = verts[v][2];
for (int k=0;k<m_vertexIndexMapping.size();k++)
{
if (tmpIndices[k]==v)
m_vertexIndexMapping[k]=ocount;
}
ocount++; // increment output vert count
btAssert( ocount >=0 && ocount <= vcount );
usedIndices[static_cast<int>(v)] = ocount; // assign new index remapping
}
}
}

View File

@@ -0,0 +1,245 @@
/*
Stan Melax Convex Hull Computation
Copyright (c) 2008 Stan Melax http://www.melax.com/
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.
*/
///includes modifications/improvements by John Ratcliff, see BringOutYourDead below.
#ifndef CD_HULL_H
#define CD_HULL_H
#include "LinearMath/btVector3.h"
#include "LinearMath/btAlignedObjectArray.h"
typedef btAlignedObjectArray<unsigned int> TUIntArray;
class HullResult
{
public:
HullResult(void)
{
mPolygons = true;
mNumOutputVertices = 0;
mNumFaces = 0;
mNumIndices = 0;
}
bool mPolygons; // true if indices represents polygons, false indices are triangles
unsigned int mNumOutputVertices; // number of vertices in the output hull
btAlignedObjectArray<btVector3> m_OutputVertices; // array of vertices
unsigned int mNumFaces; // the number of faces produced
unsigned int mNumIndices; // the total number of indices
btAlignedObjectArray<unsigned int> m_Indices; // pointer to indices.
// If triangles, then indices are array indexes into the vertex list.
// If polygons, indices are in the form (number of points in face) (p1, p2, p3, ..) etc..
};
enum HullFlag
{
QF_TRIANGLES = (1<<0), // report results as triangles, not polygons.
QF_REVERSE_ORDER = (1<<1), // reverse order of the triangle indices.
QF_DEFAULT = QF_TRIANGLES
};
class HullDesc
{
public:
HullDesc(void)
{
mFlags = QF_DEFAULT;
mVcount = 0;
mVertices = 0;
mVertexStride = sizeof(btVector3);
mNormalEpsilon = 0.001f;
mMaxVertices = 4096; // maximum number of points to be considered for a convex hull.
mMaxFaces = 4096;
};
HullDesc(HullFlag flag,
unsigned int vcount,
const btVector3 *vertices,
unsigned int stride = sizeof(btVector3))
{
mFlags = flag;
mVcount = vcount;
mVertices = vertices;
mVertexStride = stride;
mNormalEpsilon = btScalar(0.001);
mMaxVertices = 4096;
}
bool HasHullFlag(HullFlag flag) const
{
if ( mFlags & flag ) return true;
return false;
}
void SetHullFlag(HullFlag flag)
{
mFlags|=flag;
}
void ClearHullFlag(HullFlag flag)
{
mFlags&=~flag;
}
unsigned int mFlags; // flags to use when generating the convex hull.
unsigned int mVcount; // number of vertices in the input point cloud
const btVector3 *mVertices; // the array of vertices.
unsigned int mVertexStride; // the stride of each vertex, in bytes.
btScalar mNormalEpsilon; // the epsilon for removing duplicates. This is a normalized value, if normalized bit is on.
unsigned int mMaxVertices; // maximum number of vertices to be considered for the hull!
unsigned int mMaxFaces;
};
enum HullError
{
QE_OK, // success!
QE_FAIL // failed.
};
class btPlane
{
public:
btVector3 normal;
btScalar dist; // distance below origin - the D from plane equasion Ax+By+Cz+D=0
btPlane(const btVector3 &n,btScalar d):normal(n),dist(d){}
btPlane():normal(),dist(0){}
};
class ConvexH
{
public:
class HalfEdge
{
public:
short ea; // the other half of the edge (index into edges list)
unsigned char v; // the vertex at the start of this edge (index into vertices list)
unsigned char p; // the facet on which this edge lies (index into facets list)
HalfEdge(){}
HalfEdge(short _ea,unsigned char _v, unsigned char _p):ea(_ea),v(_v),p(_p){}
};
ConvexH()
{
int i;
i=0;
}
~ConvexH()
{
int i;
i=0;
}
btAlignedObjectArray<btVector3> vertices;
btAlignedObjectArray<HalfEdge> edges;
btAlignedObjectArray<btPlane> facets;
ConvexH(int vertices_size,int edges_size,int facets_size);
};
class int4
{
public:
int x,y,z,w;
int4(){};
int4(int _x,int _y, int _z,int _w){x=_x;y=_y;z=_z;w=_w;}
const int& operator[](int i) const {return (&x)[i];}
int& operator[](int i) {return (&x)[i];}
};
class PHullResult
{
public:
PHullResult(void)
{
mVcount = 0;
mIndexCount = 0;
mFaceCount = 0;
mVertices = 0;
}
unsigned int mVcount;
unsigned int mIndexCount;
unsigned int mFaceCount;
btVector3* mVertices;
TUIntArray m_Indices;
};
///The HullLibrary class can create a convex hull from a collection of vertices, using the ComputeHull method.
///The btShapeHull class uses this HullLibrary to create a approximate convex mesh given a general (non-polyhedral) convex shape.
class HullLibrary
{
btAlignedObjectArray<class Tri*> m_tris;
public:
btAlignedObjectArray<int> m_vertexIndexMapping;
HullError CreateConvexHull(const HullDesc& desc, // describes the input request
HullResult& result); // contains the resulst
HullError ReleaseResult(HullResult &result); // release memory allocated for this result, we are done with it.
private:
bool ComputeHull(unsigned int vcount,const btVector3 *vertices,PHullResult &result,unsigned int vlimit);
class Tri* allocateTriangle(int a,int b,int c);
void deAllocateTriangle(Tri*);
void b2bfix(Tri* s,Tri*t);
void removeb2b(Tri* s,Tri*t);
void checkit(Tri *t);
Tri* extrudable(btScalar epsilon);
int calchull(btVector3 *verts,int verts_count, TUIntArray& tris_out, int &tris_count,int vlimit);
int calchullgen(btVector3 *verts,int verts_count, int vlimit);
int4 FindSimplex(btVector3 *verts,int verts_count,btAlignedObjectArray<int> &allow);
class ConvexH* ConvexHCrop(ConvexH& convex,const btPlane& slice);
void extrude(class Tri* t0,int v);
ConvexH* test_cube();
//BringOutYourDead (John Ratcliff): When you create a convex hull you hand it a large input set of vertices forming a 'point cloud'.
//After the hull is generated it give you back a set of polygon faces which index the *original* point cloud.
//The thing is, often times, there are many 'dead vertices' in the point cloud that are on longer referenced by the hull.
//The routine 'BringOutYourDead' find only the referenced vertices, copies them to an new buffer, and re-indexes the hull so that it is a minimal representation.
void BringOutYourDead(const btVector3* verts,unsigned int vcount, btVector3* overts,unsigned int &ocount,unsigned int* indices,unsigned indexcount);
bool CleanupVertices(unsigned int svcount,
const btVector3* svertices,
unsigned int stride,
unsigned int &vcount, // output number of vertices
btVector3* vertices, // location to store the results.
btScalar normalepsilon,
btVector3& scale);
};
#endif

View File

@@ -0,0 +1,303 @@
#ifndef BT_HASH_MAP_H
#define BT_HASH_MAP_H
#include "btAlignedObjectArray.h"
const int BT_HASH_NULL=0xffffffff;
template <class Value>
class btHashKey
{
int m_uid;
public:
btHashKey(int uid)
:m_uid(uid)
{
}
int getUid() const
{
return m_uid;
}
//to our success
SIMD_FORCE_INLINE unsigned int getHash()const
{
int key = m_uid;
// Thomas Wang's hash
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
btHashKey getKey(const Value& value) const
{
return btHashKey(value.getUid());
}
};
template <class Value>
class btHashKeyPtr
{
int m_uid;
public:
btHashKeyPtr(int uid)
:m_uid(uid)
{
}
int getUid() const
{
return m_uid;
}
//to our success
SIMD_FORCE_INLINE unsigned int getHash()const
{
int key = m_uid;
// Thomas Wang's hash
key += ~(key << 15);
key ^= (key >> 10);
key += (key << 3);
key ^= (key >> 6);
key += ~(key << 11);
key ^= (key >> 16);
return key;
}
btHashKeyPtr getKey(const Value& value) const
{
return btHashKeyPtr(value->getUid());
}
};
///The btHashMap template class implements a generic and lightweight hashmap.
///A basic sample of how to use btHashMap is located in Demos\BasicDemo\main.cpp
template <class Key, class Value>
class btHashMap
{
btAlignedObjectArray<int> m_hashTable;
btAlignedObjectArray<int> m_next;
btAlignedObjectArray<Value> m_valueArray;
void growTables(const Key& key)
{
int newCapacity = m_valueArray.capacity();
if (m_hashTable.size() < newCapacity)
{
//grow hashtable and next table
int curHashtableSize = m_hashTable.size();
m_hashTable.resize(newCapacity);
m_next.resize(newCapacity);
int i;
for (i= 0; i < newCapacity; ++i)
{
m_hashTable[i] = BT_HASH_NULL;
}
for (i = 0; i < newCapacity; ++i)
{
m_next[i] = BT_HASH_NULL;
}
for(i=0;i<curHashtableSize;i++)
{
const Value& value = m_valueArray[i];
int hashValue = key.getKey(value).getHash() & (m_valueArray.capacity()-1); // New hash value with new mask
m_next[i] = m_hashTable[hashValue];
m_hashTable[hashValue] = i;
}
}
}
public:
void insert(const Key& key, const Value& value) {
int hash = key.getHash() & (m_valueArray.capacity()-1);
//don't add it if it is already there
if (find(key))
{
return;
}
int count = m_valueArray.size();
int oldCapacity = m_valueArray.capacity();
m_valueArray.push_back(value);
int newCapacity = m_valueArray.capacity();
if (oldCapacity < newCapacity)
{
growTables(key);
//hash with new capacity
hash = key.getHash() & (m_valueArray.capacity()-1);
}
m_next[count] = m_hashTable[hash];
m_hashTable[hash] = count;
}
void remove(const Key& key) {
int hash = key.getHash() & (m_valueArray.capacity()-1);
int pairIndex = findIndex(key);
if (pairIndex ==BT_HASH_NULL)
{
return;
}
// Remove the pair from the hash table.
int index = m_hashTable[hash];
btAssert(index != BT_HASH_NULL);
int previous = BT_HASH_NULL;
while (index != pairIndex)
{
previous = index;
index = m_next[index];
}
if (previous != BT_HASH_NULL)
{
btAssert(m_next[previous] == pairIndex);
m_next[previous] = m_next[pairIndex];
}
else
{
m_hashTable[hash] = m_next[pairIndex];
}
// We now move the last pair into spot of the
// pair being removed. We need to fix the hash
// table indices to support the move.
int lastPairIndex = m_valueArray.size() - 1;
// If the removed pair is the last pair, we are done.
if (lastPairIndex == pairIndex)
{
m_valueArray.pop_back();
return;
}
// Remove the last pair from the hash table.
const Value* lastValue = &m_valueArray[lastPairIndex];
int lastHash = key.getKey(*lastValue).getHash() & (m_valueArray.capacity()-1);
index = m_hashTable[lastHash];
btAssert(index != BT_HASH_NULL);
previous = BT_HASH_NULL;
while (index != lastPairIndex)
{
previous = index;
index = m_next[index];
}
if (previous != BT_HASH_NULL)
{
btAssert(m_next[previous] == lastPairIndex);
m_next[previous] = m_next[lastPairIndex];
}
else
{
m_hashTable[lastHash] = m_next[lastPairIndex];
}
// Copy the last pair into the remove pair's spot.
m_valueArray[pairIndex] = m_valueArray[lastPairIndex];
// Insert the last pair into the hash table
m_next[pairIndex] = m_hashTable[lastHash];
m_hashTable[lastHash] = pairIndex;
m_valueArray.pop_back();
}
int size() const
{
return m_valueArray.size();
}
const Value* getAtIndex(int index) const
{
btAssert(index < m_valueArray.size());
return &m_valueArray[index];
}
Value* getAtIndex(int index)
{
btAssert(index < m_valueArray.size());
return &m_valueArray[index];
}
Value* operator[](const Key& key) {
return find(key);
}
const Value* find(const Key& key) const
{
int index = findIndex(key);
if (index == BT_HASH_NULL)
{
return NULL;
}
return &m_valueArray[index];
}
Value* find(const Key& key)
{
int index = findIndex(key);
if (index == BT_HASH_NULL)
{
return NULL;
}
return &m_valueArray[index];
}
int findIndex(const Key& key) const
{
int hash = key.getHash() & (m_valueArray.capacity()-1);
if (hash >= m_hashTable.size())
{
return BT_HASH_NULL;
}
int index = m_hashTable[hash];
while ((index != BT_HASH_NULL) && (key.getUid() == key.getKey(m_valueArray[index]).getUid()) == false)
{
index = m_next[index];
}
return index;
}
void clear()
{
m_hashTable.clear();
m_next.clear();
m_valueArray.clear();
}
};
#endif //BT_HASH_MAP_H

View File

@@ -0,0 +1,102 @@
/*
Copyright (c) 2003-2006 Gino van den Bergen / 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 _BT_POOL_ALLOCATOR_H
#define _BT_POOL_ALLOCATOR_H
#include "btScalar.h"
#include "btAlignedAllocator.h"
///The btPoolAllocator class allows to efficiently allocate a large pool of objects, instead of dynamically allocating them separately.
class btPoolAllocator
{
int m_elemSize;
int m_maxElements;
int m_freeCount;
void* m_firstFree;
unsigned char* m_pool;
public:
btPoolAllocator(int elemSize, int maxElements)
:m_elemSize(elemSize),
m_maxElements(maxElements)
{
m_pool = (unsigned char*) btAlignedAlloc( static_cast<unsigned int>(m_elemSize*m_maxElements),16);
unsigned char* p = m_pool;
m_firstFree = p;
m_freeCount = m_maxElements;
int count = m_maxElements;
while (--count) {
*(void**)p = (p + m_elemSize);
p += m_elemSize;
}
*(void**)p = 0;
}
~btPoolAllocator()
{
btAlignedFree( m_pool);
}
int getFreeCount() const
{
return m_freeCount;
}
void* allocate(int size)
{
// release mode fix
(void)size;
btAssert(!size || size<=m_elemSize);
btAssert(m_freeCount>0);
void* result = m_firstFree;
m_firstFree = *(void**)m_firstFree;
--m_freeCount;
return result;
}
bool validPtr(void* ptr)
{
if (ptr) {
if (((unsigned char*)ptr >= m_pool && (unsigned char*)ptr < m_pool + m_maxElements * m_elemSize))
{
return true;
}
}
return false;
}
void freeMemory(void* ptr)
{
if (ptr) {
btAssert((unsigned char*)ptr >= m_pool && (unsigned char*)ptr < m_pool + m_maxElements * m_elemSize);
*(void**)ptr = m_firstFree;
m_firstFree = ptr;
++m_freeCount;
}
}
int getElementSize() const
{
return m_elemSize;
}
};
#endif //_BT_POOL_ALLOCATOR_H

View File

@@ -0,0 +1,1461 @@
// --------------------------------------------------------------------------
//
// El'Beem - the visual lattice boltzmann freesurface simulator
// All code distributed as part of El'Beem is covered by the version 2 of the
// GNU General Public License. See the file COPYING for details.
//
// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
//
// implementation of control particle handling
//
// --------------------------------------------------------------------------
// indicator for LBM inclusion
#include "ntl_geometrymodel.h"
#include "ntl_world.h"
#include "solver_class.h"
#include "controlparticles.h"
#include "mvmcoords.h"
#include <zlib.h>
#ifndef sqrtf
#define sqrtf sqrt
#endif
// brute force circle test init in initTimeArray
// replaced by mDebugInit
//#define CP_FORCECIRCLEINIT 0
void ControlParticles::initBlenderTest() {
mPartSets.clear();
ControlParticleSet cps;
mPartSets.push_back(cps);
int setCnt = mPartSets.size()-1;
ControlParticle p;
// set for time zero
mPartSets[setCnt].time = 0.;
// add single particle
p.reset();
p.pos = LbmVec(0.5, 0.5, -0.5);
mPartSets[setCnt].particles.push_back(p);
// add second set for animation
mPartSets.push_back(cps);
setCnt = mPartSets.size()-1;
mPartSets[setCnt].time = 0.15;
// insert new position
p.reset();
p.pos = LbmVec(-0.5, -0.5, 0.5);
mPartSets[setCnt].particles.push_back(p);
// applyTrafos();
initTime(0. , 1.);
}
// blender control object gets converted to mvm flui control object
int ControlParticles::initFromObject(ntlGeometryObjModel *model) {
vector<ntlTriangle> triangles;
vector<ntlVec3Gfx> vertices;
vector<ntlVec3Gfx> normals;
/*
model->loadBobjModel(string(infile));
model->setLoaded(true);
model->setGeoInitId(gid);
printf("a animated? %d\n", model->getIsAnimated());
printf("b animated? %d\n", model->getMeshAnimated());
*/
model->setGeoInitType(FGI_FLUID);
model->getTriangles(mCPSTimeStart, &triangles, &vertices, &normals, 1 );
// model->applyTransformation(mCPSTimeStart, &vertices, &normals, 0, vertices.size(), true);
// valid mesh?
if(triangles.size() <= 0) {
return 0;
}
ntlRenderGlobals *glob = new ntlRenderGlobals;
ntlScene *genscene = new ntlScene( glob, false );
genscene->addGeoClass(model);
genscene->addGeoObject(model);
genscene->buildScene(0., false);
char treeFlag = (1<<(4+model->getGeoInitId()));
ntlTree *tree = new ntlTree(
15, 8, // TREEwarning - fixed values for depth & maxtriangles here...
genscene, treeFlag );
// TODO? use params
ntlVec3Gfx start,end;
model->getExtends(start,end);
/*
printf("start - x: %f, y: %f, z: %f\n", start[0], start[1], start[2]);
printf("end - x: %f, y: %f, z: %f\n", end[0], end[1], end[2]);
printf("mCPSWidth: %f\n");
*/
LbmFloat width = mCPSWidth;
if(width<=LBM_EPSILON) { errMsg("ControlParticles::initFromMVMCMesh","Invalid mCPSWidth! "<<mCPSWidth); width=mCPSWidth=0.1; }
ntlVec3Gfx org = start+ntlVec3Gfx(width*0.5);
gfxReal distance = -1.;
vector<ntlVec3Gfx> inspos;
// printf("distance: %f, width: %f\n", distance, width);
while(org[2]<end[2]) {
while(org[1]<end[1]) {
while(org[0]<end[0]) {
if(checkPointInside(tree, org, distance)) {
inspos.push_back(org);
}
// TODO optimize, use distance
org[0] += width;
}
org[1] += width;
org[0] = start[0];
}
org[2] += width;
org[1] = start[1];
}
// printf("inspos.size(): %d\n", inspos.size());
MeanValueMeshCoords mvm;
mvm.calculateMVMCs(vertices,triangles, inspos, mCPSWeightFac);
vector<ntlVec3Gfx> ninspos;
mvm.transfer(vertices, ninspos);
// init first set, check dist
ControlParticleSet firstcps; //T
mPartSets.push_back(firstcps);
mPartSets[mPartSets.size()-1].time = mCPSTimeStart;
vector<bool> useCP;
for(int i=0; i<(int)inspos.size(); i++) {
ControlParticle p; p.reset();
p.pos = vec2L(inspos[i]);
bool usecpv = true;
mPartSets[mPartSets.size()-1].particles.push_back(p);
useCP.push_back(usecpv);
}
// init further sets, temporal mesh sampling
double tsampling = mCPSTimestep;
// printf("tsampling: %f, ninspos.size(): %d, mCPSTimeEnd: %f\n", tsampling, ninspos.size(), mCPSTimeEnd);
int tcnt=0;
for(double t=mCPSTimeStart+tsampling; ((t<mCPSTimeEnd) && (ninspos.size()>0.)); t+=tsampling) {
ControlParticleSet nextcps; //T
mPartSets.push_back(nextcps);
mPartSets[mPartSets.size()-1].time = (gfxReal)t;
vertices.clear(); triangles.clear(); normals.clear();
model->getTriangles(t, &triangles, &vertices, &normals, 1 );
mvm.transfer(vertices, ninspos);
tcnt++;
for(size_t i=0; i < ninspos.size(); i++) {
if(useCP[i]) {
ControlParticle p; p.reset();
p.pos = vec2L(ninspos[i]);
mPartSets[mPartSets.size()-1].particles.push_back(p);
}
}
}
model->setGeoInitType(FGI_CONTROL);
delete tree;
delete genscene;
delete glob;
// do reverse here
if(model->getGeoPartSlipValue())
{
mirrorTime();
}
return 1;
}
// init all zero / defaults for a single particle
void ControlParticle::reset() {
pos = LbmVec(0.,0.,0.);
vel = LbmVec(0.,0.,0.);
influence = 1.;
size = 1.;
#ifndef LBMDIM
#ifdef MAIN_2D
rotaxis = LbmVec(0.,1.,0.); // SPH xz
#else // MAIN_2D
// 3d - roate in xy plane, vortex
rotaxis = LbmVec(0.,0.,1.);
// 3d - rotate for wave
//rotaxis = LbmVec(0.,1.,0.);
#endif // MAIN_2D
#else // LBMDIM
rotaxis = LbmVec(0.,1.,0.); // LBM xy , is swapped afterwards
#endif // LBMDIM
density = 0.;
densityWeight = 0.;
avgVelAcc = avgVel = LbmVec(0.);
avgVelWeight = 0.;
}
// default preset/empty init
ControlParticles::ControlParticles() :
_influenceTangential(0.f),
_influenceAttraction(0.f),
_influenceVelocity(0.f),
_influenceMaxdist(0.f),
_radiusAtt(1.0f),
_radiusVel(1.0f),
_radiusMinMaxd(2.0f),
_radiusMaxd(3.0f),
_currTime(-1.0), _currTimestep(1.),
_initTimeScale(1.),
_initPartOffset(0.), _initPartScale(1.),
_initLastPartOffset(0.), _initLastPartScale(1.),
_initMirror(""),
_fluidSpacing(1.), _kernelWeight(-1.),
_charLength(1.), _charLengthInv(1.),
mvCPSStart(-10000.), mvCPSEnd(10000.),
mCPSWidth(0.1), mCPSTimestep(0.02), // was 0.05
mCPSTimeStart(0.), mCPSTimeEnd(0.5), mCPSWeightFac(1.),
mDebugInit(0)
{
_radiusAtt = 0.15f;
_radiusVel = 0.15f;
_radiusMinMaxd = 0.16f;
_radiusMaxd = 0.3;
_influenceAttraction = 0.f;
_influenceTangential = 0.f;
_influenceVelocity = 0.f;
// 3d tests */
}
ControlParticles::~ControlParticles() {
// nothing to do...
}
LbmFloat ControlParticles::getControlTimStart() {
if(mPartSets.size()>0) { return mPartSets[0].time; }
return -1000.;
}
LbmFloat ControlParticles::getControlTimEnd() {
if(mPartSets.size()>0) { return mPartSets[mPartSets.size()-1].time; }
return -1000.;
}
// calculate for delta t
void ControlParticles::setInfluenceVelocity(LbmFloat set, LbmFloat dt) {
const LbmFloat dtInter = 0.01;
LbmFloat facFv = 1.-set; //cparts->getInfluenceVelocity();
// mLevel[mMaxRefine].timestep
LbmFloat facNv = (LbmFloat)( 1.-pow( (double)facFv, (double)(dt/dtInter)) );
//errMsg("vwcalc","ts:"<<dt<< " its:"<<(dt/dtInter) <<" fv"<<facFv<<" nv"<<facNv<<" test:"<< pow( (double)(1.-facNv),(double)(dtInter/dt)) );
_influenceVelocity = facNv;
}
int ControlParticles::initExampleSet()
{
// unused
return 0;
}
int ControlParticles::getTotalSize()
{
int s=0;
for(int i=0; i<(int)mPartSets.size(); i++) {
s+= mPartSets[i].particles.size();
}
return s;
}
// --------------------------------------------------------------------------
// load positions & timing from text file
// WARNING - make sure file has unix format, no win/dos linefeeds...
#define LINE_LEN 100
int ControlParticles::initFromTextFile(string filename)
{
/*
const bool debugRead = false;
char line[LINE_LEN];
line[LINE_LEN-1] = '\0';
mPartSets.clear();
if(filename.size()<2) return 0;
// HACK , use "cparts" suffix as old
// e.g. "cpart2" as new
if(filename[ filename.size()-1 ]=='s') {
return initFromTextFileOld(filename);
}
FILE *infile = fopen(filename.c_str(), "r");
if(!infile) {
errMsg("ControlParticles::initFromTextFile","unable to open '"<<filename<<"' " );
// try to open as gz sequence
if(initFromBinaryFile(filename)) { return 1; }
// try mesh MVCM generation
if(initFromMVCMesh(filename)) { return 1; }
// failed...
return 0;
}
int haveNo = false;
int haveScale = false;
int haveTime = false;
int noParts = -1;
int partCnt = 0;
int setCnt = 0;
//ControlParticle p; p.reset();
// scale times by constant factor while reading
LbmFloat timeScale= 1.0;
int lineCnt = 0;
bool abortParse = false;
#define LASTCP mPartSets[setCnt].particles[ mPartSets[setCnt].particles.size()-1 ]
while( (!feof(infile)) && (!abortParse)) {
lineCnt++;
fgets(line, LINE_LEN, infile);
//if(debugRead) printf("\nDEBUG%d r '%s'\n",lineCnt, line);
if(!line) continue;
size_t len = strlen(line);
// skip empty lines and comments (#,//)
if(len<1) continue;
if( (line[0]=='#') || (line[0]=='\n') ) continue;
if((len>1) && (line[0]=='/' && line[1]=='/')) continue;
// debug remove newline
if((len>=1)&&(line[len-1]=='\n')) line[len-1]='\0';
switch(line[0]) {
case 'N': { // total number of particles, more for debugging...
noParts = atoi(line+2);
if(noParts<=0) {
errMsg("ControlParticles::initFromTextFile","file '"<<filename<<"' - invalid no of particles "<<noParts);
mPartSets.clear(); fclose(infile); return 0;
}
if(debugRead) printf("CPDEBUG%d no parts '%d'\n",lineCnt, noParts );
haveNo = true;
} break;
case 'T': { // global time scale
timeScale *= (LbmFloat)atof(line+2);
if(debugRead) printf("ControlParticles::initFromTextFile - line %d , set timescale '%f', org %f\n",lineCnt, timeScale , _initTimeScale);
if(timeScale==0.) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error: timescale = 0.! reseting to 1 ...\n",lineCnt); timeScale=1.; }
haveScale = true;
} break;
case 'I': { // influence settings, overrides others as of now...
float val = (LbmFloat)atof(line+3);
const char *setvar = "[invalid]";
switch(line[1]) {
//case 'f': { _influenceFalloff = val; setvar = "falloff"; } break;
case 't': { _influenceTangential = val; setvar = "tangential"; } break;
case 'a': { _influenceAttraction = val; setvar = "attraction"; } break;
case 'v': { _influenceVelocity = val; setvar = "velocity"; } break;
case 'm': { _influenceMaxdist = val; setvar = "maxdist"; } break;
default:
fprintf(stdout,"ControlParticles::initFromTextFile (%s) - line %d , invalid influence setting %c, %f\n",filename.c_str() ,lineCnt, line[1], val);
}
if(debugRead) printf("CPDEBUG%d set influence '%s'=%f \n",lineCnt, setvar, val);
} break;
case 'R': { // radius settings, overrides others as of now...
float val = (LbmFloat)atof(line+3);
const char *setvar = "[invalid]";
switch(line[1]) {
case 'a': { _radiusAtt = val; setvar = "r_attraction"; } break;
case 'v': { _radiusVel = val; setvar = "r_velocity"; } break;
case 'm': { _radiusMaxd = val; setvar = "r_maxdist"; } break;
default:
fprintf(stdout,"ControlParticles::initFromTextFile (%s) - line %d , invalid influence setting %c, %f\n",filename.c_str() ,lineCnt, line[1], val);
}
if(debugRead) printf("CPDEBUG%d set influence '%s'=%f \n",lineCnt, setvar, val);
} break;
case 'S': { // new particle set at time T
ControlParticleSet cps;
mPartSets.push_back(cps);
setCnt = (int)mPartSets.size()-1;
LbmFloat val = (LbmFloat)atof(line+2);
mPartSets[setCnt].time = val * timeScale;
if(debugRead) printf("CPDEBUG%d new set, time '%f', %d\n",lineCnt, mPartSets[setCnt].time, setCnt );
haveTime = true;
partCnt = -1;
} break;
case 'P': // new particle with pos
case 'n': { // new particle without pos
if((!haveTime)||(setCnt<0)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error: set missing!\n",lineCnt); abortParse=true; break; }
partCnt++;
if(partCnt>=noParts) {
if(debugRead) printf("CPDEBUG%d partset done \n",lineCnt);
haveTime = false;
} else {
ControlParticle p; p.reset();
mPartSets[setCnt].particles.push_back(p);
}
}
// only new part, or new with pos?
if(line[0] == 'n') break;
// particle properties
case 'p': { // new particle set at time T
if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|p: particle missing!\n",lineCnt); abortParse=true; break; }
float px=0.,py=0.,pz=0.;
if( sscanf(line+2,"%f %f %f",&px,&py,&pz) != 3) {
fprintf(stdout,"CPDEBUG%d, unable to parse position!\n",lineCnt); abortParse=true; break;
}
if(!(finite(px)&&finite(py)&&finite(pz))) { px=py=pz=0.; }
LASTCP.pos[0] = px;
LASTCP.pos[1] = py;
LASTCP.pos[2] = pz;
if(debugRead) printf("CPDEBUG%d part%d,%d: position %f,%f,%f \n",lineCnt,setCnt,partCnt, px,py,pz);
} break;
case 's': { // particle size
if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|s: particle missing!\n",lineCnt); abortParse=true; break; }
float ps=1.;
if( sscanf(line+2,"%f",&ps) != 1) {
fprintf(stdout,"CPDEBUG%d, unable to parse size!\n",lineCnt); abortParse=true; break;
}
if(!(finite(ps))) { ps=0.; }
LASTCP.size = ps;
if(debugRead) printf("CPDEBUG%d part%d,%d: size %f \n",lineCnt,setCnt,partCnt, ps);
} break;
case 'i': { // particle influence
if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|i: particle missing!\n",lineCnt); abortParse=true; break; }
float pinf=1.;
if( sscanf(line+2,"%f",&pinf) != 1) {
fprintf(stdout,"CPDEBUG%d, unable to parse size!\n",lineCnt); abortParse=true; break;
}
if(!(finite(pinf))) { pinf=0.; }
LASTCP.influence = pinf;
if(debugRead) printf("CPDEBUG%d part%d,%d: influence %f \n",lineCnt,setCnt,partCnt, pinf);
} break;
case 'a': { // rotation axis
if((!haveTime)||(setCnt<0)||(mPartSets[setCnt].particles.size()<1)) { fprintf(stdout,"ControlParticles::initFromTextFile - line %d ,error|a: particle missing!\n",lineCnt); abortParse=true; break; }
float px=0.,py=0.,pz=0.;
if( sscanf(line+2,"%f %f %f",&px,&py,&pz) != 3) {
fprintf(stdout,"CPDEBUG%d, unable to parse rotaxis!\n",lineCnt); abortParse=true; break;
}
if(!(finite(px)&&finite(py)&&finite(pz))) { px=py=pz=0.; }
LASTCP.rotaxis[0] = px;
LASTCP.rotaxis[1] = py;
LASTCP.rotaxis[2] = pz;
if(debugRead) printf("CPDEBUG%d part%d,%d: rotaxis %f,%f,%f \n",lineCnt,setCnt,partCnt, px,py,pz);
} break;
default:
if(debugRead) printf("CPDEBUG%d ignored: '%s'\n",lineCnt, line );
break;
}
}
if(debugRead && abortParse) printf("CPDEBUG aborted parsing after set... %d\n",(int)mPartSets.size() );
// sanity check
for(int i=0; i<(int)mPartSets.size(); i++) {
if( (int)mPartSets[i].particles.size()!=noParts) {
fprintf(stdout,"ControlParticles::initFromTextFile (%s) - invalid no of particles in set %d, is:%d, shouldbe:%d \n",filename.c_str() ,i,(int)mPartSets[i].particles.size(), noParts);
mPartSets.clear();
fclose(infile);
return 0;
}
}
// print stats
printf("ControlParticles::initFromTextFile (%s): Read %d sets, each %d particles\n",filename.c_str() ,
(int)mPartSets.size(), noParts );
if(mPartSets.size()>0) {
printf("ControlParticles::initFromTextFile (%s): Time: %f,%f\n",filename.c_str() ,mPartSets[0].time, mPartSets[mPartSets.size()-1].time );
}
// done...
fclose(infile);
applyTrafos();
*/
return 1;
}
int ControlParticles::initFromTextFileOld(string filename)
{
/*
const bool debugRead = false;
char line[LINE_LEN];
line[LINE_LEN-1] = '\0';
mPartSets.clear();
if(filename.size()<1) return 0;
FILE *infile = fopen(filename.c_str(), "r");
if(!infile) {
fprintf(stdout,"ControlParticles::initFromTextFileOld - unable to open '%s'\n",filename.c_str() );
return 0;
}
int haveNo = false;
int haveScale = false;
int haveTime = false;
int noParts = -1;
int coordCnt = 0;
int partCnt = 0;
int setCnt = 0;
ControlParticle p; p.reset();
// scale times by constant factor while reading
LbmFloat timeScale= 1.0;
int lineCnt = 0;
while(!feof(infile)) {
lineCnt++;
fgets(line, LINE_LEN, infile);
if(debugRead) printf("\nDEBUG%d r '%s'\n",lineCnt, line);
if(!line) continue;
size_t len = strlen(line);
// skip empty lines and comments (#,//)
if(len<1) continue;
if( (line[0]=='#') || (line[0]=='\n') ) continue;
if((len>1) && (line[0]=='/' && line[1]=='/')) continue;
// debug remove newline
if((len>=1)&&(line[len-1]=='\n')) line[len-1]='\0';
// first read no. of particles
if(!haveNo) {
noParts = atoi(line);
if(noParts<=0) {
fprintf(stdout,"ControlParticles::initFromTextFileOld - invalid no of particles %d\n",noParts);
mPartSets.clear();
fclose(infile);
return 0;
}
if(debugRead) printf("DEBUG%d noparts '%d'\n",lineCnt, noParts );
haveNo = true;
}
// then read time scale
else if(!haveScale) {
timeScale *= (LbmFloat)atof(line);
if(debugRead) printf("DEBUG%d tsc '%f', org %f\n",lineCnt, timeScale , _initTimeScale);
haveScale = true;
}
// then get set time
else if(!haveTime) {
ControlParticleSet cps;
mPartSets.push_back(cps);
setCnt = (int)mPartSets.size()-1;
LbmFloat val = (LbmFloat)atof(line);
mPartSets[setCnt].time = val * timeScale;
if(debugRead) printf("DEBUG%d time '%f', %d\n",lineCnt, mPartSets[setCnt].time, setCnt );
haveTime = true;
}
// default read all parts
else {
LbmFloat val = (LbmFloat)atof(line);
if(debugRead) printf("DEBUG: l%d s%d,particle%d '%f' %d,%d/%d\n",lineCnt,(int)mPartSets.size(),(int)mPartSets[setCnt].particles.size(), val ,coordCnt,partCnt,noParts);
p.pos[coordCnt] = val;
coordCnt++;
if(coordCnt>=3) {
mPartSets[setCnt].particles.push_back(p);
p.reset();
coordCnt=0;
partCnt++;
}
if(partCnt>=noParts) {
partCnt = 0;
haveTime = false;
}
//if(debugRead) printf("DEBUG%d par2 %d,%d/%d\n",lineCnt, coordCnt,partCnt,noParts);
}
//read pos, vel ...
}
// sanity check
for(int i=0; i<(int)mPartSets.size(); i++) {
if( (int)mPartSets[i].particles.size()!=noParts) {
fprintf(stdout,"ControlParticles::initFromTextFileOld - invalid no of particles in set %d, is:%d, shouldbe:%d \n",i,(int)mPartSets[i].particles.size(), noParts);
mPartSets.clear();
fclose(infile);
return 0;
}
}
// print stats
printf("ControlParticles::initFromTextFileOld: Read %d sets, each %d particles\n",
(int)mPartSets.size(), noParts );
if(mPartSets.size()>0) {
printf("ControlParticles::initFromTextFileOld: Time: %f,%f\n",mPartSets[0].time, mPartSets[mPartSets.size()-1].time );
}
// done...
fclose(infile);
applyTrafos();
*/
return 1;
}
// load positions & timing from gzipped binary file
int ControlParticles::initFromBinaryFile(string filename) {
mPartSets.clear();
if(filename.size()<1) return 0;
int fileNotFound=0;
int fileFound=0;
char ofile[256];
for(int set=0; ((set<10000)&&(fileNotFound<10)); set++) {
snprintf(ofile,256,"%s%04d.gz",filename.c_str(),set);
//errMsg("ControlParticle::initFromBinaryFile","set"<<set<<" notf"<<fileNotFound<<" ff"<<fileFound);
gzFile gzf;
gzf = gzopen(ofile, "rb");
if (!gzf) {
//errMsg("ControlParticles::initFromBinaryFile","Unable to open file for reading '"<<ofile<<"' ");
fileNotFound++;
continue;
}
fileNotFound=0;
fileFound++;
ControlParticleSet cps;
mPartSets.push_back(cps);
int setCnt = (int)mPartSets.size()-1;
//LbmFloat val = (LbmFloat)atof(line+2);
mPartSets[setCnt].time = (gfxReal)set;
int totpart = 0;
gzread(gzf, &totpart, sizeof(totpart));
for(int a=0; a<totpart; a++) {
int ptype=0;
float psize=0.0;
ntlVec3Gfx ppos,pvel;
gzread(gzf, &ptype, sizeof( ptype ));
gzread(gzf, &psize, sizeof( float ));
for(int j=0; j<3; j++) { gzread(gzf, &ppos[j], sizeof( float )); }
for(int j=0; j<3; j++) { gzread(gzf, &pvel[j], sizeof( float )); }
ControlParticle p;
p.reset();
p.pos = vec2L(ppos);
mPartSets[setCnt].particles.push_back(p);
}
gzclose(gzf);
//errMsg("ControlParticle::initFromBinaryFile","Read set "<<ofile<<", #"<<mPartSets[setCnt].particles.size() ); // DEBUG
} // sets
if(fileFound==0) return 0;
applyTrafos();
return 1;
}
int globCPIProblems =0;
bool ControlParticles::checkPointInside(ntlTree *tree, ntlVec3Gfx org, gfxReal &distance) {
// warning - stripped down version of geoInitCheckPointInside
const int globGeoInitDebug = 0;
const int flags = FGI_FLUID;
org += ntlVec3Gfx(0.0001);
ntlVec3Gfx dir = ntlVec3Gfx(1.0, 0.0, 0.0);
int OId = -1;
ntlRay ray(org, dir, 0, 1.0, NULL);
bool done = false;
bool inside = false;
int mGiObjInside = 0;
LbmFloat mGiObjDistance = -1.0;
LbmFloat giObjFirstHistSide = 0;
// if not inside, return distance to first hit
gfxReal firstHit=-1.0;
int firstOId = -1;
if(globGeoInitDebug) errMsg("IIIstart"," isect "<<org);
while(!done) {
// find first inside intersection
ntlTriangle *triIns = NULL;
distance = -1.0;
ntlVec3Gfx normal(0.0);
tree->intersectX(ray,distance,normal, triIns, flags, true);
if(triIns) {
ntlVec3Gfx norg = ray.getOrigin() + ray.getDirection()*distance;
LbmFloat orientation = dot(normal, dir);
OId = triIns->getObjectId();
if(orientation<=0.0) {
// outside hit
normal *= -1.0;
mGiObjInside++;
if(giObjFirstHistSide==0) giObjFirstHistSide = 1;
if(globGeoInitDebug) errMsg("IIO"," oid:"<<OId<<" org"<<org<<" norg"<<norg<<" orient:"<<orientation);
} else {
// inside hit
mGiObjInside++;
if(mGiObjDistance<0.0) mGiObjDistance = distance;
if(globGeoInitDebug) errMsg("III"," oid:"<<OId<<" org"<<org<<" norg"<<norg<<" orient:"<<orientation);
if(giObjFirstHistSide==0) giObjFirstHistSide = -1;
}
norg += normal * getVecEpsilon();
ray = ntlRay(norg, dir, 0, 1.0, NULL);
// remember first hit distance, in case we're not
// inside anything
if(firstHit<0.0) {
firstHit = distance;
firstOId = OId;
}
} else {
// no more intersections... return false
done = true;
}
}
distance = -1.0;
if(mGiObjInside>0) {
bool mess = false;
if((mGiObjInside%2)==1) {
if(giObjFirstHistSide != -1) mess=true;
} else {
if(giObjFirstHistSide != 1) mess=true;
}
if(mess) {
// ?
//errMsg("IIIproblem","At "<<org<<" obj inside:"<<mGiObjInside<<" firstside:"<<giObjFirstHistSide );
globCPIProblems++;
mGiObjInside++; // believe first hit side...
}
}
if(globGeoInitDebug) errMsg("CHIII"," ins="<<mGiObjInside<<" t"<<mGiObjDistance<<" d"<<distance);
if(((mGiObjInside%2)==1)&&(mGiObjDistance>0.0)) {
if( (distance<0.0) || // first intersection -> good
((distance>0.0)&&(distance>mGiObjDistance)) // more than one intersection -> use closest one
) {
distance = mGiObjDistance;
OId = 0;
inside = true;
}
}
if(!inside) {
distance = firstHit;
OId = firstOId;
}
if(globGeoInitDebug) errMsg("CHIII","ins"<<inside<<" fh"<<firstHit<<" fo"<<firstOId<<" - h"<<distance<<" o"<<OId);
return inside;
}
int ControlParticles::initFromMVCMesh(string filename) {
myTime_t mvmstart = getTime();
ntlGeometryObjModel *model = new ntlGeometryObjModel();
int gid=1;
char infile[256];
vector<ntlTriangle> triangles;
vector<ntlVec3Gfx> vertices;
vector<ntlVec3Gfx> normals;
snprintf(infile,256,"%s.bobj.gz", filename.c_str() );
model->loadBobjModel(string(infile));
model->setLoaded(true);
model->setGeoInitId(gid);
model->setGeoInitType(FGI_FLUID);
debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG,"infile:"<<string(infile) ,4);
//getTriangles(double t, vector<ntlTriangle> *triangles, vector<ntlVec3Gfx> *vertices, vector<ntlVec3Gfx> *normals, int objectId );
model->getTriangles(mCPSTimeStart, &triangles, &vertices, &normals, 1 );
debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG," tris:"<<triangles.size()<<" verts:"<<vertices.size()<<" norms:"<<normals.size() , 2);
// valid mesh?
if(triangles.size() <= 0) {
return 0;
}
ntlRenderGlobals *glob = new ntlRenderGlobals;
ntlScene *genscene = new ntlScene( glob, false );
genscene->addGeoClass(model);
genscene->addGeoObject(model);
genscene->buildScene(0., false);
char treeFlag = (1<<(4+gid));
ntlTree *tree = new ntlTree(
15, 8, // TREEwarning - fixed values for depth & maxtriangles here...
genscene, treeFlag );
// TODO? use params
ntlVec3Gfx start,end;
model->getExtends(start,end);
LbmFloat width = mCPSWidth;
if(width<=LBM_EPSILON) { errMsg("ControlParticles::initFromMVMCMesh","Invalid mCPSWidth! "<<mCPSWidth); width=mCPSWidth=0.1; }
ntlVec3Gfx org = start+ntlVec3Gfx(width*0.5);
gfxReal distance = -1.;
vector<ntlVec3Gfx> inspos;
int approxmax = (int)( ((end[0]-start[0])/width)*((end[1]-start[1])/width)*((end[2]-start[2])/width) );
debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG,"start"<<start<<" end"<<end<<" w="<<width<<" maxp:"<<approxmax, 5);
while(org[2]<end[2]) {
while(org[1]<end[1]) {
while(org[0]<end[0]) {
if(checkPointInside(tree, org, distance)) {
inspos.push_back(org);
//inspos.push_back(org+ntlVec3Gfx(width));
//inspos.push_back(start+end*0.5);
}
// TODO optimize, use distance
org[0] += width;
}
org[1] += width;
org[0] = start[0];
}
org[2] += width;
org[1] = start[1];
}
debMsgStd("ControlParticles::initFromMVMCMesh",DM_MSG,"points: "<<inspos.size()<<" initproblems: "<<globCPIProblems,5 );
MeanValueMeshCoords mvm;
mvm.calculateMVMCs(vertices,triangles, inspos, mCPSWeightFac);
vector<ntlVec3Gfx> ninspos;
mvm.transfer(vertices, ninspos);
// init first set, check dist
ControlParticleSet firstcps; //T
mPartSets.push_back(firstcps);
mPartSets[mPartSets.size()-1].time = (gfxReal)0.;
vector<bool> useCP;
bool debugPos=false;
for(int i=0; i<(int)inspos.size(); i++) {
ControlParticle p; p.reset();
p.pos = vec2L(inspos[i]);
//errMsg("COMP "," "<<inspos[i]<<" vs "<<ninspos[i] );
double cpdist = norm(inspos[i]-ninspos[i]);
bool usecpv = true;
if(debugPos) errMsg("COMP "," "<<cpdist<<usecpv);
mPartSets[mPartSets.size()-1].particles.push_back(p);
useCP.push_back(usecpv);
}
// init further sets, temporal mesh sampling
double tsampling = mCPSTimestep;
int totcnt = (int)( (mCPSTimeEnd-mCPSTimeStart)/tsampling ), tcnt=0;
for(double t=mCPSTimeStart+tsampling; ((t<mCPSTimeEnd) && (ninspos.size()>0.)); t+=tsampling) {
ControlParticleSet nextcps; //T
mPartSets.push_back(nextcps);
mPartSets[mPartSets.size()-1].time = (gfxReal)t;
vertices.clear(); triangles.clear(); normals.clear();
model->getTriangles(t, &triangles, &vertices, &normals, 1 );
mvm.transfer(vertices, ninspos);
if(tcnt%(totcnt/10)==1) debMsgStd("MeanValueMeshCoords::calculateMVMCs",DM_MSG,"Transferring animation, frame: "<<tcnt<<"/"<<totcnt,5 );
tcnt++;
for(int i=0; i<(int)ninspos.size(); i++) {
if(debugPos) errMsg("COMP "," "<<norm(inspos[i]-ninspos[i]) );
if(useCP[i]) {
ControlParticle p; p.reset();
p.pos = vec2L(ninspos[i]);
mPartSets[mPartSets.size()-1].particles.push_back(p);
}
}
}
applyTrafos();
myTime_t mvmend = getTime();
debMsgStd("ControlParticle::initFromMVMCMesh",DM_MSG,"t:"<<getTimeString(mvmend-mvmstart)<<" ",7 );
delete tree;
delete genscene;
delete glob;
//exit(1); // DEBUG
return 1;
}
#define TRISWAP(v,a,b) { LbmFloat tmp = (v)[b]; (v)[b]=(v)[a]; (v)[a]=tmp; }
#define TRISWAPALL(v,a,b) { \
TRISWAP( (v).pos ,a,b ); \
TRISWAP( (v).vel ,a,b ); \
TRISWAP( (v).rotaxis ,a,b ); }
// helper function for LBM 2D -> swap Y and Z components everywhere
void ControlParticles::swapCoords(int a, int b) {
//return;
for(int i=0; i<(int)mPartSets.size(); i++) {
for(int j=0; j<(int)mPartSets[i].particles.size(); j++) {
TRISWAPALL( mPartSets[i].particles[j],a,b );
}
}
}
// helper function for LBM 2D -> mirror time
void ControlParticles::mirrorTime() {
LbmFloat maxtime = mPartSets[mPartSets.size()-1].time;
const bool debugTimeswap = false;
for(int i=0; i<(int)mPartSets.size(); i++) {
mPartSets[i].time = maxtime - mPartSets[i].time;
}
for(int i=0; i<(int)mPartSets.size()/2; i++) {
ControlParticleSet cps = mPartSets[i];
if(debugTimeswap) errMsg("TIMESWAP", " s"<<i<<","<<mPartSets[i].time<<" and s"<<(mPartSets.size()-1-i)<<","<< mPartSets[mPartSets.size()-1-i].time <<" mt:"<<maxtime );
mPartSets[i] = mPartSets[mPartSets.size()-1-i];
mPartSets[mPartSets.size()-1-i] = cps;
}
for(int i=0; i<(int)mPartSets.size(); i++) {
if(debugTimeswap) errMsg("TIMESWAP", "done: s"<<i<<","<<mPartSets[i].time<<" "<<mPartSets[i].particles.size() );
}
}
// apply init transformations
void ControlParticles::applyTrafos() {
// apply trafos
for(int i=0; i<(int)mPartSets.size(); i++) {
mPartSets[i].time *= _initTimeScale;
/*for(int j=0; j<(int)mPartSets[i].particles.size(); j++) {
for(int k=0; k<3; k++) {
mPartSets[i].particles[j].pos[k] *= _initPartScale[k];
mPartSets[i].particles[j].pos[k] += _initPartOffset[k];
}
} now done in initarray */
}
// mirror coords...
for(int l=0; l<(int)_initMirror.length(); l++) {
switch(_initMirror[l]) {
case 'X':
case 'x':
//printf("ControlParticles::applyTrafos - mirror x\n");
swapCoords(1,2);
break;
case 'Y':
case 'y':
//printf("ControlParticles::applyTrafos - mirror y\n");
swapCoords(0,2);
break;
case 'Z':
case 'z':
//printf("ControlParticles::applyTrafos - mirror z\n");
swapCoords(0,1);
break;
case 'T':
case 't':
//printf("ControlParticles::applyTrafos - mirror time\n");
mirrorTime();
break;
case ' ':
case '-':
case '\n':
break;
default:
//printf("ControlParticles::applyTrafos - mirror unknown %c !?\n", _initMirror[l] );
break;
}
}
// reset 2d positions
#if (CP_PROJECT2D==1) && ( defined(MAIN_2D) || LBMDIM==2 )
for(size_t j=0; j<mPartSets.size(); j++)
for(size_t i=0; i<mPartSets[j].particles.size(); i++) {
// DEBUG
mPartSets[j].particles[i].pos[1] = 0.f;
}
#endif
#if defined(LBMDIM)
//? if( (getenv("ELBEEM_CPINFILE")) || (getenv("ELBEEM_CPOUTFILE")) ){
// gui control test, don swap...
//? } else {
//? swapCoords(1,2); // LBM 2D -> swap Y and Z components everywhere
//? }
#endif
initTime(0.f, 0.f);
}
#undef TRISWAP
// --------------------------------------------------------------------------
// init for a given time
void ControlParticles::initTime(LbmFloat t, LbmFloat dt)
{
//fprintf(stdout, "CPINITTIME init %f\n",t);
_currTime = t;
if(mPartSets.size()<1) return;
// init zero velocities
initTimeArray(t, _particles);
// calculate velocities from prev. timestep?
if(dt>0.) {
_currTimestep = dt;
std::vector<ControlParticle> prevparts;
initTimeArray(t-dt, prevparts);
LbmFloat invdt = 1.0/dt;
for(size_t j=0; j<_particles.size(); j++) {
ControlParticle &p = _particles[j];
ControlParticle &prevp = prevparts[j];
for(int k=0; k<3; k++) {
p.pos[k] *= _initPartScale[k];
p.pos[k] += _initPartOffset[k];
prevp.pos[k] *= _initLastPartScale[k];
prevp.pos[k] += _initLastPartOffset[k];
}
p.vel = (p.pos - prevp.pos)*invdt;
}
if(0) {
LbmVec avgvel(0.);
for(size_t j=0; j<_particles.size(); j++) {
avgvel += _particles[j].vel;
}
avgvel /= (LbmFloat)_particles.size();
//fprintf(stdout," AVGVEL %f,%f,%f \n",avgvel[0],avgvel[1],avgvel[2]); // DEBUG
}
}
}
// helper, init given array
void ControlParticles::initTimeArray(LbmFloat t, std::vector<ControlParticle> &parts) {
if(mPartSets.size()<1) return;
if(parts.size()!=mPartSets[0].particles.size()) {
//fprintf(stdout,"PRES \n");
parts.resize(mPartSets[0].particles.size());
// TODO reset all?
for(size_t j=0; j<parts.size(); j++) {
parts[j].reset();
}
}
if(parts.size()<1) return;
// debug inits
if(mDebugInit==1) {
// hard coded circle init
for(size_t j=0; j<mPartSets[0].particles.size(); j++) {
ControlParticle p = mPartSets[0].particles[j];
// remember old
p.density = parts[j].density;
p.densityWeight = parts[j].densityWeight;
p.avgVel = parts[j].avgVel;
p.avgVelAcc = parts[j].avgVelAcc;
p.avgVelWeight = parts[j].avgVelWeight;
LbmVec ppos(0.); { // DEBUG
const float tscale=10.;
const float tprevo = 0.33;
const LbmVec toff(50,50,0);
const LbmVec oscale(30,30,0);
ppos[0] = cos(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[0] + toff[0];
ppos[1] = -sin(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[1] + toff[1];
ppos[2] = toff[2]; } // DEBUG
p.pos = ppos;
parts[j] = p;
//errMsg("ControlParticle::initTimeArray","j:"<<j<<" p:"<<parts[j].pos );
}
return;
}
else if(mDebugInit==2) {
// hard coded spiral init
const float tscale=-10.;
const float tprevo = 0.33;
LbmVec toff(50,0,-50);
const LbmVec oscale(20,20,0);
toff[2] += 30. * t +30.;
for(size_t j=0; j<mPartSets[0].particles.size(); j++) {
ControlParticle p = mPartSets[0].particles[j];
// remember old
p.density = parts[j].density;
p.densityWeight = parts[j].densityWeight;
p.avgVel = parts[j].avgVel;
p.avgVelAcc = parts[j].avgVelAcc;
p.avgVelWeight = parts[j].avgVelWeight;
LbmVec ppos(0.);
ppos[1] = toff[2];
LbmFloat zscal = (ppos[1]+100.)/200.;
ppos[0] = cos(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[0]*zscal + toff[0];
ppos[2] = -sin(tscale* t - tprevo*(float)j + M_PI -0.1) * oscale[1]*zscal + toff[1];
p.pos = ppos;
parts[j] = p;
toff[2] += 0.25;
}
return;
}
// use first set
if((t<=mPartSets[0].time)||(mPartSets.size()==1)) {
//fprintf(stdout,"PINI %f \n", t);
//parts = mPartSets[0].particles;
const int i=0;
for(size_t j=0; j<mPartSets[i].particles.size(); j++) {
ControlParticle p = mPartSets[i].particles[j];
// remember old
p.density = parts[j].density;
p.densityWeight = parts[j].densityWeight;
p.avgVel = parts[j].avgVel;
p.avgVelAcc = parts[j].avgVelAcc;
p.avgVelWeight = parts[j].avgVelWeight;
parts[j] = p;
}
return;
}
for(int i=0; i<(int)mPartSets.size()-1; i++) {
if((mPartSets[i].time<=t) && (mPartSets[i+1].time>t)) {
LbmFloat d = mPartSets[i+1].time-mPartSets[i].time;
LbmFloat f = (t-mPartSets[i].time)/d;
LbmFloat omf = 1.0f - f;
for(size_t j=0; j<mPartSets[i].particles.size(); j++) {
ControlParticle *src1=&mPartSets[i ].particles[j];
ControlParticle *src2=&mPartSets[i+1].particles[j];
ControlParticle &p = parts[j];
// do linear interpolation
p.pos = src1->pos * omf + src2->pos *f;
p.vel = LbmVec(0.); // reset, calculated later on src1->vel * omf + src2->vel *f;
p.rotaxis = src1->rotaxis * omf + src2->rotaxis *f;
p.influence = src1->influence * omf + src2->influence *f;
p.size = src1->size * omf + src2->size *f;
// dont modify: density, densityWeight
}
}
}
// after last?
if(t>=mPartSets[ mPartSets.size() -1 ].time) {
//parts = mPartSets[ mPartSets.size() -1 ].particles;
const int i= (int)mPartSets.size() -1;
for(size_t j=0; j<mPartSets[i].particles.size(); j++) {
ControlParticle p = mPartSets[i].particles[j];
// restore
p.density = parts[j].density;
p.densityWeight = parts[j].densityWeight;
p.avgVel = parts[j].avgVel;
p.avgVelAcc = parts[j].avgVelAcc;
p.avgVelWeight = parts[j].avgVelWeight;
parts[j] = p;
}
}
}
// --------------------------------------------------------------------------
#define DEBUG_MODVEL 0
// recalculate
void ControlParticles::calculateKernelWeight() {
const bool debugKernel = true;
// calculate kernel area with respect to particlesize/cellsize
LbmFloat kernelw = -1.;
LbmFloat kernelnorm = -1.;
LbmFloat krad = (_radiusAtt*0.75); // FIXME use real cone approximation...?
//krad = (_influenceFalloff*1.);
#if (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
kernelw = CP_PI*krad*krad;
kernelnorm = 1.0 / (_fluidSpacing * _fluidSpacing);
#else // 2D
kernelw = CP_PI*krad*krad*krad* (4./3.);
kernelnorm = 1.0 / (_fluidSpacing * _fluidSpacing * _fluidSpacing);
#endif // MAIN_2D
if(debugKernel) debMsgStd("ControlParticles::calculateKernelWeight",DM_MSG,"kw"<<kernelw<<", norm"<<
kernelnorm<<", w*n="<<(kernelw*kernelnorm)<<", rad"<<krad<<", sp"<<_fluidSpacing<<" ", 7);
LbmFloat kernelws = kernelw*kernelnorm;
_kernelWeight = kernelws;
if(debugKernel) debMsgStd("ControlParticles::calculateKernelWeight",DM_MSG,"influence f="<<_radiusAtt<<" t="<<
_influenceTangential<<" a="<<_influenceAttraction<<" v="<<_influenceVelocity<<" kweight="<<_kernelWeight, 7);
if(_kernelWeight<=0.) {
errMsg("ControlParticles::calculateKernelWeight", "invalid kernel! "<<_kernelWeight<<", resetting");
_kernelWeight = 1.;
}
}
void
ControlParticles::prepareControl(LbmFloat simtime, LbmFloat dt, ControlParticles *motion) {
debMsgStd("ControlParticle::prepareControl",DM_MSG," simtime="<<simtime<<" dt="<<dt<<" ", 5);
//fprintf(stdout,"PREPARE \n");
LbmFloat avgdw = 0.;
for(size_t i=0; i<_particles.size(); i++) {
ControlParticle *cp = &_particles[i];
if(this->getInfluenceAttraction()<0.) {
cp->density=
cp->densityWeight = 1.0;
continue;
}
// normalize by kernel
//cp->densityWeight = (1.0 - (cp->density / _kernelWeight)); // store last
#if (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
cp->densityWeight = (1.0 - (cp->density / (_kernelWeight*cp->size*cp->size) )); // store last
#else // 2D
cp->densityWeight = (1.0 - (cp->density / (_kernelWeight*cp->size*cp->size*cp->size) )); // store last
#endif // MAIN_2D
if(i<10) debMsgStd("ControlParticle::prepareControl",DM_MSG,"kernelDebug i="<<i<<" densWei="<<cp->densityWeight<<" 1/kw"<<(1.0/_kernelWeight)<<" cpdensity="<<cp->density, 9 );
if(cp->densityWeight<0.) cp->densityWeight=0.;
if(cp->densityWeight>1.) cp->densityWeight=1.;
avgdw += cp->densityWeight;
// reset for next step
cp->density = 0.;
if(cp->avgVelWeight>0.) {
cp->avgVel = cp->avgVelAcc/cp->avgVelWeight;
cp->avgVelWeight = 0.;
cp->avgVelAcc = LbmVec(0.,0.,0.);
}
}
//if(debugKernel) for(size_t i=0; i<_particles.size(); i++) { ControlParticle *cp = &_particles[i]; fprintf(stdout,"A %f,%f \n",cp->density,cp->densityWeight); }
avgdw /= (LbmFloat)(_particles.size());
//if(motion) { printf("ControlParticle::kernel: avgdw:%f, kw%f, sp%f \n", avgdw, _kernelWeight, _fluidSpacing); }
//if((simtime>=0.) && (simtime != _currTime))
initTime(simtime, dt);
if((motion) && (motion->getSize()>0)){
ControlParticle *motionp = motion->getParticle(0);
//printf("ControlParticle::prepareControl motion: pos[%f,%f,%f] vel[%f,%f,%f] \n", motionp->pos[0], motionp->pos[1], motionp->pos[2], motionp->vel[0], motionp->vel[1], motionp->vel[2] );
for(size_t i=0; i<_particles.size(); i++) {
ControlParticle *cp = &_particles[i];
cp->pos = cp->pos + motionp->pos;
cp->vel = cp->vel + motionp->vel;
cp->size = cp->size * motionp->size;
cp->influence = cp->size * motionp->influence;
}
}
// reset to radiusAtt by default
if(_radiusVel==0.) _radiusVel = _radiusAtt;
if(_radiusMinMaxd==0.) _radiusMinMaxd = _radiusAtt;
if(_radiusMaxd==0.) _radiusMaxd = 2.*_radiusAtt;
// has to be radiusVel<radiusAtt<radiusMinMaxd<radiusMaxd
if(_radiusVel>_radiusAtt) _radiusVel = _radiusAtt;
if(_radiusAtt>_radiusMinMaxd) _radiusAtt = _radiusMinMaxd;
if(_radiusMinMaxd>_radiusMaxd) _radiusMinMaxd = _radiusMaxd;
//printf("ControlParticle::radii vel:%f att:%f min:%f max:%f \n", _radiusVel,_radiusAtt,_radiusMinMaxd,_radiusMaxd);
// prepareControl done
}
void ControlParticles::finishControl(std::vector<ControlForces> &forces, LbmFloat iatt, LbmFloat ivel, LbmFloat imaxd) {
//const LbmFloat iatt = this->getInfluenceAttraction() * this->getCurrTimestep();
//const LbmFloat ivel = this->getInfluenceVelocity();
//const LbmFloat imaxd = this->getInfluenceMaxdist() * this->getCurrTimestep();
// prepare for usage
iatt *= this->getCurrTimestep();
ivel *= 1.; // not necessary!
imaxd *= this->getCurrTimestep();
// skip when size=0
for(int i=0; i<(int)forces.size(); i++) {
if(DEBUG_MODVEL) fprintf(stdout, "CPFORGF %d , wf:%f,f:%f,%f,%f , v:%f,%f,%f \n",i, forces[i].weightAtt, forces[i].forceAtt[0],forces[i].forceAtt[1],forces[i].forceAtt[2], forces[i].forceVel[0], forces[i].forceVel[1], forces[i].forceVel[2] );
LbmFloat cfweight = forces[i].weightAtt; // always normalize
if((cfweight!=0.)&&(iatt!=0.)) {
// multiple kernels, normalize - note this does not normalize in d>r/2 region
if(ABS(cfweight)>1.) { cfweight = 1.0/cfweight; }
// multiply iatt afterwards to allow stronger force
cfweight *= iatt;
forces[i].forceAtt *= cfweight;
} else {
forces[i].weightAtt = 0.;
forces[i].forceAtt = LbmVec(0.);
}
if( (cfweight==0.) && (imaxd>0.) && (forces[i].maxDistance>0.) ) {
forces[i].forceMaxd *= imaxd;
} else {
forces[i].maxDistance= 0.;
forces[i].forceMaxd = LbmVec(0.);
}
LbmFloat cvweight = forces[i].weightVel; // always normalize
if(cvweight>0.) {
forces[i].forceVel /= cvweight;
forces[i].compAv /= cvweight;
// now modify cvweight, and write back
// important, cut at 1 - otherwise strong vel. influences...
if(cvweight>1.) { cvweight = 1.; }
// thus cvweight is in the range of 0..influenceVelocity, currently not normalized by numCParts
cvweight *= ivel;
if(cvweight<0.) cvweight=0.; if(cvweight>1.) cvweight=1.;
// LBM, FIXME todo use relaxation factor
//pvel = (cvel*0.5 * cvweight) + (pvel * (1.0-cvweight));
forces[i].weightVel = cvweight;
//errMsg("COMPAV","i"<<i<<" compav"<<forces[i].compAv<<" forcevel"<<forces[i].forceVel<<" ");
} else {
forces[i].weightVel = 0.;
if(forces[i].maxDistance==0.) forces[i].forceVel = LbmVec(0.);
forces[i].compAvWeight = 0.;
forces[i].compAv = LbmVec(0.);
}
if(DEBUG_MODVEL) fprintf(stdout, "CPFINIF %d , wf:%f,f:%f,%f,%f , v:%f,%f,%f \n",i, forces[i].weightAtt, forces[i].forceAtt[0],forces[i].forceAtt[1],forces[i].forceAtt[2], forces[i].forceVel[0],forces[i].forceVel[1],forces[i].forceVel[2] );
}
// unused...
if(DEBUG_MODVEL) fprintf(stdout,"MFC iatt:%f,%f ivel:%f,%f ifmd:%f,%f \n", iatt,_radiusAtt, ivel,_radiusVel, imaxd, _radiusMaxd);
//for(size_t i=0; i<_particles.size(); i++) { ControlParticle *cp = &_particles[i]; fprintf(stdout," %f,%f,%f ",cp->density,cp->densityWeight, (1.0 - (12.0*cp->densityWeight))); }
//fprintf(stdout,"\n\nCP DONE \n\n\n");
}
// --------------------------------------------------------------------------
// calculate forces at given position, and modify velocity
// according to timestep
void ControlParticles::calculateCpInfluenceOpt(ControlParticle *cp, LbmVec fluidpos, LbmVec fluidvel, ControlForces *force, LbmFloat fillFactor) {
// dont reset, only add...
// test distance, simple squared distance reject
const LbmFloat cpfo = _radiusAtt*cp->size;
LbmVec posDelta;
if(DEBUG_MODVEL) fprintf(stdout, "CP at %f,%f,%f bef fw:%f, f:%f,%f,%f , vw:%f, v:%f,%f,%f \n",fluidpos[0],fluidpos[1],fluidpos[2], force->weightAtt, force->forceAtt[0], force->forceAtt[1], force->forceAtt[2], force->weightVel, force->forceVel[0], force->forceVel[1], force->forceVel[2]);
posDelta = cp->pos - fluidpos;
#if LBMDIM==2 && (CP_PROJECT2D==1)
posDelta[2] = 0.; // project to xy plane, z-velocity should already be gone...
#endif
const LbmFloat distsqr = posDelta[0]*posDelta[0]+posDelta[1]*posDelta[1]+posDelta[2]*posDelta[2];
if(DEBUG_MODVEL) fprintf(stdout, " Pd at %f,%f,%f d%f \n",posDelta[0],posDelta[1],posDelta[2], distsqr);
// cut at influence=0.5 , scaling not really makes sense
if(cpfo*cpfo < distsqr) {
/*if(cp->influence>0.5) {
if(force->weightAtt == 0.) {
if(force->maxDistance*force->maxDistance > distsqr) {
const LbmFloat dis = sqrtf((float)distsqr);
const LbmFloat sc = dis-cpfo;
force->maxDistance = dis;
force->forceMaxd = (posDelta)*(sc/dis);
}
} } */
return;
}
force->weightAtt += 1e-6; // for distance
force->maxDistance = 0.; // necessary for SPH?
const LbmFloat pdistance = MAGNITUDE(posDelta);
LbmFloat pdistinv = 0.;
if(ABS(pdistance)>0.) pdistinv = 1./pdistance;
posDelta *= pdistinv;
LbmFloat falloffAtt = 0.; //CPKernel::kernel(cpfo * 1.0, pdistance);
const LbmFloat qac = pdistance / cpfo ;
if (qac < 1.0){ // return 0.;
if(qac < 0.5) falloffAtt = 1.0f;
else falloffAtt = (1.0f - qac) * 2.0f;
}
// vorticity force:
// - //LbmVec forceVort;
// - //CROSS(forceVort, posDelta, cp->rotaxis);
// - //NORMALIZE(forceVort);
// - if(falloffAtt>1.0) falloffAtt=1.0;
#if (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
// fillFactor *= 2.0 *0.75 * pdistance; // 2d>3d sampling
#endif // (CP_PROJECT2D==1) && (defined(MAIN_2D) || LBMDIM==2)
LbmFloat signum = getInfluenceAttraction() > 0.0 ? 1.0 : -1.0;
cp->density += falloffAtt * fillFactor;
force->forceAtt += posDelta *cp->densityWeight *cp->influence *signum;
force->weightAtt += falloffAtt*cp->densityWeight *cp->influence;
LbmFloat falloffVel = 0.; //CPKernel::kernel(cpfo * 1.0, pdistance);
const LbmFloat cpfv = _radiusVel*cp->size;
if(cpfv*cpfv < distsqr) { return; }
const LbmFloat qvc = pdistance / cpfo ;
//if (qvc < 1.0){
//if(qvc < 0.5) falloffVel = 1.0f;
//else falloffVel = (1.0f - qvc) * 2.0f;
//}
falloffVel = 1.-qvc;
LbmFloat pvWeight; // = (1.0-cp->densityWeight) * _currTimestep * falloffVel;
pvWeight = falloffVel *cp->influence; // std, without density influence
//pvWeight *= (1.0-cp->densityWeight); // use inverse density weight
//pvWeight *= cp->densityWeight; // test, use density weight
LbmVec modvel(0.);
modvel += cp->vel * pvWeight;
//pvWeight = 1.; modvel = partVel; // DEBUG!?
if(pvWeight>0.) {
force->forceVel += modvel;
force->weightVel += pvWeight;
cp->avgVelWeight += falloffVel;
cp->avgVel += fluidvel;
}
if(DEBUG_MODVEL) fprintf(stdout, "CP at %f,%f,%f aft fw:%f, f:%f,%f,%f , vw:%f, v:%f,%f,%f \n",fluidpos[0],fluidpos[1],fluidpos[2], force->weightAtt, force->forceAtt[0], force->forceAtt[1], force->forceAtt[2], force->weightVel, force->forceVel[0], force->forceVel[1], force->forceVel[2]);
return;
}
void ControlParticles::calculateMaxdForce(ControlParticle *cp, LbmVec fluidpos, ControlForces *force) {
if(force->weightAtt != 0.) return; // maxd force off
if(cp->influence <= 0.5) return; // ignore
LbmVec posDelta;
//if(DEBUG_MODVEL) fprintf(stdout, "CP at %f,%f,%f bef fw:%f, f:%f,%f,%f , vw:%f, v:%f,%f,%f \n",fluidpos[0],fluidpos[1],fluidpos[2], force->weightAtt, force->forceAtt[0], force->forceAtt[1], force->forceAtt[2], force->weightVel, force->forceVel[0], force->forceVel[1], force->forceVel[2]);
posDelta = cp->pos - fluidpos;
#if LBMDIM==2 && (CP_PROJECT2D==1)
posDelta[2] = 0.; // project to xy plane, z-velocity should already be gone...
#endif
// dont reset, only add...
// test distance, simple squared distance reject
const LbmFloat distsqr = posDelta[0]*posDelta[0]+posDelta[1]*posDelta[1]+posDelta[2]*posDelta[2];
// closer cp found
if(force->maxDistance*force->maxDistance < distsqr) return;
const LbmFloat dmin = _radiusMinMaxd*cp->size;
if(distsqr<dmin*dmin) return; // inside min
const LbmFloat dmax = _radiusMaxd*cp->size;
if(distsqr>dmax*dmax) return; // outside
if(DEBUG_MODVEL) fprintf(stdout, " Pd at %f,%f,%f d%f \n",posDelta[0],posDelta[1],posDelta[2], distsqr);
// cut at influence=0.5 , scaling not really makes sense
const LbmFloat dis = sqrtf((float)distsqr);
//const LbmFloat sc = dis - dmin;
const LbmFloat sc = (dis-dmin)/(dmax-dmin); // scale from 0-1
force->maxDistance = dis;
force->forceMaxd = (posDelta/dis) * sc;
//debug errMsg("calculateMaxdForce","pos"<<fluidpos<<" dis"<<dis<<" sc"<<sc<<" dmin"<<dmin<<" maxd"<< force->maxDistance <<" fmd"<<force->forceMaxd );
return;
}

View File

@@ -0,0 +1,301 @@
// --------------------------------------------------------------------------
//
// El'Beem - the visual lattice boltzmann freesurface simulator
// All code distributed as part of El'Beem is covered by the version 2 of the
// GNU General Public License. See the file COPYING for details.
//
// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
//
// control particle classes
//
// --------------------------------------------------------------------------
#ifndef CONTROLPARTICLES_H
#define CONTROLPARTICLES_H
#include "ntl_geometrymodel.h"
// indicator for LBM inclusion
//#ifndef LBMDIM
//#include <NxFoundation.h>
//#include <vector>
//class MultisphGUI;
//#define NORMALIZE(a) a.normalize()
//#define MAGNITUDE(a) a.magnitude()
//#define CROSS(a,b,c) a.cross(b,c)
//#define ABS(a) (a>0. ? (a) : -(a))
//#include "cpdefines.h"
//#else // LBMDIM
// use compatibility defines
//#define NORMALIZE(a) normalize(a)
//#define MAGNITUDE(a) norm(a)
//#define CROSS(a,b,c) a=cross(b,c)
//#endif // LBMDIM
#define MAGNITUDE(a) norm(a)
// math.h compatibility
#define CP_PI ((LbmFloat)3.14159265358979323846)
// project 2d test cases onto plane?
// if not, 3d distance is used for 2d sim as well
#define CP_PROJECT2D 1
// default init for mincpdist, ControlForces::maxDistance
#define CPF_MAXDINIT 10000.
// storage of influence for a fluid cell/particle in lbm/sph
class ControlForces
{
public:
ControlForces() { };
~ControlForces() {};
// attraction force
LbmFloat weightAtt;
LbmVec forceAtt;
// velocity influence
LbmFloat weightVel;
LbmVec forceVel;
// maximal distance influence,
// first is max. distance to first control particle
// second attraction strength
LbmFloat maxDistance;
LbmVec forceMaxd;
LbmFloat compAvWeight;
LbmVec compAv;
void resetForces() {
weightAtt = weightVel = 0.;
maxDistance = CPF_MAXDINIT;
forceAtt = forceVel = forceMaxd = LbmVec(0.,0.,0.);
compAvWeight=0.; compAv=LbmVec(0.);
};
};
// single control particle
class ControlParticle
{
public:
ControlParticle() { reset(); };
~ControlParticle() {};
// control parameters
// position
LbmVec pos;
// size (influences influence radius)
LbmFloat size;
// overall strength of influence
LbmFloat influence;
// rotation axis
LbmVec rotaxis;
// computed values
// velocity
LbmVec vel;
// computed density
LbmFloat density;
LbmFloat densityWeight;
LbmVec avgVel;
LbmVec avgVelAcc;
LbmFloat avgVelWeight;
// init all zero / defaults
void reset();
};
// container for a particle configuration at time t
class ControlParticleSet
{
public:
// time of particle set
LbmFloat time;
// particle positions
std::vector<ControlParticle> particles;
};
// container & management of control particles
class ControlParticles
{
public:
ControlParticles();
~ControlParticles();
// reset datastructures for next influence step
// if motion object is given, particle 1 of second system is used for overall
// position and speed offset
void prepareControl(LbmFloat simtime, LbmFloat dt, ControlParticles *motion);
// post control operations
void finishControl(std::vector<ControlForces> &forces, LbmFloat iatt, LbmFloat ivel, LbmFloat imaxd);
// recalculate
void calculateKernelWeight();
// calculate forces at given position, and modify velocity
// according to timestep (from initControl)
void calculateCpInfluenceOpt (ControlParticle *cp, LbmVec fluidpos, LbmVec fluidvel, ControlForces *force, LbmFloat fillFactor);
void calculateMaxdForce (ControlParticle *cp, LbmVec fluidpos, ControlForces *force);
// no. of particles
inline int getSize() { return (int)_particles.size(); }
int getTotalSize();
// get particle [i]
inline ControlParticle* getParticle(int i){ return &_particles[i]; }
// set influence parameters
void setInfluenceTangential(LbmFloat set) { _influenceTangential=set; }
void setInfluenceAttraction(LbmFloat set) { _influenceAttraction=set; }
void setInfluenceMaxdist(LbmFloat set) { _influenceMaxdist=set; }
// calculate for delta t
void setInfluenceVelocity(LbmFloat set, LbmFloat dt);
// get influence parameters
inline LbmFloat getInfluenceAttraction() { return _influenceAttraction; }
inline LbmFloat getInfluenceTangential() { return _influenceTangential; }
inline LbmFloat getInfluenceVelocity() { return _influenceVelocity; }
inline LbmFloat getInfluenceMaxdist() { return _influenceMaxdist; }
inline LbmFloat getCurrTimestep() { return _currTimestep; }
void setRadiusAtt(LbmFloat set) { _radiusAtt=set; }
inline LbmFloat getRadiusAtt() { return _radiusAtt; }
void setRadiusVel(LbmFloat set) { _radiusVel=set; }
inline LbmFloat getRadiusVel() { return _radiusVel; }
void setRadiusMaxd(LbmFloat set) { _radiusMaxd=set; }
inline LbmFloat getRadiusMaxd() { return _radiusMaxd; }
void setRadiusMinMaxd(LbmFloat set) { _radiusMinMaxd=set; }
inline LbmFloat getRadiusMinMaxd() { return _radiusMinMaxd; }
LbmFloat getControlTimStart();
LbmFloat getControlTimEnd();
// set/get characteristic length (and inverse)
void setCharLength(LbmFloat set) { _charLength=set; _charLengthInv=1./_charLength; }
inline LbmFloat getCharLength() { return _charLength;}
inline LbmFloat getCharLengthInv() { return _charLengthInv;}
// set init parameters
void setInitTimeScale(LbmFloat set) { _initTimeScale = set; };
void setInitMirror(string set) { _initMirror = set; };
string getInitMirror() { return _initMirror; };
void setLastOffset(LbmVec set) { _initLastPartOffset = set; };
void setLastScale(LbmVec set) { _initLastPartScale = set; };
void setOffset(LbmVec set) { _initPartOffset = set; };
void setScale(LbmVec set) { _initPartScale = set; };
// set/get cps params
void setCPSWith(LbmFloat set) { mCPSWidth = set; };
void setCPSTimestep(LbmFloat set) { mCPSTimestep = set; };
void setCPSTimeStart(LbmFloat set) { mCPSTimeStart = set; };
void setCPSTimeEnd(LbmFloat set) { mCPSTimeEnd = set; };
void setCPSMvmWeightFac(LbmFloat set) { mCPSWeightFac = set; };
LbmFloat getCPSWith() { return mCPSWidth; };
LbmFloat getCPSTimestep() { return mCPSTimestep; };
LbmFloat getCPSTimeStart() { return mCPSTimeStart; };
LbmFloat getCPSTimeEnd() { return mCPSTimeEnd; };
LbmFloat getCPSMvmWeightFac() { return mCPSWeightFac; };
void setDebugInit(int set) { mDebugInit = set; };
// set init parameters
void setFluidSpacing(LbmFloat set) { _fluidSpacing = set; };
// load positions & timing from text file
int initFromTextFile(string filename);
int initFromTextFileOld(string filename);
// load positions & timing from gzipped binary file
int initFromBinaryFile(string filename);
int initFromMVCMesh(string filename);
// init an example test case
int initExampleSet();
// init for a given time
void initTime(LbmFloat t, LbmFloat dt);
// blender test init
void initBlenderTest();
int initFromObject(ntlGeometryObjModel *model);
protected:
// sets influence params
friend class MultisphGUI;
// tangential and attraction influence
LbmFloat _influenceTangential, _influenceAttraction;
// direct velocity influence
LbmFloat _influenceVelocity;
// maximal distance influence
LbmFloat _influenceMaxdist;
// influence radii
LbmFloat _radiusAtt, _radiusVel, _radiusMinMaxd, _radiusMaxd;
// currently valid time & timestep
LbmFloat _currTime, _currTimestep;
// all particles
std::vector<ControlParticle> _particles;
// particle sets
std::vector<ControlParticleSet> mPartSets;
// additional parameters for initing particles
LbmFloat _initTimeScale;
LbmVec _initPartOffset;
LbmVec _initPartScale;
LbmVec _initLastPartOffset;
LbmVec _initLastPartScale;
// mirror particles for loading?
string _initMirror;
// row spacing paramter, e.g. use for approximation of kernel area/volume
LbmFloat _fluidSpacing;
// save current kernel weight
LbmFloat _kernelWeight;
// charateristic length in world coordinates for normalizatioon of forces
LbmFloat _charLength, _charLengthInv;
/*! do ani mesh CPS */
void calculateCPS(string filename);
//! ani mesh cps params
ntlVec3Gfx mvCPSStart, mvCPSEnd;
gfxReal mCPSWidth, mCPSTimestep;
gfxReal mCPSTimeStart, mCPSTimeEnd;
gfxReal mCPSWeightFac;
int mDebugInit;
protected:
// apply init transformations
void applyTrafos();
// helper function for init -> swap components everywhere
void swapCoords(int a,int b);
// helper function for init -> mirror time
void mirrorTime();
// helper, init given array
void initTimeArray(LbmFloat t, std::vector<ControlParticle> &parts);
bool checkPointInside(ntlTree *tree, ntlVec3Gfx org, gfxReal &distance);
};
#endif

View File

@@ -0,0 +1,25 @@
/******************************************************************************
*
* El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
* All code distributed as part of El'Beem is covered by the version 2 of the
* GNU General Public License. See the file COPYING for details.
* Copyright 2003-2006 Nils Thuerey
*
* Control API header
*/
#include "elbeem.h"
#include "elbeem_control.h"
// add mesh as fluidsim object
int elbeemControlAddSet(struct elbeemControl*) {
return 0;
}
int elbeemControlComputeMesh(struct elbeemMesh*) {
return 0;
}

View File

@@ -0,0 +1,62 @@
/******************************************************************************
*
* El'Beem - Free Surface Fluid Simulation with the Lattice Boltzmann Method
* All code distributed as part of El'Beem is covered by the version 2 of the
* GNU General Public License. See the file COPYING for details.
* Copyright 2003-2006 Nils Thuerey
*
* Control API header
*/
#ifndef ELBEEMCONTROL_API_H
#define ELBEEMCONTROL_API_H
// a single control particle set
typedef struct elbeemControl {
/* influence forces */
float influenceAttraction;
float *channelInfluenceAttraction;
float channelSizeInfluenceAttraction;
float influenceVelocity;
float *channelInfluenceVelocity;
float channelSizeInfluenceVelocity;
float influenceMaxdist;
float *channelInfluenceMaxdist;
float channelSizeInfluenceMaxdist;
/* influence force radii */
float radiusAttraction;
float *channelRadiusAttraction;
float channelSizeRadiusAttraction;
float radiusVelocity;
float *channelRadiusVelocity;
float channelSizeRadiusVelocity;
float radiusMindist;
float *channelRadiusMindist;
float channelSizeRadiusMindist;
float radiusMaxdist;
float *channelRadiusMaxdist;
float channelSizeRadiusMaxdist;
/* control particle positions/scale */
float offset[3];
float *channelOffset;
float channelSizeOffset;
float scale[3];
float *channelScale;
float channelSizeScale;
} elbeemControl;
// add mesh as fluidsim object
int elbeemControlAddSet(struct elbeemControl*);
// sample & track mesh control particles, TODO add return type...
int elbeemControlComputeMesh(struct elbeemMesh*);
#endif // ELBEEMCONTROL_API_H

View File

@@ -0,0 +1,193 @@
/******************************************************************************
*
// El'Beem - the visual lattice boltzmann freesurface simulator
// All code distributed as part of El'Beem is covered by the version 2 of the
// GNU General Public License. See the file COPYING for details.
//
// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
//
*
* Mean Value Mesh Coords class
*
*****************************************************************************/
#include "mvmcoords.h"
#include <algorithm>
using std::vector;
void MeanValueMeshCoords::clear()
{
mVertices.resize(0);
mNumVerts = 0;
}
void MeanValueMeshCoords::calculateMVMCs(vector<ntlVec3Gfx> &reference_vertices, vector<ntlTriangle> &tris,
vector<ntlVec3Gfx> &points, gfxReal numweights)
{
clear();
mvmTransferPoint tds;
int mem = 0;
int i = 0;
mNumVerts = (int)reference_vertices.size();
for (vector<ntlVec3Gfx>::iterator iter = points.begin(); iter != points.end(); ++iter, ++i) {
/*
if(i%(points.size()/10)==1) debMsgStd("MeanValueMeshCoords::calculateMVMCs",DM_MSG,"Computing weights, points: "<<i<<"/"<<points.size(),5 );
*/
tds.lastpos = *iter;
tds.weights.resize(0); // clear
computeWeights(reference_vertices, tris, tds, numweights);
mem += (int)tds.weights.size();
mVertices.push_back(tds);
}
int mbmem = mem * sizeof(mvmFloat) / (1024*1024);
debMsgStd("MeanValueMeshCoords::calculateMVMCs",DM_MSG,"vertices:"<<mNumVerts<<" points:"<<points.size()<<" weights:"<<mem<<", wmem:"<<mbmem<<"MB ",7 );
}
// from: mean value coordinates for closed triangular meshes
// attention: fails if a point is exactly (or very close) to a vertex
void MeanValueMeshCoords::computeWeights(vector<ntlVec3Gfx> &reference_vertices, vector<ntlTriangle>& tris,
mvmTransferPoint& tds, gfxReal numweights)
{
const bool mvmFullDebug=false;
//const ntlVec3Gfx cEPS = 1.0e-6;
const mvmFloat cEPS = 1.0e-14;
//mvmFloat d[3], s[3], phi[3],c[3];
ntlVec3d u[3],c,d,s,phi;
int indices[3];
for (int i = 0; i < (int)reference_vertices.size(); ++i) {
tds.weights.push_back(mvmIndexWeight(i, 0.0));
}
// for each triangle
//for (vector<ntlTriangle>::iterator iter = tris.begin(); iter != tris.end();) {
for(int t=0; t<(int)tris.size(); t++) {
for (int i = 0; i < 3; ++i) { //, ++iter) {
indices[i] = tris[t].getPoints()[i];
u[i] = vec2D(reference_vertices[ indices[i] ]-tds.lastpos);
d[i] = normalize(u[i]); //.normalize();
//assert(d[i] != 0.);
if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","t"<<t<<" i"<<indices[i] //<<" lp"<<tds.lastpos
<<" v"<<reference_vertices[indices[i]]<<" u"<<u[i]<<" ");
// on vertex!
//? if(d[i]<=0.) continue;
}
//for (int i = 0; i < 3; ++i) { errMsg("III"," "<<i <<" i"<<indices[i]<<reference_vertices[ indices[i] ] ); }
// arcsin is not needed, see paper
phi[0] = 2.*asin( (mvmFloat)(0.5* norm(u[1]-u[2]) ) );
phi[1] = 2.*asin( (mvmFloat)(0.5* norm(u[0]-u[2]) ) );
phi[2] = 2.*asin( (mvmFloat)(0.5* norm(u[0]-u[1]) ) );
mvmFloat h = (phi[0] + phi[1] + phi[2])*0.5;
if (M_PI-h < cEPS) {
if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","point on triangle");
tds.weights.resize(0);
tds.weights.push_back( mvmIndexWeight(indices[0], sin(phi[0])*d[1]*d[2]));
tds.weights.push_back( mvmIndexWeight(indices[1], sin(phi[1])*d[0]*d[2]));
tds.weights.push_back( mvmIndexWeight(indices[2], sin(phi[2])*d[1]*d[0]));
break;
}
mvmFloat sinh = 2.*sin(h);
c[0] = (sinh*sin(h-phi[0]))/(sin(phi[1])*sin(phi[2]))-1.;
c[1] = (sinh*sin(h-phi[1]))/(sin(phi[0])*sin(phi[2]))-1.;
c[2] = (sinh*sin(h-phi[2]))/(sin(phi[0])*sin(phi[1]))-1.;
if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","c="<<c<<" phi="<<phi<<" d="<<d);
//if (c[0] > 1. || c[0] < 0. || c[1] > 1. || c[1] < 0. || c[2] > 1. || c[2] < 0.) continue;
s[0] = sqrtf((float)(1.-c[0]*c[0]));
s[1] = sqrtf((float)(1.-c[1]*c[1]));
s[2] = sqrtf((float)(1.-c[2]*c[2]));
if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","s");
if (s[0] <= cEPS || s[1] <= cEPS || s[2] <= cEPS) {
//MSG("position lies outside the triangle on the same plane -> ignore it");
continue;
}
const mvmFloat u0x = u[0][0];
const mvmFloat u0y = u[0][1];
const mvmFloat u0z = u[0][2];
const mvmFloat u1x = u[1][0];
const mvmFloat u1y = u[1][1];
const mvmFloat u1z = u[1][2];
const mvmFloat u2x = u[2][0];
const mvmFloat u2y = u[2][1];
const mvmFloat u2z = u[2][2];
mvmFloat det = u0x*u1y*u2z - u0x*u1z*u2y + u0y*u1z*u2x - u0y*u1x*u2z + u0z*u1x*u2y - u0z*u1y*u2x;
//assert(det != 0.);
if (det < 0.) {
s[0] = -s[0];
s[1] = -s[1];
s[2] = -s[2];
}
tds.weights[indices[0]].weight += (phi[0]-c[1]*phi[2]-c[2]*phi[1])/(d[0]*sin(phi[1])*s[2]);
tds.weights[indices[1]].weight += (phi[1]-c[2]*phi[0]-c[0]*phi[2])/(d[1]*sin(phi[2])*s[0]);
tds.weights[indices[2]].weight += (phi[2]-c[0]*phi[1]-c[1]*phi[0])/(d[2]*sin(phi[0])*s[1]);
if(mvmFullDebug) { errMsg("MeanValueMeshCoords::computeWeights","i"<<indices[0]<<" o"<<tds.weights[indices[0]].weight);
errMsg("MeanValueMeshCoords::computeWeights","i"<<indices[1]<<" o"<<tds.weights[indices[1]].weight);
errMsg("MeanValueMeshCoords::computeWeights","i"<<indices[2]<<" o"<<tds.weights[indices[2]].weight);
errMsg("MeanValueMeshCoords::computeWeights","\n\n\n"); }
}
//sort weights
if((numweights>0.)&& (numweights<1.) ) {
//if( ((int)tds.weights.size() > maxNumWeights) && (maxNumWeights > 0) ) {
int maxNumWeights = (int)(tds.weights.size()*numweights);
if(maxNumWeights<=0) maxNumWeights = 1;
std::sort(tds.weights.begin(), tds.weights.end(), std::greater<mvmIndexWeight>());
// only use maxNumWeights-th largest weights
tds.weights.resize(maxNumWeights);
}
// normalize weights
mvmFloat totalWeight = 0.;
for (vector<mvmIndexWeight>::const_iterator witer = tds.weights.begin();
witer != tds.weights.end(); ++witer) {
totalWeight += witer->weight;
}
mvmFloat invTotalWeight;
if (totalWeight == 0.) {
if(mvmFullDebug) errMsg("MeanValueMeshCoords::computeWeights","totalWeight == 0");
invTotalWeight = 0.0;
} else {
invTotalWeight = 1.0/totalWeight;
}
for (vector<mvmIndexWeight>::iterator viter = tds.weights.begin();
viter != tds.weights.end(); ++viter) {
viter->weight *= invTotalWeight;
//assert(finite(viter->weight) != 0);
if(!finite(viter->weight)) viter->weight=0.;
}
}
void MeanValueMeshCoords::transfer(vector<ntlVec3Gfx> &vertices, vector<ntlVec3Gfx>& displacements)
{
displacements.resize(0);
//debMsgStd("MeanValueMeshCoords::transfer",DM_MSG,"vertices:"<<mNumVerts<<" curr_verts:"<<vertices.size()<<" ",7 );
if((int)vertices.size() != mNumVerts) {
errMsg("MeanValueMeshCoords::transfer","Different no of verts: "<<vertices.size()<<" vs "<<mNumVerts);
return;
}
for (vector<mvmTransferPoint>::iterator titer = mVertices.begin(); titer != mVertices.end(); ++titer) {
mvmTransferPoint &tds = *titer;
ntlVec3Gfx newpos(0.0);
for (vector<mvmIndexWeight>::iterator witer = tds.weights.begin();
witer != tds.weights.end(); ++witer) {
newpos += vertices[witer->index] * witer->weight;
//errMsg("transfer","np"<<newpos<<" v"<<vertices[witer->index]<<" w"<< witer->weight);
}
displacements.push_back(newpos);
//displacements.push_back(newpos - tds.lastpos);
//tds.lastpos = newpos;
}
}

View File

@@ -0,0 +1,89 @@
/******************************************************************************
*
// El'Beem - the visual lattice boltzmann freesurface simulator
// All code distributed as part of El'Beem is covered by the version 2 of the
// GNU General Public License. See the file COPYING for details.
//
// Copyright 2008 Nils Thuerey , Richard Keiser, Mark Pauly, Ulrich Ruede
//
*
* Mean Value Mesh Coords class
*
*****************************************************************************/
#ifndef MVMCOORDS_H
#define MVMCOORDS_H
#include "utilities.h"
#include "ntl_ray.h"
#include <vector>
#define mvmFloat double
#ifdef WIN32
#ifndef FREE_WINDOWS
#include "float.h"
#define isnan(n) _isnan(n)
#define finite _finite
#endif
#endif
#ifdef sun
#include "ieeefp.h"
#endif
// weight and triangle index
class mvmIndexWeight {
public:
mvmIndexWeight() : weight(0.0) {}
mvmIndexWeight(int const& i, mvmFloat const& w) :
weight(w), index(i) {}
// for sorting
bool operator> (mvmIndexWeight const& w) const { return this->weight > w.weight; }
bool operator< (mvmIndexWeight const& w) const { return this->weight < w.weight; }
mvmFloat weight;
int index;
};
// transfer point with weights
class mvmTransferPoint {
public:
//! position of transfer point
ntlVec3Gfx lastpos;
//! triangle weights
std::vector<mvmIndexWeight> weights;
};
//! compute mvmcs
class MeanValueMeshCoords {
public:
MeanValueMeshCoords() {}
~MeanValueMeshCoords() {
clear();
}
void clear();
void calculateMVMCs(std::vector<ntlVec3Gfx> &reference_vertices,
std::vector<ntlTriangle> &tris, std::vector<ntlVec3Gfx> &points, gfxReal numweights);
void transfer(std::vector<ntlVec3Gfx> &vertices, std::vector<ntlVec3Gfx>& displacements);
protected:
void computeWeights(std::vector<ntlVec3Gfx> &reference_vertices,
std::vector<ntlTriangle> &tris, mvmTransferPoint& tds, gfxReal numweights);
std::vector<mvmTransferPoint> mVertices;
int mNumVerts;
};
#endif

View File

@@ -0,0 +1,1016 @@
/******************************************************************************
*
* El'Beem - the visual lattice boltzmann freesurface simulator
* All code distributed as part of El'Beem is covered by the version 2 of the
* GNU General Public License. See the file COPYING for details.
*
* Copyright 2003-2008 Nils Thuerey
*
* control extensions
*
*****************************************************************************/
#include "solver_class.h"
#include "solver_relax.h"
#include "particletracer.h"
#include "solver_control.h"
#include "controlparticles.h"
#include "elbeem.h"
#include "ntl_geometrymodel.h"
/******************************************************************************
* LbmControlData control set
*****************************************************************************/
LbmControlSet::LbmControlSet() :
mCparts(NULL), mCpmotion(NULL), mContrPartFile(""), mCpmotionFile(""),
mcForceAtt(0.), mcForceVel(0.), mcForceMaxd(0.),
mcRadiusAtt(0.), mcRadiusVel(0.), mcRadiusMind(0.), mcRadiusMaxd(0.),
mcCpScale(1.), mcCpOffset(0.)
{
}
LbmControlSet::~LbmControlSet() {
if(mCparts) delete mCparts;
if(mCpmotion) delete mCpmotion;
}
void LbmControlSet::initCparts() {
mCparts = new ControlParticles();
mCpmotion = new ControlParticles();
}
/******************************************************************************
* LbmControlData control
*****************************************************************************/
LbmControlData::LbmControlData() :
mSetForceStrength(0.),
mCons(),
mCpUpdateInterval(8), // DG: was 16 --> causes problems (big sphere after some time), unstable
mCpOutfile(""),
mCpForces(), mCpKernel(), mMdKernel(),
mDiffVelCon(1.),
mDebugCpscale(0.),
mDebugVelScale(0.),
mDebugCompavScale(0.),
mDebugAttScale(0.),
mDebugMaxdScale(0.),
mDebugAvgVelScale(0.)
{
}
LbmControlData::~LbmControlData()
{
while (!mCons.empty()) {
delete mCons.back(); mCons.pop_back();
}
}
void LbmControlData::parseControldataAttrList(AttributeList *attr) {
// controlpart vars
mSetForceStrength = attr->readFloat("tforcestrength", mSetForceStrength,"LbmControlData", "mSetForceStrength", false);
//errMsg("tforcestrength set to "," "<<mSetForceStrength);
mCpUpdateInterval = attr->readInt("controlparticle_updateinterval", mCpUpdateInterval,"LbmControlData","mCpUpdateInterval", false);
// tracer output file
mCpOutfile = attr->readString("controlparticle_outfile",mCpOutfile,"LbmControlData","mCpOutfile", false);
if(getenv("ELBEEM_CPOUTFILE")) {
string outfile(getenv("ELBEEM_CPOUTFILE"));
mCpOutfile = outfile;
debMsgStd("LbmControlData::parseAttrList",DM_NOTIFY,"Using envvar ELBEEM_CPOUTFILE to set mCpOutfile to "<<outfile<<","<<mCpOutfile,7);
}
for(int cpii=0; cpii<10; cpii++) {
string suffix("");
//if(cpii>0)
{ suffix = string("0"); suffix[0]+=cpii; }
LbmControlSet *cset;
cset = new LbmControlSet();
cset->initCparts();
cset->mContrPartFile = attr->readString("controlparticle"+suffix+"_file",cset->mContrPartFile,"LbmControlData","cset->mContrPartFile", false);
if((cpii==0) && (getenv("ELBEEM_CPINFILE")) ) {
string infile(getenv("ELBEEM_CPINFILE"));
cset->mContrPartFile = infile;
debMsgStd("LbmControlData::parseAttrList",DM_NOTIFY,"Using envvar ELBEEM_CPINFILE to set mContrPartFile to "<<infile<<","<<cset->mContrPartFile,7);
}
LbmFloat cpvort=0.;
cset->mcRadiusAtt = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusatt", 0., "LbmControlData","mcRadiusAtt" );
cset->mcRadiusVel = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusvel", 0., "LbmControlData","mcRadiusVel" );
cset->mcRadiusVel = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusvel", 0., "LbmControlData","mcRadiusVel" );
cset->mCparts->setRadiusAtt(cset->mcRadiusAtt.get(0.));
cset->mCparts->setRadiusVel(cset->mcRadiusVel.get(0.));
// WARNING currently only for first set
//if(cpii==0) {
cset->mcForceAtt = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_attraction", 0. , "LbmControlData","cset->mcForceAtt", false);
cset->mcForceVel = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_velocity", 0. , "LbmControlData","mcForceVel", false);
cset->mcForceMaxd = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_maxdist", 0. , "LbmControlData","mcForceMaxd", false);
cset->mCparts->setInfluenceAttraction(cset->mcForceAtt.get(0.) );
// warning - stores temprorarily, value converted to dt dep. factor
cset->mCparts->setInfluenceVelocity(cset->mcForceVel.get(0.) , 0.01 ); // dummy dt
cset->mCparts->setInfluenceMaxdist(cset->mcForceMaxd.get(0.) );
cpvort = attr->readFloat("controlparticle"+suffix+"_vorticity", cpvort, "LbmControlData","cpvort", false);
cset->mCparts->setInfluenceTangential(cpvort);
cset->mcRadiusMind = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusmin", cset->mcRadiusMind.get(0.), "LbmControlData","mcRadiusMind", false);
cset->mcRadiusMaxd = attr->readChannelSinglePrecFloat("controlparticle"+suffix+"_radiusmax", cset->mcRadiusMind.get(0.), "LbmControlData","mcRadiusMaxd", false);
cset->mCparts->setRadiusMinMaxd(cset->mcRadiusMind.get(0.));
cset->mCparts->setRadiusMaxd(cset->mcRadiusMaxd.get(0.));
//}
// now local...
//LbmVec cpOffset(0.), cpScale(1.);
LbmFloat cpTimescale = 1.;
string cpMirroring("");
//cset->mcCpOffset = attr->readChannelVec3f("controlparticle"+suffix+"_offset", ntlVec3f(0.),"LbmControlData","mcCpOffset", false);
//cset->mcCpScale = attr->readChannelVec3f("controlparticle"+suffix+"_scale", ntlVec3f(1.), "LbmControlData","mcCpScale", false);
cset->mcCpOffset = attr->readChannelVec3f("controlparticle"+suffix+"_offset", ntlVec3f(0.),"LbmControlData","mcCpOffset", false);
cset->mcCpScale = attr->readChannelVec3f("controlparticle"+suffix+"_scale", ntlVec3f(1.), "LbmControlData","mcCpScale", false);
cpTimescale = attr->readFloat("controlparticle"+suffix+"_timescale", cpTimescale, "LbmControlData","cpTimescale", false);
cpMirroring = attr->readString("controlparticle"+suffix+"_mirror", cpMirroring, "LbmControlData","cpMirroring", false);
LbmFloat cpsWidth = cset->mCparts->getCPSWith();
cpsWidth = attr->readFloat("controlparticle"+suffix+"_cpswidth", cpsWidth, "LbmControlData","cpsWidth", false);
LbmFloat cpsDt = cset->mCparts->getCPSTimestep();
cpsDt = attr->readFloat("controlparticle"+suffix+"_cpstimestep", cpsDt, "LbmControlData","cpsDt", false);
LbmFloat cpsTstart = cset->mCparts->getCPSTimeStart();
cpsTstart = attr->readFloat("controlparticle"+suffix+"_cpststart", cpsTstart, "LbmControlData","cpsTstart", false);
LbmFloat cpsTend = cset->mCparts->getCPSTimeEnd();
cpsTend = attr->readFloat("controlparticle"+suffix+"_cpstend", cpsTend, "LbmControlData","cpsTend", false);
LbmFloat cpsMvmfac = cset->mCparts->getCPSMvmWeightFac();
cpsMvmfac = attr->readFloat("controlparticle"+suffix+"_cpsmvmfac", cpsMvmfac, "LbmControlData","cpsMvmfac", false);
cset->mCparts->setCPSWith(cpsWidth);
cset->mCparts->setCPSTimestep(cpsDt);
cset->mCparts->setCPSTimeStart(cpsTstart);
cset->mCparts->setCPSTimeEnd(cpsTend);
cset->mCparts->setCPSMvmWeightFac(cpsMvmfac);
cset->mCparts->setOffset( vec2L(cset->mcCpOffset.get(0.)) );
cset->mCparts->setScale( vec2L(cset->mcCpScale.get(0.)) );
cset->mCparts->setInitTimeScale( cpTimescale );
cset->mCparts->setInitMirror( cpMirroring );
int mDebugInit = 0;
mDebugInit = attr->readInt("controlparticle"+suffix+"_debuginit", mDebugInit,"LbmControlData","mDebugInit", false);
cset->mCparts->setDebugInit(mDebugInit);
// motion particle settings
LbmVec mcpOffset(0.), mcpScale(1.);
LbmFloat mcpTimescale = 1.;
string mcpMirroring("");
cset->mCpmotionFile = attr->readString("cpmotion"+suffix+"_file",cset->mCpmotionFile,"LbmControlData","mCpmotionFile", false);
mcpTimescale = attr->readFloat("cpmotion"+suffix+"_timescale", mcpTimescale, "LbmControlData","mcpTimescale", false);
mcpMirroring = attr->readString("cpmotion"+suffix+"_mirror", mcpMirroring, "LbmControlData","mcpMirroring", false);
mcpOffset = vec2L( attr->readVec3d("cpmotion"+suffix+"_offset", vec2P(mcpOffset),"LbmControlData","cpOffset", false) );
mcpScale = vec2L( attr->readVec3d("cpmotion"+suffix+"_scale", vec2P(mcpScale), "LbmControlData","cpScale", false) );
cset->mCpmotion->setOffset( vec2L(mcpOffset) );
cset->mCpmotion->setScale( vec2L(mcpScale) );
cset->mCpmotion->setInitTimeScale( mcpTimescale );
cset->mCpmotion->setInitMirror( mcpMirroring );
if(cset->mContrPartFile.length()>1) {
errMsg("LbmControlData","Using control particle set "<<cpii<<" file:"<<cset->mContrPartFile<<" cpmfile:"<<cset->mCpmotionFile<<" mirr:'"<<cset->mCpmotion->getInitMirror()<<"' " );
mCons.push_back( cset );
} else {
delete cset;
}
}
// debug, testing - make sure theres at least an empty set
if(mCons.size()<1) {
mCons.push_back( new LbmControlSet() );
mCons[0]->initCparts();
}
// take from first set
for(int cpii=1; cpii<(int)mCons.size(); cpii++) {
mCons[cpii]->mCparts->setRadiusMinMaxd( mCons[0]->mCparts->getRadiusMinMaxd() );
mCons[cpii]->mCparts->setRadiusMaxd( mCons[0]->mCparts->getRadiusMaxd() );
mCons[cpii]->mCparts->setInfluenceAttraction( mCons[0]->mCparts->getInfluenceAttraction() );
mCons[cpii]->mCparts->setInfluenceTangential( mCons[0]->mCparts->getInfluenceTangential() );
mCons[cpii]->mCparts->setInfluenceVelocity( mCons[0]->mCparts->getInfluenceVelocity() , 0.01 ); // dummy dt
mCons[cpii]->mCparts->setInfluenceMaxdist( mCons[0]->mCparts->getInfluenceMaxdist() );
}
// invert for usage in relax macro
mDiffVelCon = 1.-attr->readFloat("cpdiffvelcon", mDiffVelCon, "LbmControlData","mDiffVelCon", false);
mDebugCpscale = attr->readFloat("cpdebug_cpscale", mDebugCpscale, "LbmControlData","mDebugCpscale", false);
mDebugMaxdScale = attr->readFloat("cpdebug_maxdscale", mDebugMaxdScale, "LbmControlData","mDebugMaxdScale", false);
mDebugAttScale = attr->readFloat("cpdebug_attscale", mDebugAttScale, "LbmControlData","mDebugAttScale", false);
mDebugVelScale = attr->readFloat("cpdebug_velscale", mDebugVelScale, "LbmControlData","mDebugVelScale", false);
mDebugCompavScale = attr->readFloat("cpdebug_compavscale", mDebugCompavScale, "LbmControlData","mDebugCompavScale", false);
mDebugAvgVelScale = attr->readFloat("cpdebug_avgvelsc", mDebugAvgVelScale, "LbmControlData","mDebugAvgVelScale", false);
}
void
LbmFsgrSolver::initCpdata()
{
// enable for cps via env. vars
//if( (getenv("ELBEEM_CPINFILE")) || (getenv("ELBEEM_CPOUTFILE")) ){ mUseTestdata=1; }
// manually switch on! if this is zero, nothing is done...
mpControl->mSetForceStrength = this->mTForceStrength = 1.;
mpControl->mCons.clear();
// init all control fluid objects
int numobjs = (int)(mpGiObjects->size());
for(int o=0; o<numobjs; o++) {
ntlGeometryObjModel *obj = (ntlGeometryObjModel *)(*mpGiObjects)[o];
if(obj->getGeoInitType() & FGI_CONTROL) {
// add new control set per object
LbmControlSet *cset;
cset = new LbmControlSet();
cset->initCparts();
// dont load any file
cset->mContrPartFile = string("");
cset->mcForceAtt = obj->getCpsAttrFStr();
cset->mcRadiusAtt = obj->getCpsAttrFRad();
cset->mcForceVel = obj->getCpsVelFStr();
cset->mcRadiusVel = obj->getCpsVelFRad();
cset->mCparts->setCPSTimeStart(obj->getCpsTimeStart());
cset->mCparts->setCPSTimeEnd(obj->getCpsTimeEnd());
if(obj->getCpsQuality() > LBM_EPSILON)
cset->mCparts->setCPSWith(1.0 / obj->getCpsQuality());
// this value can be left at 0.5:
cset->mCparts->setCPSMvmWeightFac(0.5);
mpControl->mCons.push_back( cset );
mpControl->mCons[mpControl->mCons.size()-1]->mCparts->initFromObject(obj);
}
}
// NT blender integration manual test setup
if(0) {
// manually switch on! if this is zero, nothing is done...
mpControl->mSetForceStrength = this->mTForceStrength = 1.;
mpControl->mCons.clear();
// add new set
LbmControlSet *cset;
cset = new LbmControlSet();
cset->initCparts();
// dont load any file
cset->mContrPartFile = string("");
// set radii for attraction & velocity forces
// set strength of the forces
// don't set directly! but use channels:
// mcForceAtt, mcForceVel, mcForceMaxd, mcRadiusAtt, mcRadiusVel, mcRadiusMind, mcRadiusMaxd etc.
// wrong: cset->mCparts->setInfluenceAttraction(1.15); cset->mCparts->setRadiusAtt(1.5);
// right, e.g., to init some constant values:
cset->mcForceAtt = AnimChannel<float>(0.2);
cset->mcRadiusAtt = AnimChannel<float>(0.75);
cset->mcForceVel = AnimChannel<float>(0.2);
cset->mcRadiusVel = AnimChannel<float>(0.75);
// this value can be left at 0.5:
cset->mCparts->setCPSMvmWeightFac(0.5);
mpControl->mCons.push_back( cset );
// instead of reading from file (cset->mContrPartFile), manually init some particles
mpControl->mCons[0]->mCparts->initBlenderTest();
// other values that might be interesting to change:
//cset->mCparts->setCPSTimestep(0.02);
//cset->mCparts->setCPSTimeStart(0.);
//cset->mCparts->setCPSTimeEnd(1.);
//mpControl->mDiffVelCon = 1.; // more rigid velocity control, 0 (default) allows more turbulence
}
// control particle -------------------------------------------------------------------------------------
// init cppf stage, use set 0!
if(mCppfStage>0) {
if(mpControl->mCpOutfile.length()<1) mpControl->mCpOutfile = string("cpout"); // use getOutFilename !?
char strbuf[100];
const char *cpFormat = "_d%dcppf%d";
// initial coarse stage, no input
if(mCppfStage==1) {
mpControl->mCons[0]->mContrPartFile = "";
} else {
// read from prev stage
snprintf(strbuf,100, cpFormat ,LBMDIM,mCppfStage-1);
mpControl->mCons[0]->mContrPartFile = mpControl->mCpOutfile;
mpControl->mCons[0]->mContrPartFile += strbuf;
mpControl->mCons[0]->mContrPartFile += ".cpart2";
}
snprintf(strbuf,100, cpFormat ,LBMDIM,mCppfStage);
mpControl->mCpOutfile += strbuf;
} // */
for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
// now set with real dt
cparts->setInfluenceVelocity( mpControl->mCons[cpssi]->mcForceVel.get(0.), mLevel[mMaxRefine].timestep);
cparts->setCharLength( mLevel[mMaxRefine].nodeSize );
cparts->setCharLength( mLevel[mMaxRefine].nodeSize );
errMsg("LbmControlData","CppfStage "<<mCppfStage<<" in:"<<mpControl->mCons[cpssi]->mContrPartFile<<
" out:"<<mpControl->mCpOutfile<<" cl:"<< cparts->getCharLength() );
// control particle test init
if(mpControl->mCons[cpssi]->mCpmotionFile.length()>=1) cpmotion->initFromTextFile(mpControl->mCons[cpssi]->mCpmotionFile);
// not really necessary...
//? cparts->setFluidSpacing( mLevel[mMaxRefine].nodeSize ); // use grid coords!?
//? cparts->calculateKernelWeight();
//? debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles - motion inited: "<<cparts->getSize() ,10);
// ensure both are on for env. var settings
// when no particles, but outfile enabled, initialize
const int lev = mMaxRefine;
if((mpParticles) && (mpControl->mCpOutfile.length()>=1) && (cpssi==0)) {
// check if auto num
if( (mpParticles->getNumInitialParticles()<=1) &&
(mpParticles->getNumParticles()<=1) ) { // initParticles done afterwards anyway
int tracers = 0;
const int workSet = mLevel[lev].setCurr;
FSGR_FORIJK_BOUNDS(lev) {
if(RFLAG(lev,i,j,k, workSet)&(CFFluid)) tracers++;
}
if(LBMDIM==3) tracers /= 8;
else tracers /= 4;
mpParticles->setNumInitialParticles(tracers);
mpParticles->setDumpTextFile(mpControl->mCpOutfile);
debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles - set tracers #"<<tracers<<", actual #"<<mpParticles->getNumParticles() ,10);
}
if(mpParticles->getDumpTextInterval()<=0.) {
mpParticles->setDumpTextInterval(mLevel[lev].timestep * mLevel[lev].lSizex);
debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles - dump delta t not set, using dti="<< mpParticles->getDumpTextInterval()<<", sim dt="<<mLevel[lev].timestep, 5 );
}
mpParticles->setDumpParts(true); // DEBUG? also dump as particle system
}
if(mpControl->mCons[cpssi]->mContrPartFile.length()>=1) cparts->initFromTextFile(mpControl->mCons[cpssi]->mContrPartFile);
cparts->setFluidSpacing( mLevel[lev].nodeSize ); // use grid coords!?
cparts->calculateKernelWeight();
debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles mCons"<<cpssi<<" - inited, parts:"<<cparts->getTotalSize()<<","<<cparts->getSize()<<" dt:"<<mpParam->getTimestep()<<" control time:"<<cparts->getControlTimStart()<<" to "<<cparts->getControlTimEnd() ,10);
} // cpssi
if(getenv("ELBEEM_CPINFILE")) {
this->mTForceStrength = 1.0;
}
this->mTForceStrength = mpControl->mSetForceStrength;
if(mpControl->mCpOutfile.length()>=1) mpParticles->setDumpTextFile(mpControl->mCpOutfile);
// control particle init end -------------------------------------------------------------------------------------
// make sure equiv to solver init
if(this->mTForceStrength>0.) { \
mpControl->mCpForces.resize( mMaxRefine+1 );
for(int lev = 0; lev<=mMaxRefine; lev++) {
LONGINT rcellSize = (mLevel[lev].lSizex*mLevel[lev].lSizey*mLevel[lev].lSizez);
debMsgStd("LbmFsgrSolver::initControl",DM_MSG,"mCpForces init, lev="<<lev<<" rcs:"<<(int)(rcellSize+4)<<","<<(rcellSize*sizeof(ControlForces)/(1024*1024)), 9 );
mpControl->mCpForces[lev].resize( (int)(rcellSize+4) );
//for(int i=0 ;i<rcellSize; i++) mpControl->mCpForces.push_back( ControlForces() );
for(int i=0 ;i<rcellSize; i++) mpControl->mCpForces[lev][i].resetForces();
}
} // on?
debMsgStd("LbmFsgrSolver::initCpdata",DM_MSG,"ControlParticles #mCons "<<mpControl->mCons.size()<<" done", 6);
}
#define CPODEBUG 0
//define CPINTER ((int)(mpControl->mCpUpdateInterval))
#define KERN(x,y,z) mpControl->mCpKernel[ (((z)*cpkarWidth + (y))*cpkarWidth + (x)) ]
#define MDKERN(x,y,z) mpControl->mMdKernel[ (((z)*mdkarWidth + (y))*mdkarWidth + (x)) ]
#define BOUNDCHECK(x,low,high) ( ((x)<low) ? low : (((x)>high) ? high : (x) ) )
#define BOUNDSKIP(x,low,high) ( ((x)<low) || ((x)>high) )
void
LbmFsgrSolver::handleCpdata()
{
myTime_t cpstart = getTime();
int cpChecks=0;
int cpInfs=0;
//debMsgStd("ControlData::handleCpdata",DM_MSG,"called... "<<this->mTForceStrength,1);
// add cp influence
if((true) && (this->mTForceStrength>0.)) {
// ok continue...
} // on off
else {
return;
}
if((mpControl->mCpUpdateInterval<1) || (this->mStepCnt%mpControl->mCpUpdateInterval==0)) {
// do full reinit later on...
}
else if(this->mStepCnt>mpControl->mCpUpdateInterval) {
// only reinit new cells
// TODO !? remove loop dependance!?
#define NOFORCEENTRY(lev, i,j,k) (LBMGET_FORCE(lev, i,j,k).maxDistance==CPF_MAXDINIT)
// interpolate missing
for(int lev=0; lev<=mMaxRefine; lev++) {
FSGR_FORIJK_BOUNDS(lev) {
if( (RFLAG(lev,i,j,k, mLevel[lev].setCurr)) & (CFFluid|CFInter) )
//if( (RFLAG(lev,i,j,k, mLevel[lev].setCurr)) & (CFInter) )
//if(0)
{ // only check new inter? RFLAG?check
if(NOFORCEENTRY(lev, i,j,k)) {
//errMsg("CP","FE_MISSING at "<<PRINT_IJK<<" f"<<LBMGET_FORCE(lev, i,j,k).weightAtt<<" md"<<LBMGET_FORCE(lev, i,j,k).maxDistance );
LbmFloat nbs=0.;
ControlForces vals;
vals.resetForces(); vals.maxDistance = 0.;
for(int l=1; l<this->cDirNum; l++) {
int ni=i+this->dfVecX[l], nj=j+this->dfVecY[l], nk=k+this->dfVecZ[l];
//errMsg("CP","FE_MISSING check "<<PRINT_VEC(ni,nj,nk)<<" f"<<LBMGET_FORCE(lev, ni,nj,nk).weightAtt<<" md"<<LBMGET_FORCE(lev, ni,nj,nk).maxDistance );
if(!NOFORCEENTRY(lev, ni,nj,nk)) {
//? vals.weightAtt += LBMGET_FORCE(lev, ni,nj,nk).weightAtt;
//? vals.forceAtt += LBMGET_FORCE(lev, ni,nj,nk).forceAtt;
vals.maxDistance += LBMGET_FORCE(lev, ni,nj,nk).maxDistance;
vals.forceMaxd += LBMGET_FORCE(lev, ni,nj,nk).forceMaxd;
vals.weightVel += LBMGET_FORCE(lev, ni,nj,nk).weightVel;
vals.forceVel += LBMGET_FORCE(lev, ni,nj,nk).forceVel;
// ignore att/compAv/avgVel here for now
nbs += 1.;
}
}
if(nbs>0.) {
nbs = 1./nbs;
//? LBMGET_FORCE(lev, i,j,k).weightAtt = vals.weightAtt*nbs;
//? LBMGET_FORCE(lev, i,j,k).forceAtt = vals.forceAtt*nbs;
LBMGET_FORCE(lev, i,j,k).maxDistance = vals.maxDistance*nbs;
LBMGET_FORCE(lev, i,j,k).forceMaxd = vals.forceMaxd*nbs;
LBMGET_FORCE(lev, i,j,k).weightVel = vals.weightVel*nbs;
LBMGET_FORCE(lev, i,j,k).forceVel = vals.forceVel*nbs;
}
/*ControlForces *ff = &LBMGET_FORCE(lev, i,j,k); // DEBUG
errMsg("CP","FE_MISSING rec at "<<PRINT_IJK // DEBUG
<<" w:"<<ff->weightAtt<<" wa:" <<PRINT_VEC( ff->forceAtt[0],ff->forceAtt[1],ff->forceAtt[2] )
<<" v:"<<ff->weightVel<<" wv:" <<PRINT_VEC( ff->forceVel[0],ff->forceVel[1],ff->forceVel[2] )
<<" v:"<<ff->maxDistance<<" wv:" <<PRINT_VEC( ff->forceMaxd[0],ff->forceMaxd[1],ff->forceMaxd[2] ) ); // DEBUG */
// else errMsg("CP","FE_MISSING rec at "<<PRINT_IJK<<" failed!"); // DEBUG
}
}
}} // ijk, lev
// mStepCnt > mpControl->mCpUpdateInterval
return;
} else {
// nothing to do ...
return;
}
// reset
for(int lev=0; lev<=mMaxRefine; lev++) {
FSGR_FORIJK_BOUNDS(lev) { LBMGET_FORCE(lev,i,j,k).resetForces(); }
}
// do setup for coarsest level
const int coarseLev = 0;
const int fineLev = mMaxRefine;
// init for current time
for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
LbmControlSet *cset = mpControl->mCons[cpssi];
cparts->setRadiusAtt(cset->mcRadiusAtt.get(mSimulationTime));
cparts->setRadiusVel(cset->mcRadiusVel.get(mSimulationTime));
cparts->setInfluenceAttraction(cset->mcForceAtt.get(mSimulationTime) );
cparts->setInfluenceMaxdist(cset->mcForceMaxd.get(mSimulationTime) );
cparts->setRadiusMinMaxd(cset->mcRadiusMind.get(mSimulationTime));
cparts->setRadiusMaxd(cset->mcRadiusMaxd.get(mSimulationTime));
cparts->calculateKernelWeight(); // always necessary!?
cparts->setOffset( vec2L(cset->mcCpOffset.get(mSimulationTime)) );
cparts->setScale( vec2L(cset->mcCpScale.get(mSimulationTime)) );
cparts->setInfluenceVelocity( cset->mcForceVel.get(mSimulationTime), mLevel[fineLev].timestep );
cparts->setLastOffset( vec2L(cset->mcCpOffset.get(mSimulationTime-mLevel[fineLev].timestep)) );
cparts->setLastScale( vec2L(cset->mcCpScale.get(mSimulationTime-mLevel[fineLev].timestep)) );
}
// check actual values
LbmFloat iatt = ABS(mpControl->mCons[0]->mCparts->getInfluenceAttraction());
LbmFloat ivel = mpControl->mCons[0]->mCparts->getInfluenceVelocity();
LbmFloat imaxd = mpControl->mCons[0]->mCparts->getInfluenceMaxdist();
//errMsg("FINCIT","iatt="<<iatt<<" ivel="<<ivel<<" imaxd="<<imaxd);
for(int cpssi=1; cpssi<(int)mpControl->mCons.size(); cpssi++) {
LbmFloat iatt2 = ABS(mpControl->mCons[cpssi]->mCparts->getInfluenceAttraction());
LbmFloat ivel2 = mpControl->mCons[cpssi]->mCparts->getInfluenceVelocity();
LbmFloat imaxd2 = mpControl->mCons[cpssi]->mCparts->getInfluenceMaxdist();
// we allow negative attraction force here!
if(iatt2 > iatt) iatt = iatt2;
if(ivel2 >ivel) ivel = ivel2;
if(imaxd2>imaxd) imaxd= imaxd2;
//errMsg("FINCIT"," "<<cpssi<<" iatt2="<<iatt2<<" ivel2="<<ivel2<<" imaxd2="<<imaxd<<" NEW "<<" iatt="<<iatt<<" ivel="<<ivel<<" imaxd="<<imaxd);
}
if(iatt==0. && ivel==0. && imaxd==0.) {
debMsgStd("ControlData::initControl",DM_MSG,"Skipped, all zero...",4);
return;
}
//iatt = mpControl->mCons[1]->mCparts->getInfluenceAttraction(); //ivel = mpControl->mCons[1]->mCparts->getInfluenceVelocity(); //imaxd = mpControl->mCons[1]->mCparts->getInfluenceMaxdist(); // TTTTTT
// do control setup
for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
// TEST!?
bool radmod = false;
const LbmFloat minRadSize = mLevel[coarseLev].nodeSize * 1.5;
if((cparts->getRadiusAtt()>0.) && (cparts->getRadiusAtt()<minRadSize) && (!radmod) ) {
LbmFloat radfac = minRadSize / cparts->getRadiusAtt(); radmod=true;
debMsgStd("ControlData::initControl",DM_MSG,"Modified radii att, fac="<<radfac, 7);
cparts->setRadiusAtt(cparts->getRadiusAtt()*radfac);
cparts->setRadiusVel(cparts->getRadiusVel()*radfac);
cparts->setRadiusMaxd(cparts->getRadiusMaxd()*radfac);
cparts->setRadiusMinMaxd(cparts->getRadiusMinMaxd()*radfac);
} else if((cparts->getRadiusVel()>0.) && (cparts->getRadiusVel()<minRadSize) && (!radmod) ) {
LbmFloat radfac = minRadSize / cparts->getRadiusVel();
debMsgStd("ControlData::initControl",DM_MSG,"Modified radii vel, fac="<<radfac, 7);
cparts->setRadiusVel(cparts->getRadiusVel()*radfac);
cparts->setRadiusMaxd(cparts->getRadiusMaxd()*radfac);
cparts->setRadiusMinMaxd(cparts->getRadiusMinMaxd()*radfac);
} else if((cparts->getRadiusMaxd()>0.) && (cparts->getRadiusMaxd()<minRadSize) && (!radmod) ) {
LbmFloat radfac = minRadSize / cparts->getRadiusMaxd();
debMsgStd("ControlData::initControl",DM_MSG,"Modified radii maxd, fac="<<radfac, 7);
cparts->setRadiusMaxd(cparts->getRadiusMaxd()*radfac);
cparts->setRadiusMinMaxd(cparts->getRadiusMinMaxd()*radfac);
}
if(radmod) {
debMsgStd("ControlData::initControl",DM_MSG,"Modified radii: att="<<
cparts->getRadiusAtt()<<", vel=" << cparts->getRadiusVel()<<", maxd=" <<
cparts->getRadiusMaxd()<<", mind=" << cparts->getRadiusMinMaxd() ,5);
}
cpmotion->prepareControl( mSimulationTime+((LbmFloat)mpControl->mCpUpdateInterval)*(mpParam->getTimestep()), mpParam->getTimestep(), NULL );
cparts->prepareControl( mSimulationTime+((LbmFloat)mpControl->mCpUpdateInterval)*(mpParam->getTimestep()), mpParam->getTimestep(), cpmotion );
}
// do control...
for(int lev=0; lev<=mMaxRefine; lev++) {
LbmFloat levVolume = 1.;
LbmFloat levForceScale = 1.;
for(int ll=lev; ll<mMaxRefine; ll++) {
if(LBMDIM==3) levVolume *= 8.;
else levVolume *= 4.;
levForceScale *= 2.;
}
errMsg("LbmFsgrSolver::handleCpdata","levVolume="<<levVolume<<" levForceScale="<<levForceScale );
//todo: scale velocity, att by level timestep!?
for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
// ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
// if control set is not active skip it
if((cparts->getControlTimStart() > mSimulationTime) || (cparts->getControlTimEnd() < mLastSimTime))
{
continue;
}
const LbmFloat velLatticeScale = mLevel[lev].timestep/mLevel[lev].nodeSize;
LbmFloat gsx = ((mvGeoEnd[0]-mvGeoStart[0])/(LbmFloat)mLevel[lev].lSizex);
LbmFloat gsy = ((mvGeoEnd[1]-mvGeoStart[1])/(LbmFloat)mLevel[lev].lSizey);
LbmFloat gsz = ((mvGeoEnd[2]-mvGeoStart[2])/(LbmFloat)mLevel[lev].lSizez);
#if LBMDIM==2
gsz = gsx;
#endif
LbmFloat goffx = mvGeoStart[0];
LbmFloat goffy = mvGeoStart[1];
LbmFloat goffz = mvGeoStart[2];
//const LbmFloat cpwIncFac = 2.0;
// max to two thirds of domain size
const int cpw = MIN( mLevel[lev].lSizex/3, MAX( (int)( cparts->getRadiusAtt() /gsx) +1 , 2) ); // normal kernel, att,vel
const int cpkarWidth = 2*cpw+1;
mpControl->mCpKernel.resize(cpkarWidth* cpkarWidth* cpkarWidth);
ControlParticle cpt; cpt.reset();
cpt.pos = LbmVec( (gsx*(LbmFloat)cpw)+goffx, (gsy*(LbmFloat)cpw)+goffy, (gsz*(LbmFloat)cpw)+goffz ); // optimize?
cpt.density = 0.5; cpt.densityWeight = 0.5;
#if LBMDIM==3
for(int k= 0; k<cpkarWidth; ++k) {
#else // LBMDIM==3
{ int k = cpw;
#endif
for(int j= 0; j<cpkarWidth; ++j)
for(int i= 0; i<cpkarWidth; ++i) {
KERN(i,j,k).resetForces();
//LbmFloat dx = i-cpw; LbmFloat dy = j-cpw; LbmFloat dz = k-cpw;
//LbmVec dv = ( LbmVec(dx,dy,dz) );
//LbmFloat dl = norm( dv ); //LbmVec dir = dv / dl;
LbmVec pos = LbmVec( (gsx*(LbmFloat)i)+goffx, (gsy*(LbmFloat)j)+goffy, (gsz*(LbmFloat)k)+goffz ); // optimize?
cparts->calculateCpInfluenceOpt( &cpt, pos, LbmVec(0,0,0), &KERN(i,j,k) ,1. );
/*if((CPODEBUG)&&(k==cpw)) errMsg("kern"," at "<<PRINT_IJK<<" pos"<<pos<<" cpp"<<cpt.pos
<<" wf:"<<KERN(i,j,k).weightAtt<<" wa:"<< PRINT_VEC( KERN(i,j,k).forceAtt[0],KERN(i,j,k).forceAtt[1],KERN(i,j,k).forceAtt[2] )
<<" wf:"<<KERN(i,j,k).weightVel<<" wa:"<< PRINT_VEC( KERN(i,j,k).forceVel[0],KERN(i,j,k).forceVel[1],KERN(i,j,k).forceVel[2] )
<<" wf:"<<KERN(i,j,k).maxDistance<<" wa:"<< PRINT_VEC( KERN(i,j,k).forceMaxd[0],KERN(i,j,k).forceMaxd[1],KERN(i,j,k).forceMaxd[2] ) ); // */
KERN(i,j,k).weightAtt *= 2.;
KERN(i,j,k).forceAtt *= 2.;
//KERN(i,j,k).forceAtt[1] *= 2.; KERN(i,j,k).forceAtt[2] *= 2.;
KERN(i,j,k).weightVel *= 2.;
KERN(i,j,k).forceVel *= 2.;
//KERN(i,j,k).forceVel[1] *= 2.; KERN(i,j,k).forceVel[2] *= 2.;
}
}
if(CPODEBUG) errMsg("cpw"," = "<<cpw<<" f"<< cparts->getRadiusAtt()<<" gsx"<<gsx<<" kpw"<<cpkarWidth); // DEBUG
// first cp loop - add att and vel forces
for(int cppi=0; cppi<cparts->getSize(); cppi++) {
ControlParticle *cp = cparts->getParticle(cppi);
if(cp->influence<=0.) continue;
const int cpi = (int)( (cp->pos[0]-goffx)/gsx );
const int cpj = (int)( (cp->pos[1]-goffy)/gsy );
int cpk = (int)( (cp->pos[2]-goffz)/gsz );
/*if( ((LBMDIM==3)&&(BOUNDSKIP(cpk - cpwsm, getForZMinBnd(), getForZMaxBnd(lev) ))) ||
((LBMDIM==3)&&(BOUNDSKIP(cpk + cpwsm, getForZMinBnd(), getForZMaxBnd(lev) ))) ||
BOUNDSKIP(cpj - cpwsm, 0, mLevel[lev].lSizey ) ||
BOUNDSKIP(cpj + cpwsm, 0, mLevel[lev].lSizey ) ||
BOUNDSKIP(cpi - cpwsm, 0, mLevel[lev].lSizex ) ||
BOUNDSKIP(cpi + cpwsm, 0, mLevel[lev].lSizex ) ) {
continue;
} // */
int is,ie,js,je,ks,ke;
ks = BOUNDCHECK(cpk - cpw, getForZMinBnd(), getForZMaxBnd(lev) );
ke = BOUNDCHECK(cpk + cpw, getForZMinBnd(), getForZMaxBnd(lev) );
js = BOUNDCHECK(cpj - cpw, 0, mLevel[lev].lSizey );
je = BOUNDCHECK(cpj + cpw, 0, mLevel[lev].lSizey );
is = BOUNDCHECK(cpi - cpw, 0, mLevel[lev].lSizex );
ie = BOUNDCHECK(cpi + cpw, 0, mLevel[lev].lSizex );
if(LBMDIM==2) { cpk = 0; ks = 0; ke = 1; }
if(CPODEBUG) errMsg("cppft","i"<<cppi<<" cpw"<<cpw<<" gpos"<<PRINT_VEC(cpi,cpj,cpk)<<" i:"<<is<<","<<ie<<" j:"<<js<<","<<je<<" k:"<<ks<<","<<ke<<" "); // DEBUG
cpInfs++;
for(int k= ks; k<ke; ++k) {
for(int j= js; j<je; ++j) {
CellFlagType *pflag = &RFLAG(lev,is,j,k, mLevel[lev].setCurr);
ControlForces *kk = &KERN( is-cpi+cpw, j-cpj+cpw, k-cpk+cpw);
ControlForces *ff = &LBMGET_FORCE(lev,is,j,k);
pflag--; kk--; ff--;
for(int i= is; i<ie; ++i) {
// first cp loop (att,vel)
pflag++; kk++; ff++;
//add weight for bnd cells
const LbmFloat pwforce = kk->weightAtt;
// control particle mod,
// dont add multiple CFFluid fsgr boundaries
if(lev==mMaxRefine) {
//if( ( ((*pflag)&(CFFluid )) && (lev==mMaxRefine) ) ||
//( ((*pflag)&(CFGrNorm)) && (lev <mMaxRefine) ) ) {
if((*pflag)&(CFFluid|CFUnused)) {
// check not fromcoarse?
cp->density += levVolume* kk->weightAtt; // old CFFluid
} else if( (*pflag) & (CFEmpty) ) {
cp->density -= levVolume* 0.5;
} else { //if( ((*pflag) & (CFBnd)) ) {
cp->density -= levVolume* 0.2; // penalty
}
} else {
//if((*pflag)&(CFGrNorm)) {
//cp->density += levVolume* kk->weightAtt; // old CFFluid
//}
}
//else if(!((*pflag) & (CFUnused)) ) { cp->density -= levVolume* 0.2; } // penalty
if( (*pflag) & (CFFluid|CFInter) ) // RFLAG_check
{
cpChecks++;
//const LbmFloat pwforce = kk->weightAtt;
LbmFloat pwvel = kk->weightVel;
if((pwforce==0.)&&(pwvel==0.)) { continue; }
ff->weightAtt += 1e-6; // for distance
if(pwforce>0.) {
ff->weightAtt += pwforce *cp->densityWeight *cp->influence;
ff->forceAtt += kk->forceAtt *levForceScale *cp->densityWeight *cp->influence;
// old fill handling here
const int workSet =mLevel[lev].setCurr;
LbmFloat ux=0., uy=0., uz=0.;
FORDF1{
const LbmFloat dfn = QCELL(lev, i,j,k, workSet, l);
ux += (this->dfDvecX[l]*dfn);
uy += (this->dfDvecY[l]*dfn);
uz += (this->dfDvecZ[l]*dfn);
}
// control particle mod
cp->avgVelWeight += levVolume*pwforce;
cp->avgVelAcc += LbmVec(ux,uy,uz) * levVolume*pwforce;
}
if(pwvel>0.) {
// TODO make switch? vel.influence depends on density weight...
// (reduced lowering with 0.75 factor)
pwvel *= cp->influence *(1.-0.75*cp->densityWeight);
// control particle mod
// todo use Omega instead!?
ff->forceVel += cp->vel*levVolume*pwvel * velLatticeScale; // levVolume?
ff->weightVel += levVolume*pwvel; // levVolume?
ff->compAv += cp->avgVel*levVolume*pwvel; // levVolume?
ff->compAvWeight += levVolume*pwvel; // levVolume?
}
if(CPODEBUG) errMsg("cppft","i"<<cppi<<" at "<<PRINT_IJK<<" kern:"<<
PRINT_VEC(i-cpi+cpw, j-cpj+cpw, k-cpk+cpw )
//<<" w:"<<ff->weightAtt<<" wa:"
//<<PRINT_VEC( ff->forceAtt[0],ff->forceAtt[1],ff->forceAtt[2] )
//<<" v:"<<ff->weightVel<<" wv:"
//<<PRINT_VEC( ff->forceVel[0],ff->forceVel[1],ff->forceVel[2] )
//<<" v:"<<ff->maxDistance<<" wv:"
//<<PRINT_VEC( ff->forceMaxd[0],ff->forceMaxd[1],ff->forceMaxd[2] )
);
} // celltype
} // ijk
} // ijk
} // ijk
} // cpi, end first cp loop (att,vel)
debMsgStd("LbmFsgrSolver::handleCpdata",DM_MSG,"Force cpgrid "<<cpssi<<" generated checks:"<<cpChecks<<" infs:"<<cpInfs ,9);
} //cpssi
} // lev
// second loop
for(int lev=0; lev<=mMaxRefine; lev++) {
LbmFloat levVolume = 1.;
LbmFloat levForceScale = 1.;
for(int ll=lev; ll<mMaxRefine; ll++) {
if(LBMDIM==3) levVolume *= 8.;
else levVolume *= 4.;
levForceScale *= 2.;
}
// prepare maxd forces
for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
// WARNING copied from above!
const LbmFloat velLatticeScale = mLevel[lev].timestep/mLevel[lev].nodeSize;
LbmFloat gsx = ((mvGeoEnd[0]-mvGeoStart[0])/(LbmFloat)mLevel[lev].lSizex);
LbmFloat gsy = ((mvGeoEnd[1]-mvGeoStart[1])/(LbmFloat)mLevel[lev].lSizey);
LbmFloat gsz = ((mvGeoEnd[2]-mvGeoStart[2])/(LbmFloat)mLevel[lev].lSizez);
#if LBMDIM==2
gsz = gsx;
#endif
LbmFloat goffx = mvGeoStart[0];
LbmFloat goffy = mvGeoStart[1];
LbmFloat goffz = mvGeoStart[2];
//const LbmFloat cpwIncFac = 2.0;
const int mdw = MIN( mLevel[lev].lSizex/2, MAX( (int)( cparts->getRadiusMaxd() /gsx) +1 , 2) ); // wide kernel, md
const int mdkarWidth = 2*mdw+1;
mpControl->mMdKernel.resize(mdkarWidth* mdkarWidth* mdkarWidth);
ControlParticle cpt; cpt.reset();
cpt.density = 0.5; cpt.densityWeight = 0.5;
cpt.pos = LbmVec( (gsx*(LbmFloat)mdw)+goffx, (gsy*(LbmFloat)mdw)+goffy, (gsz*(LbmFloat)mdw)+goffz ); // optimize?
#if LBMDIM==3
for(int k= 0; k<mdkarWidth; ++k) {
#else // LBMDIM==3
{ int k = mdw;
#endif
for(int j= 0; j<mdkarWidth; ++j)
for(int i= 0; i<mdkarWidth; ++i) {
MDKERN(i,j,k).resetForces();
LbmVec pos = LbmVec( (gsx*(LbmFloat)i)+goffx, (gsy*(LbmFloat)j)+goffy, (gsz*(LbmFloat)k)+goffz ); // optimize?
cparts->calculateMaxdForce( &cpt, pos, &MDKERN(i,j,k) );
}
}
// second cpi loop, maxd forces
if(cparts->getInfluenceMaxdist()>0.) {
for(int cppi=0; cppi<cparts->getSize(); cppi++) {
ControlParticle *cp = cparts->getParticle(cppi);
if(cp->influence<=0.) continue;
const int cpi = (int)( (cp->pos[0]-goffx)/gsx );
const int cpj = (int)( (cp->pos[1]-goffy)/gsy );
int cpk = (int)( (cp->pos[2]-goffz)/gsz );
int is,ie,js,je,ks,ke;
ks = BOUNDCHECK(cpk - mdw, getForZMinBnd(), getForZMaxBnd(lev) );
ke = BOUNDCHECK(cpk + mdw, getForZMinBnd(), getForZMaxBnd(lev) );
js = BOUNDCHECK(cpj - mdw, 0, mLevel[lev].lSizey );
je = BOUNDCHECK(cpj + mdw, 0, mLevel[lev].lSizey );
is = BOUNDCHECK(cpi - mdw, 0, mLevel[lev].lSizex );
ie = BOUNDCHECK(cpi + mdw, 0, mLevel[lev].lSizex );
if(LBMDIM==2) { cpk = 0; ks = 0; ke = 1; }
if(CPODEBUG) errMsg("cppft","i"<<cppi<<" mdw"<<mdw<<" gpos"<<PRINT_VEC(cpi,cpj,cpk)<<" i:"<<is<<","<<ie<<" j:"<<js<<","<<je<<" k:"<<ks<<","<<ke<<" "); // DEBUG
cpInfs++;
for(int k= ks; k<ke; ++k)
for(int j= js; j<je; ++j) {
CellFlagType *pflag = &RFLAG(lev,is-1,j,k, mLevel[lev].setCurr);
for(int i= is; i<ie; ++i) {
// second cpi loop, maxd forces
pflag++;
if( (*pflag) & (CFFluid|CFInter) ) // RFLAG_check
{
cpChecks++;
ControlForces *ff = &LBMGET_FORCE(lev,i,j,k);
if(ff->weightAtt == 0.) {
ControlForces *kk = &MDKERN( i-cpi+mdw, j-cpj+mdw, k-cpk+mdw);
const LbmFloat pmdf = kk->maxDistance;
if((ff->maxDistance > pmdf) || (ff->maxDistance<0.))
ff->maxDistance = pmdf;
ff->forceMaxd = kk->forceMaxd;
// todo use Omega instead!?
ff->forceVel = cp->vel* velLatticeScale;
}
} // celltype
} } // ijk
} // cpi, md loop
} // maxd inf>0 */
debMsgStd("ControlData::initControl",DM_MSG,"Maxd cpgrid "<<cpssi<<" generated checks:"<<cpChecks<<" infs:"<<cpInfs ,9);
} //cpssi
// normalize, only done once for the whole array
mpControl->mCons[0]->mCparts->finishControl( mpControl->mCpForces[lev], iatt,ivel,imaxd );
} // lev loop
myTime_t cpend = getTime();
debMsgStd("ControlData::handleCpdata",DM_MSG,"Time for cpgrid generation:"<< getTimeString(cpend-cpstart)<<", checks:"<<cpChecks<<" infs:"<<cpInfs<<" " ,8);
// warning, may return before
}
#if LBM_USE_GUI==1
#define USE_GLUTILITIES
#include "../gui/gui_utilities.h"
void LbmFsgrSolver::cpDebugDisplay(int dispset)
{
for(int cpssi=0; cpssi<(int)mpControl->mCons.size(); cpssi++) {
ControlParticles *cparts = mpControl->mCons[cpssi]->mCparts;
//ControlParticles *cpmotion = mpControl->mCons[cpssi]->mCpmotion;
// display cp parts
const bool cpCubes = false;
const bool cpDots = true;
const bool cpCpdist = true;
const bool cpHideIna = true;
glShadeModel(GL_FLAT);
glDisable( GL_LIGHTING ); // dont light lines
// dot influence
if((mpControl->mDebugCpscale>0.) && cpDots) {
glPointSize(mpControl->mDebugCpscale * 8.);
glBegin(GL_POINTS);
for(int i=0; i<cparts->getSize(); i++) {
if((cpHideIna)&&( (cparts->getParticle(i)->influence<=0.) || (cparts->getParticle(i)->size<=0.) )) continue;
ntlVec3Gfx org( vec2G(cparts->getParticle(i)->pos ) );
//LbmFloat halfsize = 0.5;
LbmFloat scale = cparts->getParticle(i)->densityWeight;
//glColor4f( scale,scale,scale,scale );
glColor4f( 0.,scale,0.,scale );
glVertex3f( org[0],org[1],org[2] );
//errMsg("lbmDebugDisplay","CP "<<i<<" at "<<org); // DEBUG
}
glEnd();
}
// cp positions
if((mpControl->mDebugCpscale>0.) && cpDots) {
glPointSize(mpControl->mDebugCpscale * 3.);
glBegin(GL_POINTS);
glColor3f( 0,1,0 );
}
for(int i=0; i<cparts->getSize(); i++) {
if((cpHideIna)&&( (cparts->getParticle(i)->influence<=0.) || (cparts->getParticle(i)->size<=0.) )) continue;
ntlVec3Gfx org( vec2G(cparts->getParticle(i)->pos ) );
LbmFloat halfsize = 0.5;
LbmFloat scale = cparts->getRadiusAtt() * cparts->getParticle(i)->densityWeight;
if(cpCubes){ glLineWidth( 1 );
glColor3f( 1,1,1 );
ntlVec3Gfx s = org-(halfsize * (scale));
ntlVec3Gfx e = org+(halfsize * (scale));
drawCubeWire( s,e ); }
if((mpControl->mDebugCpscale>0.) && cpDots) {
glVertex3f( org[0],org[1],org[2] );
}
}
if(cpDots) glEnd();
if(mpControl->mDebugAvgVelScale>0.) {
const float scale = mpControl->mDebugAvgVelScale;
glColor3f( 1.0,1.0,1 );
glBegin(GL_LINES);
for(int i=0; i<cparts->getSize(); i++) {
if((cpHideIna)&&( (cparts->getParticle(i)->influence<=0.) || (cparts->getParticle(i)->size<=0.) )) continue;
ntlVec3Gfx org( vec2G(cparts->getParticle(i)->pos ) );
//errMsg("CPAVGVEL","i"<<i<<" pos"<<org<<" av"<<cparts->getParticle(i)->avgVel);// DEBUG
float dx = cparts->getParticle(i)->avgVel[0];
float dy = cparts->getParticle(i)->avgVel[1];
float dz = cparts->getParticle(i)->avgVel[2];
dx *= scale; dy *= scale; dz *= scale;
glVertex3f( org[0],org[1],org[2] );
glVertex3f( org[0]+dx,org[1]+dy,org[2]+dz );
}
glEnd();
} // */
if( (LBMDIM==2) && (cpCpdist) ) {
// debug, for use of e.g. LBMGET_FORCE LbmControlData *mpControl = this;
# define TESTGET_FORCE(lev,i,j,k) mpControl->mCpForces[lev][ ((k*mLevel[lev].lSizey)+j)*mLevel[lev].lSizex+i ]
glBegin(GL_LINES);
//const int lev=0;
for(int lev=0; lev<=mMaxRefine; lev++) {
FSGR_FORIJK_BOUNDS(lev) {
LbmVec pos = LbmVec(
((mvGeoEnd[0]-mvGeoStart[0])/(LbmFloat)mLevel[lev].lSizex) * ((LbmFloat)i+0.5) + mvGeoStart[0],
((mvGeoEnd[1]-mvGeoStart[1])/(LbmFloat)mLevel[lev].lSizey) * ((LbmFloat)j+0.5) + mvGeoStart[1],
((mvGeoEnd[2]-mvGeoStart[2])/(LbmFloat)mLevel[lev].lSizez) * ((LbmFloat)k+0.5) + mvGeoStart[2] );
if(LBMDIM==2) pos[2] = ((mvGeoEnd[2]-mvGeoStart[2])*0.5 + mvGeoStart[2]);
if((mpControl->mDebugMaxdScale>0.) && (TESTGET_FORCE(lev,i,j,k).weightAtt<=0.) )
if(TESTGET_FORCE(lev,i,j,k).maxDistance>=0.)
if(TESTGET_FORCE(lev,i,j,k).maxDistance<CPF_MAXDINIT ) {
const float scale = mpControl->mDebugMaxdScale*10001.;
float dx = TESTGET_FORCE(lev,i,j,k).forceMaxd[0];
float dy = TESTGET_FORCE(lev,i,j,k).forceMaxd[1];
float dz = TESTGET_FORCE(lev,i,j,k).forceMaxd[2];
dx *= scale; dy *= scale; dz *= scale;
glColor3f( 0,1,0 );
glVertex3f( pos[0],pos[1],pos[2] );
glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
} // */
if((mpControl->mDebugAttScale>0.) && (TESTGET_FORCE(lev,i,j,k).weightAtt>0.)) {
const float scale = mpControl->mDebugAttScale*100011.;
float dx = TESTGET_FORCE(lev,i,j,k).forceAtt[0];
float dy = TESTGET_FORCE(lev,i,j,k).forceAtt[1];
float dz = TESTGET_FORCE(lev,i,j,k).forceAtt[2];
dx *= scale; dy *= scale; dz *= scale;
glColor3f( 1,0,0 );
glVertex3f( pos[0],pos[1],pos[2] );
glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
} // */
// why check maxDistance?
if((mpControl->mDebugVelScale>0.) && (TESTGET_FORCE(lev,i,j,k).maxDistance+TESTGET_FORCE(lev,i,j,k).weightVel>0.)) {
float scale = mpControl->mDebugVelScale*1.;
float wvscale = TESTGET_FORCE(lev,i,j,k).weightVel;
float dx = TESTGET_FORCE(lev,i,j,k).forceVel[0];
float dy = TESTGET_FORCE(lev,i,j,k).forceVel[1];
float dz = TESTGET_FORCE(lev,i,j,k).forceVel[2];
scale *= wvscale;
dx *= scale; dy *= scale; dz *= scale;
glColor3f( 0.2,0.2,1 );
glVertex3f( pos[0],pos[1],pos[2] );
glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
} // */
if((mpControl->mDebugCompavScale>0.) && (TESTGET_FORCE(lev,i,j,k).compAvWeight>0.)) {
const float scale = mpControl->mDebugCompavScale*1.;
float dx = TESTGET_FORCE(lev,i,j,k).compAv[0];
float dy = TESTGET_FORCE(lev,i,j,k).compAv[1];
float dz = TESTGET_FORCE(lev,i,j,k).compAv[2];
dx *= scale; dy *= scale; dz *= scale;
glColor3f( 0.2,0.2,1 );
glVertex3f( pos[0],pos[1],pos[2] );
glVertex3f( pos[0]+dx,pos[1]+dy,pos[2]+dz );
} // */
} // att,maxd
}
glEnd();
}
} // cpssi
//fprintf(stderr,"BLA\n");
glEnable( GL_LIGHTING ); // dont light lines
glShadeModel(GL_SMOOTH);
}
#else // LBM_USE_GUI==1
void LbmFsgrSolver::cpDebugDisplay(int dispset) { }
#endif // LBM_USE_GUI==1

View File

@@ -0,0 +1,182 @@
/******************************************************************************
*
* El'Beem - the visual lattice boltzmann freesurface simulator
* All code distributed as part of El'Beem is covered by the version 2 of the
* GNU General Public License. See the file COPYING for details.
* Copyright 2003-2006 Nils Thuerey
*
* testing extensions
*
*****************************************************************************/
#ifndef LBM_TESTCLASS_H
#define LBM_TESTCLASS_H
//class IsoSurface;
class ParticleObject;
class ControlParticles;
class ControlForces;
//#define NUMGRIDS 2
//#define MAXNUMSWS 10
// farfield modes
#define FARF_3DONLY -1
#define FARF_BOTH 0
#define FARF_SWEONLY 1
// dont reuse 3d vars/init
#define FARF_SEPSWE 2
// relaxation macros for solver_relax.h
// WARNING has to match controlparts.h
#define CPF_ENTRIES 12
#define CPF_FORCE 0
#define CPF_VELWEIGHT 3
#define CPF_VELOCITY 4
#define CPF_FORCEWEIGHT 7
#define CPF_MINCPDIST 8
#define CPF_MINCPDIR 9
#include "controlparticles.h"
#include "ntl_geometrymodel.h"
// get force entry, set=0 is unused anyway
#define LBMGET_FORCE(lev, i,j,k) mpControl->mCpForces[lev][ (LBMGI(lev,i,j,k,0)) ]
// debug mods off...
// same as in src/solver_relax.h!
#define __PRECOLLIDE_MODS(rho,ux,uy,uz, grav) \
ux += (grav)[0]; \
uy += (grav)[1]; \
uz += (grav)[2];
//void testMaxdmod(int i, int j,int k, LbmFloat &ux,LbmFloat &uy,LbmFloat &uz,ControlForces &ff);
#if LBMDIM==3
#define MAXDGRAV \
if(myforce->forceMaxd[0]*ux+myforce->forceMaxd[1]*uy<LBM_EPSILON) { \
ux = v2w*myforce->forceVel[0]+ v2wi*ux; \
uy = v2w*myforce->forceVel[1]+ v2wi*uy; } \
/* movement inverse to g? */ \
if((uz>LBM_EPSILON)&&(uz>myforce->forceVel[2])) { \
uz = v2w*myforce->forceVel[2]+ v2wi*uz; }
#else // LBMDIM==3
#define MAXDGRAV \
if(myforce->forceMaxd[0]*ux<LBM_EPSILON) { \
ux = v2w*myforce->forceVel[0]+ v2wi*ux; } \
/* movement inverse to g? */ \
if((uy>LBM_EPSILON)&&(uy>myforce->forceVel[1])) { \
uy = v2w*myforce->forceVel[1]+ v2wi*uy; }
#endif // LBMDIM==3
// debug modifications of collide vars (testing)
// requires: lev,i,j,k
#define PRECOLLIDE_MODS(rho,ux,uy,uz, grav) \
LbmFloat attforce = 1.; \
if(this->mTForceStrength>0.) { \
ControlForces* myforce = &LBMGET_FORCE(lev,i,j,k); \
const LbmFloat vf = myforce->weightAtt;\
const LbmFloat vw = myforce->weightVel;\
if(vf!=0.) { attforce = MAX(0., 1.-vf); /* TODO FIXME? use ABS(vf) for repulsion force? */ \
ux += myforce->forceAtt[0]; \
uy += myforce->forceAtt[1]; \
uz += myforce->forceAtt[2]; \
\
} else if(( myforce->maxDistance>0.) && ( myforce->maxDistance<CPF_MAXDINIT)) {\
const LbmFloat v2w = mpControl->mCons[0]->mCparts->getInfluenceMaxdist() * \
(myforce->maxDistance-mpControl->mCons[0]->mCparts->getRadiusMinMaxd()) / (mpControl->mCons[0]->mCparts->getRadiusMaxd()-mpControl->mCons[0]->mCparts->getRadiusMinMaxd()); \
const LbmFloat v2wi = 1.-v2w; \
if(v2w>0.){ MAXDGRAV; \
/* errMsg("ERRMDTT","at "<<PRINT_IJK<<" maxd="<<myforce->maxDistance<<", newu"<<PRINT_VEC(ux,uy,uz)<<", org"<<PRINT_VEC(oux,ouy,ouz)<<", fv"<<myforce->forceVel<<" " ); */ \
}\
} \
if(vw>0.) { \
const LbmFloat vwi = 1.-vw;\
const LbmFloat vwd = mpControl->mDiffVelCon;\
ux += vw*(myforce->forceVel[0]-myforce->compAv[0] + vwd*(myforce->compAv[0]-ux) ); \
uy += vw*(myforce->forceVel[1]-myforce->compAv[1] + vwd*(myforce->compAv[1]-uy) ); \
uz += vw*(myforce->forceVel[2]-myforce->compAv[2] + vwd*(myforce->compAv[2]-uz) ); \
/* TODO test!? modify smooth vel by influence of force for each lbm step, to account for force update only each N steps */ \
myforce->compAv = (myforce->forceVel*vw+ myforce->compAv*vwi); \
} \
} \
ux += (grav)[0]*attforce; \
uy += (grav)[1]*attforce; \
uz += (grav)[2]*attforce; \
/* end PRECOLLIDE_MODS */
#define TEST_IF_CHECK \
if((!iffilled)&&(LBMGET_FORCE(lev,i,j,k).weightAtt!=0.)) { \
errMsg("TESTIFFILL"," at "<<PRINT_IJK<<" "<<mass<<" "<<rho); \
iffilled = true; \
if(mass<rho*1.0) mass = rho*1.0; myfrac = 1.0; \
}
// a single set of control particles and params
class LbmControlSet {
public:
LbmControlSet();
~LbmControlSet();
void initCparts();
// control particles
ControlParticles *mCparts;
// control particle overall motion (for easier manual generation)
ControlParticles *mCpmotion;
// cp data file
string mContrPartFile;
string mCpmotionFile;
// cp debug displau
LbmFloat mDebugCpscale, mDebugVelScale, mDebugCompavScale, mDebugAttScale, mDebugMaxdScale, mDebugAvgVelScale;
// params
AnimChannel<float> mcForceAtt;
AnimChannel<float> mcForceVel;
AnimChannel<float> mcForceMaxd;
AnimChannel<float> mcRadiusAtt;
AnimChannel<float> mcRadiusVel;
AnimChannel<float> mcRadiusMind;
AnimChannel<float> mcRadiusMaxd;
AnimChannel<ntlVec3f> mcCpScale;
AnimChannel<ntlVec3f> mcCpOffset;
};
// main control data storage
class LbmControlData
{
public:
LbmControlData();
virtual ~LbmControlData();
// control data
// contorl params
void parseControldataAttrList(AttributeList *attr);
// control strength, set for solver interface
LbmFloat mSetForceStrength;
// cp vars
std::vector<LbmControlSet*> mCons;
// update interval
int mCpUpdateInterval;
// output
string mCpOutfile;
// control particle precomputed influence
std::vector< std::vector<ControlForces> > mCpForces;
std::vector<ControlForces> mCpKernel;
std::vector<ControlForces> mMdKernel;
// activate differential velcon
LbmFloat mDiffVelCon;
// cp debug displau
LbmFloat mDebugCpscale, mDebugVelScale, mDebugCompavScale, mDebugAttScale, mDebugMaxdScale, mDebugAvgVelScale;
};
#endif // LBM_TESTCLASS_H

View File

@@ -151,7 +151,7 @@ void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset)
}
/* munmap for windows */
long munmap(void *ptr, long size)
intptr_t munmap(void *ptr, intptr_t size)
{
MemMap *mm = mmap_findlink(mmapbase, ptr);
if (!mm) {

View File

@@ -45,7 +45,10 @@
#define MAP_FAILED ((void *)-1)
void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset);
long munmap(void *ptr, long size);
#include "BLO_sys_types.h" // needed for intptr_t
void *mmap(void *start, size_t len, int prot, int flags, int fd, off_t offset);
intptr_t munmap(void *ptr, intptr_t size);
#endif
#endif

View File

@@ -118,7 +118,6 @@ install: package
ifneq ($(OS), darwin)
@[ ! -d $(OCGDIR)/bin/.blender ] || \
cp -r $(OCGDIR)/bin/.blender $(DISTDIR)
@rm -rf $(DISTDIR)/.svn $(DISTDIR)/*/.svn $(DISTDIR)/*/*/.svn
@cp $(NANBLENDERHOME)/bin/.blender/.Blanguages $(CONFDIR)
@cp $(NANBLENDERHOME)/bin/.blender/.bfont.ttf $(CONFDIR)
endif
@@ -144,8 +143,7 @@ ifneq ($(NOPLUGINS),true)
@cp ../source/blender/blenpluginapi/*.h $(DISTDIR)/plugins/include/
@chmod 755 $(DISTDIR)/plugins/bmake
@$(MAKE) -C $(DISTDIR)/plugins all > /dev/null || exit 1;
@rm -fr $(DISTDIR)/plugins/.svn $(DISTDIR)/plugins/*/.svn \
$(DISTDIR)/plugins/*/*.o
@rm -f $(DISTDIR)/plugins/*/*.o
#on OS X the plugins move to the installation directory
ifneq ($(OS),darwin)
@@ -158,7 +156,6 @@ endif
@echo "----> Copy python infrastructure"
@[ ! -d scripts ] || cp -r scripts $(CONFDIR)/scripts
@[ ! -d $(CONFDIR)/scripts ] || rm -fr $(CONFDIR)/scripts/.svn $(CONFDIR)/scripts/*/.svn $(CONFDIR)/scripts/*/*/.svn
ifeq ($(OS),darwin)
@echo "----> Move .blender to .app/Contents/MacOS/"
@@ -180,6 +177,8 @@ endif
@[ ! -x $(CONFIG_GUESS)/specific.sh ] || (\
echo "**--> Execute specific.sh in $(CONFIG_GUESS)/" && \
cd $(CONFIG_GUESS) && ./specific.sh )
@echo "----> Cleaning .svn metadata directories"
@find $(DISTDIR) -type d -name ".svn" | xargs rm -fr
pkg: install
@echo "----> Create distribution file $(BLENDNAME)$(EXT1)"

View File

@@ -68,11 +68,7 @@ def add_mesh_simple(name, verts, edges, faces):
else:
# Mesh with no data, unlikely
me.edges.extend(edges)
me.faces.extend(faces)
if is_editmode or Blender.Get('add_editmode'):
EditMode(1)
me.faces.extend(faces)
else:
# Object mode add new
@@ -95,9 +91,14 @@ def add_mesh_simple(name, verts, edges, faces):
ob_act.setMatrix(mat)
ob_act.loc = cursor
if is_editmode or Blender.Get('add_editmode'):
EditMode(1)
me.calcNormals()
if is_editmode or Blender.Get('add_editmode'):
EditMode(1)
def write_mesh_script(filepath, me):
@@ -112,7 +113,7 @@ def write_mesh_script(filepath, me):
file.write('#!BPY\n')
file.write('"""\n')
file.write('Name: \'%s\'\n' % name)
file.write('Blender: 243\n')
file.write('Blender: 245\n')
file.write('Group: \'AddMesh\'\n')
file.write('"""\n\n')
file.write('import BPyAddMesh\n')

View File

@@ -0,0 +1,479 @@
#!BPY
"""
Name: 'MilkShape3D ASCII (.txt)...'
Blender: 245
Group: 'Import'
Tooltip: 'Import from a MilkShape3D ASCII file format (.txt)'
"""
#
# Author: Markus Ilmola
# Email: markus.ilmola@pp.inet.fi
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# import needed stuff
import os.path
import re
import math
from math import *
import Blender
from Blender import Mathutils
from Blender.Mathutils import *
# Converts ms3d euler angles to a rotation matrix
def RM(a):
sy = sin(a[2])
cy = cos(a[2])
sp = sin(a[1])
cp = cos(a[1])
sr = sin(a[0])
cr = cos(a[0])
return Matrix([cp*cy, cp*sy, -sp], [sr*sp*cy+cr*-sy, sr*sp*sy+cr*cy, sr*cp],[cr*sp*cy+-sr*-sy, cr*sp*sy+-sr*cy, cr*cp])
# Converts ms3d euler angles to a quaternion
def RQ(a):
angle = a[2] * 0.5;
sy = sin(angle);
cy = cos(angle);
angle = a[1] * 0.5;
sp = sin(angle);
cp = cos(angle);
angle = a[0] * 0.5;
sr = sin(angle);
cr = cos(angle);
return Quaternion(cr*cp*cy+sr*sp*sy, sr*cp*cy-cr*sp*sy, cr*sp*cy+sr*cp*sy, cr*cp*sy-sr*sp*cy)
# takes a texture filename and tries to load it
def loadImage(path, filename):
image = None
try:
image = Blender.Image.Load(os.path.abspath(filename))
except IOError:
print "Warning: Failed to load image: " + filename + ". Trying short path instead...\n"
try:
image = Blender.Image.Load(os.path.dirname(path) + "/" + os.path.basename(filename))
except IOError:
print "Warning: Failed to load image: " + os.path.basename(filename) + "!\n"
return image
# returns the next non-empty, non-comment line from the file
def getNextLine(file):
ready = False
while ready==False:
line = file.readline()
if len(line)==0:
print "Warning: End of file reached."
return line
ready = True
line = line.strip()
if len(line)==0 or line.isspace():
ready = False
if len(line)>=2 and line[0]=='/' and line[1]=='/':
ready = False
return line
# imports a MilkShape3D ascii file to the current scene
def import_ms3d_ascii(path):
# limits
MAX_NUMMESHES = 1000
MAX_NUMVERTS = 100000
MAX_NUMNORMALS = 100000
MAX_NUMTRIS = 100000
MAX_NUMMATS = 16
MAX_NUMBONES = 100
MAX_NUMPOSKEYS = 1000
MAX_NUMROTKEYS = 1000
# get scene
scn = Blender.Scene.GetCurrent()
if scn==None:
return "No scene to import to!"
# open the file
try:
file = open(path, 'r')
except IOError:
return "Failed to open the file!"
# Read frame info
try:
lines = getNextLine(file).split()
if len(lines) != 2 or lines[0] != "Frames:":
raise ValueError
lines = getNextLine(file).split()
if len(lines) != 2 or lines[0] != "Frame:":
raise ValueError
except ValueError:
return "Frame information is invalid!"
# Create the mesh
meshOb = Blender.Object.New('Mesh', "MilkShape3D Object")
mesh = Blender.Mesh.New("MilkShape3D Mesh")
meshOb.link(mesh)
scn.objects.link(meshOb)
# read the number of meshes
try:
lines = getNextLine(file).split()
if len(lines)!=2 or lines[0]!="Meshes:":
raise ValueError
numMeshes = int(lines[1])
if numMeshes < 0 or numMeshes > MAX_NUMMESHES:
raise ValueError
except ValueError:
return "Number of meshes is invalid!"
# read meshes
vertBase = 0
faceBase = 0
boneIds = []
for i in range(numMeshes):
# read name, flags and material
try:
lines = re.findall(r'\".*\"|[^ ]+', getNextLine(file))
if len(lines)!=3:
raise ValueError
material = int(lines[2])
except ValueError:
return "Name, flags or material in mesh " + str(i+1) + " are invalid!"
# read the number of vertices
try:
numVerts = int(getNextLine(file))
if numVerts < 0 or numVerts > MAX_NUMVERTS:
raise ValueError
except ValueError:
return "Number of vertices in mesh " + str(i+1) + " is invalid!"
# read vertices
coords = []
uvs = []
for j in xrange(numVerts):
try:
lines = getNextLine(file).split()
if len(lines)!=7:
raise ValueError
coords.append([float(lines[1]), float(lines[2]), float(lines[3])])
uvs.append([float(lines[4]), 1-float(lines[5])])
boneIds.append(int(lines[6]))
except ValueError:
return "Vertex " + str(j+1) + " in mesh " + str(i+1) + " is invalid!"
mesh.verts.extend(coords)
# read number of normals
try:
numNormals = int(getNextLine(file))
if numNormals < 0 or numNormals > MAX_NUMNORMALS:
raise ValueError
except ValueError:
return "Number of normals in mesh " + str(i+1) + " is invalid!"
# read normals
normals = []
for j in xrange(numNormals):
try:
lines = getNextLine(file).split()
if len(lines)!=3:
raise ValueError
normals.append([float(lines[0]), float(lines[1]), float(lines[2])])
except ValueError:
return "Normal " + str(j+1) + " in mesh " + str(i+1) + " is invalid!"
# read the number of triangles
try:
numTris = int(getNextLine(file))
if numTris < 0 or numTris > MAX_NUMTRIS:
raise ValueError
except ValueError:
return "Number of triangles in mesh " + str(i+1) + " is invalid!"
# read triangles
faces = []
for j in xrange(numTris):
# read the triangle
try:
lines = getNextLine(file).split()
if len(lines)!=8:
raise ValueError
v1 = int(lines[1])
v2 = int(lines[2])
v3 = int(lines[3])
faces.append([v1+vertBase, v2+vertBase, v3+vertBase])
except ValueError:
return "Triangle " + str(j+1) + " in mesh " + str(i+1) + " is invalid!"
mesh.faces.extend(faces)
# set texture coordinates and material
for j in xrange(faceBase, len(mesh.faces)):
face = mesh.faces[j]
face.uv = [Vector(uvs[face.verts[0].index-vertBase]), Vector(uvs[face.verts[1].index-vertBase]), Vector(uvs[face.verts[2].index-vertBase])]
if material>=0:
face.mat = material
# increase vertex and face base
vertBase = len(mesh.verts)
faceBase = len(mesh.faces)
# read the number of materials
try:
lines = getNextLine(file).split()
if len(lines)!=2 or lines[0]!="Materials:":
raise ValueError
numMats = int(lines[1])
if numMats < 0 or numMats > MAX_NUMMATS:
raise ValueError
except ValueError:
return "Number of materials is invalid!"
# read the materials
for i in range(numMats):
# read name
name = getNextLine(file)[1:-1]
# create the material
mat = Blender.Material.New(name)
mesh.materials += [mat]
# read ambient color
try:
lines = getNextLine(file).split()
if len(lines)!=4:
raise ValueError
amb = (float(lines[0])+float(lines[1])+float(lines[2]))/3
mat.setAmb(amb)
except ValueError:
return "Ambient color in material " + str(i+1) + " is invalid!"
# read diffuse color
try:
lines = getNextLine(file).split()
if len(lines)!=4:
raise ValueError
mat.setRGBCol([float(lines[0]), float(lines[1]), float(lines[2])])
except ValueError:
return "Diffuse color in material " + str(i+1) + " is invalid!"
# read specular color
try:
lines = getNextLine(file).split()
if len(lines)!=4:
raise ValueError
mat.setSpecCol([float(lines[0]), float(lines[1]), float(lines[2])])
except ValueError:
return "Specular color in material " + str(i+1) + " is invalid!"
# read emissive color
try:
lines = getNextLine(file).split()
if len(lines)!=4:
raise ValueError
emit = (float(lines[0])+float(lines[1])+float(lines[2]))/3
mat.setEmit(emit)
except ValueError:
return "Emissive color in material " + str(i+1) + " is invalid!"
# read shininess
try:
shi = float(getNextLine(file))
#mat.setHardness(int(shi))
except ValueError:
return "Shininess in material " + str(i+1) + " is invalid!"
# read transparency
try:
alpha = float(getNextLine(file))
mat.setAlpha(alpha)
if alpha < 1:
mat.mode |= Blender.Material.Modes.ZTRANSP
except ValueError:
return "Transparency in material " + str(i+1) + " is invalid!"
# read texturemap
texturemap = getNextLine(file)[1:-1]
if len(texturemap)>0:
colorTexture = Blender.Texture.New(name + "_texture")
colorTexture.setType('Image')
colorTexture.setImage(loadImage(path, texturemap))
mat.setTexture(0, colorTexture, Blender.Texture.TexCo.UV, Blender.Texture.MapTo.COL)
# read alphamap
alphamap = getNextLine(file)[1:-1]
if len(alphamap)>0:
alphaTexture = Blender.Texture.New(name + "_alpha")
alphaTexture.setType('Image')
alphaTexture.setImage(loadImage(path, alphamap))
mat.setTexture(1, alphaTexture, Blender.Texture.TexCo.UV, Blender.Texture.MapTo.ALPHA)
# read the number of bones
try:
lines = getNextLine(file).split()
if len(lines)!=2 or lines[0]!="Bones:":
raise ValueError
numBones = int(lines[1])
if numBones < 0 or numBones > MAX_NUMBONES:
raise ValueError
except:
return "Number of bones is invalid!"
# create the armature
armature = None
armOb = None
if numBones > 0:
armOb = Blender.Object.New('Armature', "MilkShape3D Skeleton")
armature = Blender.Armature.New("MilkShape3D Skeleton")
armature.drawType = Blender.Armature.STICK
armOb.link(armature)
scn.objects.link(armOb)
armOb.makeParentDeform([meshOb])
armature.makeEditable()
# read bones
posKeys = {}
rotKeys = {}
for i in range(numBones):
# read name
name = getNextLine(file)[1:-1]
# create the bone
bone = Blender.Armature.Editbone()
armature.bones[name] = bone
# read parent
parent = getNextLine(file)[1:-1]
if len(parent)>0:
bone.parent = armature.bones[parent]
# read position and rotation
try:
lines = getNextLine(file).split()
if len(lines) != 7:
raise ValueError
pos = [float(lines[1]), float(lines[2]), float(lines[3])]
rot = [float(lines[4]), float(lines[5]), float(lines[6])]
except ValueError:
return "Invalid position or orientation in a bone!"
# set position and orientation
if bone.hasParent():
bone.head = Vector(pos) * bone.parent.matrix + bone.parent.head
bone.tail = bone.head + Vector([1,0,0])
tempM = RM(rot) * bone.parent.matrix
tempM.transpose;
bone.matrix = tempM
else:
bone.head = Vector(pos)
bone.tail = bone.head + Vector([1,0,0])
bone.matrix = RM(rot)
# Create vertex group for this bone
mesh.addVertGroup(name)
vgroup = []
for index, v in enumerate(boneIds):
if v==i:
vgroup.append(index)
mesh.assignVertsToGroup(name, vgroup, 1.0, 1)
# read the number of position key frames
try:
numPosKeys = int(getNextLine(file))
if numPosKeys < 0 or numPosKeys > MAX_NUMPOSKEYS:
raise ValueError
except ValueError:
return "Invalid number of position key frames!"
# read position key frames
posKeys[name] = []
for j in range(numPosKeys):
# read time and position
try:
lines = getNextLine(file).split()
if len(lines) != 4:
raise ValueError
time = float(lines[0])
pos = [float(lines[1]), float(lines[2]), float(lines[3])]
posKeys[name].append([time, pos])
except ValueError:
return "Invalid position key frame!"
# read the number of rotation key frames
try:
numRotKeys = int(getNextLine(file))
if numRotKeys < 0 or numRotKeys > MAX_NUMROTKEYS:
raise ValueError
except ValueError:
return "Invalid number of rotation key frames!"
# read rotation key frames
rotKeys[name] = []
for j in range(numRotKeys):
# read time and rotation
try:
lines = getNextLine(file).split()
if len(lines) != 4:
raise ValueError
time = float(lines[0])
rot = [float(lines[1]), float(lines[2]), float(lines[3])]
rotKeys[name].append([time, rot])
except ValueError:
return "Invalid rotation key frame!"
# create action and pose
action = None
pose = None
if armature != None:
armature.update()
pose = armOb.getPose()
action = armOb.getAction()
if not action:
action = Blender.Armature.NLA.NewAction()
action.setActive(armOb)
# create animation key frames
for name, pbone in pose.bones.items():
# create position keys
for key in posKeys[name]:
pbone.loc = Vector(key[1])
pbone.insertKey(armOb, int(key[0]+0.5), Blender.Object.Pose.LOC, True)
# create rotation keys
for key in rotKeys[name]:
pbone.quat = RQ(key[1])
pbone.insertKey(armOb, int(key[0]+0.5), Blender.Object.Pose.ROT, True)
# set the imported object to be the selected one
scn.objects.selected = []
meshOb.sel= 1
Blender.Redraw()
# The import was a succes!
return ""
# load the model
def fileCallback(filename):
error = import_ms3d_ascii(filename)
if error!="":
Blender.Draw.PupMenu("An error occured during import: " + error + "|Not all data might have been imported succesfully.", 2)
Blender.Window.FileSelector(fileCallback, 'Import')

View File

@@ -0,0 +1,93 @@
#!BPY
"""
Name: 'GameLogic Example'
Blender: 245
Group: 'ScriptTemplate'
Tooltip: 'Script template with examples of how to use game logic'
"""
from Blender import Window
import bpy
script_data = \
'''
# GameLogic has been added to the global namespace no need to import
# for keyboard event comparison
# import GameKeys
# support for Vector(), Matrix() types and advanced functions like AngleBetweenVecs(v1,v2) and RotationMatrix(...)
# import Mathutils
# for functions like getWindowWidth(), getWindowHeight()
# import Rasterizer
def main():
cont = GameLogic.getCurrentController()
# The KX_GameObject that owns this controller.
own = cont.getOwner()
# for scripts that deal with spacial logic
own_pos = own.getPosition()
# Some example functions, remove to write your own script.
# check for a positive sensor, will run on any object without errors.
print 'Logic info for KX_GameObject', own.getName()
input = False
for sens in cont.getSensors():
# The sensor can be on another object, we may want to use it
own_sens = sens.getOwner()
print ' sensor:', sens.getName(),
if sens.isPositive():
print '(true)'
input = True
else:
print '(false)'
for actu in cont.getActuators():
# The actuator can be on another object, we may want to use it
own_actu = actu.getOwner()
print ' actuator:', sens.getName()
# This runs the actuator or turns it off
# note that actuators will continue to run unless explicitly turned off.
if input:
GameLogic.addActiveActuator(actu, True)
else:
GameLogic.addActiveActuator(actu, False)
# Its also good practice to get sensors and actuators by names
# so any changes to their order wont break the script.
# sens_key = cont.getSensor('key_sensor')
# actu_motion = cont.getActuator('motion')
# Loop through all other objects in the scene
sce = GameLogic.getCurrentScene()
print 'Scene Objects:', sce.getName()
for ob in sce.getObjectList():
print ' ', ob.getName(), ob.getPosition()
# Example where collision objects are checked for their properties
# adding to our objects "life" property
"""
actu_collide = cont.getSensor('collision_sens')
for ob in actu_collide.getHitObjectList():
# Check to see the object has this property
if hasattr(ob, 'life'):
own.life += ob.life
ob.life = 0
print own.life
"""
main()
'''
new_text = bpy.data.texts.new('gamelogic_example.py')
new_text.write(script_data)
bpy.data.texts.active = new_text
Window.RedrawAll()

View File

@@ -0,0 +1,33 @@
#!BPY
"""
Name: 'GameLogic Template'
Blender: 245
Group: 'ScriptTemplate'
Tooltip: 'Basic template for new game logic scripts'
"""
from Blender import Window
import bpy
script_data = \
'''
def main():
cont = GameLogic.getCurrentController()
own = cont.getOwner()
sens = cont.getSensor('mySensor')
actu = cont.getActuator('myActuator')
if sens.isPositive():
GameLogic.addActiveActuator(actu, True)
else:
GameLogic.addActiveActuator(actu, False)
main()
'''
new_text = bpy.data.texts.new('gamelogic_example.py')
new_text.write(script_data)
bpy.data.texts.active = new_text
Window.RedrawAll()

View File

@@ -0,0 +1,92 @@
#!BPY
"""
Name: 'IPO Example'
Blender: 245
Group: 'ScriptTemplate'
Tooltip: 'Script template for setting the IPO'
"""
from Blender import Window
import bpy
script_data = \
'''#!BPY
"""
Name: 'My Ipo Script'
Blender: 245
Group: 'Animation'
Tooltip: 'Put some useful info here'
"""
# Add a licence here if you wish to re-distribute, we recommend the GPL
from Blender import Ipo, Mathutils, Window
import bpy, BPyMessages
def makeRandomIpo(object, firstFrame, numberOfFrames, frameStep):
# Create an new Ipo Curve of name myIpo and type Object
myIpo = bpy.data.ipos.new('myIpo', 'Object')
# Create LocX, LocY, and LocZ Ipo curves in our new Curve Object
# and store them so we can access them later
myIpo_x = myIpo.addCurve('LocX')
myIpo_y = myIpo.addCurve('LocY')
myIpo_z = myIpo.addCurve('LocZ')
# What value we want to scale our random value by
ipoScale = 4
# This Calculates the End Frame for use in an xrange() expression
endFrame = firstFrame + (numberOfFrames * frameStep) + frameStep
for frame in xrange(firstFrame, endFrame, frameStep):
# Use the Mathutils Rand() function to get random numbers
ipoValue_x = Mathutils.Rand(-1, 1) * ipoScale
ipoValue_y = Mathutils.Rand(-1, 1) * ipoScale
ipoValue_z = Mathutils.Rand(-1, 1) * ipoScale
# Append to the Ipo curve at location frame, with the value ipoValue_x
# Note that we should pass the append function a tuple or a BezTriple
myIpo_x.append((frame, ipoValue_x))
# Similar to above
myIpo_y.append((frame, ipoValue_y))
myIpo_z.append((frame, ipoValue_z))
# Link our new Ipo Curve to the passed object
object.setIpo(myIpo)
print object
def main():
# Get the active scene, since there can be multiple ones
sce = bpy.data.scenes.active
# Get the active object
object = sce.objects.active
# If there is no active object, pop up an error message
if not object:
BPyMessages.Error_NoActive()
Window.WaitCursor(1)
# Call our makeRandomIpo function
# Pass it our object, Tell it to keys from the start frame until the end frame, at a step of 10 frames
# between them
makeRandomIpo(object, sce.render.sFrame, sce.render.eFrame, 10)
Window.WaitCursor(0)
if __name__ == '__main__':
main()
'''
new_text = bpy.data.texts.new('ipo_template.py')
new_text.write(script_data)
bpy.data.texts.active = new_text
Window.RedrawAll()

View File

@@ -0,0 +1,43 @@
/**
*
* $Id: BKE_bullet.h 16773 2008-09-27 22:01:26Z ben2610 $
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef BKE_BULLET_H
#define BKE_BULLET_H
struct BulletSoftBody;
/* allocates and initializes general main data */
extern struct BulletSoftBody *bsbNew(void);
/* frees internal data and softbody itself */
extern void bsbFree(struct BulletSoftBody *sb);
#endif

View File

@@ -0,0 +1,55 @@
/**
* BKE_fluidsim.h
*
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "DNA_modifier_types.h"
#include "DNA_object_fluidsim.h" // N_T
#include "DNA_object_types.h"
#include "BKE_DerivedMesh.h"
/* old interface */
FluidsimSettings *fluidsimSettingsNew(Object *srcob);
void initElbeemMesh(Object *ob, int *numVertices, float **vertices, int *numTriangles, int **triangles, int useGlobalCoords, int modifierIndex);
/* new fluid-modifier interface */
void fluidsim_init(FluidsimModifierData *fluidmd);
void fluidsim_free(FluidsimModifierData *fluidmd);
DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams);
DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc);
// get bounding box of mesh
void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
/*RET*/ float start[3], /*RET*/ float size[3] );

View File

@@ -0,0 +1,39 @@
/**
* BKE_shrinkwrap.h
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef BKE_SIMPLE_DEFORM_H
#define BKE_SIMPLE_DEFORM_H
struct Object;
struct DerivedMesh;
struct SimpleDeformModifierData;
void SimpleDeformModifier_do(SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm, float (*vertexCos)[3], int numVerts);
#endif

View File

@@ -0,0 +1,95 @@
/*
*
* $Id: bullet.c 16776 2008-09-28 03:07:13Z erwin $
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
/* types */
#include "DNA_object_force.h" /* here is the softbody struct */
#include "BKE_bullet.h"
/* ************ Object level, exported functions *************** */
/* allocates and initializes general main data */
BulletSoftBody *bsbNew(void)
{
BulletSoftBody *bsb;
bsb= MEM_callocN(sizeof(BulletSoftBody), "bulletsoftbody");
bsb->flag = OB_BSB_BENDING_CONSTRAINTS | OB_BSB_SHAPE_MATCHING | OB_BSB_AERO_VPOINT;
bsb->linStiff = 0.5f;
bsb->angStiff = 1.0f;
bsb->volume = 1.0f;
bsb->viterations = 0;
bsb->piterations = 2;
bsb->diterations = 0;
bsb->citerations = 4;
bsb->kSRHR_CL = 0.1f;
bsb->kSKHR_CL = 1.f;
bsb->kSSHR_CL = 0.5f;
bsb->kSR_SPLT_CL = 0.5f;
bsb->kSK_SPLT_CL = 0.5f;
bsb->kSS_SPLT_CL = 0.5f;
bsb->kVCF = 1;
bsb->kDP = 0;
bsb->kDG = 0;
bsb->kLF = 0;
bsb->kPR = 0;
bsb->kVC = 0;
bsb->kDF = 0.2f;
bsb->kMT = 0.05;
bsb->kCHR = 1.0f;
bsb->kKHR = 0.1f;
bsb->kSHR = 1.0f;
bsb->kAHR = 0.7f;
bsb->collisionflags = 0;
//bsb->collisionflags = OB_BSB_COL_CL_RS + OB_BSB_COL_CL_SS;
bsb->numclusteriterations = 64;
return bsb;
}
/* frees all */
void bsbFree(BulletSoftBody *bsb)
{
/* no internal data yet */
MEM_freeN(bsb);
}

View File

@@ -0,0 +1,642 @@
/**
* fluidsim.c
*
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) Blender Foundation
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "MEM_guardedalloc.h"
#include "DNA_mesh_types.h"
#include "DNA_meshdata_types.h"
#include "DNA_object_force.h" // for pointcache
#include "DNA_particle_types.h"
#include "DNA_scene_types.h" // N_T
#include "BLI_arithb.h"
#include "BLI_blenlib.h"
#include "BKE_cdderivedmesh.h"
#include "BKE_customdata.h"
#include "BKE_DerivedMesh.h"
#include "BKE_fluidsim.h"
#include "BKE_global.h"
#include "BKE_modifier.h"
#include "BKE_mesh.h"
#include "BKE_pointcache.h"
#include "BKE_utildefines.h"
// headers for fluidsim bobj meshes
#include <stdlib.h>
#include "LBM_fluidsim.h"
#include "elbeem.h"
#include <zlib.h>
#include <string.h>
#include <stdio.h>
/* ************************* fluidsim bobj file handling **************************** */
// -----------------------------------------
// forward decleration
// -----------------------------------------
// -----------------------------------------
void fluidsim_init(FluidsimModifierData *fluidmd)
{
#ifndef DISABLE_ELBEEM
if(fluidmd)
{
FluidsimSettings *fss = MEM_callocN(sizeof(FluidsimSettings), "fluidsimsettings");
fluidmd->fss = fss;
if(!fss)
return;
fss->type = 0;
fss->show_advancedoptions = 0;
fss->resolutionxyz = 50;
fss->previewresxyz = 25;
fss->realsize = 0.03;
fss->guiDisplayMode = 2; // preview
fss->renderDisplayMode = 3; // render
fss->viscosityMode = 2; // default to water
fss->viscosityValue = 1.0;
fss->viscosityExponent = 6;
// dg TODO: change this to []
fss->gravx = 0.0;
fss->gravy = 0.0;
fss->gravz = -9.81;
fss->animStart = 0.0;
fss->animEnd = 0.30;
fss->gstar = 0.005; // used as normgstar
fss->maxRefine = -1;
// maxRefine is set according to resolutionxyz during bake
// fluid/inflow settings
// fss->iniVel --> automatically set to 0
/* elubie: changed this to default to the same dir as the render output
to prevent saving to C:\ on Windows */
BLI_strncpy(fss->surfdataPath, btempdir, FILE_MAX);
// first init of bounding box
// no bounding box needed
// todo - reuse default init from elbeem!
fss->typeFlags = 0;
fss->domainNovecgen = 0;
fss->volumeInitType = 1; // volume
fss->partSlipValue = 0.0;
fss->generateTracers = 0;
fss->generateParticles = 0.0;
fss->surfaceSmoothing = 1.0;
fss->surfaceSubdivs = 1.0;
fss->particleInfSize = 0.0;
fss->particleInfAlpha = 0.0;
// init fluid control settings
fss->attractforceStrength = 0.2;
fss->attractforceRadius = 0.75;
fss->velocityforceStrength = 0.2;
fss->velocityforceRadius = 0.75;
fss->cpsTimeStart = fss->animStart;
fss->cpsTimeEnd = fss->animEnd;
fss->cpsQuality = 10.0; // 1.0 / 10.0 => means 0.1 width
/*
BAD TODO: this is done in buttons_object.c in the moment
Mesh *mesh = ob->data;
// calculate bounding box
fluid_get_bb(mesh->mvert, mesh->totvert, ob->obmat, fss->bbStart, fss->bbSize);
*/
fss->lastgoodframe = -1;
fss->flag = 0;
}
#endif
return;
}
void fluidsim_free(FluidsimModifierData *fluidmd)
{
#ifndef DISABLE_ELBEEM
if(fluidmd)
{
MEM_freeN(fluidmd->fss);
}
#endif
return;
}
DerivedMesh *fluidsimModifier_do(FluidsimModifierData *fluidmd, Object *ob, DerivedMesh *dm, int useRenderParams, int isFinalCalc)
{
#ifndef DISABLE_ELBEEM
DerivedMesh *result = NULL;
int framenr;
FluidsimSettings *fss = NULL;
framenr= (int)G.scene->r.cfra;
// only handle fluidsim domains
if(fluidmd && fluidmd->fss && (fluidmd->fss->type != OB_FLUIDSIM_DOMAIN))
return dm;
// sanity check
if(!fluidmd || (fluidmd && !fluidmd->fss))
return dm;
fss = fluidmd->fss;
// timescale not supported yet
// clmd->sim_parms->timescale= timescale;
// support reversing of baked fluid frames here
if((fss->flag & OB_FLUIDSIM_REVERSE) && (fss->lastgoodframe >= 0))
{
framenr = fss->lastgoodframe - framenr + 1;
CLAMP(framenr, 1, fss->lastgoodframe);
}
/* try to read from cache */
if(((fss->lastgoodframe >= framenr) || (fss->lastgoodframe < 0)) && (result = fluidsim_read_cache(ob, dm, fluidmd, framenr, useRenderParams)))
{
// fss->lastgoodframe = framenr; // set also in src/fluidsim.c
return result;
}
else
{
// display last known good frame
if(fss->lastgoodframe >= 0)
{
if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams)))
{
return result;
}
// it was supposed to be a valid frame but it isn't!
fss->lastgoodframe = framenr - 1;
// this could be likely the case when you load an old fluidsim
if((result = fluidsim_read_cache(ob, dm, fluidmd, fss->lastgoodframe, useRenderParams)))
{
return result;
}
}
result = CDDM_copy(dm);
if(result)
{
return result;
}
}
return dm;
#else
return NULL;
#endif
}
#ifndef DISABLE_ELBEEM
/* read .bobj.gz file into a fluidsimDerivedMesh struct */
static DerivedMesh *fluidsim_read_obj(char *filename)
{
int wri,i,j;
float wrf;
int gotBytes;
gzFile gzf;
int numverts = 0, numfaces = 0;
DerivedMesh *dm = NULL;
MFace *mface;
MVert *mvert;
short *normals;
// ------------------------------------------------
// get numverts + numfaces first
// ------------------------------------------------
gzf = gzopen(filename, "rb");
if (!gzf)
{
return NULL;
}
// read numverts
gotBytes = gzread(gzf, &wri, sizeof(wri));
numverts = wri;
// skip verts
for(i=0; i<numverts*3; i++)
{
gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
}
// read number of normals
gotBytes = gzread(gzf, &wri, sizeof(wri));
// skip normals
for(i=0; i<numverts*3; i++)
{
gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
}
/* get no. of triangles */
gotBytes = gzread(gzf, &wri, sizeof(wri));
numfaces = wri;
gzclose( gzf );
// ------------------------------------------------
if(!numfaces || !numverts)
return NULL;
gzf = gzopen(filename, "rb");
if (!gzf)
{
return NULL;
}
dm = CDDM_new(numverts, 0, numfaces);
if(!dm)
{
gzclose( gzf );
return NULL;
}
// read numverts
gotBytes = gzread(gzf, &wri, sizeof(wri));
// read vertex position from file
mvert = CDDM_get_verts(dm);
for(i=0; i<numverts; i++)
{
MVert *mv = &mvert[i];
for(j=0; j<3; j++)
{
gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
mv->co[j] = wrf;
}
}
// should be the same as numverts
gotBytes = gzread(gzf, &wri, sizeof(wri));
if(wri != numverts)
{
if(dm)
dm->release(dm);
gzclose( gzf );
return NULL;
}
normals = MEM_callocN(sizeof(short) * numverts * 3, "fluid_tmp_normals" );
if(!normals)
{
if(dm)
dm->release(dm);
gzclose( gzf );
return NULL;
}
// read normals from file (but don't save them yet)
for(i=0; i<numverts*3; i++)
{
gotBytes = gzread(gzf, &wrf, sizeof( wrf ));
normals[i] = (short)(wrf*32767.0f);
}
/* read no. of triangles */
gotBytes = gzread(gzf, &wri, sizeof(wri));
if(wri!=numfaces)
printf("Fluidsim: error in reading data from file.\n");
// read triangles from file
mface = CDDM_get_faces(dm);
for(i=0; i<numfaces; i++)
{
int face[4];
MFace *mf = &mface[i];
gotBytes = gzread(gzf, &(face[0]), sizeof( face[0] ));
gotBytes = gzread(gzf, &(face[1]), sizeof( face[1] ));
gotBytes = gzread(gzf, &(face[2]), sizeof( face[2] ));
face[3] = 0;
// check if 3rd vertex has index 0 (not allowed in blender)
if(face[2])
{
mf->v1 = face[0];
mf->v2 = face[1];
mf->v3 = face[2];
}
else
{
mf->v1 = face[1];
mf->v2 = face[2];
mf->v3 = face[0];
}
mf->v4 = face[3];
test_index_face(mf, NULL, 0, 3);
}
gzclose( gzf );
CDDM_calc_edges(dm);
CDDM_apply_vert_normals(dm, (short (*)[3])normals);
MEM_freeN(normals);
// CDDM_calc_normals(result);
return dm;
}
DerivedMesh *fluidsim_read_cache(Object *ob, DerivedMesh *orgdm, FluidsimModifierData *fluidmd, int framenr, int useRenderParams)
{
int displaymode = 0;
int curFrame = framenr - 1 /*G.scene->r.sfra*/; /* start with 0 at start frame */
char targetDir[FILE_MAXFILE+FILE_MAXDIR], targetFile[FILE_MAXFILE+FILE_MAXDIR];
FluidsimSettings *fss = fluidmd->fss;
DerivedMesh *dm = NULL;
MFace *mface;
int numfaces;
int mat_nr, flag, i;
if(!useRenderParams) {
displaymode = fss->guiDisplayMode;
} else {
displaymode = fss->renderDisplayMode;
}
strncpy(targetDir, fss->surfdataPath, FILE_MAXDIR);
// use preview or final mesh?
if(displaymode==1)
{
// just display original object
return NULL;
}
else if(displaymode==2)
{
strcat(targetDir,"fluidsurface_preview_####");
}
else
{ // 3
strcat(targetDir,"fluidsurface_final_####");
}
BLI_convertstringcode(targetDir, G.sce);
BLI_convertstringframe(targetDir, curFrame); // fixed #frame-no
strcpy(targetFile,targetDir);
strcat(targetFile, ".bobj.gz");
dm = fluidsim_read_obj(targetFile);
if(!dm)
{
// switch, abort background rendering when fluidsim mesh is missing
const char *strEnvName2 = "BLENDER_ELBEEMBOBJABORT"; // from blendercall.cpp
if(G.background==1) {
if(getenv(strEnvName2)) {
int elevel = atoi(getenv(strEnvName2));
if(elevel>0) {
printf("Env. var %s set, fluid sim mesh '%s' not found, aborting render...\n",strEnvName2, targetFile);
exit(1);
}
}
}
// display org. object upon failure which is in dm
return NULL;
}
// assign material + flags to new dm
mface = orgdm->getFaceArray(orgdm);
mat_nr = mface[0].mat_nr;
flag = mface[0].flag;
mface = dm->getFaceArray(dm);
numfaces = dm->getNumFaces(dm);
for(i=0; i<numfaces; i++)
{
mface[i].mat_nr = mat_nr;
mface[i].flag = flag;
}
// load vertex velocities, if they exist...
// TODO? use generate flag as loading flag as well?
// warning, needs original .bobj.gz mesh loading filename
/*
if(displaymode==3)
{
readVelgz(targetFile, srcob);
}
else
{
// no data for preview, only clear...
int i,j;
for(i=0; i<mesh->totvert;i++) { for(j=0; j<3; j++) { srcob->fluidsimSettings->meshSurfNormals[i].co[j] = 0.; }}
}*/
return dm;
}
void fluid_get_bb(MVert *mvert, int totvert, float obmat[][4],
/*RET*/ float start[3], /*RET*/ float size[3] )
{
float bbsx=0.0, bbsy=0.0, bbsz=0.0;
float bbex=1.0, bbey=1.0, bbez=1.0;
int i;
float vec[3];
VECCOPY(vec, mvert[0].co);
Mat4MulVecfl(obmat, vec);
bbsx = vec[0]; bbsy = vec[1]; bbsz = vec[2];
bbex = vec[0]; bbey = vec[1]; bbez = vec[2];
for(i = 1; i < totvert; i++) {
VECCOPY(vec, mvert[i].co);
Mat4MulVecfl(obmat, vec);
if(vec[0] < bbsx){ bbsx= vec[0]; }
if(vec[1] < bbsy){ bbsy= vec[1]; }
if(vec[2] < bbsz){ bbsz= vec[2]; }
if(vec[0] > bbex){ bbex= vec[0]; }
if(vec[1] > bbey){ bbey= vec[1]; }
if(vec[2] > bbez){ bbez= vec[2]; }
}
// return values...
if(start) {
start[0] = bbsx;
start[1] = bbsy;
start[2] = bbsz;
}
if(size) {
size[0] = bbex-bbsx;
size[1] = bbey-bbsy;
size[2] = bbez-bbsz;
}
}
//-------------------------------------------------------------------------------
// old interface
//-------------------------------------------------------------------------------
//-------------------------------------------------------------------------------
// file handling
//-------------------------------------------------------------------------------
void initElbeemMesh(struct Object *ob,
int *numVertices, float **vertices,
int *numTriangles, int **triangles,
int useGlobalCoords, int modifierIndex)
{
DerivedMesh *dm = NULL;
MVert *mvert;
MFace *mface;
int countTris=0, i, totvert, totface;
float *verts;
int *tris;
dm = mesh_create_derived_index_render(ob, CD_MASK_BAREMESH, modifierIndex);
//dm = mesh_create_derived_no_deform(ob,NULL);
mvert = dm->getVertArray(dm);
mface = dm->getFaceArray(dm);
totvert = dm->getNumVerts(dm);
totface = dm->getNumFaces(dm);
*numVertices = totvert;
verts = MEM_callocN( totvert*3*sizeof(float), "elbeemmesh_vertices");
for(i=0; i<totvert; i++) {
VECCOPY( &verts[i*3], mvert[i].co);
if(useGlobalCoords) { Mat4MulVecfl(ob->obmat, &verts[i*3]); }
}
*vertices = verts;
for(i=0; i<totface; i++) {
countTris++;
if(mface[i].v4) { countTris++; }
}
*numTriangles = countTris;
tris = MEM_callocN( countTris*3*sizeof(int), "elbeemmesh_triangles");
countTris = 0;
for(i=0; i<totface; i++) {
int face[4];
face[0] = mface[i].v1;
face[1] = mface[i].v2;
face[2] = mface[i].v3;
face[3] = mface[i].v4;
tris[countTris*3+0] = face[0];
tris[countTris*3+1] = face[1];
tris[countTris*3+2] = face[2];
countTris++;
if(face[3]) {
tris[countTris*3+0] = face[0];
tris[countTris*3+1] = face[2];
tris[countTris*3+2] = face[3];
countTris++;
}
}
*triangles = tris;
dm->release(dm);
}
/* read zipped fluidsim velocities into the co's of the fluidsimsettings normals struct */
void readVelgz(char *filename, Object *srcob)
{
int wri, i, j;
float wrf;
gzFile gzf;
MVert *vverts = srcob->fluidsimSettings->meshSurfNormals;
int len = strlen(filename);
Mesh *mesh = srcob->data;
// mesh and vverts have to be valid from loading...
// clean up in any case
for(i=0; i<mesh->totvert;i++)
{
for(j=0; j<3; j++)
{
vverts[i].co[j] = 0.;
}
}
if(srcob->fluidsimSettings->domainNovecgen>0) return;
if(len<7)
{
return;
}
// .bobj.gz , correct filename
// 87654321
filename[len-6] = 'v';
filename[len-5] = 'e';
filename[len-4] = 'l';
gzf = gzopen(filename, "rb");
if (!gzf)
return;
gzread(gzf, &wri, sizeof( wri ));
if(wri != mesh->totvert)
{
return;
}
for(i=0; i<mesh->totvert;i++)
{
for(j=0; j<3; j++)
{
gzread(gzf, &wrf, sizeof( wrf ));
vverts[i].co[j] = wrf;
}
}
gzclose(gzf);
}
#endif // DISABLE_ELBEEM

View File

@@ -0,0 +1,248 @@
/**
* deform_simple.c
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) Blender Foundation.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): André Pinto
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "DNA_object_types.h"
#include "DNA_modifier_types.h"
#include "DNA_meshdata_types.h"
#include "BKE_simple_deform.h"
#include "BKE_DerivedMesh.h"
#include "BKE_deform.h"
#include "BKE_utildefines.h"
#include "BLI_arithb.h"
#include "BKE_shrinkwrap.h"
#include <string.h>
#include <math.h>
//Clamps/Limits the given coordinate to: limits[0] <= co[axis] <= limits[1]
//The ammount of clamp is saved on dcut
static void axis_limit(int axis, const float limits[2], float co[3], float dcut[3])
{
float val = co[axis];
if(limits[0] > val) val = limits[0];
if(limits[1] < val) val = limits[1];
dcut[axis] = co[axis] - val;
co[axis] = val;
}
static void simpleDeform_taper(const float factor, const float dcut[3], float *co)
{
float x = co[0], y = co[1], z = co[2];
float scale = z*factor;
co[0] = x + x*scale;
co[1] = y + y*scale;
co[2] = z;
if(dcut)
{
co[0] += dcut[0];
co[1] += dcut[1];
co[2] += dcut[2];
}
}
static void simpleDeform_stretch(const float factor, const float dcut[3], float *co)
{
float x = co[0], y = co[1], z = co[2];
float scale;
scale = (z*z*factor-factor + 1.0);
co[0] = x*scale;
co[1] = y*scale;
co[2] = z*(1.0+factor);
if(dcut)
{
co[0] += dcut[0];
co[1] += dcut[1];
co[2] += dcut[2];
}
}
static void simpleDeform_twist(const float factor, const float *dcut, float *co)
{
float x = co[0], y = co[1], z = co[2];
float theta, sint, cost;
theta = z*factor;
sint = sin(theta);
cost = cos(theta);
co[0] = x*cost - y*sint;
co[1] = x*sint + y*cost;
co[2] = z;
if(dcut)
{
co[0] += dcut[0];
co[1] += dcut[1];
co[2] += dcut[2];
}
}
static void simpleDeform_bend(const float factor, const float dcut[3], float *co)
{
float x = co[0], y = co[1], z = co[2];
float theta, sint, cost;
theta = x*factor;
sint = sin(theta);
cost = cos(theta);
if(fabs(factor) > 1e-7f)
{
co[0] = -(y-1.0f/factor)*sint;
co[1] = (y-1.0f/factor)*cost + 1.0f/factor;
co[2] = z;
}
if(dcut)
{
co[0] += cost*dcut[0];
co[1] += sint*dcut[0];
co[2] += dcut[2];
}
}
/* simple deform modifier */
void SimpleDeformModifier_do(SimpleDeformModifierData *smd, struct Object *ob, struct DerivedMesh *dm, float (*vertexCos)[3], int numVerts)
{
static const float lock_axis[2] = {0.0f, 0.0f};
int i;
int limit_axis = 0;
float smd_limit[2], smd_factor;
SpaceTransform *transf = NULL, tmp_transf;
void (*simpleDeform_callback)(const float factor, const float dcut[3], float *co) = NULL; //Mode callback
int vgroup = get_named_vertexgroup_num(ob, smd->vgroup_name);
MDeformVert *dvert = NULL;
//Safe-check
if(smd->origin == ob) smd->origin = NULL; //No self references
if(smd->limit[0] < 0.0) smd->limit[0] = 0.0f;
if(smd->limit[0] > 1.0) smd->limit[0] = 1.0f;
smd->limit[0] = MIN2(smd->limit[0], smd->limit[1]); //Upper limit >= than lower limit
//Calculate matrixs do convert between coordinate spaces
if(smd->origin)
{
transf = &tmp_transf;
if(smd->originOpts & MOD_SIMPLEDEFORM_ORIGIN_LOCAL)
{
space_transform_from_matrixs(transf, ob->obmat, smd->origin->obmat);
}
else
{
Mat4CpyMat4(transf->local2target, smd->origin->obmat);
Mat4Invert(transf->target2local, transf->local2target);
}
}
//Setup vars
limit_axis = (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) ? 0 : 2; //Bend limits on X.. all other modes limit on Z
//Update limits if needed
{
float lower = FLT_MAX;
float upper = -FLT_MAX;
for(i=0; i<numVerts; i++)
{
float tmp[3];
VECCOPY(tmp, vertexCos[i]);
if(transf) space_transform_apply(transf, tmp);
lower = MIN2(lower, tmp[limit_axis]);
upper = MAX2(upper, tmp[limit_axis]);
}
//SMD values are normalized to the BV, calculate the absolut values
smd_limit[1] = lower + (upper-lower)*smd->limit[1];
smd_limit[0] = lower + (upper-lower)*smd->limit[0];
smd_factor = smd->factor / MAX2(FLT_EPSILON, smd_limit[1]-smd_limit[0]);
}
if(dm)
dvert = dm->getVertDataArray(dm, CD_MDEFORMVERT);
switch(smd->mode)
{
case MOD_SIMPLEDEFORM_MODE_TWIST: simpleDeform_callback = simpleDeform_twist; break;
case MOD_SIMPLEDEFORM_MODE_BEND: simpleDeform_callback = simpleDeform_bend; break;
case MOD_SIMPLEDEFORM_MODE_TAPER: simpleDeform_callback = simpleDeform_taper; break;
case MOD_SIMPLEDEFORM_MODE_STRETCH: simpleDeform_callback = simpleDeform_stretch; break;
default:
return; //No simpledeform mode?
}
for(i=0; i<numVerts; i++)
{
float weight = vertexgroup_get_vertex_weight(dvert, i, vgroup);
if(weight != 0.0f)
{
float co[3], dcut[3] = {0.0f, 0.0f, 0.0f};
if(transf) space_transform_apply(transf, vertexCos[i]);
VECCOPY(co, vertexCos[i]);
//Apply axis limits
if(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) //Bend mode shoulnt have any lock axis
{
if(smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) axis_limit(0, lock_axis, co, dcut);
if(smd->axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) axis_limit(1, lock_axis, co, dcut);
}
axis_limit(limit_axis, smd_limit, co, dcut);
simpleDeform_callback(smd_factor, dcut, co); //Apply deform
VecLerpf(vertexCos[i], vertexCos[i], co, weight); //Use vertex weight has coef of linear interpolation
if(transf) space_transform_invert(transf, vertexCos[i]);
}
}
}

View File

@@ -0,0 +1,9 @@
#include "GLBlendEquation.h"
void FRS_glBlendEquation(GLenum mode) {
if( glBlendEquation ) {
glBlendEquation(mode);
} else if ( glBlendEquationEXT ) {
glBlendEquationEXT(mode);
}
}

View File

@@ -0,0 +1,16 @@
#ifndef GLBLENDEQUATION_H
#define GLBLENDEQUATION_H
#include <GL/glew.h>
#ifdef WIN32
# include <windows.h>
#endif
#ifdef __MACH__
# include <OpenGL/gl.h>
#else
# include <GL/gl.h>
#endif
void FRS_glBlendEquation(GLenum mode);
#endif // GLBLENDEQUATION_H

View File

@@ -0,0 +1,117 @@
/**
* $Id: BIF_keyframing.h 14444 2008-04-16 22:40:48Z aligorith $
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place * Suite 330, Boston, MA 02111*1307, USA.
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender (with some old code)
*
* Contributor(s): Joshua Leung
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef BIF_KEYFRAMING_H
#define BIF_KEYFRAMING_H
struct ListBase;
struct ID;
struct IpoCurve;
struct BezTriple;
/* ************ Keyframing Management **************** */
/* Lesser Keyframing API call:
* Use this when validation of necessary animation data isn't necessary as it already
* exists, and there is a beztriple that can be directly copied into the array.
*/
int insert_bezt_icu(struct IpoCurve *icu, struct BezTriple *bezt);
/* Main Keyframing API call:
* Use this when validation of necessary animation data isn't necessary as it
* already exists. It will insert a keyframe using the current value being keyframed.
*/
void insert_vert_icu(struct IpoCurve *icu, float x, float y, short flag);
/* flags for use in insert_key(), and insert_vert_icu() */
enum {
INSERTKEY_NEEDED = (1<<0), /* only insert keyframes where they're needed */
INSERTKEY_MATRIX = (1<<1), /* insert 'visual' keyframes where possible/needed */
INSERTKEY_FAST = (1<<2), /* don't recalculate handles,etc. after adding key */
INSERTKEY_FASTR = (1<<3), /* don't realloc mem (or increase count, as array has already been set out) */
INSERTKEY_REPLACE = (1<<4), /* only replace an existing keyframe (this overrides INSERTKEY_NEEDED) */
} eInsertKeyFlags;
/* -------- */
/* Main Keyframing API calls:
* Use this to create any necessary animation data,, and then insert a keyframe
* using the current value being keyframed, in the relevant place. Returns success.
*/
// TODO: adapt this for new data-api -> this blocktype, etc. stuff is evil!
short insertkey(struct ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag);
/* Main Keyframing API call:
* Use this to delete keyframe on current frame for relevant channel. Will perform checks just in case.
*/
short deletekey(struct ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag);
/* Main Keyframe Management calls:
* These handle keyframes management from various spaces. They will handle the menus
* required for each space.
*/
void common_insertkey(void);
void common_deletekey(void);
/* ************ Auto-Keyframing ********************** */
/* Notes:
* - All the defines for this (User-Pref settings and Per-Scene settings)
* are defined in DNA_userdef_types.h
* - Scene settings take presidence over those for userprefs, with old files
* inheriting userpref settings for the scene settings
* - "On/Off + Mode" are stored per Scene, but "settings" are currently stored
* as userprefs
*/
/* Auto-Keying macros for use by various tools */
/* check if auto-keyframing is enabled (per scene takes presidence) */
#define IS_AUTOKEY_ON ((G.scene) ? (G.scene->autokey_mode & AUTOKEY_ON) : (U.autokey_mode & AUTOKEY_ON))
/* check the mode for auto-keyframing (per scene takes presidence) */
#define IS_AUTOKEY_MODE(mode) ((G.scene) ? (G.scene->autokey_mode == AUTOKEY_MODE_##mode) : (U.autokey_mode == AUTOKEY_MODE_##mode))
/* check if a flag is set for auto-keyframing (as userprefs only!) */
#define IS_AUTOKEY_FLAG(flag) (U.autokey_flag & AUTOKEY_FLAG_##flag)
/* ************ Keyframe Checking ******************** */
/* Checks whether a keyframe exists for the given ID-block one the given frame */
short id_cfra_has_keyframe(struct ID *id, short filter);
/* filter flags fr id_cfra_has_keyframe */
enum {
/* general */
ANIMFILTER_ALL = 0, /* include all available animation data */
ANIMFILTER_LOCAL = (1<<0), /* only include locally available anim data */
/* object specific */
ANIMFILTER_MAT = (1<<1), /* include material keyframes too */
ANIMFILTER_SKEY = (1<<2), /* shape keys (for geometry) */
} eAnimFilterFlags;
#endif /* BIF_KEYFRAMING_H */

View File

@@ -0,0 +1,1870 @@
/**
* $Id: keyframing.c 14881 2008-05-18 10:41:42Z aligorith $
*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* The Original Code is Copyright (C) 2008, Blender Foundation
* This is a new part of Blender (with some old code)
*
* Contributor(s): Joshua Leung
*
* ***** END GPL LICENSE BLOCK *****
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <math.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "MEM_guardedalloc.h"
#include "BLI_arithb.h"
#include "BLI_blenlib.h"
#include "BLI_dynstr.h"
#include "DNA_listBase.h"
#include "DNA_ID.h"
#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_camera_types.h"
#include "DNA_constraint_types.h"
#include "DNA_curve_types.h"
#include "DNA_ipo_types.h"
#include "DNA_lamp_types.h"
#include "DNA_object_types.h"
#include "DNA_material_types.h"
#include "DNA_screen_types.h"
#include "DNA_scene_types.h"
#include "DNA_space_types.h"
#include "DNA_texture_types.h"
#include "DNA_userdef_types.h"
#include "DNA_vec_types.h"
#include "DNA_view3d_types.h"
#include "DNA_world_types.h"
#include "BKE_global.h"
#include "BKE_utildefines.h"
#include "BKE_blender.h"
#include "BKE_main.h"
#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_constraint.h"
#include "BKE_curve.h"
#include "BKE_depsgraph.h"
#include "BKE_ipo.h"
#include "BKE_object.h"
#include "BIF_keyframing.h"
#include "BIF_butspace.h"
#include "BIF_editaction.h"
#include "BIF_editkey.h"
#include "BIF_interface.h"
#include "BIF_mywindow.h"
#include "BIF_poseobject.h"
#include "BIF_screen.h"
#include "BIF_space.h"
#include "BIF_toolbox.h"
#include "BIF_toets.h"
#include "BSE_editipo.h"
#include "BSE_node.h"
#include "BSE_time.h"
#include "BSE_view.h"
#include "blendef.h"
#include "PIL_time.h" /* sleep */
#include "mydevice.h"
/* ************************************************** */
/* LOCAL TYPES AND DEFINES */
/* -------------- Keying Sets ------------------- */
/* keying set - a set of channels that will be keyframed together */
// TODO: move this to a header to allow custom sets someday?
typedef struct bKeyingSet {
/* callback func to consider if keyingset should be included
* (by default, if this is undefined, item will be shown)
*/
short (*include_cb)(struct bKeyingSet *, const char *);
char name[48]; /* name of keyingset */
int blocktype; /* blocktype that all channels belong to */ // in future, this may be eliminated
short flag; /* flags to use when setting keyframes */
short chan_num; /* number of channels to insert keyframe in */
short adrcodes[32]; /* adrcodes for channels to insert keys for (ideally would be variable-len, but limit of 32 will suffice) */
} bKeyingSet;
/* keying set context - an array of keying sets and the number of them */
typedef struct bKeyingContext {
bKeyingSet *keyingsets; /* array containing the keyingsets of interest */
bKeyingSet *lastused; /* item that was chosen last time*/
int tot; /* number of keyingsets in */
} bKeyingContext;
/* ----------- Common KeyData Sources ------------ */
/* temporary struct to gather data combos to keyframe */
typedef struct bCommonKeySrc {
struct bCommonKeySrc *next, *prev;
/* general data/destination-source settings */
ID *id; /* id-block this comes from */
char *actname; /* name of action channel */
char *constname; /* name of constraint channel */
/* general destination source settings */
Ipo *ipo; /* ipo-block that id-block has (optional) */
bAction *act; /* action-block that id-block has (optional) */
/* pose-level settings */
bPoseChannel *pchan; /* pose channel */
/* buttons-window settings */
int map; /* offset to apply to certain adrcodes */
} bCommonKeySrc;
/* ************************************************** */
/* KEYFRAME INSERTION */
/* -------------- BezTriple Insertion -------------------- */
/* threshold for inserting keyframes - threshold here should be good enough for now, but should become userpref */
#define BEZT_INSERT_THRESH 0.00001
/* Binary search algorithm for finding where to insert BezTriple. (for use by insert_bezt_icu)
* Returns the index to insert at (data already at that index will be offset if replace is 0)
*/
static int binarysearch_bezt_index (BezTriple array[], BezTriple *item, int arraylen, short *replace)
{
int start=0, end=arraylen;
int loopbreaker= 0, maxloop= arraylen * 2;
const float frame= (item)? item->vec[1][0] : 0.0f;
/* initialise replace-flag first */
*replace= 0;
/* sneaky optimisations (don't go through searching process if...):
* - keyframe to be added is to be added out of current bounds
* - keyframe to be added would replace one of the existing ones on bounds
*/
if ((arraylen <= 0) || ELEM(NULL, array, item)) {
printf("Warning: binarysearch_bezt_index encountered invalid array \n");
return 0;
}
else {
/* check whether to add before/after/on */
float framenum;
/* 'First' Keyframe (when only one keyframe, this case is used) */
framenum= array[0].vec[1][0];
if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) {
*replace = 1;
return 0;
}
else if (frame < framenum)
return 0;
/* 'Last' Keyframe */
framenum= array[(arraylen-1)].vec[1][0];
if (IS_EQT(frame, framenum, BEZT_INSERT_THRESH)) {
*replace= 1;
return (arraylen - 1);
}
else if (frame > framenum)
return arraylen;
}
/* most of the time, this loop is just to find where to put it
* 'loopbreaker' is just here to prevent infinite loops
*/
for (loopbreaker=0; (start <= end) && (loopbreaker < maxloop); loopbreaker++) {
/* compute and get midpoint */
int mid = (start + end) / 2;
float midfra= array[mid].vec[1][0];
/* check if exactly equal to midpoint */
if (IS_EQT(frame, midfra, BEZT_INSERT_THRESH)) {
*replace = 1;
return mid;
}
/* repeat in upper/lower half */
if (frame > midfra)
start= mid + 1;
else if (frame < midfra)
end= mid - 1;
}
/* print error if loop-limit exceeded */
if (loopbreaker == (maxloop-1)) {
printf("Error: binarysearch_bezt_index was taking too long \n");
// include debug info
printf("\tround = %d: start = %d, end = %d, arraylen = %d \n", loopbreaker, start, end, arraylen);
}
/* not found, so return where to place it */
return start;
}
/* This function adds a given BezTriple to an IPO-Curve. It will allocate
* memory for the array if needed, and will insert the BezTriple into a
* suitable place in chronological order.
*
* NOTE: any recalculate of the IPO-Curve that needs to be done will need to
* be done by the caller.
*/
int insert_bezt_icu (IpoCurve *icu, BezTriple *bezt)
{
BezTriple *newb;
int i= 0;
if (icu->bezt == NULL) {
icu->bezt= MEM_callocN(sizeof(BezTriple), "beztriple");
*(icu->bezt)= *bezt;
icu->totvert= 1;
}
else {
short replace = -1;
i = binarysearch_bezt_index(icu->bezt, bezt, icu->totvert, &replace);
if (replace) {
/* sanity check: 'i' may in rare cases exceed arraylen */
if ((i >= 0) && (i < icu->totvert))
*(icu->bezt + i) = *bezt;
}
else {
/* add new */
newb= MEM_callocN((icu->totvert+1)*sizeof(BezTriple), "beztriple");
/* add the beztriples that should occur before the beztriple to be pasted (originally in ei->icu) */
if (i > 0)
memcpy(newb, icu->bezt, i*sizeof(BezTriple));
/* add beztriple to paste at index i */
*(newb + i)= *bezt;
/* add the beztriples that occur after the beztriple to be pasted (originally in icu) */
if (i < icu->totvert)
memcpy(newb+i+1, icu->bezt+i, (icu->totvert-i)*sizeof(BezTriple));
/* replace (+ free) old with new */
MEM_freeN(icu->bezt);
icu->bezt= newb;
icu->totvert++;
}
}
/* we need to return the index, so that some tools which do post-processing can
* detect where we added the BezTriple in the array
*/
return i;
}
/* This function is a wrapper for insert_bezt_icu, and should be used when
* adding a new keyframe to a curve, when the keyframe doesn't exist anywhere
* else yet.
*
* 'fast' - is only for the python API where importing BVH's would take an extreamly long time.
*/
void insert_vert_icu (IpoCurve *icu, float x, float y, short fast)
{
BezTriple beztr;
int a, h1, h2;
/* set all three points, for nicer start position */
memset(&beztr, 0, sizeof(BezTriple));
beztr.vec[0][0]= x;
beztr.vec[0][1]= y;
beztr.vec[1][0]= x;
beztr.vec[1][1]= y;
beztr.vec[2][0]= x;
beztr.vec[2][1]= y;
beztr.hide= IPO_BEZ;
beztr.f1= beztr.f2= beztr.f3= SELECT;
beztr.h1= beztr.h2= HD_AUTO;
/* add temp beztriple to keyframes */
a= insert_bezt_icu(icu, &beztr);
if (!fast) calchandles_ipocurve(icu);
/* set handletype */
if (icu->totvert > 2) {
BezTriple *bezt;
h1= h2= HD_AUTO;
bezt= (icu->bezt + a);
if (a > 0) h1= (bezt-1)->h2;
if (a < icu->totvert-1) h2= (bezt+1)->h1;
bezt->h1= h1;
bezt->h2= h2;
if (!fast) calchandles_ipocurve(icu);
}
}
/* ------------------- Get Data ------------------------ */
/* Get pointer to use to get values from */
// FIXME: this should not be possible with Data-API
static void *get_context_ipo_poin(ID *id, int blocktype, char *actname, char *constname, IpoCurve *icu, int *vartype)
{
switch (blocktype) {
case ID_PO: /* posechannel */
if (GS(id->name)==ID_OB) {
Object *ob= (Object *)id;
bPoseChannel *pchan= get_pose_channel(ob->pose, actname);
if (pchan) {
*vartype= IPO_FLOAT;
return get_pchan_ipo_poin(pchan, icu->adrcode);
}
}
break;
case ID_CO: /* constraint */
if ((GS(id->name)==ID_OB) && (constname && constname[0])) {
Object *ob= (Object *)id;
bConstraint *con;
/* assume that we only want the influence (as only used for Constraint Channels) */
if ((ob->ipoflag & OB_ACTION_OB) && !strcmp(actname, "Object")) {
for (con= ob->constraints.first; con; con= con->next) {
if (strcmp(constname, con->name)==0) {
*vartype= IPO_FLOAT;
return &con->enforce;
}
}
}
else if (ob->pose) {
bPoseChannel *pchan= get_pose_channel(ob->pose, actname);
if (pchan) {
for (con= pchan->constraints.first; con; con= con->next) {
if (strcmp(constname, con->name)==0) {
*vartype= IPO_FLOAT;
return &con->enforce;
}
}
}
}
}
break;
case ID_OB: /* object */
/* hack: layer channels for object need to be keyed WITHOUT localview flag...
* tsk... tsk... why must we just dump bitflags upon users :/
*/
if ((GS(id->name)==ID_OB) && (icu->adrcode==OB_LAY)) {
Object *ob= (Object *)id;
static int layer = 0;
/* init layer to be the object's layer var, then remove local view from it */
layer = ob->lay;
layer &= 0xFFFFFF;
*vartype= IPO_INT_BIT;
/* return pointer to this static var
* - assumes that this pointer won't be stored for use later, so may not be threadsafe
* if multiple keyframe calls are made, but that is unlikely to happen in the near future
*/
return (void *)(&layer);
}
/* no break here for other ob channel-types - as they can be done normally */
default: /* normal data-source */
return get_ipo_poin(id, icu, vartype);
}
/* not valid... */
return NULL;
}
/* -------------- 'Smarter' Keyframing Functions -------------------- */
/* return codes for new_key_needed */
enum {
KEYNEEDED_DONTADD = 0,
KEYNEEDED_JUSTADD,
KEYNEEDED_DELPREV,
KEYNEEDED_DELNEXT
} eKeyNeededStatus;
/* This helper function determines whether a new keyframe is needed */
/* Cases where keyframes should not be added:
* 1. Keyframe to be added bewteen two keyframes with similar values
* 2. Keyframe to be added on frame where two keyframes are already situated
* 3. Keyframe lies at point that intersects the linear line between two keyframes
*/
static short new_key_needed (IpoCurve *icu, float cFrame, float nValue)
{
BezTriple *bezt=NULL, *prev=NULL;
int totCount, i;
float valA = 0.0f, valB = 0.0f;
/* safety checking */
if (icu == NULL) return KEYNEEDED_JUSTADD;
totCount= icu->totvert;
if (totCount == 0) return KEYNEEDED_JUSTADD;
/* loop through checking if any are the same */
bezt= icu->bezt;
for (i=0; i<totCount; i++) {
float prevPosi=0.0f, prevVal=0.0f;
float beztPosi=0.0f, beztVal=0.0f;
/* get current time+value */
beztPosi= bezt->vec[1][0];
beztVal= bezt->vec[1][1];
if (prev) {
/* there is a keyframe before the one currently being examined */
/* get previous time+value */
prevPosi= prev->vec[1][0];
prevVal= prev->vec[1][1];
/* keyframe to be added at point where there are already two similar points? */
if (IS_EQ(prevPosi, cFrame) && IS_EQ(beztPosi, cFrame) && IS_EQ(beztPosi, prevPosi)) {
return KEYNEEDED_DONTADD;
}
/* keyframe between prev+current points ? */
if ((prevPosi <= cFrame) && (cFrame <= beztPosi)) {
/* is the value of keyframe to be added the same as keyframes on either side ? */
if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal)) {
return KEYNEEDED_DONTADD;
}
else {
float realVal;
/* get real value of curve at that point */
realVal= eval_icu(icu, cFrame);
/* compare whether it's the same as proposed */
if (IS_EQ(realVal, nValue))
return KEYNEEDED_DONTADD;
else
return KEYNEEDED_JUSTADD;
}
}
/* new keyframe before prev beztriple? */
if (cFrame < prevPosi) {
/* A new keyframe will be added. However, whether the previous beztriple
* stays around or not depends on whether the values of previous/current
* beztriples and new keyframe are the same.
*/
if (IS_EQ(prevVal, nValue) && IS_EQ(beztVal, nValue) && IS_EQ(prevVal, beztVal))
return KEYNEEDED_DELNEXT;
else
return KEYNEEDED_JUSTADD;
}
}
else {
/* just add a keyframe if there's only one keyframe
* and the new one occurs before the exisiting one does.
*/
if ((cFrame < beztPosi) && (totCount==1))
return KEYNEEDED_JUSTADD;
}
/* continue. frame to do not yet passed (or other conditions not met) */
if (i < (totCount-1)) {
prev= bezt;
bezt++;
}
else
break;
}
/* Frame in which to add a new-keyframe occurs after all other keys
* -> If there are at least two existing keyframes, then if the values of the
* last two keyframes and the new-keyframe match, the last existing keyframe
* gets deleted as it is no longer required.
* -> Otherwise, a keyframe is just added. 1.0 is added so that fake-2nd-to-last
* keyframe is not equal to last keyframe.
*/
bezt= (icu->bezt + (icu->totvert - 1));
valA= bezt->vec[1][1];
if (prev)
valB= prev->vec[1][1];
else
valB= bezt->vec[1][1] + 1.0f;
if (IS_EQ(valA, nValue) && IS_EQ(valA, valB))
return KEYNEEDED_DELPREV;
else
return KEYNEEDED_JUSTADD;
}
/* ------------------ 'Visual' Keyframing Functions ------------------ */
/* internal status codes for visualkey_can_use */
enum {
VISUALKEY_NONE = 0,
VISUALKEY_LOC,
VISUALKEY_ROT
};
/* This helper function determines if visual-keyframing should be used when
* inserting keyframes for the given channel. As visual-keyframing only works
* on Object and Pose-Channel blocks, this should only get called for those
* blocktypes, when using "standard" keying but 'Visual Keying' option in Auto-Keying
* settings is on.
*/
static short visualkey_can_use (ID *id, int blocktype, char *actname, char *constname, int adrcode)
{
Object *ob= NULL;
bConstraint *con= NULL;
short searchtype= VISUALKEY_NONE;
/* validate data */
if ((id == NULL) || (GS(id->name)!=ID_OB) || !(ELEM(blocktype, ID_OB, ID_PO)))
return 0;
/* get first constraint and determine type of keyframe constraints to check for*/
ob= (Object *)id;
if (blocktype == ID_OB) {
con= ob->constraints.first;
if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z))
searchtype= VISUALKEY_LOC;
else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z))
searchtype= VISUALKEY_ROT;
}
else if (blocktype == ID_PO) {
bPoseChannel *pchan= get_pose_channel(ob->pose, actname);
con= pchan->constraints.first;
if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z))
searchtype= VISUALKEY_LOC;
else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z))
searchtype= VISUALKEY_ROT;
}
/* only search if a searchtype and initial constraint are available */
if (searchtype && con) {
for (; con; con= con->next) {
/* only consider constraint if it is not disabled, and has influence */
if (con->flag & CONSTRAINT_DISABLE) continue;
if (con->enforce == 0.0f) continue;
/* some constraints may alter these transforms */
switch (con->type) {
/* multi-transform constraints */
case CONSTRAINT_TYPE_CHILDOF:
return 1;
case CONSTRAINT_TYPE_TRANSFORM:
return 1;
case CONSTRAINT_TYPE_FOLLOWPATH:
return 1;
/* single-transform constraits */
case CONSTRAINT_TYPE_TRACKTO:
if (searchtype==VISUALKEY_ROT) return 1;
break;
case CONSTRAINT_TYPE_ROTLIMIT:
if (searchtype==VISUALKEY_ROT) return 1;
break;
case CONSTRAINT_TYPE_LOCLIMIT:
if (searchtype==VISUALKEY_LOC) return 1;
break;
case CONSTRAINT_TYPE_ROTLIKE:
if (searchtype==VISUALKEY_ROT) return 1;
break;
case CONSTRAINT_TYPE_DISTLIMIT:
if (searchtype==VISUALKEY_LOC) return 1;
break;
case CONSTRAINT_TYPE_LOCLIKE:
if (searchtype==VISUALKEY_LOC) return 1;
break;
case CONSTRAINT_TYPE_LOCKTRACK:
if (searchtype==VISUALKEY_ROT) return 1;
break;
case CONSTRAINT_TYPE_MINMAX:
if (searchtype==VISUALKEY_LOC) return 1;
break;
default:
break;
}
}
}
/* when some condition is met, this function returns, so here it can be 0 */
return 0;
}
/* This helper function extracts the value to use for visual-keyframing
* In the event that it is not possible to perform visual keying, try to fall-back
* to using the poin method. Assumes that all data it has been passed is valid.
*/
static float visualkey_get_value (ID *id, int blocktype, char *actname, char *constname, int adrcode, IpoCurve *icu)
{
Object *ob;
void *poin = NULL;
int index, vartype;
/* validate situtation */
if ((id==NULL) || (GS(id->name)!=ID_OB) || (ELEM(blocktype, ID_OB, ID_PO)==0))
return 0.0f;
/* get object */
ob= (Object *)id;
/* only valid for objects or posechannels */
if (blocktype == ID_OB) {
/* parented objects are not supported, as the effects of the parent
* are included in the matrix, which kindof beats the point
*/
if (ob->parent == NULL) {
/* only Location or Rotation keyframes are supported now */
if (ELEM3(adrcode, OB_LOC_X, OB_LOC_Y, OB_LOC_Z)) {
/* assumes that OB_LOC_Z > OB_LOC_Y > OB_LOC_X */
index= adrcode - OB_LOC_X;
return ob->obmat[3][index];
}
else if (ELEM3(adrcode, OB_ROT_X, OB_ROT_Y, OB_ROT_Z)) {
float eul[3];
/* assumes that OB_ROT_Z > OB_ROT_Y > OB_ROT_X */
index= adrcode - OB_ROT_X;
Mat4ToEul(ob->obmat, eul);
return eul[index]*(5.72958);
}
}
}
else if (blocktype == ID_PO) {
bPoseChannel *pchan;
float tmat[4][4];
/* get data to use */
pchan= get_pose_channel(ob->pose, actname);
/* Although it is not strictly required for this particular space conversion,
* arg1 must not be null, as there is a null check for the other conversions to
* be safe. Therefore, the active object is passed here, and in many cases, this
* will be what owns the pose-channel that is getting this anyway.
*/
Mat4CpyMat4(tmat, pchan->pose_mat);
constraint_mat_convertspace(ob, pchan, tmat, CONSTRAINT_SPACE_POSE, CONSTRAINT_SPACE_LOCAL);
/* Loc, Rot/Quat keyframes are supported... */
if (ELEM3(adrcode, AC_LOC_X, AC_LOC_Y, AC_LOC_Z)) {
/* assumes that AC_LOC_Z > AC_LOC_Y > AC_LOC_X */
index= adrcode - AC_LOC_X;
/* only use for non-connected bones */
if ((pchan->bone->parent) && !(pchan->bone->flag & BONE_CONNECTED))
return tmat[3][index];
else if (pchan->bone->parent == NULL)
return tmat[3][index];
}
else if (ELEM4(adrcode, AC_QUAT_W, AC_QUAT_X, AC_QUAT_Y, AC_QUAT_Z)) {
float trimat[3][3], quat[4];
/* assumes that AC_QUAT_Z > AC_QUAT_Y > AC_QUAT_X > AC_QUAT_W */
index= adrcode - AC_QUAT_W;
Mat3CpyMat4(trimat, tmat);
Mat3ToQuat_is_ok(trimat, quat);
return quat[index];
}
}
/* as the function hasn't returned yet, try reading from poin */
poin= get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype);
if (poin)
return read_ipo_poin(poin, vartype);
else
return 0.0;
}
/* ------------------------- Insert Key API ------------------------- */
/* Main Keyframing API call:
* Use this when validation of necessary animation data isn't necessary as it
* already exists. It will insert a keyframe using the current value being keyframed.
*
* The flag argument is used for special settings that alter the behaviour of
* the keyframe insertion. These include the 'visual' keyframing modes, quick refresh,
* and extra keyframe filtering.
*/
short insertkey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag)
{
IpoCurve *icu;
/* get ipo-curve */
icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 1);
/* only continue if we have an ipo-curve to add keyframe to */
if (icu) {
float cfra = frame_to_float(CFRA);
float curval= 0.0f;
void *poin = NULL;
int vartype;
/* apply special time tweaking */
if (GS(id->name) == ID_OB) {
Object *ob= (Object *)id;
/* apply NLA-scaling (if applicable) */
if (actname && actname[0])
cfra= get_action_frame(ob, cfra);
/* ancient time-offset cruft */
if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) {
/* actually frametofloat calc again! */
cfra-= give_timeoffset(ob)*G.scene->r.framelen;
}
}
/* get pointer to data to read from */
poin= get_context_ipo_poin(id, blocktype, actname, constname, icu, &vartype);
if (poin == NULL) return 0;
/* obtain value to give keyframe */
if ( (flag & INSERTKEY_MATRIX) &&
(visualkey_can_use(id, blocktype, actname, constname, adrcode)) )
{
/* visual-keying is only available for object and pchan datablocks, as
* it works by keyframing using a value extracted from the final matrix
* instead of using the kt system to extract a value.
*/
curval= visualkey_get_value(id, blocktype, actname, constname, adrcode, icu);
}
else {
/* use kt's read_poin function to extract value (kt->read_poin should
* exist in all cases, but it never hurts to check)
*/
curval= read_ipo_poin(poin, vartype);
}
/* only insert keyframes where they are needed */
if (flag & INSERTKEY_NEEDED) {
short insert_mode;
/* check whether this curve really needs a new keyframe */
insert_mode= new_key_needed(icu, cfra, curval);
/* insert new keyframe at current frame */
if (insert_mode)
insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST));
/* delete keyframe immediately before/after newly added */
switch (insert_mode) {
case KEYNEEDED_DELPREV:
delete_icu_key(icu, icu->totvert-2, 1);
break;
case KEYNEEDED_DELNEXT:
delete_icu_key(icu, 1, 1);
break;
}
}
else {
/* just insert keyframe */
insert_vert_icu(icu, cfra, curval, (flag & INSERTKEY_FAST));
}
/* return success */
return 1;
}
/* return failure */
return 0;
}
/* ************************************************** */
/* KEYFRAME DELETION */
/* Main Keyframing API call:
* Use this when validation of necessary animation data isn't necessary as it
* already exists. It will delete a keyframe at the current frame.
*
* The flag argument is used for special settings that alter the behaviour of
* the keyframe deletion. These include the quick refresh options.
*/
short deletekey (ID *id, int blocktype, char *actname, char *constname, int adrcode, short flag)
{
Ipo *ipo;
IpoCurve *icu;
/* get ipo-curve
* Note: here is one of the places where we don't want new ipo + ipo-curve added!
* so 'add' var must be 0
*/
ipo= verify_ipo(id, blocktype, actname, constname, NULL, 0);
icu= verify_ipocurve(id, blocktype, actname, constname, NULL, adrcode, 0);
/* only continue if we have an ipo-curve to remove keyframes from */
if (icu) {
BezTriple bezt;
float cfra = frame_to_float(CFRA);
short found = -1;
int i;
/* apply special time tweaking */
if (GS(id->name) == ID_OB) {
Object *ob= (Object *)id;
/* apply NLA-scaling (if applicable) */
if (actname && actname[0])
cfra= get_action_frame(ob, cfra);
/* ancient time-offset cruft */
if ( (ob->ipoflag & OB_OFFS_OB) && (give_timeoffset(ob)) ) {
/* actually frametofloat calc again! */
cfra-= give_timeoffset(ob)*G.scene->r.framelen;
}
}
/* only need to set bezt->vec[1][0], as that's all binarysearch uses */
memset(&bezt, 0, sizeof(BezTriple));
bezt.vec[1][0]= cfra;
/* try to find index of beztriple to get rid of */
i = binarysearch_bezt_index(icu->bezt, &bezt, icu->totvert, &found);
if (found) {
/* delete the key at the index (will sanity check + do recalc afterwards ) */
delete_icu_key(icu, i, 1);
/* Only delete curve too if there isn't an ipo-driver still hanging around on an empty curve */
if (icu->totvert==0 && icu->driver==NULL) {
BLI_remlink(&ipo->curve, icu);
free_ipo_curve(icu);
}
/* return success */
return 1;
}
}
/* return failure */
return 0;
}
/* ************************************************** */
/* COMMON KEYFRAME MANAGEMENT (common_insertkey/deletekey) */
/* ------------- KeyingSet Defines ------------ */
/* Note: these must all be named with the defks_* prefix, otherwise the template macro will not work! */
/* macro for defining keyingset contexts */
#define KSC_TEMPLATE(ctx_name) {&defks_##ctx_name[0], NULL, sizeof(defks_##ctx_name)/sizeof(bKeyingSet)}
/* --- */
/* check if option not available for deleting keys */
static short incl_non_del_keys (bKeyingSet *ks, const char mode[])
{
/* as optimisation, assume that it is sufficient to check only first letter
* of mode (int comparison should be faster than string!)
*/
//if (strcmp(mode, "Delete")==0)
if (mode && mode[0]=='D')
return 0;
return 1;
}
/* Object KeyingSets ------ */
/* check if include shapekey entry */
static short incl_v3d_ob_shapekey (bKeyingSet *ks, const char mode[])
{
Object *ob= (G.obedit)? (G.obedit) : (OBACT);
char *newname= NULL;
/* not available for delete mode */
if (strcmp(mode, "Delete")==0)
return 0;
/* check if is geom object that can get shapekeys */
switch (ob->type) {
/* geometry? */
case OB_MESH: newname= "Mesh"; break;
case OB_CURVE: newname= "Curve"; break;
case OB_SURF: newname= "Surface"; break;
case OB_LATTICE: newname= "Lattice"; break;
/* not geometry! */
default:
return 0;
}
/* if ks is shapekey entry (this could be callled for separator before too!) */
if (ks->flag == -3)
sprintf(ks->name, newname);
/* if it gets here, it's ok */
return 1;
}
/* array for object keyingset defines */
bKeyingSet defks_v3d_object[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{NULL, "Loc", ID_OB, 0, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}},
{NULL, "Rot", ID_OB, 0, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}},
{NULL, "Scale", ID_OB, 0, 3, {OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "LocRot", ID_OB, 0, 6,
{OB_LOC_X,OB_LOC_Y,OB_LOC_Z,
OB_ROT_X,OB_ROT_Y,OB_ROT_Z}},
{NULL, "LocScale", ID_OB, 0, 6,
{OB_LOC_X,OB_LOC_Y,OB_LOC_Z,
OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}},
{NULL, "LocRotScale", ID_OB, 0, 9,
{OB_LOC_X,OB_LOC_Y,OB_LOC_Z,
OB_ROT_X,OB_ROT_Y,OB_ROT_Z,
OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}},
{NULL, "RotScale", ID_OB, 0, 6,
{OB_ROT_X,OB_ROT_Y,OB_ROT_Z,
OB_SIZE_X,OB_SIZE_Y,OB_SIZE_Z}},
{incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator
{incl_non_del_keys, "VisualLoc", ID_OB, INSERTKEY_MATRIX, 3, {OB_LOC_X,OB_LOC_Y,OB_LOC_Z}},
{incl_non_del_keys, "VisualRot", ID_OB, INSERTKEY_MATRIX, 3, {OB_ROT_X,OB_ROT_Y,OB_ROT_Z}},
{incl_non_del_keys, "VisualLocRot", ID_OB, INSERTKEY_MATRIX, 6,
{OB_LOC_X,OB_LOC_Y,OB_LOC_Z,
OB_ROT_X,OB_ROT_Y,OB_ROT_Z}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Layer", ID_OB, 0, 1, {OB_LAY}}, // icky option...
{NULL, "Available", ID_OB, -2, 0, {0}},
{incl_v3d_ob_shapekey, "%l%l", 0, -1, 0, {0}}, // separator (linked to shapekey entry)
{incl_v3d_ob_shapekey, "<ShapeKey>", ID_OB, -3, 0, {0}}
};
/* PoseChannel KeyingSets ------ */
/* array for posechannel keyingset defines */
bKeyingSet defks_v3d_pchan[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{NULL, "Loc", ID_PO, 0, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}},
{NULL, "Rot", ID_PO, 0, 4, {AC_QUAT_W,AC_QUAT_X,AC_QUAT_Y,AC_QUAT_Z}},
{NULL, "Scale", ID_PO, 0, 3, {AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "LocRot", ID_PO, 0, 7,
{AC_LOC_X,AC_LOC_Y,AC_LOC_Z,AC_QUAT_W,
AC_QUAT_X,AC_QUAT_Y,AC_QUAT_Z}},
{NULL, "LocScale", ID_PO, 0, 6,
{AC_LOC_X,AC_LOC_Y,AC_LOC_Z,
AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}},
{NULL, "LocRotScale", ID_PO, 0, 10,
{AC_LOC_X,AC_LOC_Y,AC_LOC_Z,AC_QUAT_W,AC_QUAT_X,
AC_QUAT_Y,AC_QUAT_Z,AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}},
{NULL, "RotScale", ID_PO, 0, 7,
{AC_QUAT_W,AC_QUAT_X,AC_QUAT_Y,AC_QUAT_Z,
AC_SIZE_X,AC_SIZE_Y,AC_SIZE_Z}},
{incl_non_del_keys, "%l", 0, -1, 0, {0}}, // separator
{incl_non_del_keys, "VisualLoc", ID_PO, INSERTKEY_MATRIX, 3, {AC_LOC_X,AC_LOC_Y,AC_LOC_Z}},
{incl_non_del_keys, "VisualRot", ID_PO, INSERTKEY_MATRIX, 4, {AC_QUAT_W,AC_QUAT_X,AC_QUAT_Y,AC_QUAT_Z}},
{incl_non_del_keys, "VisualLocRot", ID_PO, INSERTKEY_MATRIX, 7,
{AC_LOC_X,AC_LOC_Y,AC_LOC_Z,AC_QUAT_W,
AC_QUAT_X,AC_QUAT_Y,AC_QUAT_Z}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Available", ID_PO, -2, 0, {0}}
};
/* Material KeyingSets ------ */
/* array for material keyingset defines */
bKeyingSet defks_buts_shading_mat[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{NULL, "RGB", ID_MA, 0, 3, {MA_COL_R,MA_COL_G,MA_COL_B}},
{NULL, "Alpha", ID_MA, 0, 1, {MA_ALPHA}},
{NULL, "Halo Size", ID_MA, 0, 1, {MA_HASIZE}},
{NULL, "Mode", ID_MA, 0, 1, {MA_MODE}}, // evil bitflags
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "All Color", ID_MA, 0, 18,
{MA_COL_R,MA_COL_G,MA_COL_B,
MA_ALPHA,MA_HASIZE, MA_MODE,
MA_SPEC_R,MA_SPEC_G,MA_SPEC_B,
MA_REF,MA_EMIT,MA_AMB,MA_SPEC,MA_HARD,
MA_MODE,MA_TRANSLU,MA_ADD}},
{NULL, "All Mirror", ID_MA, 0, 5,
{MA_RAYM,MA_FRESMIR,MA_FRESMIRI,
MA_FRESTRA,MA_FRESTRAI}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Ofs", ID_MA, 0, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}},
{NULL, "Size", ID_MA, 0, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}},
{NULL, "All Mapping", ID_MA, 0, 14,
{MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z,
MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z,
MAP_R,MAP_G,MAP_B,MAP_DVAR,
MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Available", ID_MA, -2, 0, {0}}
};
/* World KeyingSets ------ */
/* array for world keyingset defines */
bKeyingSet defks_buts_shading_wo[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{NULL, "Zenith RGB", ID_WO, 0, 3, {WO_ZEN_R,WO_ZEN_G,WO_ZEN_B}},
{NULL, "Horizon RGB", ID_WO, 0, 3, {WO_HOR_R,WO_HOR_G,WO_HOR_B}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Mist", ID_WO, 0, 4, {WO_MISI,WO_MISTDI,WO_MISTSTA,WO_MISTHI}},
{NULL, "Stars", ID_WO, 0, 5, {WO_STAR_R,WO_STAR_G,WO_STAR_B,WO_STARDIST,WO_STARSIZE}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Ofs", ID_WO, 0, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}},
{NULL, "Size", ID_WO, 0, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}},
{NULL, "All Mapping", ID_WO, 0, 14,
{MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z,
MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z,
MAP_R,MAP_G,MAP_B,MAP_DVAR,
MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Available", ID_WO, -2, 0, {0}}
};
/* Lamp KeyingSets ------ */
/* array for lamp keyingset defines */
bKeyingSet defks_buts_shading_la[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{NULL, "RGB", ID_LA, 0, 3, {LA_COL_R,LA_COL_G,LA_COL_B}},
{NULL, "Energy", ID_LA, 0, 1, {LA_ENERGY}},
{NULL, "Spot Size", ID_LA, 0, 1, {LA_SPOTSI}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Ofs", ID_LA, 0, 3, {MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z}},
{NULL, "Size", ID_LA, 0, 3, {MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z}},
{NULL, "All Mapping", ID_LA, 0, 14,
{MAP_OFS_X,MAP_OFS_Y,MAP_OFS_Z,
MAP_SIZE_X,MAP_SIZE_Y,MAP_SIZE_Z,
MAP_R,MAP_G,MAP_B,MAP_DVAR,
MAP_COLF,MAP_NORF,MAP_VARF,MAP_DISP}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Available", ID_LA, -2, 0, {0}}
};
/* Texture KeyingSets ------ */
/* array for texture keyingset defines */
bKeyingSet defks_buts_shading_tex[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{NULL, "Clouds", ID_TE, 0, 5,
{TE_NSIZE,TE_NDEPTH,TE_NTYPE,
TE_MG_TYP,TE_N_BAS1}},
{NULL, "Marble", ID_TE, 0, 7,
{TE_NSIZE,TE_NDEPTH,TE_NTYPE,
TE_TURB,TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}},
{NULL, "Stucci", ID_TE, 0, 5,
{TE_NSIZE,TE_NTYPE,TE_TURB,
TE_MG_TYP,TE_N_BAS1}},
{NULL, "Wood", ID_TE, 0, 6,
{TE_NSIZE,TE_NTYPE,TE_TURB,
TE_MG_TYP,TE_N_BAS1,TE_N_BAS2}},
{NULL, "Magic", ID_TE, 0, 2, {TE_NDEPTH,TE_TURB}},
{NULL, "Blend", ID_TE, 0, 1, {TE_MG_TYP}},
{NULL, "Musgrave", ID_TE, 0, 6,
{TE_MG_TYP,TE_MGH,TE_MG_LAC,
TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN}},
{NULL, "Voronoi", ID_TE, 0, 9,
{TE_VNW1,TE_VNW2,TE_VNW3,TE_VNW4,
TE_VNMEXP,TE_VN_DISTM,TE_VN_COLT,
TE_ISCA,TE_NSIZE}},
{NULL, "Distorted Noise", ID_TE, 0, 4,
{TE_MG_OCT,TE_MG_OFF,TE_MG_GAIN,TE_DISTA}},
{NULL, "Color Filter", ID_TE, 0, 5,
{TE_COL_R,TE_COL_G,TE_COL_B,TE_BRIGHT,TE_CONTRA}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Available", ID_TE, -2, 0, {0}}
};
/* Object Buttons KeyingSets ------ */
/* check if include particles entry */
static short incl_buts_ob (bKeyingSet *ks, const char mode[])
{
Object *ob= OBACT;
/* only if object is mesh type */
return (ob->type == OB_MESH);
}
/* array for texture keyingset defines */
bKeyingSet defks_buts_object[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{incl_buts_ob, "Surface Damping", ID_OB, 0, 1, {OB_PD_SDAMP}},
{incl_buts_ob, "Random Damping", ID_OB, 0, 1, {OB_PD_RDAMP}},
{incl_buts_ob, "Permeability", ID_OB, 0, 1, {OB_PD_PERM}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Force Strength", ID_OB, 0, 1, {OB_PD_FSTR}},
{NULL, "Force Falloff", ID_OB, 0, 1, {OB_PD_FFALL}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Available", ID_OB, -2, 0, {0}} // this will include ob-transforms too!
};
/* Camera Buttons KeyingSets ------ */
/* check if include internal-renderer entry */
static short incl_buts_cam1 (bKeyingSet *ks, const char mode[])
{
/* only if renderer is internal renderer */
return (G.scene->r.renderer==R_INTERN);
}
/* check if include external-renderer entry */
static short incl_buts_cam2 (bKeyingSet *ks, const char mode[])
{
/* only if renderer is internal renderer */
return (G.scene->r.renderer!=R_INTERN);
}
/* array for camera keyingset defines */
bKeyingSet defks_buts_cam[] =
{
/* include_cb, name, blocktype, flag, chan_num, adrcodes */
{NULL, "Lens", ID_CA, 0, 1, {CAM_LENS}},
{NULL, "Clipping", ID_CA, 0, 2, {CAM_STA,CAM_END}},
{NULL, "Focal Distance", ID_CA, 0, 1, {CAM_YF_FDIST}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{incl_buts_cam2, "Aperture", ID_CA, 0, 1, {CAM_YF_APERT}},
{incl_buts_cam1, "Viewplane Shift", ID_CA, 0, 2, {CAM_SHIFT_X,CAM_SHIFT_Y}},
{NULL, "%l", 0, -1, 0, {0}}, // separator
{NULL, "Available", ID_CA, -2, 0, {0}}
};
/* --- */
/* Keying Context Defines - Must keep in sync with enumeration (eKS_Contexts) */
bKeyingContext ks_contexts[] =
{
KSC_TEMPLATE(v3d_object),
KSC_TEMPLATE(v3d_pchan),
KSC_TEMPLATE(buts_shading_mat),
KSC_TEMPLATE(buts_shading_wo),
KSC_TEMPLATE(buts_shading_la),
KSC_TEMPLATE(buts_shading_tex),
KSC_TEMPLATE(buts_object),
KSC_TEMPLATE(buts_cam)
};
/* Keying Context Enumeration - Must keep in sync with definitions*/
typedef enum eKS_Contexts {
KSC_V3D_OBJECT = 0,
KSC_V3D_PCHAN,
KSC_BUTS_MAT,
KSC_BUTS_WO,
KSC_BUTS_LA,
KSC_BUTS_TEX,
KSC_BUTS_OB,
KSC_BUTS_CAM,
/* make sure this last one remains untouched! */
KSC_TOT_TYPES
} eKS_Contexts;
/* ---------------- KeyingSet Tools ------------------- */
/* helper for commonkey_context_get() - get keyingsets for 3d-view */
static void commonkey_context_getv3d (ListBase *sources, bKeyingContext **ksc)
{
Object *ob;
IpoCurve *icu;
if ((OBACT) && (OBACT->flag & OB_POSEMODE)) {
bPoseChannel *pchan;
/* pose-level */
ob= OBACT;
*ksc= &ks_contexts[KSC_V3D_PCHAN];
set_pose_keys(ob); /* sets pchan->flag to POSE_KEY if bone selected, and clears if not */
/* loop through posechannels */
for (pchan=ob->pose->chanbase.first; pchan; pchan=pchan->next) {
if (pchan->flag & POSE_KEY) {
bCommonKeySrc *cks;
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set id-block to key to, and action */
cks->id= (ID *)ob;
cks->act= ob->action;
/* set pchan */
cks->pchan= pchan;
cks->actname= pchan->name;
}
}
}
else {
Base *base;
/* object-level */
*ksc= &ks_contexts[KSC_V3D_OBJECT];
/* loop through bases */
for (base= FIRSTBASE; base; base= base->next) {
if (TESTBASELIB(base)) {
bCommonKeySrc *cks;
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set id-block to key to */
ob= base->object;
cks->id= (ID *)ob;
/* when ob's keyframes are in an action, default to using 'Object' as achan name */
if (ob->ipoflag & OB_ACTION_OB)
cks->actname= "Object";
/* set ipo-flags */
// TODO: add checks for lib-linked data
if ((ob->ipo) || (ob->action)) {
if (ob->ipo) {
cks->ipo= ob->ipo;
}
else {
bActionChannel *achan;
cks->act= ob->action;
achan= get_action_channel(ob->action, cks->actname);
if (achan && achan->ipo)
cks->ipo= achan->ipo;
}
/* deselect all ipo-curves */
for (icu= cks->ipo->curve.first; icu; icu= icu->next) {
icu->flag &= ~IPO_SELECT;
}
}
}
}
}
}
/* helper for commonkey_context_get() - get keyingsets for buttons window */
static void commonkey_context_getsbuts (ListBase *sources, bKeyingContext **ksc)
{
bCommonKeySrc *cks;
/* check on tab-type */
switch (G.buts->mainb) {
case CONTEXT_SHADING: /* ------------- Shading buttons ---------------- */
/* subtabs include "Material", "Texture", "Lamp", "World"*/
switch (G.buts->tab[CONTEXT_SHADING]) {
case TAB_SHADING_MAT: /* >------------- Material Tab -------------< */
{
Material *ma= editnode_get_active_material(G.buts->lockpoin);
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set data */
cks->id= (ID *)ma;
cks->ipo= ma->ipo;
cks->map= texchannel_to_adrcode(ma->texact);
/* set keyingsets */
*ksc= &ks_contexts[KSC_BUTS_MAT];
return;
}
break;
case TAB_SHADING_WORLD: /* >------------- World Tab -------------< */
{
World *wo= G.buts->lockpoin;
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set data */
cks->id= (ID *)wo;
cks->ipo= wo->ipo;
cks->map= texchannel_to_adrcode(wo->texact);
/* set keyingsets */
*ksc= &ks_contexts[KSC_BUTS_WO];
return;
}
break;
case TAB_SHADING_LAMP: /* >------------- Lamp Tab -------------< */
{
Lamp *la= G.buts->lockpoin;
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set data */
cks->id= (ID *)la;
cks->ipo= la->ipo;
cks->map= texchannel_to_adrcode(la->texact);
/* set keyingsets */
*ksc= &ks_contexts[KSC_BUTS_LA];
return;
}
break;
case TAB_SHADING_TEX: /* >------------- Texture Tab -------------< */
{
Tex *tex= G.buts->lockpoin;
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set data */
cks->id= (ID *)tex;
cks->ipo= tex->ipo;
/* set keyingsets */
*ksc= &ks_contexts[KSC_BUTS_TEX];
return;
}
break;
}
break;
case CONTEXT_OBJECT: /* ------------- Object buttons ---------------- */
{
Object *ob= OBACT;
if (ob) {
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set id-block to key to */
cks->id= (ID *)ob;
cks->ipo= ob->ipo;
/* set keyingsets */
*ksc= &ks_contexts[KSC_BUTS_OB];
return;
}
}
break;
case CONTEXT_EDITING: /* ------------- Editing buttons ---------------- */
{
Object *ob= OBACT;
if ((ob) && (ob->type==OB_CAMERA) && (G.buts->lockpoin)) { /* >---------------- camera buttons ---------------< */
Camera *ca= G.buts->lockpoin;
/* add new keyframing destination */
cks= MEM_callocN(sizeof(bCommonKeySrc), "bCommonKeySrc");
BLI_addtail(sources, cks);
/* set id-block to key to */
cks->id= (ID *)ca;
cks->ipo= ca->ipo;
/* set keyingsets */
*ksc= &ks_contexts[KSC_BUTS_CAM];
return;
}
}
break;
}
/* if nothing happened... */
*ksc= NULL;
}
/* get keyingsets for appropriate context */
static void commonkey_context_get (ScrArea *sa, ListBase *sources, bKeyingContext **ksc)
{
/* check view type */
switch (sa->spacetype) {
/* 3d view - first one tested as most often used */
case SPACE_VIEW3D:
{
commonkey_context_getv3d(sources, ksc);
}
break;
/* buttons view */
case SPACE_BUTS:
{
commonkey_context_getsbuts(sources, ksc);
}
break;
/* timeline view - keyframe buttons */
case SPACE_TIME:
{
ScrArea *sab;
/* try to find largest 3d-view available
* (mostly of the time, this is what when user will want this,
* as it's a standard feature in all other apps)
*/
sab= find_biggest_area_of_type(SPACE_VIEW3D);
if (sab) {
commonkey_context_getv3d(sources, ksc);
return;
}
/* otherwise, try to find the biggest area
* WARNING: must check if that area is another timeline, as that would cause infinite loop
*/
sab= closest_bigger_area();
if ((sab) && (sab->spacetype != SPACE_TIME))
commonkey_context_get(sab, sources, ksc);
}
break;
}
}
/* flush updates after all operations */
static void commonkey_context_finish (ListBase *sources)
{
/* check view type */
switch (curarea->spacetype) {
/* 3d view - first one tested as most often used */
case SPACE_VIEW3D:
{
/* either pose or object level */
if (OBACT && (OBACT->pose)) {
Object *ob= OBACT;
/* recalculate ipo handles, etc. */
if (ob->action)
remake_action_ipos(ob->action);
/* recalculate bone-paths on adding new keyframe? */
// TODO: currently, there is no setting to turn this on/off globally
if (ob->pose->flag & POSE_RECALCPATHS)
pose_recalculate_paths(ob);
}
else {
bCommonKeySrc *cks;
/* loop over bases (as seen in sources) */
for (cks= sources->first; cks; cks= cks->next) {
Object *ob= (Object *)cks->id;
/* simply set recalc flag */
ob->recalc |= OB_RECALC_OB;
}
}
}
break;
}
}
/* flush refreshes after undo */
static void commonkey_context_refresh (void)
{
/* check view type */
switch (curarea->spacetype) {
/* 3d view - first one tested as most often used */
case SPACE_VIEW3D:
{
/* do refreshes */
DAG_scene_flush_update(G.scene, screen_view3d_layers(), 0);
allspace(REMAKEIPO, 0);
allqueue(REDRAWVIEW3D, 0);
allqueue(REDRAWMARKER, 0);
}
break;
}
}
/* --- */
/* Build menu-string of available keying-sets (allocates memory for string)
* NOTE: mode must not be longer than 64 chars
*/
static char *build_keyingsets_menu (bKeyingContext *ksc, const char mode[48])
{
DynStr *pupds= BLI_dynstr_new();
bKeyingSet *ks;
char buf[64];
char *str;
int i, n;
/* add title first */
BLI_snprintf(buf, 64, "%s Key %%t|", mode);
BLI_dynstr_append(pupds, buf);
/* loop through keyingsets, adding them */
for (ks=ksc->keyingsets, i=0, n=1; i < ksc->tot; ks++, i++, n++) {
/* check if keyingset can be used */
if (ks->flag == -1) {
/* optional separator? */
if (ks->include_cb) {
if (ks->include_cb(ks, mode)) {
BLI_snprintf( buf, 64, "%s%s", ks->name, ((n < ksc->tot)?"|":"") );
BLI_dynstr_append(pupds, buf);
}
}
else {
BLI_snprintf( buf, 64, "%%l%s", ((n < ksc->tot)?"|":"") );
BLI_dynstr_append(pupds, buf);
}
}
else if ( (ks->include_cb==NULL) || (ks->include_cb(ks, mode)) ) {
/* entry can be included */
BLI_dynstr_append(pupds, ks->name);
/* check if special "shapekey" entry */
if (ks->flag == -3)
BLI_snprintf( buf, 64, "%%x0%s", ((n < ksc->tot)?"|":"") );
else
BLI_snprintf( buf, 64, "%%x%d%s", n, ((n < ksc->tot)?"|":"") );
BLI_dynstr_append(pupds, buf);
}
}
/* convert to normal MEM_malloc'd string */
str= BLI_dynstr_get_cstring(pupds);
BLI_dynstr_free(pupds);
return str;
}
/* Get the keying set that was chosen by the user from the menu */
static bKeyingSet *get_keyingset_fromcontext (bKeyingContext *ksc, short index)
{
/* check if index is valid */
if (ELEM(NULL, ksc, ksc->keyingsets))
return NULL;
if ((index < 1) || (index > ksc->tot))
return NULL;
/* index starts from 1, and should directly correspond to keyingset in array */
return (bKeyingSet *)(ksc->keyingsets + (index - 1));
}
/* ---------------- Keyframe Management API -------------------- */
/* mode for common_modifykey */
enum {
COMMONKEY_MODE_INSERT = 0,
COMMONKEY_MODE_DELETE,
} eCommonModifyKey_Modes;
/* Display a menu for handling the insertion of keyframes based on the active view */
// TODO: add back an option for repeating last keytype
void common_modifykey (short mode)
{
ListBase dsources = {NULL, NULL};
bKeyingContext *ksc= NULL;
bCommonKeySrc *cks;
bKeyingSet *ks = NULL;
char *menustr, buf[64];
short menu_nr;
/* check if mode is valid */
if (ELEM(mode, COMMONKEY_MODE_INSERT, COMMONKEY_MODE_DELETE)==0)
return;
/* delegate to other functions or get keyingsets to use */
switch (curarea->spacetype) {
/* spaces with their own methods */
case SPACE_IPO:
if (mode == COMMONKEY_MODE_INSERT)
insertkey_editipo();
return;
case SPACE_ACTION:
if (mode == COMMONKEY_MODE_INSERT)
insertkey_action();
return;
/* TODO: based on UI elements? will that even be handled here??? */
/* default - check per view */
default:
/* get the keyingsets and the data to add keyframes to */
commonkey_context_get(curarea, &dsources, &ksc);
break;
}
/* check that there is data to operate on */
if (ELEM(NULL, dsources.first, ksc)) {
BLI_freelistN(&dsources);
return;
}
/* get menu and process it */
if (mode == COMMONKEY_MODE_DELETE)
menustr= build_keyingsets_menu(ksc, "Delete");
else
menustr= build_keyingsets_menu(ksc, "Insert");
menu_nr= pupmenu(menustr);
if (menustr) MEM_freeN(menustr);
/* no item selected or shapekey entry? */
if (menu_nr < 1) {
/* free temp sources */
BLI_freelistN(&dsources);
/* check if insert new shapekey */
if ((menu_nr == 0) && (mode == COMMONKEY_MODE_INSERT))
insert_shapekey(OBACT);
else
ksc->lastused= NULL;
return;
}
else {
/* try to get keyingset */
ks= get_keyingset_fromcontext(ksc, menu_nr);
if (ks == NULL) {
BLI_freelistN(&dsources);
return;
}
}
/* loop over each destination, applying the keying set */
for (cks= dsources.first; cks; cks= cks->next) {
short success= 0;
/* special hacks for 'available' option */
if (ks->flag == -2) {
IpoCurve *icu= NULL, *icn= NULL;
/* get first IPO-curve */
if (cks->act && cks->actname) {
bActionChannel *achan= get_action_channel(cks->act, cks->actname);
// FIXME: what about constraint channels?
if (achan && achan->ipo)
icu= achan->ipo->curve.first;
}
else
icu= cks->ipo->curve.first;
/* we get adrcodes directly from IPO curves (see method below...) */
for (; icu; icu= icn) {
short flag;
/* get next ipo-curve in case current is deleted */
icn= icu->next;
/* insert mode or delete mode */
if (mode == COMMONKEY_MODE_DELETE) {
/* local flags only add on to global flags */
flag = 0;
/* delete keyframe */
success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag);
}
else {
/* local flags only add on to global flags */
flag = ks->flag;
if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX;
if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED;
// if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE;
/* insert keyframe */
success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, icu->adrcode, flag);
}
}
}
else {
int i;
/* loop over channels available in keyingset */
for (i=0; i < ks->chan_num; i++) {
short flag, adrcode;
/* get adrcode
* - certain adrcodes (for MTEX channels need special offsets) // BAD CRUFT!!!
*/
adrcode= ks->adrcodes[i];
if (ELEM3(ks->blocktype, ID_MA, ID_LA, ID_WO)) {
switch (adrcode) {
case MAP_OFS_X: case MAP_OFS_Y: case MAP_OFS_Z:
case MAP_SIZE_X: case MAP_SIZE_Y: case MAP_SIZE_Z:
case MAP_R: case MAP_G: case MAP_B: case MAP_DVAR:
case MAP_COLF: case MAP_NORF: case MAP_VARF: case MAP_DISP:
adrcode += cks->map;
break;
}
}
/* insert mode or delete mode */
if (mode == COMMONKEY_MODE_DELETE) {
/* local flags only add on to global flags */
flag = 0;
/* delete keyframe */
success += deletekey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag);
}
else {
/* local flags only add on to global flags */
flag = ks->flag;
if (IS_AUTOKEY_FLAG(AUTOMATKEY)) flag |= INSERTKEY_MATRIX;
if (IS_AUTOKEY_FLAG(INSERTNEEDED)) flag |= INSERTKEY_NEEDED;
// if (IS_AUTOKEY_MODE(EDITKEYS)) flag |= INSERTKEY_REPLACE;
/* insert keyframe */
success += insertkey(cks->id, ks->blocktype, cks->actname, cks->constname, adrcode, flag);
}
}
}
/* special handling for some key-sources */
if (success) {
/* set pose recalc-paths flag */
if (cks->pchan) {
Object *ob= (Object *)cks->id;
bPoseChannel *pchan= cks->pchan;
/* set flag to trigger path recalc */
if (pchan->path)
ob->pose->flag |= POSE_RECALCPATHS;
/* clear unkeyed flag (it doesn't matter if it's set or not) */
if (pchan->bone)
pchan->bone->flag &= ~BONE_UNKEYED;
}
}
}
/* apply post-keying flushes for this data sources */
commonkey_context_finish(&dsources);
ksc->lastused= ks;
/* free temp data */
BLI_freelistN(&dsources);
/* undo pushes */
if (mode == COMMONKEY_MODE_DELETE)
BLI_snprintf(buf, 64, "Delete %s Key", ks->name);
else
BLI_snprintf(buf, 64, "Insert %s Key", ks->name);
BIF_undo_push(buf);
/* queue updates for contexts */
commonkey_context_refresh();
}
/* ---- */
/* used to insert keyframes from any view */
void common_insertkey (void)
{
common_modifykey(COMMONKEY_MODE_INSERT);
}
/* used to insert keyframes from any view */
void common_deletekey (void)
{
common_modifykey(COMMONKEY_MODE_DELETE);
}
/* ************************************************** */