// 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;iMatrices[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;iKeyCount) { 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;iInit(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;iRelease(); 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;iRelease(); 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;iAddRef(); 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;iInit(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;iBoneCount; *(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;jBoneCount;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;iCount; *data++ = face->Cluster; if(vert16Bit) { for(sInt j=0;jCount;j++) { *(sU16 *) data = face->Vertices[j]; data += 2; } } else { for(sInt j=0;jCount;j++) { *(sU32 *) data = face->Vertices[j]; data += 4; } } } // save clusters *(sU32 *) data = Clusters.Count; data += 4; for(sInt i=0;iRenderPass | (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;iBoneCount = *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;jBoneCount;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;iCount = *data++; face->Cluster = *data++; if(vert16Bit) { for(sInt j=0;jCount;j++) { face->Vertices[j] = *(sU16 *) data; data += 2; } } else { for(sInt j=0;jCount;j++) { face->Vertices[j] = *(sU32 *) data; data += 4; } } } // free existing clusters for(sInt i=0;iMtrl = 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;ySelect = 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;xSelect = 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(srcSelect; 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=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;jCluster && 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;jCount;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;jCount;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;iCount;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;iCount;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;iAddRef(); ChangeTopo(); } void GenMinMesh::MergeClusters() { sInt *rev; sInt revcount; for(sInt i=0;i=0) { for(sInt j=0;j=0); Faces[i].Cluster = Clusters[Faces[i].Cluster].Temp; } for(sInt i=0;iAddRef(); for(sInt i=0;iRelease(); sVERIFY(revcount>0); sVERIFY(rev[0]==0); for(sInt i=0;iVertices,face->Count*4); for(sInt j=0;jCount;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;iCount;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;iSelect) || (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 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;iPos) & (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= 3 ? Faces[i].Count : 0; // make edge list GenMinTempEdge *edges = new GenMinTempEdge[numEdges], *edgePtr = edges; for(sInt i=0;iCount < 3) continue; for(sInt j=0;jCount;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;iv0 && 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;iCount < 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;jCount;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;iRelease(); 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;iBoneCount>0) { v1.Init(0,0,0,0); v0.Init(vp->Pos.x,vp->Pos.y,vp->Pos.z,1); for(sInt j=0;jBoneCount;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;iSelect = 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;iVertices.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;iVertices.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;yPos.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;jVertices[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;iPos.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;iFaces.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;iClusters.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;iAdd(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;iVertices.Count;i++) mesh->Vertices[i].Select = (mode&1); if(mode&4) for(sInt i=0;iFaces.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;iVertices.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;iVertices.Count;i++) mesh->Vertices[i].Select = SelectLogic(mesh->Vertices[i].Select,mesh->Vertices[i].TempByte,mode); break; case MMS_FULLFACE: for(sInt i=0;iFaces.Count;i++) { GenMinFace *face = &mesh->Faces[i]; sBool ok = 1; for(sInt j=0;jCount;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;iFaces.Count;i++) { GenMinFace *face = &mesh->Faces[i]; sBool ok = 0; for(sInt j=0;jCount;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;iFaces.Count;i++) mesh->Faces[i].Select = !mesh->Faces[i].Select; break; case 1: for(sInt i=0;iVertices.Count;i++) mesh->Vertices[i].Select = !mesh->Vertices[i].Select; break; case 2: for(sInt i=0;iVertices.Count;i++) mesh->Vertices[i].Select = 0; for(sInt i=0;iFaces.Count;i++) { GenMinFace *face = &mesh->Faces[i]; if(face->Select) for(sInt j=0;jCount;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;iFaces.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;iVertices.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;iVertices.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;iVertices.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;iVertices.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;iClusters.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;iAnimation->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;iVertices.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+1BoneCount = 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;iAnimation->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;iAnimation->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;iCount;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;iKeyCount*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; 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;iFaces.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;zFaces.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;iFaces.Count;i++) { GenMinFace *curFace = &mesh->Faces[i]; if(mesh->Faces[i].Count <= 3) *outFace++ = *curFace; else { for(sInt j=2;jCount;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;iVertices.Count;i++) vertRemap[i] = i; for(sInt faceInd=0;faceIndFaces[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;iAdjacent[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;faceIndFaces[faceInd]; if(!curFace->Select || curFace->Count <= 2) continue; for(sInt j=0;jCount;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;iFaces[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;jCount;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;iVertices.Count;i++) mesh->Vertices[i].Select = 0; for(sInt grp=0;grpFaces[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;jCount;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;iFaces[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;iFaces.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;iiFaces.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;jVertices[face->Vertices[j]].Pos); facecenter.Scale(1.0f/cnt); // top and bottom sInt va[KMM_MAXVERT]; for(sInt k=0;kVertices[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;jCount = 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;jCount = 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;jVertices[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;jVertices[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;iiFaces.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;jCount;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;jCount;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 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;iVertices.Count;i++) remap[i] = i; // build adjacency, step 1 mesh->CalcAdjacencyCore(remap); //mesh->CalcAdjacency(); delete[] remap; // make list of boundary edges sArray boundaryEdges; boundaryEdges.Init(); for(sInt i=0;iFaces.Count;i++) { GenMinFace *curFace = &mesh->Faces[i]; for(sInt j=0;jCount;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;iFaces.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;iVertices.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;jAdjacent[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;iDegree;i++) { if(v == f->Adjacent[i]) { for(j=0;jDegree;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;iDegree;i++) { MeshElement *v = f->Adjacent[i]; if(!v) continue; for(j=0;jDegree;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;iDegree;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;jDegree;j++) { MeshElement *f1 = v->Adjacent[j]; MeshElement *f2 = v->Adjacent[lj]; if(f1 != 0 && f2 != 0) { sBool consistent = sFALSE; for(sInt i1=0;i1Degree;i1++) for(sInt i2=0;i2Degree;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;iDegree;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;iFaces.Count;i++) FaceCount += mesh->Faces[i].Count >= 3; VertCount = 0; VertMerge = mesh->CalcMergeVerts(); for(sInt i=0;iVertices.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;iFaces.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;seedFaceFaces.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;iDegree;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;jDegree;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;iAdjacent[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;iDegree;i++) ActivateVD(f,i); // complete this connected component do { // pick vertex to complete sInt bestDegree = 256; sInt bestI; for(sInt i=0;iDegree;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;jDegree;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;iFaces[i]; face->Count = f->Degree; for(sInt j=0;jDegree;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;iVertices[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;iVertices.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(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;icpfx;i++) font3DAddPoint(tess,mesh,tpc->apfx[i].toVector(xPos)); break; case 2: // qspline for(sInt i=0;icpfx-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;iFaces.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;iVertices[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;iFaces[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;iFaces[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;iFaces.Count;i++) { GenMinFace *face = &mesh->Faces[i]; for(sInt j=0;jCount;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;segCount;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;iVertices[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((dist/objdist)+0.5f),64,1); else middle = 1; for(sInt m=0;mVertices.Count; mesh->Add(mesh0); vmax = mesh->Vertices.Count; for(sInt i=vmin;iVertices[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;iVertices[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;iVertices[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;iVertices.Count; mesh->Add(mesh2); vmax = mesh->Vertices.Count; mat = m0; mat.l.AddScale3(d,(i+1.0f)/(middle+1)); for(sInt i=vmin;iVertices[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;jVertices.Count; out->Add(mesh); //,!!j); // 0:not keepmaterial: 1..n keepmaterial tmp.MulA(lxform,xform); for(sInt i=startvert;iVertices.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;z3Vertices.Count; GenMinMesh *in = (&inMesh)[sMin(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;iVertices.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;iRelease(); 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;iVertices.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<=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;iVertices.Count;i++) mesh->Vertices[i].TempByte=-1; for(sInt i=0;iFaces.Count;i++) { face = &mesh->Faces[i]; face->Temp = face->Normal.Classify()^1; for(sInt j=0;jCount;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;iVertices.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; } /****************************************************************************/ /****************************************************************************/