Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time
// This file is distributed under a BSD license. See LICENSE.txt for details.
#include "genminmesh.hpp"
#include "genmaterial.hpp"
#include "genbitmap.hpp"
#include "genoverlay.hpp"
#include "genblobspline.hpp"
#include "engine.hpp"
#if sLINK_LOADER
#include "_loader.hpp"
#include "_wavefront.hpp"
#endif
/****************************************************************************/
/****************************************************************************/
void GenMinVector::UnitSafe()
{
sF64 e;
e = x*x+y*y+z*z;
if(e<1e-20f)
{
Init(1,0,0);
}
else
{
e = sFInvSqrt(e);
x *= e;
y *= e;
z *= e;
}
}
void GenMinVector::Cross(const GenMinVector &a,const GenMinVector &b)
{
x=a.y*b.z-a.z*b.y;
y=a.z*b.x-a.x*b.z;
z=a.x*b.y-a.y*b.x;
}
sInt GenMinVector::Classify()
{
sVector v;
v.Init(x,y,z);
return v.Classify();
}
/****************************************************************************/
sBool GenMinVert::IsSame(const GenMinVert &v,sBool uv)
{
const sF32 e=0.00001f;
if(sFAbs(Pos.x-v.Pos.x)>e) return 0;
if(sFAbs(Pos.y-v.Pos.y)>e) return 0;
if(sFAbs(Pos.z-v.Pos.z)>e) return 0;
if(BoneCount!=v.BoneCount) return 0;
if(BoneCount)
{
if(sCmpMem(Weights,v.Weights,BoneCount*4)!=0) return 0;
if(sCmpMem(Matrix ,v.Matrix ,BoneCount*2)!=0) return 0;
}
if(uv)
{
if(sCmpMem(UV,v.UV,sizeof(UV))!=0) return 0;
}
return 1;
}
/****************************************************************************/
void GenMinMatrix::Init()
{
BasePose.Init();
Parent = -1;
KeyCount = 0;
SPtr = 0;
RPtr = 0;
TPtr = 0;
Offset = 0;
Factor = 1;
Spread = 0;
Spline = 0;
NoAnimation.Init();
Used = 0;
// Index = -1;
}
void GenMinMatrix::Exit()
{
sDeleteArray(SPtr);
sDeleteArray(RPtr);
sDeleteArray(TPtr);
sRelease(Spline);
}
/****************************************************************************/
GenMinMeshAnim::GenMinMeshAnim(sInt matrixCount)
{
Matrices.Init(matrixCount);
Matrices.Resize(matrixCount);
for(sInt i=0;i<matrixCount;i++)
Matrices[i].Init();
}
GenMinMeshAnim::~GenMinMeshAnim()
{
for(sInt i=0;i<Matrices.Count;i++)
Matrices[i].Exit();
Matrices.Exit();
}
sInt GenMinMeshAnim::GetMatrixCount()
{
return Matrices.Count;
}
GenMinMeshAnim *GenMinMeshAnim::Copy()
{
GenMinMeshAnim *dest;
GenMinMatrix *d,*s;
sInt count = Matrices.Count;
dest = new GenMinMeshAnim(count);
for(sInt i=0;i<count;i++)
{
s = &Matrices[i];
d = &dest->Matrices[i];
d->Init();
d->BasePose = s->BasePose;
d->Parent = s->Parent;
d->Factor = s->Factor;
d->Spread = s->Spread;
d->Offset = s->Offset;
d->KeyCount = s->KeyCount;
for(sInt j=0;j<9;j++)
d->SRT[j] = s->SRT[j];
if(s->SPtr)
{
d->SPtr = new sF32[s->KeyCount*3];
sCopyMem(d->SPtr,s->SPtr,s->KeyCount*3*4);
}
if(s->RPtr)
{
d->RPtr = new sF32[s->KeyCount*3];
sCopyMem(d->RPtr,s->RPtr,s->KeyCount*3*4);
}
if(s->TPtr)
{
d->TPtr = new sF32[s->KeyCount*3];
sCopyMem(d->TPtr,s->TPtr,s->KeyCount*3*4);
}
if(s->Spline)
{
d->Spline = s->Spline;
d->Spline->AddRef();
}
d->NoAnimation = s->NoAnimation;
}
return dest;
}
void GenMinMeshAnim::EvalAnimation(sF32 time,sF32 metamorph,sMatrix *matrices)
{
GenMinMatrix *mp;
sMatrix mat;
sF32 srt[9];
if(time<0)
time = 0;
for(sInt i=0;i<Matrices.Count;i++)
{
mp = &Matrices.Array[i];
// get animated matrix
if(mp->KeyCount)
{
sInt k0,k1;
sF32 f;
sF32 v0,v1;
k0 = (sInt)(mp->KeyCount*time*1024);
f = (k0 & 1023)/1024.0f;
k0 = (k0/1024) % mp->KeyCount;
k1 = (k0+1) % mp->KeyCount;
for(sInt i=0;i<9;i++)
srt[i] = mp->SRT[i];
for(sInt i=0;i<3;i++)
{
if((&mp->SPtr)[i])
{
for(sInt j=0;j<3;j++)
{
v0 = (&mp->SPtr)[i][k0*3+j];
v1 = (&mp->SPtr)[i][k1*3+j];
srt[i*3+j] = v0 + (v1-v0)*f;
}
}
}
//srt[0] = srt[1] = srt[2] = 1.0f; // HACK HACK HACK
mat.InitSRT(srt);
}
else if(mp->Spline)
{
sF32 dummy;
sF32 scalef = (Matrices.Count > 1) ? 1.0f / (Matrices.Count - 1) : 0.0f;
mp->Spline->Eval(time*mp->Factor+mp->Offset+i*mp->Spread*scalef,time,mat,dummy);
}
else
{
mat = mp->NoAnimation;
}
// concatenate matrices
if(mp->Parent>=0)
mp->Temp.MulA(mat,Matrices[mp->Parent].Temp);
else
mp->Temp = mat;
matrices[i].MulA(mp->BasePose,mp->Temp);
}
}
/****************************************************************************/
/****************************************************************************/
sU32 GenMinMesh::HashVector(const GenMinVector &v)
{
const sU8 *src = (const sU8 *) &v.x;
sU32 hash = 2166136261;
for(sInt i=0;i<sizeof(GenMinVector);i++)
hash = (hash ^ *src++) * 16777619;
return hash;
}
/****************************************************************************/
GenMinMesh::GenMinMesh()
{
ClassId = KC_MINMESH;
Vertices.Init();
Faces.Init();
Clusters.Init();
Clusters.Add()->Init(0); // deleted cluster
AddCluster(0,0); // default cluster
Animation = 0;
Stripped = sFALSE;
CompletelyRigid = sFALSE;
NormalsOK = 0;
ChangeFlag = 3;
PreparedMesh = 0;
#if !sPLAYER
WireMesh = 0;
#endif
}
GenMinMesh::~GenMinMesh()
{
for(sInt i=0;i<Clusters.Count;i++)
if(Clusters[i].Mtrl)
Clusters[i].Mtrl->Release();
sRelease(Animation);
Vertices.Exit();
Faces.Exit();
Clusters.Exit();
UnPrepare();
#if !sPLAYER
UnPrepareWire();
#endif
}
void GenMinMesh::Copy(KObject *o)
{
GenMinMesh *mm;
sVERIFY(o->ClassId==KC_MINMESH);
mm = (GenMinMesh *) o;
for(sInt i=0;i<Clusters.Count;i++)
if(Clusters[i].Mtrl)
Clusters[i].Mtrl->Release();
Clusters.Count = 0;
if(Animation)
Animation->Release();
Vertices.Copy(mm->Vertices);
Faces.Copy(mm->Faces);
Clusters.Copy(mm->Clusters);
if(mm->Animation)
Animation = mm->Animation->Copy();
for(sInt i=0;i<Clusters.Count;i++)
if(Clusters[i].Mtrl)
Clusters[i].Mtrl->AddRef();
CompletelyRigid = mm->CompletelyRigid;
ChangeTopo();
}
void GenMinMesh::Clear()
{
Vertices.Count = 0;
Faces.Count = 0;
ChangeTopo();
}
/****************************************************************************/
void GenMinMesh::ChangeGeo()
{
NormalsOK = 0;
}
void GenMinMesh::ChangeTopo()
{
NormalsOK = 0;
}
/****************************************************************************/
sInt GenMinMesh::AddCluster(GenMaterial *mtrl,sInt pass,sInt id,sInt anim,sInt mtx)
{
if(mtrl==0)
mtrl = GenOverlayManager->DefaultMat;
for(sInt i=1;i<Clusters.Count;i++)
if(Clusters[i].Mtrl == mtrl && Clusters[i].Id == id
&& Clusters[i].AnimType == anim && Clusters[i].AnimMatrix == mtx)
return i;
sInt result = Clusters.Count;
Clusters.Add()->Init(mtrl,pass,id,anim,mtx);
mtrl->AddRef();
return result;
}
void GenMinMesh::CreateAnimation(sInt matrixCount)
{
sRelease(Animation);
Animation = new GenMinMeshAnim(matrixCount);
}
sInt GenMinMesh::OppositeEdge(sInt edgeTag) const
{
return Faces[edgeTag >> 3].Adjacent[edgeTag & 7];
}
sInt GenMinMesh::NextFaceEdge(sInt edgeTag) const
{
const GenMinFace *face = &Faces[edgeTag >> 3];
if((edgeTag & 7) == face->Count - 1)
return edgeTag & ~7;
else
return edgeTag + 1;
}
sInt GenMinMesh::PrevFaceEdge(sInt edgeTag) const
{
const GenMinFace *face = &Faces[edgeTag >> 3];
if((edgeTag & 7) == 0)
return edgeTag + face->Count - 1;
else
return edgeTag - 1;
}
sInt GenMinMesh::NextVertEdge(sInt edgeTag) const
{
return OppositeEdge(PrevFaceEdge(edgeTag));
}
sInt GenMinMesh::PrevVertEdge(sInt edgeTag) const
{
return NextFaceEdge(OppositeEdge(edgeTag));
}
sInt GenMinMesh::GetVertexId(sInt edgeTag) const
{
return Faces[edgeTag >> 3].Vertices[edgeTag & 7];
}
void GenMinMesh::EdgeFlip(sInt e0)
{
sInt e1 = OppositeEdge(e0);
sVERIFY(e1 != -1); // may only flip internal edges
sInt f0n = e0 >> 3;
sInt f1n = e1 >> 3;
e0 &= 7;
e1 &= 7;
sVERIFY(e0 < 3 && e1 < 3);
GenMinFace *f0 = &Faces[f0n];
GenMinFace *f1 = &Faces[f1n];
sVERIFY(f0->Count == 3 && f1->Count == 3); // may only flip triangles
static const sInt wrap[5] = { 0,1,2,0,1 }; // wrap[n]=n%3
// get vertices
sInt va = f0->Vertices[e0];
sInt vb = f1->Vertices[wrap[e1+2]];
sInt vc = f1->Vertices[e1];
sInt vd = f0->Vertices[wrap[e0+2]];
// outer edges
sInt oab = f1->Adjacent[wrap[e1+1]];
sInt obc = f1->Adjacent[wrap[e1+2]];
sInt ocd = f0->Adjacent[wrap[e0+1]];
sInt oda = f0->Adjacent[wrap[e0+2]];
// gen output triangles
f0->Vertices[0] = vb;
f0->Vertices[1] = vd;
f0->Vertices[2] = va;
f1->Vertices[0] = vd;
f1->Vertices[1] = vb;
f1->Vertices[2] = vc;
// gen output adjacency
f0->Adjacent[0] = f1n<<3;
f0->Adjacent[1] = oda;
f0->Adjacent[2] = oab;
f1->Adjacent[0] = f0n<<3;
f1->Adjacent[1] = obc;
f1->Adjacent[2] = ocd;
// update outer adjacency
if(oab!=-1) Faces[oab>>3].Adjacent[oab&7] = (f0n<<3)|2;
if(obc!=-1) Faces[obc>>3].Adjacent[obc&7] = (f1n<<3)|1;
if(ocd!=-1) Faces[ocd>>3].Adjacent[ocd&7] = (f1n<<2)|2;
if(oda!=-1) Faces[oda>>3].Adjacent[oda&7] = (f0n<<3)|1;
}
/****************************************************************************/
sU8 *GenMinMesh::ExportToBlob(sInt &size) const
{
// determine how much space we need. layout:
//
// # vertices (4b)
// per vertex:
// bonecount (1b)
// color (4b)
// pos (12b)
// uv0 (8b) we don't save uv1!
// per bone: (bonecount times)
// weight (2b as f16)
// matrix (2b)
//
// # faces (4b)
// per face:
// count (1b)
// cluster (1b)
// per vertex: (count times)
// index (2b/4b)
//
// # clusters (4b)
// per cluster:
// pass/animtype (4b)
// id (4b)
// matrix (4b)
sInt nTotalBones = 0,nVertexRefs = 0;
sBool vert16Bit = Vertices.Count < 65536;
for(sInt i=0;i<Vertices.Count;i++)
nTotalBones += Vertices[i].BoneCount;
for(sInt i=0;i<Faces.Count;i++)
nVertexRefs += Faces[i].Count;
size = 4 + 25*Vertices.Count + 4*nTotalBones
+ 4 + 2*Faces.Count + (vert16Bit ? 2 : 4)*nVertexRefs
+ 4 + 12*Clusters.Count;
// allocate
sU8 *dataStart = new sU8[size];
sU8 *data = dataStart;
// save vertices
*(sU32 *) data = Vertices.Count; data += 4;
for(sInt i=0;i<Vertices.Count;i++)
{
const GenMinVert *vert = &Vertices[i];
*data++ = vert->BoneCount;
*(sU32 *) data = vert->Color; data += 4;
*(sF32 *) data = vert->Pos.x; data += 4;
*(sF32 *) data = vert->Pos.y; data += 4;
*(sF32 *) data = vert->Pos.z; data += 4;
*(sF32 *) data = vert->UV[0][0]; data += 4;
*(sF32 *) data = vert->UV[0][1]; data += 4;
for(sInt j=0;j<vert->BoneCount;j++)
{
sWriteF16(data,vert->Weights[j]);
*(sU16 *) data = vert->Matrix[j]; data += 2;
}
}
// save faces
*(sU32 *) data = Faces.Count; data += 4;
for(sInt i=0;i<Faces.Count;i++)
{
const GenMinFace *face = &Faces[i];
*data++ = face->Count;
*data++ = face->Cluster;
if(vert16Bit)
{
for(sInt j=0;j<face->Count;j++)
{
*(sU16 *) data = face->Vertices[j]; data += 2;
}
}
else
{
for(sInt j=0;j<face->Count;j++)
{
*(sU32 *) data = face->Vertices[j]; data += 4;
}
}
}
// save clusters
*(sU32 *) data = Clusters.Count; data += 4;
for(sInt i=0;i<Clusters.Count;i++)
{
const GenMinCluster *cluster = &Clusters[i];
*(sU32 *) data = cluster->RenderPass | (cluster->AnimType << 24); data += 4;
*(sU32 *) data = cluster->Id; data += 4;
*(sU32 *) data = cluster->AnimMatrix; data += 4;
}
// return (we may write slightly less than anticipated due to optimizations
// in sWriteF16)
sVERIFY(data <= &dataStart[size]);
size = data - dataStart;
return dataStart;
}
void GenMinMesh::ImportFromBlob(const sU8 *data)
{
// read vertices
sInt nVerts = *(sU32 *) data; data += 4;
sBool vert16Bit = nVerts < 65536;
Vertices.Resize(nVerts);
sSetMem(&Vertices[0],0,nVerts * sizeof(GenMinVert));
for(sInt i=0;i<nVerts;i++)
{
GenMinVert *vert = &Vertices[i];
vert->BoneCount = *data++;
vert->Color = *(sU32 *) data; data += 4;
vert->Pos.x = *(sF32 *) data; data += 4;
vert->Pos.y = *(sF32 *) data; data += 4;
vert->Pos.z = *(sF32 *) data; data += 4;
vert->UV[0][0] = *(sF32 *) data; data += 4;
vert->UV[0][1] = *(sF32 *) data; data += 4;
for(sInt j=0;j<vert->BoneCount;j++)
{
vert->Weights[j] = sReadF16(data);
vert->Matrix[j] = *(sU16 *) data; data += 2;
}
}
// read faces
sInt nFaces = *(sU32 *) data; data += 4;
Faces.Resize(nFaces);
sSetMem(&Faces[0],0,nFaces * sizeof(GenMinFace));
for(sInt i=0;i<nFaces;i++)
{
GenMinFace *face = &Faces[i];
face->Count = *data++;
face->Cluster = *data++;
if(vert16Bit)
{
for(sInt j=0;j<face->Count;j++)
{
face->Vertices[j] = *(sU16 *) data; data += 2;
}
}
else
{
for(sInt j=0;j<face->Count;j++)
{
face->Vertices[j] = *(sU32 *) data; data += 4;
}
}
}
// free existing clusters
for(sInt i=0;i<Clusters.Count;i++)
sRelease(Clusters[i].Mtrl);
// read clusters
sInt nClusters = *(sU32 *) data; data += 4;
Clusters.Resize(nClusters);
sSetMem(&Clusters[0],0,nClusters * sizeof(GenMinCluster));
for(sInt i=0;i<nClusters;i++)
{
GenMinCluster *cluster = &Clusters[i];
if(i) // every cluster except the first one gets the default material
{
cluster->Mtrl = GenOverlayManager->DefaultMat;
cluster->Mtrl->AddRef();
}
sU32 passAnim = *(sU32 *) data; data += 4;
cluster->RenderPass = passAnim & 0xffffff;
cluster->AnimType = passAnim >> 24;
cluster->Id = *(sU32 *) data; data += 4;
cluster->AnimMatrix = *(sU32 *) data; data += 4;
}
// that should be everything!
}
/****************************************************************************/
// flags:
//
// 0x01 close x
// 0x02 close y
// 0x04 stitch x
// 0x08 stitch y
// 0x10 invert grid
// 0x20 use cluster 0, making the grid invisible/deleted
GenMinVert *GenMinMesh::MakeGrid(sInt tx,sInt ty,sInt flags)
{
sInt x,y;
sInt vi,fi,vc,fc,va,fa,bits;
GenMinVert *vp;
GenMinFace *fp;
sInt cluster;
// additions (top/bottom)
bits = 0;
if(flags & 1) { bits++; }
if(flags & 2) { bits++; }
fa = tx*bits;
va = bits;
if(flags & 4) { fa+=tx+1; }
if(flags & 8) { fa+=ty+1; }
cluster = (flags&32)?0:1;
// vertices
vc = (tx+1)*(ty+1);
vi = Vertices.Count;
Vertices.SetMax(vi+vc+va);
vp = &Vertices[vi];
Vertices.Count = vi+vc+va;
sSetMem(vp,0,sizeof(*vp)*(vc+va));
for(y=0;y<=ty;y++)
{
for(x=0;x<=tx;x++)
{
vp->Pos.x = (1.0f*x/tx)-0.5f;
vp->Pos.y = (1.0f*y/ty)-0.5f;
vp->UV[0][0] = 1.0f*x/tx;
vp->UV[0][1] = 1.0f-1.0f*(y)/(ty);
vp->UV[1][0] = 1.0f*((x==tx)?0:x)/tx;
vp->UV[1][1] = 1.0f-1.0f*((y==ty)?0:y)/(ty);
vp->Select = 1;
vp->Color = 0;//~0;
vp++;
}
}
// faces
fc = tx*ty;
fi = Faces.Count;
Faces.SetMax(fi+fc+fa);
fp = &Faces[fi];
Faces.Count = fi+fc+fa;
sSetMem(fp,0,sizeof(*fp)*(fc+fa));
for(y=0;y<ty;y++)
{
for(x=0;x<tx;x++)
{
fp->Select = 1;
fp->Count = 4;
fp->Cluster = cluster;
fp->Vertices[0] = vi+(y+0)*(tx+1)+(x+0);
fp->Vertices[1] = vi+(y+1)*(tx+1)+(x+0);
fp->Vertices[2] = vi+(y+1)*(tx+1)+(x+1);
fp->Vertices[3] = vi+(y+0)*(tx+1)+(x+1);
if(flags & 16)
{
sSwap(fp->Vertices[0],fp->Vertices[3]);
sSwap(fp->Vertices[1],fp->Vertices[2]);
}
fp++;
}
}
// additions (top / bottom)
sInt center = vi+vc;
sInt border = vi;
for(sInt i=0;i<2;i++) // note that i is 0 or 1
{
if(flags & (i+1))
{
vp->UV[0][0] = 1.0f;
vp->UV[0][1] = 1-i;
vp->Select = 0;
vp->Color = 0;//~0;
vp++;
for(x=0;x<tx;x++)
{
fp->Select = 1;
fp->Count = 3;
fp->Cluster = cluster;
fp->Vertices[0] = center;
fp->Vertices[1] = border+i;
border++;
fp->Vertices[2] = border-i;
fp++;
}
}
border = vi+(ty)*(tx+1);
if(flags&1) center++;
}
// additions: stitches
if(flags&4)
{
for(sInt i=0;i<=tx;i++)
{
fp->Count = 2;
fp->Vertices[0] = vi+i;
fp->Vertices[1] = vi+i+ty*(tx+1);
fp++;
}
}
if(flags&8)
{
for(sInt i=0;i<=ty;i++)
{
fp->Count = 2;
fp->Vertices[0] = vi+i*(tx+1);
fp->Vertices[1] = vi+i*(tx+1)+tx;
fp++;
}
}
return &Vertices[vi];
}
void GenMinMesh::Transform(sInt sel,const sMatrix &mat,sInt src,sInt dest,sInt op)
{
sVector v;
GenMinVert *vp;
sBool ok;
vp = Vertices.Array;
src--;
dest--;
sVERIFY(src<KMM_MAXUV);
sVERIFY(dest<KMM_MAXUV);
for(sInt i=0;i<Vertices.Count;i++)
{
ok = 1;
if(sel==MMU_SELECTED)
ok = vp->Select;
else if(sel==MMU_UNSELECTED)
ok = !vp->Select;
if(ok)
{
if(src<0)
vp->Pos.Out(v,1);
else
v.Init(vp->UV[src][0],vp->UV[src][1],0,1);
v.Rotate34(mat);
if(op==1) // normalize
{
v.Unit3();
}
else if(op==2) // polar mapping
{
sF32 u_,v_;
u_ = sFATan2(v.x,v.z)/sPI2F+0.5f;
v_ = 0.5f-sFATan2(v.y,sFSqrt(v.x*v.x+v.z*v.z))/sPIF;
v.Init(u_,v_,0,0);
}
if(dest<0)
{
vp->Pos.Init(v);
}
else
{
vp->UV[dest][0] = v.x;
vp->UV[dest][1] = v.y;
}
}
vp++;
}
}
void GenMinMesh::CalcNormals()
{
GenMinFace *mf;
GenMinVert *mv;
sInt i,j;
GenMinVector n,t;
GenMinVector d0,d1;
sInt p0,p1,p2;
if(NormalsOK)
return;
NormalsOK = sTRUE;
mf = &Faces[0];
mv = &Vertices[0];
for(i=0;i<Vertices.Count;i++)
{
mv[i].Normal.Init();
mv[i].Tangent.Init();
}
for(i=0;i<Faces.Count;i++)
{
n.Init();
if(mf[i].Count==2)
{
p0 = mf[i].Vertices[0];
p1 = mf[i].Vertices[1];
mv[p0].Normal.Add(mv[p1].Normal);
mv[p1].Normal = mv[p0].Normal;
mv[p0].Tangent.Add(mv[p1].Tangent);
mv[p1].Tangent = mv[p0].Tangent;
}
else if(mf[i].Count>=3)
{
// normal
p0 = mf[i].Vertices[0];
p1 = mf[i].Vertices[1];
p2 = mf[i].Vertices[2];
d0.Sub(mv[p1].Pos,mv[p0].Pos);
d1.Sub(mv[p2].Pos,mv[p0].Pos);
n.Cross(d0,d1);
n.UnitSafe();
mf[i].Normal = n;
// tangent
sF32 b1 = mv[p1].UV[0][0] - mv[p0].UV[0][0];
sF32 b2 = mv[p2].UV[0][0] - mv[p0].UV[0][0];
sF32 c1 = mv[p1].UV[0][1] - mv[p0].UV[0][1];
sF32 c2 = mv[p2].UV[0][1] - mv[p0].UV[0][1];
sF32 ix = b1 * c2 - c1 * b2;
t.Init(0,0,0);
if(sFAbs(ix) > 1e-20f)
{
ix = 1.0f / ix;
t.AddScale(d0,c2 * ix);
t.AddScale(d1,-c1 * ix);
}
// orthogonalize
t.AddScale(n,-t.Dot(n));
t.UnitSafe();
// accumulate
for(j=0;j<mf[i].Count;j++)
{
sInt v = mf[i].Vertices[j];
mv[v].Normal.Add(n);
mv[v].Tangent.Add(t);
}
}
}
for(i=0;i<Vertices.Count;i++)
{
mv[i].Normal.UnitSafe();
mv[i].Tangent.UnitSafe();
}
}
/*
void GenMinMesh::Cleanup()
{
sInt *temp = new sInt[Vertices.Count];
for(sInt i=0;i<Vertices.Count;i++)
temp[i] = 0;
// delete faces and mark used vertices
sInt fc = 0;
for(sInt i=0;i<Faces.Count;i++)
{
GenMinFace *face = &Faces[i];
if(face->Cluster && face->Count==3) // skip double vertices (like a quad that is actually a tri)
{
sInt last = face->Vertices[face->Count-1];
sInt pc = 0; // quads that are actually a line will have two vertices after this.
for(sInt j=0;j<face->Count;j++)
{
if(face->Vertices[j]!=last)
last = face->Vertices[pc++] = face->Vertices[j];
}
face->Count = (pc>=3)?pc:0;
}
if(face->Count>0) // skip degenerated faces (like generated by the previous pass)
{
for(sInt j=0;j<face->Count;j++) // mark used vertices
temp[face->Vertices[j]] = 1;
Faces[fc++] = Faces[i]; // copy face.
}
}
Faces.Count = fc;
// remap vertices
sInt vc = 0;
for(sInt i=0;i<Vertices.Count;i++)
{
if(temp[i])
{
Vertices[vc] = Vertices[i]; // copy the vertex
temp[i] = vc++; // update remapping
}
else
{
temp[i] = -1;
}
}
Vertices.Count = vc;
// remap faces
for(sInt i=0;i<Faces.Count;i++)
{
GenMinFace *face = &Faces[i];
for(sInt j=0;j<face->Count;j++)
{
face->Vertices[j] = temp[face->Vertices[j]];
sVERIFY(face->Vertices[j]>=0);
}
}
// done
delete[] temp;
}
*/
void GenMinMesh::Add(GenMinMesh *other)
{
sInt p,a;
// copy faces
p = Faces.Count;
a = other->Faces.Count;
Faces.Resize(p+a);
sCopyMem(&Faces[p],&other->Faces[0],a*sizeof(GenMinFace));
// update vertex and cluster in faces
for(sInt i=p;i<Faces.Count;i++)
{
GenMinFace *f = &Faces[i];
for(sInt j=0;j<f->Count;j++)
f->Vertices[j] += Vertices.Count;
if(f->Cluster>0)
f->Cluster += Clusters.Count-1;
}
// copy vertices
p = Vertices.Count;
a = other->Vertices.Count;
Vertices.Resize(p+a);
sCopyMem(&Vertices[p],&other->Vertices[0],a*sizeof(GenMinVert));
// copy and refcount clusters. clusters should get merged...
p = Clusters.Count;
a = other->Clusters.Count-1;
Clusters.Resize(p+a);
sCopyMem(&Clusters[p],&other->Clusters[1],a*sizeof(GenMinCluster));
for(sInt i=p;i<Clusters.Count;i++)
if(Clusters[i].Mtrl)
Clusters[i].Mtrl->AddRef();
ChangeTopo();
}
void GenMinMesh::MergeClusters()
{
sInt *rev;
sInt revcount;
for(sInt i=0;i<Clusters.Count;i++)
Clusters[i].Temp = -1;
Clusters[0].Temp = 0;
for(sInt i=0;i<Faces.Count;i++)
Clusters[Faces[i].Cluster].Temp = 0;
rev = new sInt[Clusters.Count];
revcount = 0;
for(sInt i=0;i<Clusters.Count;i++)
{
if(Clusters[i].Temp>=0)
{
for(sInt j=0;j<revcount;j++)
{
if(Clusters[i].Equals(Clusters[rev[j]]))
{
Clusters[i].Temp = Clusters[rev[j]].Temp;
goto next;
}
}
rev[revcount] = i;
Clusters[i].Temp = revcount;
revcount++;
next:;
}
}
for(sInt i=0;i<Faces.Count;i++)
{
sVERIFY(Clusters[Faces[i].Cluster].Temp>=0);
Faces[i].Cluster = Clusters[Faces[i].Cluster].Temp;
}
for(sInt i=0;i<revcount;i++)
if(Clusters[rev[i]].Mtrl)
Clusters[rev[i]].Mtrl->AddRef();
for(sInt i=0;i<Clusters.Count;i++)
if(Clusters[i].Mtrl)
Clusters[i].Mtrl->Release();
sVERIFY(revcount>0);
sVERIFY(rev[0]==0);
for(sInt i=0;i<revcount;i++)
{
sVERIFY(i<=rev[i]);
Clusters[i] = Clusters[rev[i]];
}
Clusters.Count = revcount;
delete[] rev;
}
void GenMinMesh::Invert()
{
for(sInt i=0;i<Faces.Count;i++)
{
sInt buffer[KMM_MAXVERT];
GenMinFace *face = &Faces[i];
sCopyMem(buffer,face->Vertices,face->Count*4);
for(sInt j=0;j<face->Count;j++)
face->Vertices[j] = buffer[face->Count-1-j];
}
ChangeTopo();
}
void GenMinMesh::CalcBBox(sAABox &box) const
{
box.Min.Init( 1e+15f, 1e+15f, 1e+15f, 1.0f);
box.Max.Init(-1e+15f, -1e+15f, -1e+15f, 1.0f);
for(sInt i=0;i<Faces.Count;i++)
{
const GenMinFace *face = &Faces[i];
for(sInt j=0;j<face->Count;j++)
{
const GenMinVert *vert = &Vertices[face->Vertices[j]];
box.Min.x = sMin(box.Min.x,vert->Pos.x);
box.Min.y = sMin(box.Min.y,vert->Pos.y);
box.Min.z = sMin(box.Min.z,vert->Pos.z);
box.Max.x = sMax(box.Max.x,vert->Pos.x);
box.Max.y = sMax(box.Max.y,vert->Pos.y);
box.Max.z = sMax(box.Max.z,vert->Pos.z);
}
}
}
void GenMinMesh::SelectAll(sInt in,sInt out)
{
GenMinVert *vp;
sInt ok;
vp = Vertices.Array;
for(sInt i=0;i<Vertices.Count;i++)
{
ok = (in==MMU_ALL || (in==MMU_SELECTED&&vp->Select) || (in==MMU_UNSELECTED&&!vp->Select));
switch(out)
{
case MMS_ADD:
if(ok) vp->Select = 1;
break;
case MMS_SUB:
if(ok) vp->Select = 0;
break;
case MMS_SET:
vp->Select = ok;
break;
case MMS_SETNOT:
vp->Select = !ok;
break;
}
vp++;
}
}
/****************************************************************************/
sInt *GenMinMesh::CalcMergeVerts() const
{
/*
// this code does nothing. use it when you think this function does not work
sInt *remap = new sInt[Vertices.Count];
for(sInt i=0;i<Vertices.Count;i++)
remap[i] = i;
return remap;
*/
sInt *next = new sInt[Vertices.Count];
sInt *remap = new sInt[Vertices.Count];
sInt hashSize = 1024;
while(Vertices.Count > hashSize * 8)
hashSize *= 2;
// alloc+clear hash table
sInt *hash = new sInt[hashSize];
sSetMem(hash,0xff,hashSize*sizeof(sInt));
// find vertices with identical position
for(sInt i=0;i<Vertices.Count;i++)
{
// calc bucket
const GenMinVert *vert = &Vertices[i];
sU32 bucket = HashVector(vert->Pos) & (hashSize - 1);
// search for first matching vertex
sInt first;
for(first = hash[bucket]; first != -1; first = next[first])
{
const GenMinVert *cmp = &Vertices[first];
if(1/*cmp->MergeTag == vert->MergeTag*/
&& !sCmpMem(&cmp->Pos,&vert->Pos,sizeof(vert->Pos))
&& !sCmpMem(&cmp->Matrix,&vert->Matrix,sizeof(vert->Matrix))
&& !sCmpMem(&cmp->Weights,&vert->Weights,sizeof(vert->Weights)))
{
remap[i] = first;
break;
}
}
// append to hash list if it's the first at that position
if(first == -1)
{
remap[i] = i;
next[i] = hash[bucket];
hash[bucket] = i;
}
}
delete[] hash;
delete[] next;
return remap;
}
/****************************************************************************/
struct GenMinTempEdge
{
sInt FaceVert;
sInt v0,v1; // v0 < v1!
bool operator < (const GenMinTempEdge &b) const
{
return v0 < b.v0 || v0 == b.v0 && v1 < b.v1;
}
};
static void SiftDownEdge(GenMinTempEdge *list,sInt n,sInt k)
{
GenMinTempEdge v = list[k];
while(k < (n >> 1))
{
sInt j = k*2+1;
if(j+1 < n && list[j] < list[j+1])
j++;
if(!(v < list[j]))
break;
list[k] = list[j];
k = j;
}
list[k] = v;
}
static void HeapSortEdges(GenMinTempEdge *list,sInt n)
{
for(sInt k=(n >> 1)-1;k>=0;k--)
SiftDownEdge(list,n,k);
while(--n > 0)
{
sSwap(list[0],list[n]);
SiftDownEdge(list,n,0);
}
}
static void ConnectFaces(GenMinFace *faces,const sInt *buf,sInt count)
{
sVERIFY(count <= 2);
if(count == 1) // boundary
faces[*buf >> 3].Adjacent[*buf & 7] = -1;
else if(count == 2) // inner (shared) edge
{
sInt f0 = buf[0], f1 = buf[1];
faces[f0 >> 3].Adjacent[f0 & 7] = f1;
faces[f1 >> 3].Adjacent[f1 & 7] = f0;
}
}
sBool GenMinMesh::CalcAdjacencyCore(const sInt *remap)
{
// calculate number of edges we need
sInt numEdges = 0;
for(sInt i=0;i<Faces.Count;i++)
numEdges += Faces[i].Count >= 3 ? Faces[i].Count : 0;
// make edge list
GenMinTempEdge *edges = new GenMinTempEdge[numEdges], *edgePtr = edges;
for(sInt i=0;i<Faces.Count;i++)
{
GenMinFace *face = &Faces[i];
if(face->Count < 3)
continue;
for(sInt j=0;j<face->Count;j++)
{
edgePtr->FaceVert = i * 8 + j;
edgePtr->v0 = remap[face->Vertices[j]];
edgePtr->v1 = remap[face->Vertices[(j + 1 == face->Count) ? 0 : j+1]];
if(edgePtr->v0 > edgePtr->v1)
sSwap(edgePtr->v0,edgePtr->v1);
edgePtr++;
}
}
// sort edge list (using heapsort)
HeapSortEdges(edges,numEdges);
// generate adjacency
sInt last0 = -1, last1 = -1, count = 0, temp[2];
sBool closed = sTRUE;
for(sInt i=0;i<numEdges;i++)
{
edgePtr = &edges[i];
if(last0 == edgePtr->v0 && last1 == edgePtr->v1)
{
temp[count++] = edgePtr->FaceVert;
if(count == 2) // got a complete edge
{
ConnectFaces(Faces.Array,temp,count);
count = 0;
}
}
else
{
if(count != 0)
closed = sFALSE;
ConnectFaces(Faces.Array,temp,count);
temp[0] = edgePtr->FaceVert;
count = 1;
}
last0 = edgePtr->v0;
last1 = edgePtr->v1;
}
if(count != 0)
closed = sFALSE;
ConnectFaces(Faces.Array,temp,count);
// cleanup
delete[] edges;
return closed;
}
sBool GenMinMesh::CalcAdjacency()
{
sInt *remap = CalcMergeVerts();
sBool result = CalcAdjacencyCore(remap);
delete[] remap;
return result;
}
void GenMinMesh::VerifyAdjacency()
{
sBool closed = sTRUE;
for(sInt i=0;i<Faces.Count;i++)
{
GenMinFace *face = &Faces[i];
if(face->Count < 2)
continue;
else if(face->Count == 2)
{
sInt v0 = face->Vertices[0];
sInt v1 = face->Vertices[1];
sVERIFY(!sCmpMem(&Vertices[v0].Pos,&Vertices[v1].Pos,sizeof(GenMinVector)));
}
else
{
for(sInt j=0;j<face->Count;j++)
{
sInt opposite = face->Adjacent[j];
if(opposite != -1)
{
sVERIFY(Faces[opposite >> 3].Adjacent[opposite & 7] == i*8+j);
}
else
closed = sFALSE;
}
}
}
if(!closed)
sDPrintF("mesh is not closed\n");
}
/****************************************************************************/
#if sLINK_MINMESH
void GenMinMesh::Prepare()
{
if(!PreparedMesh)
{
PreparedMesh = new EngMesh;
PreparedMesh->FromGenMinMesh(this);
}
}
#endif
void GenMinMesh::UnPrepare()
{
if(PreparedMesh)
{
PreparedMesh->Release();
PreparedMesh = 0;
}
}
#if !sPLAYER
void GenMinMesh::PrepareWire(sInt flags,sU32 selMask)
{
if(!WireMesh || WireFlags != flags || WireSelMask != selMask)
{
UnPrepareWire();
WireMesh = new EngMesh;
WireMesh->FromGenMinMeshWire(this,flags,selMask);
WireFlags = flags;
WireSelMask = selMask;
}
}
void GenMinMesh::UnPrepareWire()
{
if(WireMesh)
{
WireMesh->Release();
WireMesh = 0;
}
}
#endif
sBool GenMinMesh::Strip()
{
if(PreparedMesh)
{
for(sInt i=0;i<Clusters.Count;i++)
if(Clusters[i].Mtrl)
Clusters[i].Mtrl->Release();
Vertices.Resize(0);
Faces.Resize(0);
Clusters.Resize(0);
Vertices.Compact();
Faces.Compact();
Clusters.Compact();
Stripped = sTRUE;
return sTRUE;
}
else
return sFALSE;
}
sBool GenMinMesh::IsStripped()
{
return Stripped;
}
void GenMinMesh::BakeAnimation(sF32 fade,sF32 metamorph)
{
if(!Animation)
return;
// sDPrintF("%f\n",fade);
sMatrix *matrices = new sMatrix[Animation->Matrices.Count];
Animation->EvalAnimation(fade,metamorph,matrices);
GenMinVert *vp = Vertices.Array;
for(sInt i=0;i<Vertices.Count;i++,vp++)
{
sVector v0,v1,v;
if(vp->BoneCount>0)
{
v1.Init(0,0,0,0);
v0.Init(vp->Pos.x,vp->Pos.y,vp->Pos.z,1);
for(sInt j=0;j<vp->BoneCount;j++)
{
v.Rotate34(matrices[vp->Matrix[j]],v0);
v1.AddScale3(v,vp->Weights[j]);
}
vp->Pos.Init(v1);
vp->BoneCount = 0;
}
}
delete[] matrices;
sRelease(Animation);
for(sInt i=1;i<Clusters.Count;i++)
Clusters[i].AnimType = 0;
NormalsOK = 0;
}
void GenMinMesh::AutoStitch()
{
sInt *merge = CalcMergeVerts();
sInt *cycleNext = new sInt[Vertices.Count];
sInt *cycleLast = new sInt[Vertices.Count];
// build cycle links
for(sInt i=0;i<Vertices.Count;i++)
{
sInt first = merge[i];
if(first == i)
{
cycleNext[i] = i;
cycleLast[i] = i;
}
else
{
cycleNext[i] = first;
cycleNext[cycleLast[first]] = i;
cycleLast[first] = i;
}
}
// add stitches
for(sInt i=0;i<Vertices.Count;i++)
{
sInt next = cycleNext[i];
if(next != i)
{
GenMinFace *stitch = Faces.Add();
stitch->Select = 0;
stitch->Count = 2;
stitch->Cluster = 0;
stitch->Vertices[0] = i;
stitch->Vertices[1] = next;
stitch->Flags = 0;
}
}
delete[] merge;
delete[] cycleNext;
delete[] cycleLast;
}
/****************************************************************************/
/****************************************************************************/
/*** ***/
/*** Ops ***/
/*** ***/
/****************************************************************************/
/****************************************************************************/
sBool CheckMinMesh(GenMinMesh *&mesh)
{
GenMinMesh *oldmesh;
if(mesh==0)
return 1;
if(mesh->ClassId!=KC_MINMESH)
return 1;
if(mesh->RefCount>1)
{
oldmesh = mesh;
mesh = new GenMinMesh;
mesh->Copy(oldmesh);
oldmesh->Release();
}
return 0;
}
/****************************************************************************/
/*** ***/
/*** Generator Ops ***/
/*** ***/
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_SingleVert()
{
GenMinMesh *mesh;
GenMinVert *vp;
GenMinFace *fp;
mesh = new GenMinMesh;
vp = mesh->Vertices.Add();
sSetMem(vp,0,sizeof(*vp));
vp->Color = 0;//~0;
fp = mesh->Faces.Add();
sSetMem(fp,0,sizeof(*fp));
fp->Count = 1;
fp->Cluster = 1;
fp->Flags = 0;
return mesh;
}
GenMinMesh * __stdcall MinMesh_Grid(sInt mode,sInt tx,sInt ty)
{
GenMinMesh *mesh;
sMatrix mat;
mesh = new GenMinMesh;
mesh->MakeGrid(tx,ty,0);
if(mode&1)
mesh->MakeGrid(tx,ty,16);
mat.Init();
mat.i.Init(-1,0,0,0);
mat.j.Init(0,0,1,0);
mesh->Transform(MMU_ALL,mat);
mesh->SelectAll(MMU_ALL,MMS_SET);
return mesh;
}
GenMinMesh * __stdcall MinMesh_Cube(sInt tx,sInt ty,sInt tz,sInt flags,sFSRT srt)
{
GenMinMesh *mesh;
sMatrix mat;
sInt *tess=&tx;
const static sS8 cube[6][9] =
{
{ 0,1, 1, 1, 0, 0,-1 ,1 , 0 },
{ 2,1, 1, 1, -1, 0, 0 ,0 , 16 },
{ 0,1, 1, 1, 0, 0, 1 ,3 , 16 },
{ 2,1, 1, 1, 1, 0, 0 ,2 , 0 },
{ 0,2, 1, 1, 0, 1, 0 ,0 , 0 },
{ 0,2, 1, 1, 0,-1, 0 ,0 , 16 },
};
const static sS8 sign[2] = { -1,1 };
mat.Init();
mesh = new GenMinMesh;
for(sInt i=0;i<6;i++)
{
sSetMem(&mat,0,sizeof(mat));
mesh->MakeGrid(tess[cube[i][0]],tess[cube[i][1]],cube[i][8]);
(&mat.i.x)[cube[i][0]] = cube[i][2];
(&mat.j.x)[cube[i][1]] = cube[i][3];
mat.l.Init(cube[i][4]*0.5f,cube[i][5]*0.5f,cube[i][6]*0.5f,1);
mesh->Transform(MMU_SELECTED,mat);
mat.Init();
if(cube[i][8])
{
mat.l.x = 1;
mat.i.x = -1;
}
if(flags&2) // wraparound
{
mat.l.x += cube[i][7];
}
mesh->Transform(MMU_SELECTED,mat,1,1);
mesh->SelectAll(MMU_ALL,MMS_SETNOT);
}
mesh->SelectAll(MMU_ALL,MMS_SET);
// post transform
mat.InitSRT(srt.v);
mesh->Transform(MMU_ALL,mat);
// scale uv)
if(flags&8)
{
mat.Init();
mat.i.x = srt.s.x;
mat.j.y = srt.s.y;
mat.k.z = srt.s.z;
mesh->Transform(MMU_ALL,mat,1,1);
}
// center / bottom
if(flags&4)
{
mat.Init();
mat.l.y = srt.s.y/2;
mesh->Transform(MMU_ALL,mat);
}
return mesh;
}
GenMinMesh * __stdcall MinMesh_Torus(sInt tx,sInt ty,sF32 ro,sF32 ri,sF32 phase,sF32 arclen,sInt flags)
{
GenMinMesh *mesh;
GenMinVert *vp;
sF32 fx,fy;
sInt closed;
closed = (arclen == 1.0f);
mesh = new GenMinMesh;
mesh->MakeGrid(ty,tx,closed?12:8+3);
if(flags&1) // absolute radii
{
ri = (ro - ri) * 0.5f;
ro -= ri;
}
vp = mesh->Vertices.Array;
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
fx = (1-vp->UV[1][0]-(phase/ty))*sPI2F;
fy = ((vp->UV[closed][1])*arclen)*sPI2F;
vp->Pos.x = -sFCos(fy)*(ro+sFSin(fx)*ri);
vp->Pos.y = -sFCos(fx)*ri;
vp->Pos.z = sFSin(fy)*(ro+sFSin(fx)*ri);
vp++;
}
if(!closed)
{
vp[-2].Pos.x = -sFCos(arclen*sPI2F)*ro;
vp[-2].Pos.y = 0;
vp[-2].Pos.z = sFSin(arclen*sPI2F)* ro;
vp[-1].Pos.x = -sFCos(0)*ro;
vp[-1].Pos.y = 0;
vp[-1].Pos.z = sFSin(0)*ro;
}
// center / bottom
if(flags&2)
{
sMatrix mat;
mat.Init();
mat.l.y = ri;
mesh->Transform(MMU_ALL,mat);
}
return mesh;
}
GenMinMesh * __stdcall MinMesh_Sphere(sInt tx,sInt ty)
{
GenMinMesh *mesh;
GenMinVert *vp;
sF32 fx,fy;
mesh = new GenMinMesh;
mesh->MakeGrid(tx,ty,3+8);
vp = mesh->Vertices.Array;
for(sInt i=0;i<mesh->Vertices.Count-2;i++)
{
fx = (1-vp->UV[1][0])*sPI2F;
fy = ((0.5f/(ty+1.0f))+(vp->UV[0][1]*((ty)/(ty+1.0f))))*sPIF;
vp->Pos.x = -sFSin(fy)*sFSin(fx)*0.5f;
vp->Pos.y = sFCos(fy)*0.5f;
vp->Pos.z = -sFSin(fy)*sFCos(fx)*0.5f;
vp++;
}
vp->Pos.y = mesh->Vertices[0].Pos.y;
vp++;
vp->Pos.y = vp[-2].Pos.y;
vp++;
return mesh;
}
GenMinMesh * __stdcall MinMesh_Cylinder(sInt tx,sInt ty,sInt flags,sInt tz,sInt arc)
{
GenMinMesh *mesh;
GenMinVert *vp;
sF32 fx,fy;
sInt x,y;
sInt closed;
sInt count;
if(tx<3) tx=3;
if(ty<1) ty=1;
if(tz<1) tz=1;
count = tx;
if(arc>tx-1)
arc = tx-1;
if(arc>0)
tx = count - arc + 2;
closed = (flags & 1) ? 32 : 0;
mesh = new GenMinMesh;
// middle
vp = mesh->MakeGrid(tx,ty,8);
for(y=0;y<=ty;y++)
{
for(x=0;x<=tx;x++)
{
fx = ((x==tx)?0:x)*sPI2F/count;
fy = y*1.0f/ty;
if(x!=tx-1 || arc==0)
{
vp->Pos.x = sFSin(fx)*0.5f;
vp->Pos.z = -sFCos(fx)*0.5f;
}
else
{
vp->Pos.x = 0;
vp->Pos.z = 0;
}
vp->Pos.y = fy-0.5f;
vp->UV[0][0] = x*1.0f/tx;
vp->UV[0][1] = 1-fy;
vp++;
}
}
MinMesh_SelectAll(mesh,4);
// mesh->SelectAll(MMU_ALL,MMS_);
// bottom
vp = mesh->MakeGrid(tx,tz-1,closed|1);
for(y=0;y<tz;y++)
{
for(x=0;x<=tx;x++)
{
fx = ((x==tx)?0:x)*sPI2F/count;
fy = (y+1)*0.5f/(tz);
if(x!=tx-1 || arc==0)
{
vp->Pos.x = sFSin(fx)*0.5f;
vp->Pos.z = -sFCos(fx)*0.5f;
}
else
{
vp->Pos.x = 0;
vp->Pos.z = 0;
}
vp->Pos.y = -0.5f;
vp->UV[0][0] = vp->Pos.x+0.5f;
vp->UV[0][1] = vp->Pos.z+0.5f;
vp++;
}
}
vp->Pos.y = -0.5f;
vp->UV[0][0] = 0.5f;
vp->UV[0][1] = 0.5f;
vp++;
// top
vp = mesh->MakeGrid(tx,tz-1,closed|2);
for(y=tz-1;y>=0;y--)
{
for(x=0;x<=tx;x++)
{
fx = ((x==tx)?0:x)*sPI2F/count;
fy = (y+1)*0.5f/(tz);
if(x!=tx-1 || arc==0)
{
vp->Pos.x = sFSin(fx)*0.5f;
vp->Pos.z = -sFCos(fx)*0.5f;
}
else
{
vp->Pos.x = 0;
vp->Pos.z = 0;
}
vp->Pos.y = 0.5f;
vp->UV[0][0] = vp->Pos.x+0.5f;
vp->UV[0][1] = -vp->Pos.z+0.5f;
vp++;
}
}
vp->Pos.y = 0.5f;
vp->UV[0][0] = 0.5f;
vp->UV[0][1] = 0.5f;
vp++;
// center / bottom
if(flags&2)
{
sMatrix mat; mat.Init();
mat.l.y = 0.5f;
mesh->Transform(MMU_ALL,mat);
}
return mesh;
}
GenMinMesh * __stdcall MinMesh_XSI(KOp *op,sChar *filename)
{
GenMinMesh *mesh = 0;
#if sLINK_LOADER
sLoader::Scene *scene = new sLoader::Scene;
if(scene->LoadXSI(filename))
{
mesh = new GenMinMesh;
scene->CreateMesh(mesh);
mesh->AutoStitch();
sInt size = 0;
sU8 *exportedMesh = mesh->ExportToBlob(size);
op->SetBlob(exportedMesh,size);
sDPrintF("mesh->blob export ok, size: %d bytes\n",size);
delete mesh;
mesh = 0;
}
else
op->SetBlob(0,0);
delete scene;
#endif
mesh = new GenMinMesh;
if(op->GetBlobSize())
mesh->ImportFromBlob(op->GetBlobData());
else
mesh->MakeGrid(1,1,0);
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_OBJ(KOp *op,sChar *filename)
{
GenMinMesh *mesh = 0;
#if sLINK_LOADER
OBJFileReader reader;
if(reader.read(filename))
{
mesh = new GenMinMesh;
mesh->Vertices.AtLeast(reader.vertices.size());
for(sInt i=0;i<(sInt)reader.vertices.size();i++)
{
const OBJFileReader::VertInfo& inVert = reader.vertices[i];
GenMinVert* outVert = mesh->Vertices.Add();
sSetMem(outVert,0,sizeof(GenMinVert));
outVert->Pos.Init(reader.positions[inVert.pos]);
if(inVert.norm != -1)
outVert->Normal.Init(reader.normals[inVert.norm]);
if(inVert.tex != -1)
{
outVert->UV[0][0] = reader.texcoords[inVert.tex].x;
outVert->UV[0][1] = reader.texcoords[inVert.tex].y;
}
}
mesh->Faces.AtLeast(reader.faces.size());
for(sInt i=0;i<(sInt)reader.faces.size();i++)
{
const OBJFileReader::FaceInfo& inFace = reader.faces[i];
GenMinFace* outFace = mesh->Faces.Add();
sSetMem(outFace,0,sizeof(GenMinFace));
outFace->Cluster = 1;
outFace->Count = inFace.count;
for(sInt j=0;j<inFace.count;j++)
outFace->Vertices[j] = inFace.start + j;
}
mesh->AutoStitch();
mesh->CalcNormals();
}
#endif
return mesh;
}
/****************************************************************************/
static sS16 KopuliData[][12] =
{
{ 3066,6360,-2241,4152,9760,-2241,895,11405,-2241,895,11405,-2241, },
{ 3066,6360,-2241,895,11405,-2241,-388,8663,-2241,-388,8663,-2241, },
{ 3066,6360,-2241,-388,8663,-2241,1586,5044,-2241,1586,5044,-2241, },
{ 3066,6360,-2241,8988,-1,-2241,8001,3399,-2241,8001,3399,-2241, },
{ 3066,6360,-2241,1586,5044,-2241,8988,-1,-2241,8988,-1,-2241, },
{ 4152,9760,-2241,6422,10637,-2241,3461,13269,-2241,3461,13269,-2241, },
{ 4152,9760,-2241,6817,7566,-2241,6422,10637,-2241,6422,10637,-2241, },
{ 4152,9760,-2241,1191,14037,-2241,895,11405,-2241,895,11405,-2241, },
{ 4152,9760,-2241,3461,13269,-2241,1191,14037,-2241,1191,14037,-2241, },
{ 6817,7566,-2241,8594,11953,-2241,6422,10637,-2241,6422,10637,-2241, },
{ 8594,11953,-2241,9877,11076,-2241,8001,14037,-2241,8001,14037,-2241, },
{ 8594,11953,-2241,8001,14037,-2241,6422,10637,-2241,6422,10637,-2241, },
{ 9877,11076,-2241,10666,12501,-2241,8001,14037,-2241,8001,14037,-2241, },
{ 3461,13269,-2241,5337,15024,-2241,303,15901,-2241,303,15901,-2241, },
{ 3461,13269,-2241,303,15901,-2241,1191,14037,-2241,1191,14037,-2241, },
{ 5337,15024,-2241,5731,18423,-2241,303,15901,-2241,303,15901,-2241, },
{ 5731,18423,-2241,3363,20507,-2241,303,15901,-2241,303,15901,-2241, },
{ 3363,20507,-2241,402,18752,-2241,303,15901,-2241,303,15901,-2241, },
{ 1191,14037,-2241,-881,14585,-2241,895,11405,-2241,895,11405,-2241, },
{ -881,14585,-2241,-5224,16120,-2241,895,11405,-2241,895,11405,-2241, },
{ -881,14585,-2241,-5224,18423,-2241,-5224,16120,-2241,-5224,16120,-2241, },
{ -5224,18423,-2241,-8481,16340,-2241,-5224,16120,-2241,-5224,16120,-2241, },
{ -8481,16340,-2241,-7198,14256,-2241,-5224,16120,-2241,-5224,16120,-2241, },
{ -388,8663,-2241,204,5483,-2241,1586,5044,-2241,1586,5044,-2241, },
{ -388,8663,-2241,-1967,6908,-2241,204,5483,-2241,204,5483,-2241, },
{ -388,8663,-2241,-2461,10747,-2241,-1967,6908,-2241,-1967,6908,-2241, },
{ -2461,10747,-2241,-4435,7018,-2241,-1967,6908,-2241,-1967,6908,-2241, },
{ -4435,7018,-2241,-6606,6250,-2241,-4237,3838,-2241,-4237,3838,-2241, },
{ -4435,7018,-2241,-4237,3838,-2241,-1967,6908,-2241,-1967,6908,-2241, },
{ -4435,7018,-2241,-6409,9869,-2241,-6606,6250,-2241,-6606,6250,-2241, },
{ -6409,9869,-2241,-9271,6360,-2241,-6606,6250,-2241,-6606,6250,-2241, },
{ -9271,6360,-2241,-12331,7084,-2241,-9172,2631,-2241,-9172,2631,-2241, },
{ -9271,6360,-2241,-9172,2631,-2241,-6606,6250,-2241,-6606,6250,-2241, },
{ -9271,6360,-2241,-10850,9387,-2241,-12331,7084,-2241,-12331,7084,-2241, },
{ 204,5483,-2241,-388,2302,-2241,1586,5044,-2241,1586,5044,-2241, },
{ -388,2302,-2241,895,1206,-2241,1586,5044,-2241,1586,5044,-2241, },
{ 8988,-1,-2241,12245,3728,-2241,8001,3399,-2241,8001,3399,-2241, },
{ 12245,3728,-2241,10370,6579,-2241,8001,3399,-2241,8001,3399,-2241, },
{ 4152,9760,2200,4152,9760,-2241,3066,6360,-2241,3066,6360,-2241, },
{ 3066,6360,-2241,3066,6360,2200,4152,9760,2200,4152,9760,2200, },
{ 3066,6360,-2241,8001,3399,-2241,8001,3399,2200,8001,3399,2200, },
{ 8001,3399,2200,3066,6360,2200,3066,6360,-2241,3066,6360,-2241, },
{ 6817,7566,2200,6817,7566,-2241,4152,9760,-2241,4152,9760,-2241, },
{ 4152,9760,-2241,4152,9760,2200,6817,7566,2200,6817,7566,2200, },
{ 8594,11953,2200,8594,11953,-2241,6817,7566,-2241,6817,7566,-2241, },
{ 6817,7566,-2241,6817,7566,2200,8594,11953,2200,8594,11953,2200, },
{ 9877,11076,2200,9877,11076,-2241,8594,11953,-2241,8594,11953,-2241, },
{ 8594,11953,-2241,8594,11953,2200,9877,11076,2200,9877,11076,2200, },
{ 10666,12501,2200,10666,12501,-2241,9877,11076,-2241,9877,11076,-2241, },
{ 9877,11076,-2241,9877,11076,2200,10666,12501,2200,10666,12501,2200, },
{ 8001,14037,2200,8001,14037,-2241,10666,12501,-2241,10666,12501,-2241, },
{ 10666,12501,-2241,10666,12501,2200,8001,14037,2200,8001,14037,2200, },
{ 6422,10637,2200,6422,10637,-2241,8001,14037,-2241,8001,14037,-2241, },
{ 8001,14037,-2241,8001,14037,2200,6422,10637,2200,6422,10637,2200, },
{ 3461,13269,2200,3461,13269,-2241,6422,10637,-2241,6422,10637,-2241, },
{ 6422,10637,-2241,6422,10637,2200,3461,13269,2200,3461,13269,2200, },
{ 5337,15024,2200,5337,15024,-2241,3461,13269,-2241,3461,13269,-2241, },
{ 3461,13269,-2241,3461,13269,2200,5337,15024,2200,5337,15024,2200, },
{ 5731,18423,2200,5731,18423,-2241,5337,15024,-2241,5337,15024,-2241, },
{ 5337,15024,-2241,5337,15024,2200,5731,18423,2200,5731,18423,2200, },
{ 3363,20507,2200,3363,20507,-2241,5731,18423,-2241,5731,18423,-2241, },
{ 5731,18423,-2241,5731,18423,2200,3363,20507,2200,3363,20507,2200, },
{ 402,18752,2200,402,18752,-2241,3363,20507,-2241,3363,20507,-2241, },
{ 3363,20507,-2241,3363,20507,2200,402,18752,2200,402,18752,2200, },
{ 303,15901,2200,303,15901,-2241,402,18752,-2241,402,18752,-2241, },
{ 402,18752,-2241,402,18752,2200,303,15901,2200,303,15901,2200, },
{ 1191,14037,2200,1191,14037,-2241,303,15901,-2241,303,15901,-2241, },
{ 303,15901,-2241,303,15901,2200,1191,14037,2200,1191,14037,2200, },
{ -881,14585,2200,-881,14585,-2241,1191,14037,-2241,1191,14037,-2241, },
{ 1191,14037,-2241,1191,14037,2200,-881,14585,2200,-881,14585,2200, },
{ -5224,18423,2200,-5224,18423,-2241,-881,14585,-2241,-881,14585,-2241, },
{ -881,14585,-2241,-881,14585,2200,-5224,18423,2200,-5224,18423,2200, },
{ -8481,16340,2200,-8481,16340,-2241,-5224,18423,-2241,-5224,18423,-2241, },
{ -5224,18423,-2241,-5224,18423,2200,-8481,16340,2200,-8481,16340,2200, },
{ -7198,14256,2200,-7198,14256,-2241,-8481,16340,-2241,-8481,16340,-2241, },
{ -8481,16340,-2241,-8481,16340,2200,-7198,14256,2200,-7198,14256,2200, },
{ -5224,16120,2200,-5224,16120,-2241,-7198,14256,-2241,-7198,14256,-2241, },
{ -7198,14256,-2241,-7198,14256,2200,-5224,16120,2200,-5224,16120,2200, },
{ 895,11405,2200,895,11405,-2241,-5224,16120,-2241,-5224,16120,-2241, },
{ -5224,16120,-2241,-5224,16120,2200,895,11405,2200,895,11405,2200, },
{ -388,8663,2200,-388,8663,-2241,895,11405,-2241,895,11405,-2241, },
{ 895,11405,-2241,895,11405,2200,-388,8663,2200,-388,8663,2200, },
{ -2461,10747,2200,-2461,10747,-2241,-388,8663,-2241,-388,8663,-2241, },
{ -388,8663,-2241,-388,8663,2200,-2461,10747,2200,-2461,10747,2200, },
{ -4435,7018,2200,-4435,7018,-2241,-2461,10747,-2241,-2461,10747,-2241, },
{ -2461,10747,-2241,-2461,10747,2200,-4435,7018,2200,-4435,7018,2200, },
{ -6409,9869,2200,-6409,9869,-2241,-4435,7018,-2241,-4435,7018,-2241, },
{ -4435,7018,-2241,-4435,7018,2200,-6409,9869,2200,-6409,9869,2200, },
{ -9271,6360,2200,-9271,6360,-2241,-6409,9869,-2241,-6409,9869,-2241, },
{ -6409,9869,-2241,-6409,9869,2200,-9271,6360,2200,-9271,6360,2200, },
{ -10850,9387,2200,-10850,9387,-2241,-9271,6360,-2241,-9271,6360,-2241, },
{ -9271,6360,-2241,-9271,6360,2200,-10850,9387,2200,-10850,9387,2200, },
{ -12331,7084,2200,-12331,7084,-2241,-10850,9387,-2241,-10850,9387,-2241, },
{ -10850,9387,-2241,-10850,9387,2200,-12331,7084,2200,-12331,7084,2200, },
{ -9172,2631,2200,-9172,2631,-2241,-12331,7084,-2241,-12331,7084,-2241, },
{ -12331,7084,-2241,-12331,7084,2200,-9172,2631,2200,-9172,2631,2200, },
{ -6606,6250,2200,-6606,6250,-2241,-9172,2631,-2241,-9172,2631,-2241, },
{ -9172,2631,-2241,-9172,2631,2200,-6606,6250,2200,-6606,6250,2200, },
{ -4237,3838,2200,-4237,3838,-2241,-6606,6250,-2241,-6606,6250,-2241, },
{ -6606,6250,-2241,-6606,6250,2200,-4237,3838,2200,-4237,3838,2200, },
{ -1967,6908,2200,-1967,6908,-2241,-4237,3838,-2241,-4237,3838,-2241, },
{ -4237,3838,-2241,-4237,3838,2200,-1967,6908,2200,-1967,6908,2200, },
{ 204,5483,2200,204,5483,-2241,-1967,6908,-2241,-1967,6908,-2241, },
{ -1967,6908,-2241,-1967,6908,2200,204,5483,2200,204,5483,2200, },
{ -388,2302,2200,-388,2302,-2241,204,5483,-2241,204,5483,-2241, },
{ 204,5483,-2241,204,5483,2200,-388,2302,2200,-388,2302,2200, },
{ 895,1206,2200,895,1206,-2241,-388,2302,-2241,-388,2302,-2241, },
{ -388,2302,-2241,-388,2302,2200,895,1206,2200,895,1206,2200, },
{ 1586,5044,2200,1586,5044,-2241,895,1206,-2241,895,1206,-2241, },
{ 895,1206,-2241,895,1206,2200,1586,5044,2200,1586,5044,2200, },
{ 8988,0,2200,8988,-1,-2241,1586,5044,-2241,1586,5044,-2241, },
{ 1586,5044,-2241,1586,5044,2200,8988,0,2200,8988,0,2200, },
{ 12245,3728,2200,12245,3728,-2241,8988,-1,-2241,8988,-1,-2241, },
{ 8988,-1,-2241,8988,0,2200,12245,3728,2200,12245,3728,2200, },
{ 10370,6579,2200,10370,6579,-2241,12245,3728,-2241,12245,3728,-2241, },
{ 12245,3728,-2241,12245,3728,2200,10370,6579,2200,10370,6579,2200, },
{ 8001,3399,2200,8001,3399,-2241,10370,6579,-2241,10370,6579,-2241, },
{ 10370,6579,-2241,10370,6579,2200,8001,3399,2200,8001,3399,2200, },
{ 895,11405,2200,4152,9760,2200,3066,6360,2200,3066,6360,2200, },
{ -388,8663,2200,895,11405,2200,3066,6360,2200,3066,6360,2200, },
{ 1586,5044,2200,-388,8663,2200,3066,6360,2200,3066,6360,2200, },
{ 8001,3399,2200,8988,0,2200,3066,6360,2200,3066,6360,2200, },
{ 8988,0,2200,1586,5044,2200,3066,6360,2200,3066,6360,2200, },
{ 3461,13269,2200,6422,10637,2200,4152,9760,2200,4152,9760,2200, },
{ 6422,10637,2200,6817,7566,2200,4152,9760,2200,4152,9760,2200, },
{ 895,11405,2200,1191,14037,2200,4152,9760,2200,4152,9760,2200, },
{ 1191,14037,2200,3461,13269,2200,4152,9760,2200,4152,9760,2200, },
{ 6422,10637,2200,8594,11953,2200,6817,7566,2200,6817,7566,2200, },
{ 8001,14037,2200,9877,11076,2200,8594,11953,2200,8594,11953,2200, },
{ 6422,10637,2200,8001,14037,2200,8594,11953,2200,8594,11953,2200, },
{ 8001,14037,2200,10666,12501,2200,9877,11076,2200,9877,11076,2200, },
{ 303,15901,2200,5337,15024,2200,3461,13269,2200,3461,13269,2200, },
{ 1191,14037,2200,303,15901,2200,3461,13269,2200,3461,13269,2200, },
{ 303,15901,2200,5731,18423,2200,5337,15024,2200,5337,15024,2200, },
{ 303,15901,2200,3363,20507,2200,5731,18423,2200,5731,18423,2200, },
{ 303,15901,2200,402,18752,2200,3363,20507,2200,3363,20507,2200, },
{ 895,11405,2200,-881,14585,2200,1191,14037,2200,1191,14037,2200, },
{ 895,11405,2200,-5224,16120,2200,-881,14585,2200,-881,14585,2200, },
{ -5224,16120,2200,-5224,18423,2200,-881,14585,2200,-881,14585,2200, },
{ -5224,16120,2200,-8481,16340,2200,-5224,18423,2200,-5224,18423,2200, },
{ -5224,16120,2200,-7198,14256,2200,-8481,16340,2200,-8481,16340,2200, },
{ 1586,5044,2200,204,5483,2200,-388,8663,2200,-388,8663,2200, },
{ 204,5483,2200,-1967,6908,2200,-388,8663,2200,-388,8663,2200, },
{ -1967,6908,2200,-2461,10747,2200,-388,8663,2200,-388,8663,2200, },
{ -1967,6908,2200,-4435,7018,2200,-2461,10747,2200,-2461,10747,2200, },
{ -4237,3838,2200,-6606,6250,2200,-4435,7018,2200,-4435,7018,2200, },
{ -1967,6908,2200,-4237,3838,2200,-4435,7018,2200,-4435,7018,2200, },
{ -6606,6250,2200,-6409,9869,2200,-4435,7018,2200,-4435,7018,2200, },
{ -6606,6250,2200,-9271,6360,2200,-6409,9869,2200,-6409,9869,2200, },
{ -9172,2631,2200,-12331,7084,2200,-9271,6360,2200,-9271,6360,2200, },
{ -6606,6250,2200,-9172,2631,2200,-9271,6360,2200,-9271,6360,2200, },
{ -12331,7084,2200,-10850,9387,2200,-9271,6360,2200,-9271,6360,2200, },
{ 1586,5044,2200,-388,2302,2200,204,5483,2200,204,5483,2200, },
{ 1586,5044,2200,895,1206,2200,-388,2302,2200,-388,2302,2200, },
{ 8001,3399,2200,12245,3728,2200,8988,0,2200,8988,0,2200, },
{ 8001,3399,2200,10370,6579,2200,12245,3728,2200,12245,3728,2200, },
};
GenMinMesh * __stdcall MinMesh_Kopuli()
{
GenMinMesh *mesh;
sInt i,fc;
GenMinVert *v;
GenMinFace *f;
fc = sizeof(KopuliData)/24;
mesh = new GenMinMesh;
mesh->Vertices.SetMax(fc*4);
mesh->Vertices.Count = fc*4;
v = mesh->Vertices.Array;
sSetMem(v,0,sizeof(*v)*(fc*4));
mesh->Faces.SetMax(fc);
mesh->Faces.Count = fc;
f = mesh->Faces.Array;
sSetMem(f,0,sizeof(*f)*fc);
for(i=0;i<fc;i++)
{
v->Pos.x = KopuliData[i][ 0]/16384.0f;
v->Pos.y = KopuliData[i][ 1]/16384.0f;
v->Pos.z = KopuliData[i][ 2]/16384.0f;
v->Select = 1;
v->Color = 0;//~0;
v++;
v->Pos.x = KopuliData[i][ 3]/16384.0f;
v->Pos.y = KopuliData[i][ 4]/16384.0f;
v->Pos.z = KopuliData[i][ 5]/16384.0f;
v->Select = 1;
v->Color = 0;//~0;
v++;
v->Pos.x = KopuliData[i][ 6]/16384.0f;
v->Pos.y = KopuliData[i][ 7]/16384.0f;
v->Pos.z = KopuliData[i][ 8]/16384.0f;
v->Select = 1;
v->Color = 0;//~0;
v++;
v->Pos.x = KopuliData[i][ 9]/16384.0f;
v->Pos.y = KopuliData[i][10]/16384.0f;
v->Pos.z = KopuliData[i][11]/16384.0f;
v->Select = 1;
v->Color = 0;//~0;
v++;
f->Select = 1;
f->Cluster = 1;
f->Flags = 0;
if(KopuliData[i][6]==KopuliData[i][9] && KopuliData[i][7]==KopuliData[i][10] && KopuliData[i][8]==KopuliData[i][11])
{
f->Count = 3;
f->Vertices[0] = i*4+2;
f->Vertices[1] = i*4+1;
f->Vertices[2] = i*4+0;
}
else
{
f->Count = 4;
f->Vertices[0] = i*4+3;
f->Vertices[1] = i*4+2;
f->Vertices[2] = i*4+1;
f->Vertices[3] = i*4+0;
}
f++;
}
return mesh;
}
/****************************************************************************/
/*** ***/
/*** Special Ops ***/
/*** ***/
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_MatLink(GenMinMesh *mesh,GenMaterial *mtrl,sInt select,sInt pass)
{
GenMinFace *face;
if(!mtrl || mtrl->ClassId != KC_MATERIAL || CheckMinMesh(mesh))
return 0;
sInt id = mesh->AddCluster(mtrl,pass);
for(sInt i=0;i<mesh->Faces.Count;i++)
{
face = &mesh->Faces[i];
if(select==0 || (select==1&&face->Select) || (select==2&&!face->Select))
if(face->Cluster!=0)
face->Cluster = id;
}
mesh->ChangeTopo();
mtrl->Release();
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_MatLinkId(GenMinMesh *mesh,GenMaterial *mtrl,sInt id,sInt pass)
{
if(!mtrl || mtrl->ClassId != KC_MATERIAL || CheckMinMesh(mesh))
return 0;
for(sInt i=0;i<mesh->Clusters.Count;i++)
{
if(mesh->Clusters[i].Id==id)
{
sRelease(mesh->Clusters[i].Mtrl);
mesh->Clusters[i].Mtrl = mtrl; mtrl->AddRef();
mesh->Clusters[i].RenderPass = pass;
}
}
mesh->ChangeTopo();
mtrl->Release();
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_Add(sInt count,GenMinMesh *mesh,...)
{
if(CheckMinMesh(mesh)) return 0;
for(sInt i=1;i<count;i++)
{
GenMinMesh *b = (&mesh)[i];
mesh->Add(b);
b->Release();
}
mesh->ChangeTopo();
mesh->MergeClusters();
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_SelectAll(GenMinMesh *mesh,sU32 mode)
{
if(CheckMinMesh(mesh)) return 0;
if(mode&2)
for(sInt i=0;i<mesh->Vertices.Count;i++)
mesh->Vertices[i].Select = (mode&1);
if(mode&4)
for(sInt i=0;i<mesh->Faces.Count;i++)
mesh->Faces[i].Select = (mode&1);
mesh->ChangeGeo();
return mesh;
}
/****************************************************************************/
sBool SelectLogic(sInt old,sInt sel,sInt mode)
{
switch(mode&3)
{
case MMS_ADD:
if(sel) return 1;
break;
case MMS_SUB:
if(sel) return 0;
break;
case MMS_SET:
return sel;
break;
case MMS_SETNOT:
return !sel;
break;
}
return old;
}
GenMinMesh * __stdcall MinMesh_SelectCube(GenMinMesh *mesh,sInt mode,sF323 center,sF323 size)
{
if(CheckMinMesh(mesh)) return 0;
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
sBool a = sFAbs(mesh->Vertices[i].Pos.x-center.x)<=size.x/2;
sBool b = sFAbs(mesh->Vertices[i].Pos.y-center.y)<=size.y/2;
sBool c = sFAbs(mesh->Vertices[i].Pos.z-center.z)<=size.z/2;
mesh->Vertices[i].TempByte = (a && b && c);
}
switch(mode&12)
{
case MMS_VERTEX:
for(sInt i=0;i<mesh->Vertices.Count;i++)
mesh->Vertices[i].Select = SelectLogic(mesh->Vertices[i].Select,mesh->Vertices[i].TempByte,mode);
break;
case MMS_FULLFACE:
for(sInt i=0;i<mesh->Faces.Count;i++)
{
GenMinFace *face = &mesh->Faces[i];
sBool ok = 1;
for(sInt j=0;j<face->Count;j++)
if(!mesh->Vertices[face->Vertices[j]].TempByte)
ok = 0;
face->Select = SelectLogic(face->Select,ok,mode);
}
break;
case MMS_PARTFACE:
for(sInt i=0;i<mesh->Faces.Count;i++)
{
GenMinFace *face = &mesh->Faces[i];
sBool ok = 0;
for(sInt j=0;j<face->Count;j++)
if(mesh->Vertices[face->Vertices[j]].TempByte)
ok = 1;
face->Select = SelectLogic(face->Select,ok,mode);
}
break;
}
mesh->ChangeGeo();
return mesh;
}
GenMinMesh * __stdcall MinMesh_SelectLogic(GenMinMesh *mesh,sInt mode)
{
if(CheckMinMesh(mesh)) return 0;
switch(mode)
{
case 0:
for(sInt i=0;i<mesh->Faces.Count;i++)
mesh->Faces[i].Select = !mesh->Faces[i].Select;
break;
case 1:
for(sInt i=0;i<mesh->Vertices.Count;i++)
mesh->Vertices[i].Select = !mesh->Vertices[i].Select;
break;
case 2:
for(sInt i=0;i<mesh->Vertices.Count;i++)
mesh->Vertices[i].Select = 0;
for(sInt i=0;i<mesh->Faces.Count;i++)
{
GenMinFace *face = &mesh->Faces[i];
if(face->Select)
for(sInt j=0;j<face->Count;j++)
mesh->Vertices[face->Vertices[j]].Select = 1;
}
break;
}
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_DeleteFaces(GenMinMesh *mesh)
{
if(CheckMinMesh(mesh)) return 0;
for(sInt i=0;i<mesh->Faces.Count;i++)
if(mesh->Faces[i].Select)
mesh->Faces[i].Cluster = 0;
mesh->ChangeTopo();
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_Invert(GenMinMesh *mesh)
{
if(CheckMinMesh(mesh)) return 0;
mesh->Invert();
return mesh;
}
/****************************************************************************/
/*** ***/
/*** Geometry Modifiers ***/
/*** ***/
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_TransformEx(GenMinMesh *mesh,sInt mask,sFSRT srt)
{
sMatrix mat;
if(CheckMinMesh(mesh)) return 0;
mat.InitSRT(srt.v);
mesh->Transform(mask&3,mat,(mask>>2)&7,(mask>>5)&7,(mask>>8)&3);
mesh->ChangeGeo();
return mesh;
}
GenMinMesh * __stdcall MinMesh_Displace(GenMinMesh *mesh,class GenBitmap *bmp,sInt mask,sF323 ampli)
{
BilinearContext ctx;
sU16 height[4];
if(CheckMinMesh(mesh)) return 0;
mesh->CalcNormals();
// prepare bilinear samples
BilinearSetup(&ctx,bmp->Data,bmp->XSize,bmp->YSize,0);
sF32 xScale = bmp->XSize * 65536.0f;
sF32 yScale = bmp->YSize * 65536.0f;
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
GenMinVert *vert = &mesh->Vertices[i];
if(mask==0 || (mask==1&&vert->Select) || (mask==2&&!vert->Select))
{
BilinearFilter(&ctx,(sU64 *)height,vert->UV[0][0]*xScale,vert->UV[0][1]*yScale);
sF32 h = (height[0]-16384.0f) * (1.0f/32767.0f);
vert->Pos.x += vert->Normal.x*h*ampli.x;
vert->Pos.y += vert->Normal.y*h*ampli.y;
vert->Pos.z += vert->Normal.z*h*ampli.z;
}
}
mesh->ChangeGeo();
bmp->Release();
return mesh;
}
GenMinMesh * __stdcall MinMesh_Perlin(GenMinMesh *mesh,sInt mask,sFSRT srt,sF323 ampl)
{
if(CheckMinMesh(mesh)) return 0;
sMatrix mat; mat.InitSRT(srt.v);
sVector amp; amp.Init(ampl.x,ampl.y,ampl.z,0);
sInt oldcw;
// setup fpu: single precision, round towards neg. infinity
__asm
{
fstcw [oldcw];
push 0143fh;
fldcw [esp];
pop eax;
}
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
GenMinVert *vert = &mesh->Vertices[i];
if(mask==0 || (mask==1&&vert->Select) || (mask==2&&!vert->Select))
{
sVector t0,t1,t2;
sVector pos;
sF32 fs[3];
sInt is[3];
// rotate input vector, calc sampling points
vert->Pos.Out(pos,1);
t0.Rotate34(mat,pos);
for(sInt j=0;j<3;j++)
{
is[j] = sFtol(t0[j]); // integer coordinate
fs[j] = t0[j] - is[j]; // fractional part
is[j] &= 255; // integer grid wraps round 256
fs[j] = fs[j]*fs[j]*fs[j]*(10.0f+fs[j]*(6.0f*fs[j]-15.0f));
}
#define ix is[0]
#define iy is[1]
#define iz is[2]
#define P sPerlinPermute
#define G sPerlinGradient3D
// trilinear interpolation of grid points
t0.Lin3(G[P[P[P[ix]+iy]+iz]&15],G[P[P[P[ix+1]+iy]+iz]&15],fs[0]);
t1.Lin3(G[P[P[P[ix]+iy+1]+iz]&15],G[P[P[P[ix+1]+iy+1]+iz]&15],fs[0]);
t0.Lin3(t0,t1,fs[1]);
t1.Lin3(G[P[P[P[ix]+iy]+iz+1]&15],G[P[P[P[ix+1]+iy]+iz+1]&15],fs[0]);
t2.Lin3(G[P[P[P[ix]+iy+1]+iz+1]&15],G[P[P[P[ix+1]+iy+1]+iz+1]&15],fs[0]);
t1.Lin3(t1,t2,fs[1]);
t0.Lin3(t0,t1,fs[2]);
#undef ix
#undef iy
#undef iz
#undef P
#undef G
vert->Pos.x += t0.x*amp.x;
vert->Pos.y += t0.y*amp.y;
vert->Pos.z += t0.z*amp.z;
}
}
// restore fpu state
__asm
{
fldcw [oldcw];
}
mesh->ChangeGeo();
return mesh;
}
GenMinMesh * __stdcall MinMesh_ExtrudeNormal(GenMinMesh *mesh,sInt mask,sF32 distance)
{
if(CheckMinMesh(mesh)) return 0;
mesh->CalcNormals();
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
GenMinVert *vert = &mesh->Vertices[i];
if(mask==0 || (mask==1&&vert->Select) || (mask==2&&!vert->Select))
vert->Pos.AddScale(vert->Normal,distance);
}
mesh->ChangeGeo();
return mesh;
}
GenMinMesh * __stdcall MinMesh_Bend2(GenMinMesh *mesh,sF323 center,sF323 rotate,sF32 len,sF32 angle)
{
sMatrix mt,mb;
sVector vt;
sF32 vx,vy,t,sa,ca;
sInt i;
if(CheckMinMesh(mesh)) return 0;
mt.InitEulerPI2(&rotate.x);
mt.l.x = -center.x;
mt.l.y = -center.y;
mt.l.z = -center.z;
mb = mt;
mb.TransR();
angle *= sPI2F;
for(i=0;i<mesh->Vertices.Count;i++)
{
GenMinVert *vert = &mesh->Vertices[i];
sVector pos;vert->Pos.Out(pos,1);
vt.Rotate34(mt,pos);
t = vt.y;
if(t>=0.0f)
vt.y -= sMin(t,len);
t = sRange(t/len,1.0f,0.0f) * angle;
sa = sFSin(t);
ca = sFCos(t);
vx = vt.x;
vy = vt.y;
vt.x = ca * vx - sa * vy;
vt.y = sa * vx + ca * vy;
vt.Rotate34(mb);
vert->Pos.Init(vt);
}
mesh->ChangeGeo();
return mesh;
}
GenMinMesh * __stdcall MinMesh_BakeAnimation(GenMinMesh *mesh,sF32 fade,sF32 metamorph)
{
if(CheckMinMesh(mesh)) return 0;
mesh->BakeAnimation(fade,metamorph);
return mesh;
}
GenMinMesh * __stdcall MinMesh_BoneChain(GenMinMesh *mesh,sF323 p0f,sF323 p1f,sInt count,sInt flags)
{
GenMinVert *vp;
GenMinCluster *cl;
sVector p0,p1,dir,diff;
sAABox box;
if(CheckMinMesh(mesh)) return 0;
for(sInt i=1;i<mesh->Clusters.Count;i++)
{
cl = &mesh->Clusters[i];
cl->AnimType = 2;
}
if(flags & 1)
{
mesh->CalcBBox(box);
p0.Init(0,0,box.Min.z);
p1.Init(0,0,box.Max.z);
}
else
{
p0.Init(p0f.x,p0f.y,p0f.z,1);
p1.Init(p1f.x,p1f.y,p1f.z,1);
}
dir.Sub4(p1,p0);
dir.Scale3(1.0f/dir.Dot3(dir));
mesh->CreateAnimation(count);
for(sInt i=0;i<count;i++)
{
mesh->Animation->Matrices[i].Temp.Init();
mesh->Animation->Matrices[i].Temp.l.Lin4(p0,p1,(1.0f*i)/(count-1));
mesh->Animation->Matrices[i].BasePose.Invert(mesh->Animation->Matrices[i].Temp);
mesh->Animation->Matrices[i].NoAnimation = mesh->Animation->Matrices[i].Temp;
}
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
vp = &mesh->Vertices[i];
diff.Init(vp->Pos.x-p0.x,vp->Pos.x-p0.y,vp->Pos.z-p0.z,0);
sF32 fade = dir.Dot3(diff)*(count-1);
sInt fi = sRange(sInt(fade*1024),count*1024-1,0);
sInt index = fi/1024;
fade = (fi&1023)/1024.0f;
if(index<0)
{
vp->BoneCount = 1;
vp->Matrix[0] = 0;
vp->Weights[0] = 1.0f;
}
else if(index+1<count)
{
vp->BoneCount = 4;
vp->Matrix[0] = sMax(0,index-1);
vp->Matrix[1] = index;
vp->Matrix[2] = sMin(count-1,index+1);
vp->Matrix[3] = sMin(count-1,index+2);
sF32 f1 = fade;
sF32 f2 = fade*fade;
sF32 f3 = fade*fade*fade;
vp->Weights[0] = -0.5f*f3 +1.0f*f2 -0.5f*f1 ;
vp->Weights[1] = 1.5f*f3 -2.5f*f2 +1;
vp->Weights[2] = -1.5f*f3 +2.0f*f2 +0.5f*f1 ;
vp->Weights[3] = 0.5f*f3 -0.5f*f2 ;
}
else
{
vp->BoneCount = 1;
vp->Matrix[0] = count-1;
vp->Weights[0] = 1.0f;
}
}
return mesh;
}
GenMinMesh * __stdcall MinMesh_BoneTrain(GenMinMesh *mesh,GenSpline *spline,sF32 delta,sInt mode,sF32 offset)
{
if(CheckMinMesh(mesh)) { sRelease(spline); return 0; }
if(!spline) return mesh;
if(mesh->Animation)
{
for(sInt i=0;i<mesh->Animation->Matrices.Count;i++)
{
GenMinMatrix *mat = &mesh->Animation->Matrices[i];
sF32 time = i*delta/(mesh->Animation->Matrices.Count-1)+offset;
sRelease(mat->Spline);
mat->Spline = spline;
mat->Spline->AddRef();
switch(mode&3)
{
case 0:
mat->Offset = time;
mat->Factor = 1;
mat->Spread = 0;
break;
case 1:
mat->Offset = 0;
mat->Factor = time;
mat->Spread = 0;
break;
case 2:
mat->Offset = 0;
mat->Factor = 0;
mat->Spread = 1;
break;
}
}
}
sRelease(spline);
return mesh;
}
GenMinMesh * __stdcall MinMesh_ScaleAnim(GenMinMesh *mesh,sF32 scale)
{
if(CheckMinMesh(mesh)) return 0;
if(mesh->Animation)
{
for(sInt i=0;i<mesh->Animation->Matrices.Count;i++)
{
GenMinMatrix *mm = &mesh->Animation->Matrices[i];
mm->BasePose.l.Scale3(scale);
mm->NoAnimation.l.Scale3(scale);
if(mm->Spline)
{
BlobSpline *bs = mm->Spline->GetBlobSpline();
for(sInt i=0;i<bs->Count;i++)
{
bs->Keys[i].px *= scale;
bs->Keys[i].py *= scale;
bs->Keys[i].pz *= scale;
}
}
mm->SRT[6] *= scale;
mm->SRT[7] *= scale;
mm->SRT[8] *= scale;
if(mm->TPtr)
{
for(sInt i=0;i<mm->KeyCount*3;i++)
mm->TPtr[i] *= scale;
}
}
}
sMatrix m;
m.Init();
m.i.x = scale;
m.j.y = scale;
m.k.z = scale;
mesh->Transform(MMU_ALL,m);
return mesh;
}
/****************************************************************************/
/*** ***/
/*** Marching Tetrahedra ***/
/*** ***/
/****************************************************************************/
#if !sINTRO
enum
{
MTShift = 4,
MTGrid = 1<<MTShift,
MTMask = MTGrid-1,
MTYStep = MTGrid,
MTZStep = MTYStep*MTGrid,
MTMaxVert = 65535,
// this is the biggest you can make the grid without changing data types!
};
static sInt MTHash[1024];
static sInt MTData[MTMaxVert*2];
static sF32 MTDensity[MTGrid*MTGrid*MTGrid];
// marching cubes tables coming up!
static const sU16 MTEdgeMaskTable[256]=
{
0x000,0x109,0x203,0x30a,0x406,0x50f,0x605,0x70c,0x80c,0x905,0xa0f,0xb06,0xc0a,0xd03,0xe09,0xf00,
0x190,0x099,0x393,0x29a,0x596,0x49f,0x795,0x69c,0x99c,0x895,0xb9f,0xa96,0xd9a,0xc93,0xf99,0xe90,
0x230,0x339,0x033,0x13a,0x636,0x73f,0x435,0x53c,0xa3c,0xb35,0x83f,0x936,0xe3a,0xf33,0xc39,0xd30,
0x3a0,0x2a9,0x1a3,0x0aa,0x7a6,0x6af,0x5a5,0x4ac,0xbac,0xaa5,0x9af,0x8a6,0xfaa,0xea3,0xda9,0xca0,
0x460,0x569,0x663,0x76a,0x066,0x16f,0x265,0x36c,0xc6c,0xd65,0xe6f,0xf66,0x86a,0x963,0xa69,0xb60,
0x5f0,0x4f9,0x7f3,0x6fa,0x1f6,0x0ff,0x3f5,0x2fc,0xdfc,0xcf5,0xfff,0xef6,0x9fa,0x8f3,0xbf9,0xaf0,
0x650,0x759,0x453,0x55a,0x256,0x35f,0x055,0x15c,0xe5c,0xf55,0xc5f,0xd56,0xa5a,0xb53,0x859,0x950,
0x7c0,0x6c9,0x5c3,0x4ca,0x3c6,0x2cf,0x1c5,0x0cc,0xfcc,0xec5,0xdcf,0xcc6,0xbca,0xac3,0x9c9,0x8c0,
0x8c0,0x9c9,0xac3,0xbca,0xcc6,0xdcf,0xec5,0xfcc,0x0cc,0x1c5,0x2cf,0x3c6,0x4ca,0x5c3,0x6c9,0x7c0,
0x950,0x859,0xb53,0xa5a,0xd56,0xc5f,0xf55,0xe5c,0x15c,0x055,0x35f,0x256,0x55a,0x453,0x759,0x650,
0xaf0,0xbf9,0x8f3,0x9fa,0xef6,0xfff,0xcf5,0xdfc,0x2fc,0x3f5,0x0ff,0x1f6,0x6fa,0x7f3,0x4f9,0x5f0,
0xb60,0xa69,0x963,0x86a,0xf66,0xe6f,0xd65,0xc6c,0x36c,0x265,0x16f,0x066,0x76a,0x663,0x569,0x460,
0xca0,0xda9,0xea3,0xfaa,0x8a6,0x9af,0xaa5,0xbac,0x4ac,0x5a5,0x6af,0x7a6,0x0aa,0x1a3,0x2a9,0x3a0,
0xd30,0xc39,0xf33,0xe3a,0x936,0x83f,0xb35,0xa3c,0x53c,0x435,0x73f,0x636,0x13a,0x033,0x339,0x230,
0xe90,0xf99,0xc93,0xd9a,0xa96,0xb9f,0x895,0x99c,0x69c,0x795,0x49f,0x596,0x29a,0x393,0x099,0x190,
0xf00,0xe09,0xd03,0xc0a,0xb06,0xa0f,0x905,0x80c,0x70c,0x605,0x50f,0x406,0x30a,0x203,0x109,0x000
};
static const sU8 MTTriTable[256][16] =
{
{12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 0, 8, 3,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 0, 1, 9,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 1, 8, 3, 9, 8, 1,12,12,12,12,12,12,12,12,12,12},
{ 1, 2,10,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 0, 8, 3, 1, 2,10,12,12,12,12,12,12,12,12,12,12},
{ 9, 2,10, 0, 2, 9,12,12,12,12,12,12,12,12,12,12},
{ 2, 8, 3, 2,10, 8,10, 9, 8,12,12,12,12,12,12,12},
{ 3,11, 2,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 0,11, 2, 8,11, 0,12,12,12,12,12,12,12,12,12,12},
{ 1, 9, 0, 2, 3,11,12,12,12,12,12,12,12,12,12,12},
{ 1,11, 2, 1, 9,11, 9, 8,11,12,12,12,12,12,12,12},
{ 3,10, 1,11,10, 3,12,12,12,12,12,12,12,12,12,12},
{ 0,10, 1, 0, 8,10, 8,11,10,12,12,12,12,12,12,12},
{ 3, 9, 0, 3,11, 9,11,10, 9,12,12,12,12,12,12,12},
{ 9, 8,10,10, 8,11,12,12,12,12,12,12,12,12,12,12},
{ 4, 7, 8,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 4, 3, 0, 7, 3, 4,12,12,12,12,12,12,12,12,12,12},
{ 0, 1, 9, 8, 4, 7,12,12,12,12,12,12,12,12,12,12},
{ 4, 1, 9, 4, 7, 1, 7, 3, 1,12,12,12,12,12,12,12},
{ 1, 2,10, 8, 4, 7,12,12,12,12,12,12,12,12,12,12},
{ 3, 4, 7, 3, 0, 4, 1, 2,10,12,12,12,12,12,12,12},
{ 9, 2,10, 9, 0, 2, 8, 4, 7,12,12,12,12,12,12,12},
{ 2,10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4,12,12,12,12},
{ 8, 4, 7, 3,11, 2,12,12,12,12,12,12,12,12,12,12},
{11, 4, 7,11, 2, 4, 2, 0, 4,12,12,12,12,12,12,12},
{ 9, 0, 1, 8, 4, 7, 2, 3,11,12,12,12,12,12,12,12},
{ 4, 7,11, 9, 4,11, 9,11, 2, 9, 2, 1,12,12,12,12},
{ 3,10, 1, 3,11,10, 7, 8, 4,12,12,12,12,12,12,12},
{ 1,11,10, 1, 4,11, 1, 0, 4, 7,11, 4,12,12,12,12},
{ 4, 7, 8, 9, 0,11, 9,11,10,11, 0, 3,12,12,12,12},
{ 4, 7,11, 4,11, 9, 9,11,10,12,12,12,12,12,12,12},
{ 9, 5, 4,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 9, 5, 4, 0, 8, 3,12,12,12,12,12,12,12,12,12,12},
{ 0, 5, 4, 1, 5, 0,12,12,12,12,12,12,12,12,12,12},
{ 8, 5, 4, 8, 3, 5, 3, 1, 5,12,12,12,12,12,12,12},
{ 1, 2,10, 9, 5, 4,12,12,12,12,12,12,12,12,12,12},
{ 3, 0, 8, 1, 2,10, 4, 9, 5,12,12,12,12,12,12,12},
{ 5, 2,10, 5, 4, 2, 4, 0, 2,12,12,12,12,12,12,12},
{ 2,10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8,12,12,12,12},
{ 9, 5, 4, 2, 3,11,12,12,12,12,12,12,12,12,12,12},
{ 0,11, 2, 0, 8,11, 4, 9, 5,12,12,12,12,12,12,12},
{ 0, 5, 4, 0, 1, 5, 2, 3,11,12,12,12,12,12,12,12},
{ 2, 1, 5, 2, 5, 8, 2, 8,11, 4, 8, 5,12,12,12,12},
{10, 3,11,10, 1, 3, 9, 5, 4,12,12,12,12,12,12,12},
{ 4, 9, 5, 0, 8, 1, 8,10, 1, 8,11,10,12,12,12,12},
{ 5, 4, 0, 5, 0,11, 5,11,10,11, 0, 3,12,12,12,12},
{ 5, 4, 8, 5, 8,10,10, 8,11,12,12,12,12,12,12,12},
{ 9, 7, 8, 5, 7, 9,12,12,12,12,12,12,12,12,12,12},
{ 9, 3, 0, 9, 5, 3, 5, 7, 3,12,12,12,12,12,12,12},
{ 0, 7, 8, 0, 1, 7, 1, 5, 7,12,12,12,12,12,12,12},
{ 1, 5, 3, 3, 5, 7,12,12,12,12,12,12,12,12,12,12},
{ 9, 7, 8, 9, 5, 7,10, 1, 2,12,12,12,12,12,12,12},
{10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3,12,12,12,12},
{ 8, 0, 2, 8, 2, 5, 8, 5, 7,10, 5, 2,12,12,12,12},
{ 2,10, 5, 2, 5, 3, 3, 5, 7,12,12,12,12,12,12,12},
{ 7, 9, 5, 7, 8, 9, 3,11, 2,12,12,12,12,12,12,12},
{ 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7,11,12,12,12,12},
{ 2, 3,11, 0, 1, 8, 1, 7, 8, 1, 5, 7,12,12,12,12},
{11, 2, 1,11, 1, 7, 7, 1, 5,12,12,12,12,12,12,12},
{ 9, 5, 8, 8, 5, 7,10, 1, 3,10, 3,11,12,12,12,12},
{ 5, 7, 0, 5, 0, 9, 7,11, 0, 1, 0,10,11,10, 0,12},
{11,10, 0,11, 0, 3,10, 5, 0, 8, 0, 7, 5, 7, 0,12},
{11,10, 5, 7,11, 5,12,12,12,12,12,12,12,12,12,12},
{10, 6, 5,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 0, 8, 3, 5,10, 6,12,12,12,12,12,12,12,12,12,12},
{ 9, 0, 1, 5,10, 6,12,12,12,12,12,12,12,12,12,12},
{ 1, 8, 3, 1, 9, 8, 5,10, 6,12,12,12,12,12,12,12},
{ 1, 6, 5, 2, 6, 1,12,12,12,12,12,12,12,12,12,12},
{ 1, 6, 5, 1, 2, 6, 3, 0, 8,12,12,12,12,12,12,12},
{ 9, 6, 5, 9, 0, 6, 0, 2, 6,12,12,12,12,12,12,12},
{ 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8,12,12,12,12},
{ 2, 3,11,10, 6, 5,12,12,12,12,12,12,12,12,12,12},
{11, 0, 8,11, 2, 0,10, 6, 5,12,12,12,12,12,12,12},
{ 0, 1, 9, 2, 3,11, 5,10, 6,12,12,12,12,12,12,12},
{ 5,10, 6, 1, 9, 2, 9,11, 2, 9, 8,11,12,12,12,12},
{ 6, 3,11, 6, 5, 3, 5, 1, 3,12,12,12,12,12,12,12},
{ 0, 8,11, 0,11, 5, 0, 5, 1, 5,11, 6,12,12,12,12},
{ 3,11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9,12,12,12,12},
{ 6, 5, 9, 6, 9,11,11, 9, 8,12,12,12,12,12,12,12},
{ 5,10, 6, 4, 7, 8,12,12,12,12,12,12,12,12,12,12},
{ 4, 3, 0, 4, 7, 3, 6, 5,10,12,12,12,12,12,12,12},
{ 1, 9, 0, 5,10, 6, 8, 4, 7,12,12,12,12,12,12,12},
{10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4,12,12,12,12},
{ 6, 1, 2, 6, 5, 1, 4, 7, 8,12,12,12,12,12,12,12},
{ 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7,12,12,12,12},
{ 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6,12,12,12,12},
{ 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9,12},
{ 3,11, 2, 7, 8, 4,10, 6, 5,12,12,12,12,12,12,12},
{ 5,10, 6, 4, 7, 2, 4, 2, 0, 2, 7,11,12,12,12,12},
{ 0, 1, 9, 4, 7, 8, 2, 3,11, 5,10, 6,12,12,12,12},
{ 9, 2, 1, 9,11, 2, 9, 4,11, 7,11, 4, 5,10, 6,12},
{ 8, 4, 7, 3,11, 5, 3, 5, 1, 5,11, 6,12,12,12,12},
{ 5, 1,11, 5,11, 6, 1, 0,11, 7,11, 4, 0, 4,11,12},
{ 0, 5, 9, 0, 6, 5, 0, 3, 6,11, 6, 3, 8, 4, 7,12},
{ 6, 5, 9, 6, 9,11, 4, 7, 9, 7,11, 9,12,12,12,12},
{10, 4, 9, 6, 4,10,12,12,12,12,12,12,12,12,12,12},
{ 4,10, 6, 4, 9,10, 0, 8, 3,12,12,12,12,12,12,12},
{10, 0, 1,10, 6, 0, 6, 4, 0,12,12,12,12,12,12,12},
{ 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1,10,12,12,12,12},
{ 1, 4, 9, 1, 2, 4, 2, 6, 4,12,12,12,12,12,12,12},
{ 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4,12,12,12,12},
{ 0, 2, 4, 4, 2, 6,12,12,12,12,12,12,12,12,12,12},
{ 8, 3, 2, 8, 2, 4, 4, 2, 6,12,12,12,12,12,12,12},
{10, 4, 9,10, 6, 4,11, 2, 3,12,12,12,12,12,12,12},
{ 0, 8, 2, 2, 8,11, 4, 9,10, 4,10, 6,12,12,12,12},
{ 3,11, 2, 0, 1, 6, 0, 6, 4, 6, 1,10,12,12,12,12},
{ 6, 4, 1, 6, 1,10, 4, 8, 1, 2, 1,11, 8,11, 1,12},
{ 9, 6, 4, 9, 3, 6, 9, 1, 3,11, 6, 3,12,12,12,12},
{ 8,11, 1, 8, 1, 0,11, 6, 1, 9, 1, 4, 6, 4, 1,12},
{ 3,11, 6, 3, 6, 0, 0, 6, 4,12,12,12,12,12,12,12},
{ 6, 4, 8,11, 6, 8,12,12,12,12,12,12,12,12,12,12},
{ 7,10, 6, 7, 8,10, 8, 9,10,12,12,12,12,12,12,12},
{ 0, 7, 3, 0,10, 7, 0, 9,10, 6, 7,10,12,12,12,12},
{10, 6, 7, 1,10, 7, 1, 7, 8, 1, 8, 0,12,12,12,12},
{10, 6, 7,10, 7, 1, 1, 7, 3,12,12,12,12,12,12,12},
{ 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7,12,12,12,12},
{ 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9,12},
{ 7, 8, 0, 7, 0, 6, 6, 0, 2,12,12,12,12,12,12,12},
{ 7, 3, 2, 6, 7, 2,12,12,12,12,12,12,12,12,12,12},
{ 2, 3,11,10, 6, 8,10, 8, 9, 8, 6, 7,12,12,12,12},
{ 2, 0, 7, 2, 7,11, 0, 9, 7, 6, 7,10, 9,10, 7,12},
{ 1, 8, 0, 1, 7, 8, 1,10, 7, 6, 7,10, 2, 3,11,12},
{11, 2, 1,11, 1, 7,10, 6, 1, 6, 7, 1,12,12,12,12},
{ 8, 9, 6, 8, 6, 7, 9, 1, 6,11, 6, 3, 1, 3, 6,12},
{ 0, 9, 1,11, 6, 7,12,12,12,12,12,12,12,12,12,12},
{ 7, 8, 0, 7, 0, 6, 3,11, 0,11, 6, 0,12,12,12,12},
{ 7,11, 6,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 7, 6,11,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 3, 0, 8,11, 7, 6,12,12,12,12,12,12,12,12,12,12},
{ 0, 1, 9,11, 7, 6,12,12,12,12,12,12,12,12,12,12},
{ 8, 1, 9, 8, 3, 1,11, 7, 6,12,12,12,12,12,12,12},
{10, 1, 2, 6,11, 7,12,12,12,12,12,12,12,12,12,12},
{ 1, 2,10, 3, 0, 8, 6,11, 7,12,12,12,12,12,12,12},
{ 2, 9, 0, 2,10, 9, 6,11, 7,12,12,12,12,12,12,12},
{ 6,11, 7, 2,10, 3,10, 8, 3,10, 9, 8,12,12,12,12},
{ 7, 2, 3, 6, 2, 7,12,12,12,12,12,12,12,12,12,12},
{ 7, 0, 8, 7, 6, 0, 6, 2, 0,12,12,12,12,12,12,12},
{ 2, 7, 6, 2, 3, 7, 0, 1, 9,12,12,12,12,12,12,12},
{ 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6,12,12,12,12},
{10, 7, 6,10, 1, 7, 1, 3, 7,12,12,12,12,12,12,12},
{10, 7, 6, 1, 7,10, 1, 8, 7, 1, 0, 8,12,12,12,12},
{ 0, 3, 7, 0, 7,10, 0,10, 9, 6,10, 7,12,12,12,12},
{ 7, 6,10, 7,10, 8, 8,10, 9,12,12,12,12,12,12,12},
{ 6, 8, 4,11, 8, 6,12,12,12,12,12,12,12,12,12,12},
{ 3, 6,11, 3, 0, 6, 0, 4, 6,12,12,12,12,12,12,12},
{ 8, 6,11, 8, 4, 6, 9, 0, 1,12,12,12,12,12,12,12},
{ 9, 4, 6, 9, 6, 3, 9, 3, 1,11, 3, 6,12,12,12,12},
{ 6, 8, 4, 6,11, 8, 2,10, 1,12,12,12,12,12,12,12},
{ 1, 2,10, 3, 0,11, 0, 6,11, 0, 4, 6,12,12,12,12},
{ 4,11, 8, 4, 6,11, 0, 2, 9, 2,10, 9,12,12,12,12},
{10, 9, 3,10, 3, 2, 9, 4, 3,11, 3, 6, 4, 6, 3,12},
{ 8, 2, 3, 8, 4, 2, 4, 6, 2,12,12,12,12,12,12,12},
{ 0, 4, 2, 4, 6, 2,12,12,12,12,12,12,12,12,12,12},
{ 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8,12,12,12,12},
{ 1, 9, 4, 1, 4, 2, 2, 4, 6,12,12,12,12,12,12,12},
{ 8, 1, 3, 8, 6, 1, 8, 4, 6, 6,10, 1,12,12,12,12},
{10, 1, 0,10, 0, 6, 6, 0, 4,12,12,12,12,12,12,12},
{ 4, 6, 3, 4, 3, 8, 6,10, 3, 0, 3, 9,10, 9, 3,12},
{10, 9, 4, 6,10, 4,12,12,12,12,12,12,12,12,12,12},
{ 4, 9, 5, 7, 6,11,12,12,12,12,12,12,12,12,12,12},
{ 0, 8, 3, 4, 9, 5,11, 7, 6,12,12,12,12,12,12,12},
{ 5, 0, 1, 5, 4, 0, 7, 6,11,12,12,12,12,12,12,12},
{11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5,12,12,12,12},
{ 9, 5, 4,10, 1, 2, 7, 6,11,12,12,12,12,12,12,12},
{ 6,11, 7, 1, 2,10, 0, 8, 3, 4, 9, 5,12,12,12,12},
{ 7, 6,11, 5, 4,10, 4, 2,10, 4, 0, 2,12,12,12,12},
{ 3, 4, 8, 3, 5, 4, 3, 2, 5,10, 5, 2,11, 7, 6,12},
{ 7, 2, 3, 7, 6, 2, 5, 4, 9,12,12,12,12,12,12,12},
{ 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7,12,12,12,12},
{ 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0,12,12,12,12},
{ 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8,12},
{ 9, 5, 4,10, 1, 6, 1, 7, 6, 1, 3, 7,12,12,12,12},
{ 1, 6,10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4,12},
{ 4, 0,10, 4,10, 5, 0, 3,10, 6,10, 7, 3, 7,10,12},
{ 7, 6,10, 7,10, 8, 5, 4,10, 4, 8,10,12,12,12,12},
{ 6, 9, 5, 6,11, 9,11, 8, 9,12,12,12,12,12,12,12},
{ 3, 6,11, 0, 6, 3, 0, 5, 6, 0, 9, 5,12,12,12,12},
{ 0,11, 8, 0, 5,11, 0, 1, 5, 5, 6,11,12,12,12,12},
{ 6,11, 3, 6, 3, 5, 5, 3, 1,12,12,12,12,12,12,12},
{ 1, 2,10, 9, 5,11, 9,11, 8,11, 5, 6,12,12,12,12},
{ 0,11, 3, 0, 6,11, 0, 9, 6, 5, 6, 9, 1, 2,10,12},
{11, 8, 5,11, 5, 6, 8, 0, 5,10, 5, 2, 0, 2, 5,12},
{ 6,11, 3, 6, 3, 5, 2,10, 3,10, 5, 3,12,12,12,12},
{ 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2,12,12,12,12},
{ 9, 5, 6, 9, 6, 0, 0, 6, 2,12,12,12,12,12,12,12},
{ 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8,12},
{ 1, 5, 6, 2, 1, 6,12,12,12,12,12,12,12,12,12,12},
{ 1, 3, 6, 1, 6,10, 3, 8, 6, 5, 6, 9, 8, 9, 6,12},
{10, 1, 0,10, 0, 6, 9, 5, 0, 5, 6, 0,12,12,12,12},
{ 0, 3, 8, 5, 6,10,12,12,12,12,12,12,12,12,12,12},
{10, 5, 6,12,12,12,12,12,12,12,12,12,12,12,12,12},
{11, 5,10, 7, 5,11,12,12,12,12,12,12,12,12,12,12},
{11, 5,10,11, 7, 5, 8, 3, 0,12,12,12,12,12,12,12},
{ 5,11, 7, 5,10,11, 1, 9, 0,12,12,12,12,12,12,12},
{10, 7, 5,10,11, 7, 9, 8, 1, 8, 3, 1,12,12,12,12},
{11, 1, 2,11, 7, 1, 7, 5, 1,12,12,12,12,12,12,12},
{ 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2,11,12,12,12,12},
{ 9, 7, 5, 9, 2, 7, 9, 0, 2, 2,11, 7,12,12,12,12},
{ 7, 5, 2, 7, 2,11, 5, 9, 2, 3, 2, 8, 9, 8, 2,12},
{ 2, 5,10, 2, 3, 5, 3, 7, 5,12,12,12,12,12,12,12},
{ 8, 2, 0, 8, 5, 2, 8, 7, 5,10, 2, 5,12,12,12,12},
{ 9, 0, 1, 5,10, 3, 5, 3, 7, 3,10, 2,12,12,12,12},
{ 9, 8, 2, 9, 2, 1, 8, 7, 2,10, 2, 5, 7, 5, 2,12},
{ 1, 3, 5, 3, 7, 5,12,12,12,12,12,12,12,12,12,12},
{ 0, 8, 7, 0, 7, 1, 1, 7, 5,12,12,12,12,12,12,12},
{ 9, 0, 3, 9, 3, 5, 5, 3, 7,12,12,12,12,12,12,12},
{ 9, 8, 7, 5, 9, 7,12,12,12,12,12,12,12,12,12,12},
{ 5, 8, 4, 5,10, 8,10,11, 8,12,12,12,12,12,12,12},
{ 5, 0, 4, 5,11, 0, 5,10,11,11, 3, 0,12,12,12,12},
{ 0, 1, 9, 8, 4,10, 8,10,11,10, 4, 5,12,12,12,12},
{10,11, 4,10, 4, 5,11, 3, 4, 9, 4, 1, 3, 1, 4,12},
{ 2, 5, 1, 2, 8, 5, 2,11, 8, 4, 5, 8,12,12,12,12},
{ 0, 4,11, 0,11, 3, 4, 5,11, 2,11, 1, 5, 1,11,12},
{ 0, 2, 5, 0, 5, 9, 2,11, 5, 4, 5, 8,11, 8, 5,12},
{ 9, 4, 5, 2,11, 3,12,12,12,12,12,12,12,12,12,12},
{ 2, 5,10, 3, 5, 2, 3, 4, 5, 3, 8, 4,12,12,12,12},
{ 5,10, 2, 5, 2, 4, 4, 2, 0,12,12,12,12,12,12,12},
{ 3,10, 2, 3, 5,10, 3, 8, 5, 4, 5, 8, 0, 1, 9,12},
{ 5,10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2,12,12,12,12},
{ 8, 4, 5, 8, 5, 3, 3, 5, 1,12,12,12,12,12,12,12},
{ 0, 4, 5, 1, 0, 5,12,12,12,12,12,12,12,12,12,12},
{ 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5,12,12,12,12},
{ 9, 4, 5,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 4,11, 7, 4, 9,11, 9,10,11,12,12,12,12,12,12,12},
{ 0, 8, 3, 4, 9, 7, 9,11, 7, 9,10,11,12,12,12,12},
{ 1,10,11, 1,11, 4, 1, 4, 0, 7, 4,11,12,12,12,12},
{ 3, 1, 4, 3, 4, 8, 1,10, 4, 7, 4,11,10,11, 4,12},
{ 4,11, 7, 9,11, 4, 9, 2,11, 9, 1, 2,12,12,12,12},
{ 9, 7, 4, 9,11, 7, 9, 1,11, 2,11, 1, 0, 8, 3,12},
{11, 7, 4,11, 4, 2, 2, 4, 0,12,12,12,12,12,12,12},
{11, 7, 4,11, 4, 2, 8, 3, 4, 3, 2, 4,12,12,12,12},
{ 2, 9,10, 2, 7, 9, 2, 3, 7, 7, 4, 9,12,12,12,12},
{ 9,10, 7, 9, 7, 4,10, 2, 7, 8, 7, 0, 2, 0, 7,12},
{ 3, 7,10, 3,10, 2, 7, 4,10, 1,10, 0, 4, 0,10,12},
{ 1,10, 2, 8, 7, 4,12,12,12,12,12,12,12,12,12,12},
{ 4, 9, 1, 4, 1, 7, 7, 1, 3,12,12,12,12,12,12,12},
{ 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1,12,12,12,12},
{ 4, 0, 3, 7, 4, 3,12,12,12,12,12,12,12,12,12,12},
{ 4, 8, 7,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 9,10, 8,10,11, 8,12,12,12,12,12,12,12,12,12,12},
{ 3, 0, 9, 3, 9,11,11, 9,10,12,12,12,12,12,12,12},
{ 0, 1,10, 0,10, 8, 8,10,11,12,12,12,12,12,12,12},
{ 3, 1,10,11, 3,10,12,12,12,12,12,12,12,12,12,12},
{ 1, 2,11, 1,11, 9, 9,11, 8,12,12,12,12,12,12,12},
{ 3, 0, 9, 3, 9,11, 1, 2, 9, 2,11, 9,12,12,12,12},
{ 0, 2,11, 8, 0,11,12,12,12,12,12,12,12,12,12,12},
{ 3, 2,11,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 2, 3, 8, 2, 8,10,10, 8, 9,12,12,12,12,12,12,12},
{ 9,10, 2, 0, 9, 2,12,12,12,12,12,12,12,12,12,12},
{ 2, 3, 8, 2, 8,10, 0, 1, 8, 1,10, 8,12,12,12,12},
{ 1,10, 2,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 1, 3, 8, 9, 1, 8,12,12,12,12,12,12,12,12,12,12},
{ 0, 9, 1,12,12,12,12,12,12,12,12,12,12,12,12,12},
{ 0, 3, 8,12,12,12,12,12,12,12,12,12,12,12,12,12},
{12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12}
};
static void MTProcessCell(GenMinMesh *mesh,sInt pos,sF32 isoValue)
{
enum { X = 1, Y = MTYStep, Z = MTZStep };
static const sInt Step[8] = {
0+0+0,0+0+X,0+Y+X,0+Y+0,Z+0+0,Z+0+X,Z+Y+X,Z+Y+0
};
static const sInt EdgeOffs[2][12] = {
{ 0+0+0,0+0+X,0+Y+X,0+Y+0,Z+0+0,Z+0+X,Z+Y+X,Z+Y+0,0+0+0,0+0+X,0+Y+X,0+Y+0 },
{ 0+0+X,0+Y+X,0+Y+0,0+0+0,Z+0+X,Z+Y+X,Z+Y+0,Z+0+0,Z+0+0,Z+0+X,Z+Y+X,Z+Y+0 }
};
static const sF32 EdgeStartC[3][12] = {
{ 0,1,1,0,0,1,1,0,0,1,1,0 },
{ 0,0,1,1,0,0,1,1,0,0,1,1 },
{ 0,0,0,0,1,1,1,1,0,0,0,0 }
};
static const sF32 EdgeDirC[3][12] = {
{ 1, 0,-1, 0, 1, 0,-1, 0, 0, 0, 0, 0 },
{ 0, 1, 0,-1, 0, 1, 0,-1, 0, 0, 0, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }
};
static const sInt EdgeCode[12] = {
(0+0+0)*4+0,(0+0+X)*4+1,(0+Y+0)*4+0,(0+0+0)*4+1,
(Z+0+0)*4+0,(Z+0+X)*4+1,(Z+Y+0)*4+0,(Z+0+0)*4+1,
(0+0+0)*4+2,(0+0+X)*4+2,(0+Y+X)*4+2,(0+Y+0)*4+2
};
// calc vertex flags
sInt mask = 0;
for(sInt i=0,tmask=1;i<8;i++,tmask+=tmask)
mask |= MTDensity[pos + Step[i]] < isoValue ? tmask : 0;
// get edge flags
sInt edgeMask = MTEdgeMaskTable[mask];
if(!edgeMask)
return;
// calc actual position and lookup base
sInt lookupBase = pos << 2;
sInt posx = pos & MTMask;
sInt posy = (pos >> MTShift) & MTMask;
sInt posz = (pos >> (2*MTShift)) & MTMask;
// calc edge vertices
sInt edgeVert[12];
for(sInt i=0,tmask=edgeMask;i<12;i++,tmask>>=1)
{
if((tmask & 1) == 0)
continue;
sInt lookupCode = lookupBase + EdgeCode[i];
sInt bucket = lookupCode & 1023;
sInt v = -1;
// lookup vertex in hash table
for(sInt h=MTHash[bucket];h!=-1;h=MTData[h*2+1])
{
if(MTData[h*2] == lookupCode)
{
v = h;
break;
}
}
// not found
if(v == -1)
{
// alloc new vertex
v = mesh->Vertices.Count++;
MTData[v*2+0] = lookupCode;
MTData[v*2+1] = MTHash[bucket];
MTHash[bucket] = v;
// calc intersection t
sF32 d0 = MTDensity[pos + EdgeOffs[0][i]];
sF32 d1 = MTDensity[pos + EdgeOffs[1][i]];
sF32 t = (isoValue - d0) / (d1 - d0);
sVERIFY(t >= 0.0f && t <= 1.0f);
// calc vertex position
GenMinVert *vp = &mesh->Vertices[v];
vp->Pos.x = posx + EdgeStartC[0][i] + t * EdgeDirC[0][i];
vp->Pos.y = posy + EdgeStartC[1][i] + t * EdgeDirC[1][i];
vp->Pos.z = posz + EdgeStartC[2][i] + t * EdgeDirC[2][i];
}
// store vertex index
edgeVert[i] = v;
}
// create tris
GenMinFace *face = &mesh->Faces[mesh->Faces.Count];
sInt ctr = 0;
for(const sU8 *tris=MTTriTable[mask];*tris != 12;tris++)
{
face->Vertices[ctr] = edgeVert[*tris];
if(++ctr == 3)
{
mesh->Faces.Count++;
face++;
ctr = 0;
}
}
}
GenMinMesh * __stdcall MinMesh_MTetra(sF32 isoValue)
{
// create mesh, set up vertices and faces as necessary
GenMinMesh *mesh = new GenMinMesh;
mesh->Vertices.AtLeast(MTMaxVert);
mesh->Faces.AtLeast(MTMaxVert*2);
sSetMem(&mesh->Vertices[0],0,sizeof(GenMinVert)*mesh->Vertices.Alloc);
for(sInt i=0;i<mesh->Faces.Alloc;i++)
{
GenMinFace *face = &mesh->Faces[i];
face->Select = 0;
face->Count = 3;
face->Cluster = 1;
face->Flags = 0;
}
// clear hash
sSetMem(MTHash,0xff,sizeof(MTHash));
// calc density field
for(sInt z=0;z<MTGrid;z++)
{
for(sInt y=0;y<MTGrid;y++)
{
for(sInt x=0;x<MTGrid;x++)
{
sF32 dx,dy,dz,rsq,dens;
dens = 0.0f;
dx = x - 8.0f;
dy = y - 8.0f;
dz = z - 8.0f;
rsq = dx*dx + dy*dy + dz*dz;
dens += 1.0f / rsq;
MTDensity[x + y*MTYStep + z*MTZStep] = dens;
}
}
}
// polygonise it!
for(sInt z=0;z<MTGrid-1;z++)
{
for(sInt y=0;y<MTGrid-1;y++)
{
for(sInt x=0;x<MTGrid-1;x++)
MTProcessCell(mesh,x + y*MTYStep + z*MTZStep,isoValue);
}
}
return mesh;
}
#endif
/****************************************************************************/
/*** ***/
/*** Topology Modifiers ***/
/*** ***/
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_Triangulate(GenMinMesh *mesh)
{
if(CheckMinMesh(mesh)) return 0;
// calculate new number of faces
sInt outFaceCount = 0;
for(sInt i=0;i<mesh->Faces.Count;i++)
{
if(mesh->Faces[i].Count <= 3)
outFaceCount++;
else
outFaceCount += mesh->Faces[i].Count - 2;
}
// make new face list
GenMinFace *outFaces = new GenMinFace[outFaceCount];
GenMinFace *outFace = outFaces;
for(sInt i=0;i<mesh->Faces.Count;i++)
{
GenMinFace *curFace = &mesh->Faces[i];
if(mesh->Faces[i].Count <= 3)
*outFace++ = *curFace;
else
{
for(sInt j=2;j<curFace->Count;j++)
{
*outFace = *curFace;
outFace->Count = 3;
outFace->Vertices[1] = curFace->Vertices[j-1];
outFace->Vertices[2] = curFace->Vertices[j];
outFace++;
}
}
}
// exchange face lists
delete[] mesh->Faces.Array;
mesh->Faces.Array = outFaces;
mesh->Faces.Alloc = mesh->Faces.Count = outFaceCount;
mesh->ChangeTopo();
return mesh;
}
static void __stdcall ExtrudeOnce(GenMinMesh *mesh,sInt *groups)
{
// initialize
sInt faceCount = mesh->Faces.Count;
sInt *vertRemap = new sInt[mesh->Vertices.Count];
for(sInt i=0;i<mesh->Vertices.Count;i++)
vertRemap[i] = i;
for(sInt faceInd=0;faceInd<faceCount;faceInd++)
{
GenMinFace *curFace = &mesh->Faces[faceInd];
if(!curFace->Select || curFace->Count <= 2)
continue;
// go through halfedges for this face
sInt count = curFace->Count;
sInt group = groups[faceInd];
for(sInt i=0;i<count;i++)
{
sInt adjacent = curFace->Adjacent[i] >> 3;
if(adjacent != -1 && groups[adjacent] == group) // not a boundary
continue;
// this edge is a boundary, so extrude a quad
sInt oldVert[2],newVert[2];
for(sInt j=0;j<2;j++)
{
sInt old = curFace->Vertices[(i + j) % count];
if(vertRemap[old] == old) // not repositioned yet
{
vertRemap[old] = mesh->Vertices.Count;
GenMinVert *nv = mesh->Vertices.Add();
*nv = mesh->Vertices[old];
}
oldVert[j] = old;
newVert[j] = vertRemap[old];
}
// add the new face
GenMinFace *newFace = mesh->Faces.Add();
curFace = &mesh->Faces[faceInd];
newFace->Select = 0;
newFace->Count = 4;
newFace->Cluster = curFace->Cluster;
newFace->Flags = curFace->Flags;
newFace->Vertices[0] = oldVert[0];
newFace->Vertices[1] = oldVert[1];
newFace->Vertices[2] = newVert[1];
newFace->Vertices[3] = newVert[0];
sSetMem(newFace->Adjacent,0xff,sizeof(newFace->Adjacent));
}
}
for(sInt faceInd=0;faceInd<faceCount;faceInd++)
{
GenMinFace *curFace = &mesh->Faces[faceInd];
if(!curFace->Select || curFace->Count <= 2)
continue;
for(sInt j=0;j<curFace->Count;j++)
curFace->Vertices[j] = vertRemap[curFace->Vertices[j]];
}
delete[] vertRemap;
}
GenMinMesh * __stdcall MinMesh_Extrude(GenMinMesh *mesh,sInt mode,sInt count,sF323 distance)
{
if(CheckMinMesh(mesh)) return 0;
mesh->CalcAdjacency();
mesh->CalcNormals();
// prepare
sF32 angThresh = 0.4995f; // cos(threshold angle)
sInt origFaceCount = mesh->Faces.Count;
sInt *faceGroup = new sInt[origFaceCount];
sInt *groupNext = new sInt[origFaceCount];
sInt displaceMode = mode & 3;
// build groups (depth search)
sSetMem(faceGroup,0xff,origFaceCount * sizeof(sInt));
sInt groupCount = 0;
for(sInt i=0;i<origFaceCount;i++)
{
GenMinFace *curFace = &mesh->Faces[i];
if(!curFace->Select || curFace->Count <= 2 || faceGroup[i] != -1)
continue;
sInt *faceStackPtr = groupNext;
*faceStackPtr++ = i;
while(faceStackPtr != groupNext)
{
sInt top = *--faceStackPtr;
faceGroup[top] = groupCount;
GenMinFace *face = &mesh->Faces[top];
for(sInt j=0;j<face->Count;j++)
{
sInt adjacent = face->Adjacent[j] >> 3;
GenMinFace *aface = &mesh->Faces[adjacent];
if(adjacent != -1 && faceGroup[adjacent] == -1 && aface->Select
&& face->Normal.Dot(aface->Normal) >= angThresh)
{
faceGroup[adjacent] = -2;
*faceStackPtr++ = adjacent;
}
}
}
groupCount++;
}
// build linked lists for groups
sInt *groupFirst = new sInt[groupCount];
sSetMem(groupFirst,0xff,sizeof(sInt) * groupCount);
for(sInt i=origFaceCount-1;i>=0;i--)
{
sInt grp = faceGroup[i];
if(grp >= 0)
{
groupNext[i] = groupFirst[grp];
groupFirst[grp] = i;
}
}
// iterate over extrusion steps
while(count--)
{
// perform extrusion
ExtrudeOnce(mesh,faceGroup);
for(sInt i=0;i<mesh->Vertices.Count;i++)
mesh->Vertices[i].Select = 0;
for(sInt grp=0;grp<groupCount;grp++)
{
// skip empty groups
sInt first = groupFirst[grp];
// vertex displacement: first compute group normal (if necessary)
GenMinVector avgDir;
if(displaceMode == 0) // average (face!) normal
{
avgDir.Init(0,0,0);
for(sInt face=first;face != -1;face = groupNext[face])
avgDir.Add(mesh->Faces[face].Normal);
avgDir.UnitSafe();
}
else
avgDir.Init(1,1,1);
// reposition vertices
for(sInt face=first;face != -1;face = groupNext[face])
{
GenMinFace *curFace = &mesh->Faces[face];
sVERIFY(curFace->Select);
for(sInt j=0;j<curFace->Count;j++)
{
GenMinVert *vert = &mesh->Vertices[curFace->Vertices[j]];
if(vert->Select)
continue;
const GenMinVector *disp = &avgDir;
if(displaceMode == 1) // individual normal
disp = &vert->Normal;
vert->Pos.x += disp->x * distance.x;
vert->Pos.y += disp->y * distance.y;
vert->Pos.z += disp->z * distance.z;
vert->Select = 1;
}
}
}
}
// swap stitches to end
sInt pos = mesh->Faces.Count;
for(sInt i=0;i<pos;i++)
if(mesh->Faces[i].Count == 2)
sSwap(mesh->Faces[i],mesh->Faces[--pos]);
// cleanup
delete[] faceGroup;
delete[] groupFirst;
delete[] groupNext;
mesh->ChangeTopo();
return mesh;
}
/****************************************************************************/
/****************************************************************************/
class GenKineticSpline : public GenSpline
{
public:
sVector Pos;
sVector Speed;
sVector Gravity;
sVector Rotation;
GenKineticSpline();
void Eval(sF32 time,sF32 phase,sMatrix &mat,sF32 &zoom);
};
GenKineticSpline::GenKineticSpline()
{
Pos.Init();
Speed.Init();
Gravity.Init();
Rotation.Init();
}
void GenKineticSpline::Eval(sF32 time,sF32 phase,sMatrix &mat,sF32 &zoom)
{
if(time<0) time = 0;
sF32 t2 = time*time;
mat.InitEuler(Rotation.x*sPI2F*time,Rotation.y*sPI2F*time,Rotation.z*sPI2F*time);
mat.l.x = Pos.x + Speed.x*time + Gravity.x*t2;
mat.l.y = Pos.y + Speed.y*time + Gravity.y*t2;
mat.l.z = Pos.z + Speed.z*time + Gravity.z*t2;
zoom = 0;
}
/****************************************************************************/
class GenKineticSpline2 : public GenSpline
{
public:
sVector Pos;
sVector Speed;
sVector Gravity;
sVector Axis;
sF32 Angle;
GenKineticSpline2();
void Eval(sF32 time,sF32 phase,sMatrix &mat,sF32 &zoom);
};
GenKineticSpline2::GenKineticSpline2()
{
Pos.Init();
Speed.Init();
Gravity.Init();
Axis.Init(0,0,1);
Angle = 0;
}
void GenKineticSpline2::Eval(sF32 time,sF32 phase,sMatrix &mat,sF32 &zoom)
{
if(time<0) time = 0;
sF32 t2 = time*time;
mat.InitRot(Axis,Angle*time*sPI2F);
mat.l.x = Pos.x + Speed.x*time + Gravity.x*t2;
mat.l.y = Pos.y + Speed.y*time + Gravity.y*t2;
mat.l.z = Pos.z + Speed.z*time + Gravity.z*t2;
zoom = 0;
}
/****************************************************************************/
static void area(const sVector &p0,const sVector &p1,const sVector &p2,sVector &c)
{
sVector a,b;
a.Sub3(p0,p1);
b.Sub3(p1,p2);
c.Cross3(a,b);
}
GenMinMesh * __stdcall MinMesh_Explode(GenMinMesh *input,GenBitmap *bitmap,
sF323 center,sF32 extrude,sF32 speednormal,sF32 speedcenter,sF32 speedgravity,sF32 speedrandom,
sF323 rotationspeed,sF32 todist,sF32 topower,sF32 torandom,sF32 toconst,
sInt mode,sF323 towards,sF32 extrudeflat,sF32 tensorrand)
{
GenMinMesh *mesh;
GenMinFace *face;
GenMinVert *vert,*vert0,*vert1;
GenMinFace *fp;
GenMinVert *vp;
BilinearContext sampler;
mesh = new GenMinMesh;
extrudeflat = 1-extrudeflat;
towards.x -= center.x;
towards.y -= center.y;
towards.z -= center.z;
sF32 towardsdiv = towards.x*towards.x + towards.y*towards.y + towards.z*towards.z;
sF32 towards2x = towards.x/towardsdiv;
sF32 towards2y = towards.y/towardsdiv;
sF32 towards2z = towards.z/towardsdiv;
// count & check limit
input->CalcNormals();
sInt fc=0;
sInt vc=0;
sInt faces=0;
sInt i;
for(sInt i=0;i<input->Faces.Count;i++)
{
face = &input->Faces[i];
sInt cnt = face->Count;
if(cnt>=3 && face->Cluster>0)
{
fc += 2*(cnt-2) + cnt;
vc += 2*cnt + 4*cnt;
faces++;
}
}
if(faces>0xffff || faces<=0)
{
input->Release();
return mesh;
}
// create geometry
mesh->Vertices.SetMax(vc);
mesh->Vertices.Count = vc;
vp = &mesh->Vertices[0];
sSetMem(vp,0,vc*sizeof(*vp));
mesh->Faces.SetMax(fc);
mesh->Faces.Count = fc;
fp = &mesh->Faces[0];
sSetMem(fp,0,fc*sizeof(*fp));
sSetRndSeed(1);
vc = 0;
i = 0;
for(sInt ii=0;ii<input->Faces.Count;ii++)
{
GenMinVector facecenter;
face = &input->Faces[ii];
if(face->Count<3 || face->Cluster==0) continue;
sInt cnt = face->Count;
facecenter.Init();
for(sInt j=0;j<cnt;j++)
facecenter.Add(input->Vertices[face->Vertices[j]].Pos);
facecenter.Scale(1.0f/cnt);
// top and bottom
sInt va[KMM_MAXVERT];
for(sInt k=0;k<cnt;k++)
va[k] = face->Vertices[k];
if(cnt==4 && (mode&2))
{
sVector p0,p1,p2,p3;
p0.Init(input->Vertices[va[0]].Pos.x,input->Vertices[va[0]].Pos.y,input->Vertices[va[0]].Pos.z);
p1.Init(input->Vertices[va[1]].Pos.x,input->Vertices[va[1]].Pos.y,input->Vertices[va[1]].Pos.z);
p2.Init(input->Vertices[va[2]].Pos.x,input->Vertices[va[2]].Pos.y,input->Vertices[va[2]].Pos.z);
p3.Init(input->Vertices[va[3]].Pos.x,input->Vertices[va[3]].Pos.y,input->Vertices[va[3]].Pos.z);
sVector a0,a1,a2,a3;
area(p0,p1,p2,a1);
area(p1,p2,p3,a2);
area(p2,p3,p0,a3);
area(p3,p0,p1,a0);
if(a0.Dot3(a2) > a1.Dot3(a3))
{
sInt s = va[0];
va[0] = va[1];
va[1] = va[2];
va[2] = va[3];
va[3] = s;
}
}
for(sInt j=2;j<cnt;j++)
{
fp->Count = 3;
fp->Cluster = 1;
fp->Flags = 0;
fp->Vertices[0] = vc+0;
fp->Vertices[1] = vc+j-1;
fp->Vertices[2] = vc+j;
fp++;
}
for(sInt j=2;j<cnt;j++)
{
fp->Count = 3;
fp->Cluster = 1;
fp->Flags = 0;
fp->Vertices[2] = vc+cnt+0;
fp->Vertices[1] = vc+cnt+j-1;
fp->Vertices[0] = vc+cnt+j;
fp++;
}
for(sInt j=0;j<cnt;j++)
{
vert = &input->Vertices[va[j]];
vp[j] = *vert;
vp[j].MergeTag = ii;
vp[j].BoneCount = 1;
vp[j].Matrix[0] = i;
vp[j].Weights[0] = 1;
vp[j+cnt] = vp[j];
vp[j+cnt].Pos.Sub(facecenter); vp[j+cnt].Pos.Scale(extrudeflat); vp[j+cnt].Pos.Add(facecenter);
vp[j+cnt].Pos.AddScale(vert->Normal,-extrude);
}
vc += face->Count*2;
vp += face->Count*2;
// around
for(sInt j=0;j<cnt;j++)
{
vert0 = &input->Vertices[va[j]];
vert1 = &input->Vertices[va[(j+1)%cnt]];
fp->Count = 4;
fp->Cluster = 1;
fp->Select = 1;
fp->Flags = 0;
fp->Vertices[0] = vc+2;
fp->Vertices[1] = vc+3;
fp->Vertices[2] = vc+1;
fp->Vertices[3] = vc+0;
vp[0] = *vert0;
vp[0].MergeTag = ii;
vp[0].BoneCount = 1;
vp[0].Matrix[0] = i;
vp[0].Weights[0] = 1;
vp[1] = *vert1;
vp[1].MergeTag = ii;
vp[1].BoneCount = 1;
vp[1].Matrix[0] = i;
vp[1].Weights[0] = 1;
vp[2] = vp[0];
vp[2].Pos.Sub(facecenter); vp[2].Pos.Scale(extrudeflat); vp[2].Pos.Add(facecenter);
vp[2].Pos.AddScale(vert0->Normal,-extrude);
vp[3] = vp[1];
vp[3].Pos.Sub(facecenter); vp[3].Pos.Scale(extrudeflat); vp[3].Pos.Add(facecenter);
vp[3].Pos.AddScale(vert1->Normal,-extrude);
vc+=4;
vp+=4;
fp+=1;
}
i++;
}
// create animation
if(bitmap)
BilinearSetup(&sampler,bitmap->Data,bitmap->XSize,bitmap->YSize,0);
mesh->Clusters[1].AnimType = 2;
mesh->CreateAnimation(faces);
i = 0;
for(sInt ii=0;ii<input->Faces.Count;ii++)
{
GenMinVector facecenter,speed,f;
GenMinMatrix *amat;
face = &input->Faces[ii];
if(face->Count<3 || face->Cluster==0) continue;
facecenter.Init();
for(sInt j=0;j<face->Count;j++)
facecenter.Add(input->Vertices[face->Vertices[j]].Pos);
facecenter.Scale(1.0f/face->Count);
speed.Init();
f.x = facecenter.x - center.x;
f.y = facecenter.y - center.y;
f.z = facecenter.z - center.z;
speed.AddScale(f,speedcenter);
speed.AddScale(face->Normal,speednormal);
speed.x += (sFGetRnd()*2-1)*speedrandom;
speed.y += (sFGetRnd()*2-1)*speedrandom;
speed.z += (sFGetRnd()*2-1)*speedrandom;
amat = &mesh->Animation->Matrices[i];
amat->Temp.Init();
amat->Temp.l.x = facecenter.x;
amat->Temp.l.y = facecenter.y;
amat->Temp.l.z = facecenter.z;
amat->BasePose.Invert(amat->Temp);
amat->NoAnimation = amat->Temp;
if(bitmap)
{
sF32 u0,v0;
sU64 col64;
sF32 val;
u0 = v0 = 0;
for(sInt j=0;j<face->Count;j++)
{
u0 += input->Vertices[face->Vertices[j]].UV[0][0];
v0 += input->Vertices[face->Vertices[j]].UV[0][1];
}
u0 /= face->Count;
v0 /= face->Count;
BilinearFilter(&sampler,&col64,sInt(u0*0x10000*bitmap->XSize),sInt(v0*0x10000*bitmap->XSize));
if((col64&0xffff)!=0)
{
val = 1-((col64&0xffff)/32767.0f);
amat->Offset = -todist*sFPow(val,topower);
}
else
{
amat->Offset = -9999;
}
}
else
{
if(mode&1)
{
sF32 dist = (facecenter.x-center.x)*towards2x
+ (facecenter.y-center.y)*towards2y
+ (facecenter.z-center.z)*towards2z;
f.x = facecenter.x-center.x - dist*towards.x;
f.y = facecenter.y-center.y - dist*towards.y;
f.z = facecenter.z-center.z - dist*towards.z;
sF32 dist2 = sFSqrt(f.x*f.x+f.y*f.y+f.z*f.z);
dist2 -= topower;
if(dist2 < 0)
{
if(dist2<-topower)
dist2 = 0;
else
dist2 = topower+dist2;
dist -= sFSqrt(topower*topower-dist2*dist2)/sFSqrt(towardsdiv);
dist2 = 0;
}
amat->Offset = -todist*dist2-dist;
}
else
{
amat->Offset = -todist*sFPow(f.x*f.x+f.y*f.y+f.z*f.z,topower);
}
}
amat->Offset += - toconst - sFGetRnd()*torandom;
if(!(mode & 4)) // euler
{
GenKineticSpline *spline;
amat->Spline = spline = new GenKineticSpline;
spline->Pos.Init(facecenter.x,facecenter.y,facecenter.z);
spline->Speed.Init(speed.x,speed.y,speed.z);
spline->Gravity.Init(0,speedgravity,0);
spline->Rotation.Init((sFGetRnd()*2-1)*rotationspeed.x,(sFGetRnd()*2-1)*rotationspeed.y,(sFGetRnd()*2-1)*rotationspeed.z);
}
else // vector & axis, for tensor
{
GenKineticSpline2 *spline;
amat->Spline = spline = new GenKineticSpline2;
spline->Pos.Init(facecenter.x,facecenter.y,facecenter.z);
spline->Speed.Init(speed.x,speed.y,speed.z);
spline->Gravity.Init(0,speedgravity,0);
spline->Axis.Init((sFGetRnd()*2-1),(sFGetRnd()*2-1),(sFGetRnd()*2-1));
spline->Axis.Unit3();
spline->Angle = (sFGetRnd()*2-1)*tensorrand;
}
i++;
}
mesh->CompletelyRigid = sTRUE;
// done
input->Release();
mesh->ChangeTopo();
mesh->CalcNormals();
if(bitmap)
bitmap->Release();
return mesh;
}
/****************************************************************************/
/*** ***/
/*** Mesh Compression ***/
/*** ***/
/****************************************************************************/
static sInt GetValence(GenMinMesh *mesh,sInt edgeTag)
{
sInt e = edgeTag;
sInt count = 0;
do
{
count++;
e = mesh->NextVertEdge(e);
}
while(e != edgeTag);
return count;
}
struct MeshElement
{
sInt Degree;
sInt Edge;
MeshElement **Adjacent;
sInt Find(MeshElement *ref,sInt adjust) const;
};
sInt MeshElement::Find(MeshElement *ref,sInt adjust) const
{
for(sInt i=0;i<Degree;i++)
if(Adjacent[i] == ref)
return (i + adjust + Degree) % Degree;
sVERIFYFALSE;
return -1;
}
class MeshCoder
{
GenMinMesh *Mesh;
MeshElement *Elems[2]; // 0=faces 1=vertices
sInt ElemCount[2]; // 0=faces 1=vertices
MeshElement **Links,**LinkPtr,**LinkEnd;
MeshElement **FaceMap;
MeshElement **VertMap;
sU8 *Data;
sInt FaceCount;
sInt VertCount;
sInt *VertMerge;
sInt *VertOrder;
sArray<sInt> Active;
// encode and decode
MeshElement *AddElement(sInt type,sInt deg);
void AddFaceToVertex(MeshElement *f,sInt i,MeshElement *v,sInt j);
sInt ForceFV(MeshElement *f,sInt j,sInt dir);
// encode only
void ActivateVE(MeshElement *f,sInt i);
// decode only
MeshElement *DecodeFace();
void ActivateVD(MeshElement *f,sInt i);
// debug only
void AssertSingleFV(MeshElement *f,MeshElement *v);
void AssertFV(MeshElement *f);
void AssertLinkedEdge(MeshElement *v);
public:
// regularizing a mesh
GenMinMesh *CloseBoundaries(GenMinMesh *mesh);
// main coding api
sInt *Encode(GenMinMesh *mesh,sU8 *&p,sInt &outVertCount);
GenMinMesh *Decode(sU8 *&p);
};
static sInt FindEdgeByVert(GenMinTempEdge *list,sInt n,sInt startVert)
{
// binary search
sInt l,r,x;
l = 0;
r = n;
while(l < r)
{
x = (l + r) / 2;
if(list[x].v0 == startVert)
return x;
else if(list[x].v0 < startVert) // continue in right half
l = x + 1;
else // continue in left half
r = x;
}
return -1;
}
GenMinMesh *MeshCoder::CloseBoundaries(GenMinMesh *inMesh)
{
// first, just make a copy of the mesh
GenMinMesh *mesh = new GenMinMesh;
mesh->Copy(inMesh);
// one-to-one remap
sInt *remap = new sInt[mesh->Vertices.Count];
for(sInt i=0;i<mesh->Vertices.Count;i++)
remap[i] = i;
// build adjacency, step 1
mesh->CalcAdjacencyCore(remap);
//mesh->CalcAdjacency();
delete[] remap;
// make list of boundary edges
sArray<GenMinTempEdge> boundaryEdges;
boundaryEdges.Init();
for(sInt i=0;i<mesh->Faces.Count;i++)
{
GenMinFace *curFace = &mesh->Faces[i];
for(sInt j=0;j<curFace->Count;j++)
{
if(curFace->Adjacent[j] == -1) // boundary
{
GenMinTempEdge *te = boundaryEdges.Add();
te->v0 = curFace->Vertices[(j+1) % curFace->Count];
te->v1 = curFace->Vertices[j];
}
}
}
// sort boundary edges by first vertex
if(boundaryEdges.Count)
HeapSortEdges(&boundaryEdges[0],boundaryEdges.Count);
// fill boundaries
sInt boundaryFaces = 0;
while(boundaryEdges.Count)
{
// insert a boundary helper vertex
sInt boundaryVert = mesh->Vertices.Count;
GenMinVert *tempVert = mesh->Vertices.Add();
tempVert->Pos.Init(0,0,0);
// pick the first boundary edge available and follow the loop
sInt vertex = boundaryEdges[0].v1;
sInt edge;
do
{
// find next edge in this loop
edge = FindEdgeByVert(&boundaryEdges[0],boundaryEdges.Count,vertex);
sVERIFY(edge != -1); // we should ALWAYS be able to find one.
sInt nextVertex = boundaryEdges[edge].v1;
// deleted this edge from the list of candidates
boundaryEdges.Count--;
for(sInt i=edge;i<boundaryEdges.Count;i++)
boundaryEdges[i] = boundaryEdges[i+1];
// insert a "deleted" face to close the mesh
GenMinFace *face = mesh->Faces.Add();
face->Count = 3;
face->Vertices[0] = boundaryVert;
face->Vertices[1] = vertex;
face->Vertices[2] = nextVertex;
boundaryFaces++;
// continue following
vertex = nextVertex;
}
while(edge != 0);
}
boundaryEdges.Exit();
sDPrintF("%d faces inserted to close boundary.\n",boundaryFaces);
// everything should be closed now. re-build adjacency and check.
remap = new sInt[mesh->Vertices.Count];
for(sInt i=0;i<mesh->Vertices.Count;i++)
remap[i] = i;
sBool closed = mesh->CalcAdjacencyCore(remap);
//sBool closed = mesh->CalcAdjacency();
delete[] remap;
// assert everything was ok and return the new mesh
sVERIFY(closed);
return mesh;
}
MeshElement *MeshCoder::AddElement(sInt type,sInt deg)
{
MeshElement *elem = &Elems[type][ElemCount[type]++];
elem->Degree = deg;
elem->Adjacent = LinkPtr;
memset(LinkPtr,0,deg * sizeof(MeshElement *));
LinkPtr += deg;
sVERIFY(LinkPtr <= LinkEnd);
sVERIFY(ElemCount[0] <= FaceCount);
sVERIFY(ElemCount[1] <= VertCount);
return elem;
}
void MeshCoder::AddFaceToVertex(MeshElement *f,sInt i,MeshElement *v,sInt j)
{
v->Adjacent[j] = f;
for(sInt dir=-1;dir<=1;dir+=2)
{
MeshElement *fp = v->Adjacent[(j+dir+v->Degree)%v->Degree];
sInt in = (i-dir+f->Degree)%f->Degree;
if(fp && !f->Adjacent[in])
f->Adjacent[in] = fp->Adjacent[fp->Find(v,dir)];
}
AssertLinkedEdge(v);
}
sInt MeshCoder::ForceFV(MeshElement *f,sInt j,sInt dir)
{
MeshElement *v = f->Adjacent[0];
sInt i = 0;
while(1)
{
i = (i + dir + f->Degree) % f->Degree;
MeshElement *vt = v->Adjacent[(j - dir + v->Degree) % v->Degree];
v = f->Adjacent[i];
if(!i || !vt || !v)
break;
j = v->Find(vt,-dir);
AddFaceToVertex(f,i,v,j);
}
return i;
}
void MeshCoder::ActivateVE(MeshElement *f,sInt i)
{
GenMinFace *face = &Mesh->Faces[f->Edge >> 3];
sInt vertSlot = ((f->Edge & 7) + i) % face->Count;
sInt edgeTag = (f->Edge & ~7) + vertSlot;
sInt vertex = face->Vertices[vertSlot];
MeshElement *v;
// (try to) find vertex in active vertex list
sInt j;
for(j=0;j<Active.Count;j++)
if(Active[j] == vertex)
break;
if(j == Active.Count) // new vertex
{
sVERIFY(VertMap[vertex] == 0);
sInt val = GetValence(Mesh,edgeTag);
v = AddElement(1,val);
*Data++ = val;
VertOrder[ElemCount[1]-1] = vertex;
f->Adjacent[i] = v;
v->Adjacent[0] = f;
v->Edge = edgeTag;
*Active.Add() = vertex;
VertMap[vertex] = v;
}
else // split
{
v = VertMap[vertex];
sVERIFY(j < 65536);
*Data++ = 0;
*(sU16 *) Data = j; Data += 2;
j=0;
sInt e,ee;
e = ee = v->Edge;
while((e & ~7) != (f->Edge & ~7))
{
j++;
e = Mesh->NextVertEdge(e);
}
*Data++ = j;
f->Adjacent[i] = v;
AddFaceToVertex(f,i,v,j);
}
AssertSingleFV(f,v);
}
MeshElement *MeshCoder::DecodeFace()
{
sInt deg = *Data++;
MeshElement *f = AddElement(0,deg);
return f;
}
void MeshCoder::ActivateVD(MeshElement *f,sInt i)
{
MeshElement *v;
sInt val = *Data++;
sInt j;
if(val) // new vertex
{
v = AddElement(1,val);
*Active.Add() = v - Elems[1];
j = 0;
f->Adjacent[i] = v;
v->Adjacent[0] = f;
}
else // split
{
v = &Elems[1][Active[*(sU16 *) Data]]; Data += 2;
j = *Data++;
f->Adjacent[i] = v;
AddFaceToVertex(f,i,v,j);
}
AssertSingleFV(f,v);
}
void MeshCoder::AssertSingleFV(MeshElement *f,MeshElement *v)
{
sInt i,j;
for(i=0;i<f->Degree;i++)
{
if(v == f->Adjacent[i])
{
for(j=0;j<v->Degree;j++)
if(v->Adjacent[j] == f)
break;
sVERIFY(j != v->Degree);
break;
}
}
sVERIFY(i != f->Degree);
}
void MeshCoder::AssertFV(MeshElement *f)
{
sInt i,j;
// assert FV consistency
for(i=0;i<f->Degree;i++)
{
MeshElement *v = f->Adjacent[i];
if(!v)
continue;
for(j=0;j<v->Degree;j++)
if(v->Adjacent[j] == f)
break;
sVERIFY(j != v->Degree);
}
// then assert that we actually represent the right topology
sInt e = f->Edge;
for(i=0;i<f->Degree;i++)
{
MeshElement *realVert = VertMap[Mesh->Faces[e >> 3].Vertices[e & 7]];
sVERIFY(realVert == f->Adjacent[i]);
e = Mesh->NextFaceEdge(e);
}
}
void MeshCoder::AssertLinkedEdge(MeshElement *v)
{
sInt j,lj;
// assert linked edge consistency
lj = v->Degree - 1;
for(j=0;j<v->Degree;j++)
{
MeshElement *f1 = v->Adjacent[j];
MeshElement *f2 = v->Adjacent[lj];
if(f1 != 0 && f2 != 0)
{
sBool consistent = sFALSE;
for(sInt i1=0;i1<f1->Degree;i1++)
for(sInt i2=0;i2<f2->Degree;i2++)
if(f1->Adjacent[i1] == f2->Adjacent[i2])
consistent = sTRUE;
sVERIFY(consistent);
}
lj = j;
}
// then assert that we actually represent the right topology
sInt e = v->Edge;
for(sInt i=0;i<v->Degree;i++)
{
MeshElement *realFace = FaceMap[e >> 3];
sVERIFY(!v->Adjacent[i] || realFace == v->Adjacent[i]);
e = Mesh->NextVertEdge(e);
}
}
sInt *MeshCoder::Encode(GenMinMesh *mesh,sU8 *&p,sInt &outVertCount)
{
// currently assumes a closed mesh
// additional simplifications apply:
// - no deleted faces
Mesh = mesh;
// count real number of faces and vertices
FaceCount = 0;
for(sInt i=0;i<mesh->Faces.Count;i++)
FaceCount += mesh->Faces[i].Count >= 3;
VertCount = 0;
VertMerge = mesh->CalcMergeVerts();
for(sInt i=0;i<mesh->Vertices.Count;i++)
VertCount += VertMerge[i] == i;
// remember everything i've said about counting the real number of
// vertices and stuff? just ignore it.
VertCount = mesh->Vertices.Count;
// create data structures
FaceMap = new MeshElement *[mesh->Faces.Count];
VertMap = new MeshElement *[mesh->Vertices.Count];
memset(FaceMap,0,mesh->Faces.Count * sizeof(sInt));
memset(VertMap,0,mesh->Vertices.Count * sizeof(sInt));
Elems[0] = new MeshElement[FaceCount];
Elems[1] = new MeshElement[VertCount];
ElemCount[0] = 0;
ElemCount[1] = 0;
VertOrder = new sInt[VertCount];
// count number of edges, reserve space for links
sInt edgeCount = 0;
for(sInt i=0;i<mesh->Faces.Count;i++)
if(mesh->Faces[i].Count >= 3)
edgeCount += mesh->Faces[i].Count;
Links = new MeshElement *[edgeCount * 2];
LinkPtr = Links;
LinkEnd = Links + (edgeCount * 2);
Active.Init();
Data = p;
*(sU32 *) Data = VertCount; Data += 4;
*(sU32 *) Data = FaceCount; Data += 4;
*(sU32 *) Data = edgeCount * 2; Data += 4;
// encode connected components in turn
for(sInt seedFace=0;seedFace<mesh->Faces.Count;seedFace++)
{
// face already encoded? skip.
if(FaceMap[seedFace] || mesh->Faces[seedFace].Count == 2)
continue;
// encode seed face
sInt k = mesh->Faces[seedFace].Count;
*Data++ = k;
MeshElement *f = AddElement(0,k);
f->Edge = seedFace << 3;
FaceMap[seedFace] = f;
for(sInt i=0;i<k;i++)
ActivateVE(f,i);
while(Active.Count)
{
// pick vertex to complete
sInt bestDegree = 256;
sInt bestI = -1;
MeshElement *v = 0;
for(sInt i=0;i<Active.Count;i++)
{
MeshElement *testV = VertMap[Active[i]];
sVERIFY(testV != 0);
sInt k = 0;
for(sInt j=0;j<testV->Degree;j++)
k += !testV->Adjacent[j];
if(k < bestDegree)
{
bestI = i;
v = testV;
bestDegree = k;
}
}
// try to complete this vertex
sInt edge = v->Edge;
for(sInt j=0;j<v->Degree;j++)
{
if(!v->Adjacent[j])
{
// activate this face
sInt face = edge >> 3;
sVERIFY(FaceMap[face] == 0);
sInt d = Mesh->Faces[face].Count;
*Data++ = d;
MeshElement *f = AddElement(0,d);
FaceMap[face] = f;
f->Edge = edge;
f->Adjacent[0] = v;
AddFaceToVertex(f,0,v,j);
// complete this face
sInt i = ForceFV(f,j,1);
sInt iend = ForceFV(f,j,-1);
if(i)
while(i <= iend)
ActivateVE(f,i++);
// verify that this face is fully and correctly connected now
for(i=0;i<d;i++)
sVERIFY(f->Adjacent[i] != 0);
AssertFV(f);
}
edge = mesh->NextVertEdge(edge);
}
sVERIFY(edge == v->Edge);
// remove this vertex from the queue
Active[bestI] = Active[--Active.Count];
}
}
sVERIFY(ElemCount[0] == FaceCount);
sVERIFY(ElemCount[1] == VertCount);
outVertCount = VertCount;
p = Data;
Active.Exit();
delete[] Links;
delete[] Elems[0];
delete[] Elems[1];
delete[] FaceMap;
delete[] VertMap;
delete[] VertMerge;
return VertOrder;
}
GenMinMesh *MeshCoder::Decode(sU8 *&p)
{
Mesh = new GenMinMesh;
Data = p;
sU32 vertCount,faceCount,linkCount;
vertCount = *(sU32 *) Data; Data += 4;
faceCount = *(sU32 *) Data; Data += 4;
linkCount = *(sU32 *) Data; Data += 4;
Elems[0] = new MeshElement[faceCount];
Elems[1] = new MeshElement[vertCount];
ElemCount[0] = 0;
ElemCount[1] = 0;
Links = new MeshElement *[linkCount];
LinkPtr = Links;
LinkEnd = Links + linkCount;
Active.Init();
// while not all faces are coded
while(ElemCount[0] < sInt(faceCount))
{
// get a seed face and activate its vertices
MeshElement *f = DecodeFace();
for(sInt i=0;i<f->Degree;i++)
ActivateVD(f,i);
// complete this connected component
do
{
// pick vertex to complete
sInt bestDegree = 256;
sInt bestI;
for(sInt i=0;i<Active.Count;i++)
{
MeshElement *v = &Elems[1][Active[i]];
sInt deg = 0;
for(sInt k=0;k<v->Degree;k++)
deg += !v->Adjacent[k];
if(deg < bestDegree)
{
bestDegree = deg;
bestI = i;
}
}
// complete this vertex
MeshElement *v = &Elems[1][Active[bestI]];
for(sInt j=0;j<v->Degree;j++)
{
if(!v->Adjacent[j])
{
f = DecodeFace();
f->Adjacent[0] = v;
AddFaceToVertex(f,0,v,j);
sInt i = ForceFV(f,j,1);
sInt iend = ForceFV(f,j,-1);
if(i)
while(i <= iend)
ActivateVD(f,i++);
}
}
Active[bestI] = Active[--Active.Count];
}
while(Active.Count);
}
// now convert to mesh
Mesh->Vertices.Resize(vertCount);
Mesh->Faces.Resize(faceCount);
for(sInt i=0;i<ElemCount[1];i++)
Elems[1][i].Edge = i;
for(sInt i=0;i<ElemCount[0];i++)
{
MeshElement *f = &Elems[0][i];
GenMinFace *face = &Mesh->Faces[i];
face->Count = f->Degree;
for(sInt j=0;j<f->Degree;j++)
face->Vertices[j] = f->Adjacent[j]->Edge;
face->Cluster = 1;
}
Mesh->ChangeTopo();
// cleanup.
Active.Exit();
delete[] Links;
delete[] Elems[0];
delete[] Elems[1];
p = Data;
return Mesh;
}
GenMinMesh * __stdcall MinMesh_Compress(GenMinMesh *inMesh)
{
if(CheckMinMesh(inMesh))
return 0;
MeshCoder coder;
static sU8 buffer[256*1024];
sU8 *bufPtr = buffer;
// ---- regularize
sDPrintF("-- regularize\n");
GenMinMesh *mesh = coder.CloseBoundaries(inMesh);
inMesh->Release();
// ---- encode
sDPrintF("-- encode\n");
// encode topology
sInt vertCount;
sInt *vertOrder = coder.Encode(mesh,bufPtr,vertCount);
sSystem->SaveFile("topo.dat",buffer,bufPtr - buffer);
sDPrintF("topology coded size: %d bytes\n",bufPtr - buffer);
// encode vertex positions
for(sInt i=0;i<vertCount;i++)
{
GenMinVert *vert = &mesh->Vertices[vertOrder[i]];
*((sF32 *) bufPtr) = vert->Pos.x; bufPtr += 4;
*((sF32 *) bufPtr) = vert->Pos.y; bufPtr += 4;
*((sF32 *) bufPtr) = vert->Pos.z; bufPtr += 4;
}
delete[] vertOrder;
// coding stats
mesh->Release();
sDPrintF("coded size: %d bytes\n",bufPtr - buffer);
// ---- decode
sDPrintF("-- decode\n");
// decode topology
bufPtr = buffer;
mesh = coder.Decode(bufPtr);
// decode vertex positions
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
GenMinVert *vert = &mesh->Vertices[i];
vert->Pos.x = *((sF32 *) bufPtr); bufPtr += 4;
vert->Pos.y = *((sF32 *) bufPtr); bufPtr += 4;
vert->Pos.z = *((sF32 *) bufPtr); bufPtr += 4;
}
// stats
sDPrintF("decoded, read %d bytes\n",bufPtr - buffer);
return mesh;
}
/****************************************************************************/
#pragma comment(lib,"glu32.lib")
struct GLUtesselator;
extern "C"
{
// Win32
typedef void *HDC;
typedef void *HFONT;
struct FIXED
{
union
{
struct
{
sU16 fract;
sS16 value;
};
sS32 full;
};
};
struct POINTFX
{
FIXED x;
FIXED y;
GenMinVector toVector(sInt xShift) const
{
GenMinVector vect;
vect.Init((xShift + x.full / 65536.0f) / 128.0f,y.full / (65536.0f * 128.0f),0.0f);
return vect;
}
};
struct MAT2
{
FIXED eM11,eM12;
FIXED eM21,eM22;
};
struct GLYPHMETRICS
{
sU32 gmBlackBoxX,gmBlackBoxY;
sInt origin[2];
sS16 gmCellIncX,gmCellIncY;
};
struct TTPOLYGONHEADER
{
sU32 cb;
sU32 dwType;
POINTFX pfxStart;
};
struct TTPOLYCURVE
{
sU16 wType;
sU16 cpfx;
POINTFX apfx[1];
};
HDC __stdcall CreateCompatibleDC(HDC hDC);
HFONT __stdcall CreateFontA(sInt height,sInt width,sInt escape,sInt orient,
sInt weight,sInt italic,sInt underline,sInt strikeOut,sU32 charSet,
sU32 outPrecision,sU32 clipPrecision,sU32 quality,sU32 pitchAndFamily,
const sChar *face);
HFONT __stdcall SelectObject(HDC hDC,HFONT hFont);
sU32 __stdcall GetGlyphOutlineA(HDC hDC,sU32 nChar,sU32 fuFormat,
GLYPHMETRICS *lpgm,sU32 cjBuffer,void *buffer,const MAT2 *lpmat2);
void __stdcall DeleteObject(HFONT hFont);
void __stdcall DeleteDC(HDC hDC);
// GLU
GLUtesselator * __stdcall gluNewTess();
void __stdcall gluDeleteTess(GLUtesselator *tess);
void __stdcall gluTessBeginPolygon(GLUtesselator *tess,void *polygon_data);
void __stdcall gluTessBeginContour(GLUtesselator *tess);
void __stdcall gluTessVertex(GLUtesselator *tess,sF64 coords[3],void *data);
void __stdcall gluTessEndContour(GLUtesselator *tess);
void __stdcall gluTessEndPolygon(GLUtesselator *tess);
void __stdcall gluTessNormal(GLUtesselator *tess,sF64 x,sF64 y,sF64 z);
void __stdcall gluTessCallback(GLUtesselator *tess,sInt which,void (__stdcall *fn)());
typedef void (__stdcall *gluTessCB)(void);
#define GLU_TESS_BEGIN 100100
#define GLU_TESS_VERTEX 100101
#define GLU_TESS_END 100102
#define GLU_TESS_ERROR 100103
#define GLU_TESS_EDGE_FLAG 100104
#define GLU_TESS_COMBINE 100105
#define GLU_TESS_BEGIN_DATA 100106
#define GLU_TESS_VERTEX_DATA 100107
#define GLU_TESS_END_DATA 100108
#define GLU_TESS_ERROR_DATA 100109
#define GLU_TESS_EDGE_FLAG_DATA 100110
#define GLU_TESS_COMBINE_DATA 100111
}
static void font3DAddPoint(GLUtesselator *tess,GenMinMesh *mesh,const GenMinVector &pt)
{
GenMinVert *vert = mesh->Vertices.Add();
sSetMem(vert,0,sizeof(GenMinVert));
vert->Pos = pt;
vert->Normal.z = -1.0f;
double coords[3];
coords[0] = vert->Pos.x;
coords[1] = vert->Pos.y;
coords[2] = vert->Pos.z;
gluTessVertex(tess,coords,(void *) (mesh->Vertices.Count - 1));
}
static void font3DAddCurve(GLUtesselator *tess,GenMinMesh *mesh,const GenMinVector &p1,const GenMinVector &p2,sF32 toleranceSq,sInt depth=0)
{
const GenMinVector &p0 = mesh->Vertices[mesh->Vertices.Count-1].Pos;
GenMinVector d;
d.Lin3(p0,p2,0.5f);
d.Sub(p1);
if(depth >= 12 || d.Dot(d) <= toleranceSq)
font3DAddPoint(tess,mesh,p2);
else
{
GenMinVector l,r,m;
l.Lin3(p0,p1,0.5f);
r.Lin3(p1,p2,0.5f);
m.Lin3(l,r,0.5f);
font3DAddCurve(tess,mesh,l,m,toleranceSq,depth+1);
font3DAddCurve(tess,mesh,r,p2,toleranceSq,depth+1);
}
}
static void __stdcall font3DBeginCB(sInt type,GenMinMesh *mesh)
{
if(!mesh->Faces.Count || mesh->Faces[mesh->Faces.Count-1].Count)
{
GenMinFace *face = mesh->Faces.Add();
face->Select = 0;
face->Count = 0;
face->Cluster = 1;
face->Temp = 0;
face->Flags = 0;
}
}
static void __stdcall font3DVertexCB(void *index,GenMinMesh *mesh)
{
sInt ind = reinterpret_cast<int>(index);
GenMinFace *face = &mesh->Faces[mesh->Faces.Count-1];
face->Vertices[face->Count++] = ind;
if(face->Count == 3)
font3DBeginCB(0,mesh);
}
static void __stdcall font3DCombineCB(sF64 coords[3],void *d[4],sF32 w[4],sInt *out,GenMinMesh *mesh)
{
GenMinVert *vert = mesh->Vertices.Add();
sSetMem(vert,0,sizeof(GenMinVert));
vert->Pos.x = coords[0];
vert->Pos.y = coords[1];
vert->Pos.z = coords[2];
*out = mesh->Vertices.Count - 1;
}
static void __stdcall font3DEdgeFlagCB(sBool flag,GenMinMesh *mesh)
{
}
GenMinMesh * __stdcall MinMesh_Font3D(sF32 height,sF32 extrude,sF32 maxErr,sChar *text,sChar *font)
{
height = sMax(height,8/128.0f);
GenMinMesh *mesh = new GenMinMesh;
HDC hDC = CreateCompatibleDC(0);
HFONT hFont = CreateFontA(height*128,0,0,0,500,0,0,0,0,0,0,0,0,font);
HFONT hOldFont = SelectObject(hDC,hFont);
MAT2 mat;
sSetMem(&mat,0,sizeof(mat));
mat.eM11.value = 1;
mat.eM22.value = 1;
static const sInt bufSize = 256*1024;
sU8 *buffer = new sU8[bufSize];
sInt xPos = 0;
GLUtesselator *tess = gluNewTess();
gluTessNormal(tess,0.0,0.0,-1.0);
gluTessCallback(tess,GLU_TESS_BEGIN_DATA,(gluTessCB) font3DBeginCB);
gluTessCallback(tess,GLU_TESS_VERTEX_DATA,(gluTessCB) font3DVertexCB);
gluTessCallback(tess,GLU_TESS_COMBINE_DATA,(gluTessCB) font3DCombineCB);
gluTessCallback(tess,GLU_TESS_EDGE_FLAG_DATA,(gluTessCB) font3DEdgeFlagCB);
sF32 tolerance = height * 0.1f * maxErr;
tolerance *= tolerance;
for(sInt chr=0;text[chr];chr++)
{
GLYPHMETRICS gm;
sU32 size = GetGlyphOutlineA(hDC,text[chr],0x102,&gm,bufSize,buffer,&mat);
if(!size)
{
xPos += gm.gmCellIncX;
continue;
}
gluTessBeginPolygon(tess,mesh);
sU8 *ptr = buffer, *end = buffer + size;
while(ptr < end)
{
TTPOLYGONHEADER *hdr = (TTPOLYGONHEADER *) ptr;
sU8 *polyEnd = ptr + hdr->cb;
gluTessBeginContour(tess);
font3DAddPoint(tess,mesh,hdr->pfxStart.toVector(xPos));
ptr += sizeof(TTPOLYGONHEADER);
while(ptr < polyEnd)
{
TTPOLYCURVE *tpc = (TTPOLYCURVE *) ptr;
switch(tpc->wType)
{
case 1: // line
for(sInt i=0;i<tpc->cpfx;i++)
font3DAddPoint(tess,mesh,tpc->apfx[i].toVector(xPos));
break;
case 2: // qspline
for(sInt i=0;i<tpc->cpfx-1;i++)
{
GenMinVector b = tpc->apfx[i].toVector(xPos);
GenMinVector c = tpc->apfx[i+1].toVector(xPos);
if(i < tpc->cpfx - 2)
c.Lin3(b,c,0.5f);
font3DAddCurve(tess,mesh,b,c,tolerance);
}
}
ptr += sizeof(TTPOLYCURVE) + (tpc->cpfx - 1) * sizeof(POINTFX);
}
gluTessEndContour(tess);
}
gluTessEndPolygon(tess);
xPos += gm.gmCellIncX;
}
// last face generated is empty.
if(mesh->Faces.Count)
mesh->Faces.Count--;
// stop tesselation stuff
gluDeleteTess(tess);
delete[] buffer;
SelectObject(hDC,hOldFont);
DeleteObject(hFont);
DeleteDC(hDC);
// calc adjacency and try to clean up triangulation by flipping degenerate tris
mesh->CalcAdjacency();
for(sInt i=0;i<mesh->Faces.Count;i++)
{
GenMinFace *face = &mesh->Faces[i];
const GenMinVector &v0 = mesh->Vertices[face->Vertices[0]].Pos;
const GenMinVector &v1 = mesh->Vertices[face->Vertices[1]].Pos;
const GenMinVector &v2 = mesh->Vertices[face->Vertices[2]].Pos;
GenMinVector d1,d2,n;
d1.Sub(v1,v0);
d2.Sub(v2,v0);
n.Cross(d2,d1);
if(n.z < 1e-6f)
{
// try to flip it
for(sInt j=0;j<3;j++)
{
sInt e0 = (i << 3) + j;
if(mesh->OppositeEdge(e0) != -1)
{
mesh->EdgeFlip(e0);
break;
}
}
}
}
// extrude if necessary
if(extrude)
{
// create copy of vertices moved back a bit.
sInt oldVC = mesh->Vertices.Count;
mesh->Vertices.Resize(oldVC*4);
sCopyMem(&mesh->Vertices[oldVC],&mesh->Vertices[0],oldVC * sizeof(GenMinVert));
for(sInt i=oldVC;i<oldVC*2;i++)
mesh->Vertices[i].Pos.z = extrude;
// copies of vertices for sharp edges
sCopyMem(&mesh->Vertices[oldVC*2],&mesh->Vertices[0],oldVC * 2 * sizeof(GenMinVert));
// create copy of faces and invert them
sInt oldFC = mesh->Faces.Count;
mesh->Faces.Resize(oldFC*2);
sCopyMem(&mesh->Faces[oldFC],&mesh->Faces[0],oldFC * sizeof(GenMinFace));
for(sInt i=oldFC;i<oldFC*2;i++)
{
GenMinFace *face = &mesh->Faces[i];
sSwap(face->Vertices[0],face->Vertices[2]);
for(sInt j=0;j<3;j++)
face->Vertices[j] += oldVC;
}
// create extrusion faces
for(sInt i=0;i<oldFC;i++)
{
for(sInt j=0;j<3;j++)
{
if(mesh->Faces[i].Adjacent[j] == -1)
{
sInt e0 = mesh->Faces[i].Vertices[j];
sInt e1 = mesh->Faces[i].Vertices[(j+1)%3];
GenMinFace *ef = mesh->Faces.Add();
ef->Select = 0;
ef->Count = 4;
ef->Cluster = 1;
ef->Temp = 0;
ef->Vertices[0] = e1 + oldVC*2;
ef->Vertices[1] = e0 + oldVC*2;
ef->Vertices[2] = e0 + oldVC*3;
ef->Vertices[3] = e1 + oldVC*3;
}
}
}
// this code is tested and works, but it should only be used when a
// flag is set, so it's commented out for now.
/*
// recalc adjacency and go through edges again, doubling vertices
// on sharp edges
mesh->CalcAdjacency();
mesh->ChangeTopo();
mesh->CalcNormals();
for(sInt i=0;i<mesh->Faces.Count;i++)
{
GenMinFace *face = &mesh->Faces[i];
for(sInt j=0;j<face->Count;j++)
{
// only go through edges once
sInt adj = face->Adjacent[j];
if((i<<3) >= adj)
continue;
const GenMinFace *otherFace = &mesh->Faces[adj>>3];
if(face->Normal.Dot(otherFace->Normal) <= 0.5f) // >=60° angle?
{
sInt vi[2];
sInt e0v[2],e1v[2];
vi[0] = j;
vi[1] = (j+1 == face->Count) ? 0 : j+1;
e0v[0] = face->Vertices[vi[0]];
e1v[0] = mesh->GetVertexId(mesh->NextFaceEdge(adj));
e0v[1] = face->Vertices[vi[1]];
e1v[1] = mesh->GetVertexId(adj);
for(sInt k=0;k<2;k++)
{
// only need to do something if the vertex is shared
if(e0v[k] != e1v[k])
continue;
// clone it!
sInt vInd = mesh->Vertices.Count;
mesh->Vertices.Add();
mesh->Vertices[vInd] = mesh->Vertices[e0v[k]];
face->Vertices[vi[k]] = vInd;
}
}
}
}*/
}
sMatrix mtx;
mtx.Init();
mtx.k.x = 1.0f;
mesh->Transform(0,mtx,0,1);
mesh->ChangeTopo();
return mesh;
}
/****************************************************************************/
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_Pipe(GenSpline *spline_,GenMinMesh *mesh0,GenMinMesh *mesh1,GenMinMesh *mesh2,sInt flags,sF32 texzoom,sF32 ringdist,sF32 objdist)
{
if(spline_->GetBlobSpline()==0) return 0;
if(spline_->GetBlobSpline()->Mode!=4) return 0;
if(spline_->GetBlobPipe()==0) return 0;
if(CheckMinMesh(mesh0)) return 0;
GenMinMesh *mesh;
BlobSpline *spline;
BlobPipe *pipe;
sMatrix m0,m1,mat;
sAABox box;
sF32 zmin[2],zmax[2];
sInt vmin,vmax;
sVector d;
// prepare
mesh = new GenMinMesh;
mesh0->CalcBBox(box);
zmin[0] = box.Min.z;
zmax[0] = box.Max.z;
if(mesh1)
mesh1->CalcBBox(box);
zmin[1] = box.Min.z;
zmax[1] = box.Max.z;
spline = spline_->GetBlobSpline();
pipe = spline_->GetBlobPipe();
m1.Init();
sF32 distabs = 0;
sVERIFY(pipe->Count*2==spline->Count);
for(sInt seg=0;seg<pipe->Count;seg++)
{
// add curved
if(seg>0)
{
sVector center;
sVector axis;
sVector d0,d1;
BlobPipeKey *kp = &pipe->Keys[seg-1];
BlobSplineKey *k0 = &spline->Keys[seg*2-1];
BlobSplineKey *k1 = &spline->Keys[seg*2+0];
d0.x = (k0->px - kp->PosX);
d0.y = (k0->py - kp->PosY);
d0.z = (k0->pz - kp->PosZ);
d1.x = (k1->px - kp->PosX);
d1.y = (k1->py - kp->PosY);
d1.z = (k1->pz - kp->PosZ);
center.x = kp->PosX;
center.y = kp->PosY;
center.z = kp->PosZ;
d0.Unit3();
d1.Unit3();
axis.Cross3(d0,d1);
sF32 gamma = sFACos(d0.Dot3(d1));
d.Add3(d0,d1);
d.Unit3();
center.AddScale3(d,kp->Radius/sFCos(gamma/2));
vmin = mesh->Vertices.Count;
mesh->Add(mesh1 ? mesh1 : mesh0);
vmax = mesh->Vertices.Count;
d.Sub3(m1.l,m0.l);
sF32 dist = kp->Radius*gamma;
for(sInt i=vmin;i<vmax;i++)
{
sVector v;
v.x = mesh->Vertices[i].Pos.x;
v.y = mesh->Vertices[i].Pos.y;
v.z = mesh->Vertices[i].Pos.z;
sF32 f = (v.z-zmin[1])/(zmax[1]-zmin[1]);
mat.InitRot(axis,-f*(sPI-gamma));
v.z = 0;
v.Rotate34(m1);
v.Sub3(center);
v.Rotate34(mat);
v.Add3(center);
mesh->Vertices[i].Pos.x = v.x;
mesh->Vertices[i].Pos.y = v.y;
mesh->Vertices[i].Pos.z = v.z;
if((flags & 17)==17)
mesh->Vertices[i].UV[0][1] = f*texzoom*dist+distabs;
}
if((flags & 24)==24)
distabs += dist*texzoom;
}
// scan points
{
BlobSplineKey *k0 = &spline->Keys[seg*2+0];
BlobSplineKey *k1 = &spline->Keys[seg*2+1];
sQuaternion quat;
quat.Init(k0->Zoom,k0->rx,k0->ry,k0->rz);
quat.ToMatrix(m0);
m0.l.Init4(k0->px,k0->py,k0->pz,1);
quat.Init(k1->Zoom,k1->rx,k1->ry,k1->rz);
quat.ToMatrix(m1);
m1.l.Init4(k1->px,k1->py,k1->pz,1);
// add straight
d.Sub3(m1.l,m0.l);
sF32 dist = d.Abs3();
sInt middle;
if(flags&4)
middle = sRange<sInt>(sInt((dist/objdist)+0.5f),64,1);
else
middle = 1;
for(sInt m=0;m<middle;m++)
{
vmin = mesh->Vertices.Count;
mesh->Add(mesh0);
vmax = mesh->Vertices.Count;
for(sInt i=vmin;i<vmax;i++)
{
sVector v;
v.x = mesh->Vertices[i].Pos.x;
v.y = mesh->Vertices[i].Pos.y;
v.z = mesh->Vertices[i].Pos.z;
sF32 f = (v.z-zmin[0])/(zmax[0]-zmin[0]);
f = (f+m) / middle ;
v.z = 0;
v.Rotate34(m0);
v.AddScale3(d,f);
mesh->Vertices[i].Pos.x = v.x;
mesh->Vertices[i].Pos.y = v.y;
mesh->Vertices[i].Pos.z = v.z;
if(flags & 1)
mesh->Vertices[i].UV[0][1] = f*texzoom*dist+distabs;
}
}
if(flags & 8)
distabs += dist*texzoom;
if(mesh2)
{
vmin = mesh->Vertices.Count;
mesh->Add(mesh2);
vmax = mesh->Vertices.Count;
for(sInt i=vmin;i<vmax;i++)
{
sVector v;
v.x = mesh->Vertices[i].Pos.x;
v.y = mesh->Vertices[i].Pos.y;
v.z = mesh->Vertices[i].Pos.z;
v.Rotate34(m0);
mesh->Vertices[i].Pos.x = v.x;
mesh->Vertices[i].Pos.y = v.y;
mesh->Vertices[i].Pos.z = v.z;
}
vmin = mesh->Vertices.Count;
mesh->Add(mesh2);
vmax = mesh->Vertices.Count;
for(sInt i=vmin;i<vmax;i++)
{
sVector v;
v.x = mesh->Vertices[i].Pos.x;
v.y = mesh->Vertices[i].Pos.y;
v.z = mesh->Vertices[i].Pos.z;
v.Rotate34(m1);
mesh->Vertices[i].Pos.x = v.x;
mesh->Vertices[i].Pos.y = v.y;
mesh->Vertices[i].Pos.z = v.z;
}
if((flags&2) && ringdist>0.1f)
{
sInt middle = sInt((dist/ringdist)-0.5f);
for(sInt i=0;i<middle;i++)
{
vmin = mesh->Vertices.Count;
mesh->Add(mesh2);
vmax = mesh->Vertices.Count;
mat = m0;
mat.l.AddScale3(d,(i+1.0f)/(middle+1));
for(sInt i=vmin;i<vmax;i++)
{
sVector v;
v.x = mesh->Vertices[i].Pos.x;
v.y = mesh->Vertices[i].Pos.y;
v.z = mesh->Vertices[i].Pos.z;
v.Rotate34(mat);
mesh->Vertices[i].Pos.x = v.x;
mesh->Vertices[i].Pos.y = v.y;
mesh->Vertices[i].Pos.z = v.z;
}
}
}
}
}
}
// done;
sRelease(spline_);
sRelease(mesh0);
sRelease(mesh1);
sRelease(mesh2);
mesh->MergeClusters();
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_Multiply(KOp *,GenMinMesh *mesh,sFSRT srt,sInt count,sInt mode,sF32 tu,sF32 tv,sF323 lrot,sF32 extrude)
{
GenMinMesh *out;
sMatrix xform,step,lxform,lstep,tmp;
sVector p;
if(CheckMinMesh(mesh)) return 0;
step.InitSRT(srt.v);
lstep.InitEulerPI2(&lrot.x);
out = new GenMinMesh;
xform.Init();
lxform.Init();
// if(extrude)
// mesh->NeedAllNormals();
sSetRndSeed(count);
for(sInt j=0;j<count;j++)
{
sInt startvert = out->Vertices.Count;
out->Add(mesh); //,!!j); // 0:not keepmaterial: 1..n keepmaterial
tmp.MulA(lxform,xform);
for(sInt i=startvert;i<out->Vertices.Count;i++)
{
GenMinVert *v = &out->Vertices[i];
p.x = v->Pos.x;
p.y = v->Pos.y;
p.z = v->Pos.z;
p.Rotate34(tmp);
v->Pos.x = p.x;
v->Pos.y = p.y;
v->Pos.z = p.z;
if(mode&1)
{
v->UV[0][0] += j*tu;
v->UV[0][1] += j*tv;
}
}
// if(extrude)
// {
// tmp.Init();
// tmp.i.x = j*extrude;
// tmp.j.y = j*extrude;
// tmp.k.z = j*extrude;
// out->TransVert(tmp,sGMI_NORMAL,sGMI_POS | 0x10);
// }
if(mode&2)
xform.InitRandomSRT(srt.v);
else
xform.MulA(step);
lxform.MulA(lstep);
}
mesh->Release();
out->MergeClusters();
return out;
}
GenMinMesh * __stdcall MinMesh_Multiply2(sInt seed,sInt3 count1,sF323 translate1,sInt3 count2,sF323 translate2,sInt random,sInt3 count3,sF323 translate3,sInt inCount,GenMinMesh *inMesh,...)
{
if(!inCount)
return 0;
GenMinMesh *mesh = new GenMinMesh;
sSetRndSeed(seed);
sInt start = 0;
sMatrix xform;
xform.Init();
if(count3.x*count3.y*count3.z * count2.x*count2.y*count2.z * count1.x*count1.y*count1.z > 1024)
{
count3.Init(1,1,1);
count2.Init(1,1,1);
count1.Init(1,1,1);
}
for(sInt z3=0;z3<count3.z;z3++)
{
for(sInt y3=0;y3<count3.y;y3++)
{
for(sInt x3=0;x3<count3.x;x3++)
{
for(sInt z2=0;z2<count2.z;z2++)
{
for(sInt y2=0;y2<count2.y;y2++)
{
for(sInt x2=0;x2<count2.x;x2++)
{
for(sInt z1=0;z1<count1.z;z1++)
{
for(sInt y1=0;y1<count1.y;y1++)
{
for(sInt x1=0;x1<count1.x;x1++)
{
start = mesh->Vertices.Count;
GenMinMesh *in = (&inMesh)[sMin<sInt>(sGetRnd(inCount+random),inCount-1)];
mesh->Add(in);
xform.l.x = x1 * translate1.x + x2 * translate2.x + x3 * translate3.x;
xform.l.y = y1 * translate1.y + y2 * translate2.y + y3 * translate3.y;
xform.l.z = z1 * translate1.z + z2 * translate2.z + z3 * translate3.z;
for(sInt i=start;i<mesh->Vertices.Count;i++)
{
mesh->Vertices[i].Pos.x += xform.l.x;
mesh->Vertices[i].Pos.y += xform.l.y;
mesh->Vertices[i].Pos.z += xform.l.z;
}
}
}
}
}
}
}
}
}
}
for(sInt i=0;i<inCount;i++)
(&inMesh)[i]->Release();
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_Center(GenMinMesh *mesh,sInt which)
{
sAABox box;
sF32 tx,ty,tz;
if(CheckMinMesh(mesh)) return 0;
mesh->CalcBBox(box);
tx = (which&1)?(box.Max.x+box.Min.x)/2:0;
ty = (which&2)?(box.Max.y+box.Min.y)/2:0;
tz = (which&4)?(box.Max.z+box.Min.z)/2:0;
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
mesh->Vertices[i].Pos.x -= tx;
mesh->Vertices[i].Pos.y -= ty;
mesh->Vertices[i].Pos.z -= tz;
}
return mesh;
}
/****************************************************************************/
GenMinMesh * __stdcall MinMesh_RenderAutoMap(GenMinMesh *mesh,sInt flags)
{
sAABox box;
GenMinFace *face;
GenMinVert *vert;
if(CheckMinMesh(mesh)) return 0;
sF32 map[6][6];
sF32 uv[6][2];
sMatrix mat;
sVector min,max;
sInt fields[6];
sInt fieldmax;
mesh->CalcNormals();
mesh->CalcBBox(box);
fieldmax = 0;
for(sInt i=0;i<6;i++)
{
fields[i] = -1;
if(flags & (1<<i))
fields[i] = fieldmax++;
else
fields[i] = -1;
}
if(fields[0]==-1 && fields[1]>=0) fields[0] = fields[1]; // provide good defaults for unspecified directions
if(fields[0]==-1 && fields[4]>=0) fields[0] = fields[4];
if(fields[0]==-1 && fields[5]>=0) fields[0] = fields[5];
if(fields[0]==-1 && fields[2]>=0) fields[0] = fields[2];
if(fields[0]==-1 && fields[3]>=0) fields[0] = fields[3];
if(fields[1]==-1) fields[1] = fields[0];
if(fields[4]==-1 && fields[5]>=0) fields[4] = fields[5];
if(fields[4]==-1) fields[4] = fields[0];
if(fields[5]==-1) fields[5] = fields[4];
if(fields[2]==-1 && fields[3]>=0) fields[2] = fields[3];
if(fields[2]==-1) fields[2] = fields[0];
if(fields[3]==-1) fields[3] = fields[2];
for(sInt i=0;i<mesh->Vertices.Count;i++)
mesh->Vertices[i].TempByte=-1;
for(sInt i=0;i<mesh->Faces.Count;i++)
{
face = &mesh->Faces[i];
face->Temp = face->Normal.Classify()^1;
for(sInt j=0;j<face->Count;j++)
mesh->Vertices[face->Vertices[j]].TempByte = face->Temp;
}
for(sInt i=0;i<6;i++)
{
mat.InitClassify(i);
min.Rotate34(mat,box.Min);
max.Rotate34(mat,box.Max);
map[i][0] = mat.i.x / sFAbs(max.x-min.x) * (1.0f/fieldmax);
map[i][1] = mat.i.y / sFAbs(max.y-min.y) * (1.0f/fieldmax);
map[i][2] = mat.i.z / sFAbs(max.z-min.z) * (1.0f/fieldmax);
map[i][3] = mat.j.x / sFAbs(max.x-min.x);
map[i][4] = mat.j.y / sFAbs(max.y-min.y);
map[i][5] = mat.j.z / sFAbs(max.z-min.z);
// if(min.x>max.x) sSwap(min.x,max.x);
// if(min.y>max.y) sSwap(min.y,max.y);
// if(min.z>max.z) sSwap(min.z,max.z);
uv[i][0] = -(map[i][0]*min.x + map[i][1]*min.y + map[i][2]*min.z) + (1.0f*fields[i]/fieldmax);
uv[i][1] = -(map[i][3]*min.x + map[i][4]*min.y + map[i][5]*min.z);
}
for(sInt i=0;i<mesh->Vertices.Count;i++)
{
vert = &mesh->Vertices[i];
sVERIFY(vert->TempByte != -1);
vert->UV[0][0] = map[vert->TempByte][0]*vert->Pos.x
+ map[vert->TempByte][1]*vert->Pos.y
+ map[vert->TempByte][2]*vert->Pos.z
+ uv[vert->TempByte][0];
vert->UV[0][1] = map[vert->TempByte][3]*vert->Pos.x
+ map[vert->TempByte][4]*vert->Pos.y
+ map[vert->TempByte][5]*vert->Pos.z
+ uv[vert->TempByte][1];
vert->UV[0][1] = 1-vert->UV[0][1];
}
return mesh;
}
/****************************************************************************/
/****************************************************************************/