#include "pch.h" ////////////////////////////////////////////////////////////////////////////// // // Tokenizer for text X-Files // ////////////////////////////////////////////////////////////////////////////// class XToken : public IObjectSingle { public: virtual bool EnterSection(ZString& strType, ZString& strName) = 0; virtual bool LeaveSection() = 0; virtual bool ReadFloat(float& value) = 0; virtual bool ReadInt(int& value) = 0; virtual bool ReadString(ZString& str) = 0; virtual bool ReadReference(ZString& str) = 0; virtual bool Error() = 0; virtual void SetError(const ZString& str) = 0; }; ////////////////////////////////////////////////////////////////////////////// // // Tokenizer for text X-Files // ////////////////////////////////////////////////////////////////////////////// const WORD TOKEN_TEMPLATE = 31; const WORD TOKEN_NAME = 1; const WORD TOKEN_INTEGER_LIST = 6; const WORD TOKEN_REALNUM_LIST = 7; const WORD TOKEN_OBRACE = 10; const WORD TOKEN_CBRACE = 11; class XBinaryToken : public XToken { private: PCC m_p; PCC m_pend; int m_intCount; int m_floatCount; ZString m_strError; bool MoreTokens() { return m_p < m_pend; } bool Is(WORD id) { if (*(WORD*)m_p == id) { m_p += sizeof(WORD); return true; } return false; } int GetInt() { int value = *((int*)m_p); m_p += sizeof(int); return value; } bool ReadString(ZString& str) { if (Is(TOKEN_NAME)) { int length = GetInt(); str = ZString(m_p, length); m_p += length; return true; } return false; } public: XBinaryToken(PCC p, int length) : m_p(p), m_pend(p + length), m_intCount(0), m_floatCount(0) { } bool EnterSection(ZString& strType, ZString& strName) { // skip any templates while (Is(TOKEN_TEMPLATE)) { while (!Is(TOKEN_CBRACE)) m_p += 2; } if (ReadString(strType)) { ReadString(strName); return Is(TOKEN_OBRACE); } return false; } bool LeaveSection() { int level = 1; ZString str; while (MoreTokens() && level > 0) { while (ReadString(str)); if (Is(TOKEN_REALNUM_LIST)) { m_p += sizeof(float) * GetInt(); } else if (Is(TOKEN_INTEGER_LIST)) { m_p += sizeof(int) * GetInt(); } else if (Is(TOKEN_OBRACE)) { level++; } else if (Is(TOKEN_CBRACE)) { level--; } else { m_p += 2; } } return level == 0; } bool ReadFloat(float& value) { ZAssert(m_intCount == 0); if (m_floatCount == 0) { if (Is(TOKEN_REALNUM_LIST)) { m_floatCount = GetInt(); } else { return false; } } m_floatCount--; value = *((float*)m_p); m_p += sizeof(float); return true; } bool ReadInt(int& value) { ZAssert(m_floatCount == 0); if (m_intCount == 0) { if (Is(TOKEN_INTEGER_LIST)) { m_intCount = GetInt(); } else { return false; } } m_intCount--; value = *((int*)m_p); m_p += sizeof(int); return true; } bool ReadReference(ZString& str) { ZUnimplemented(); return false; } bool Error() { return !m_strError.IsEmpty(); } void SetError(const ZString& str) { m_strError = str; } }; ////////////////////////////////////////////////////////////////////////////// // // Tokenizer for text X-Files // ////////////////////////////////////////////////////////////////////////////// class XTextToken : public XToken, public TextTokenImpl { private: int Comma ; int SemiColon ; int LeftParen ; int RightParen ; int LeftBracket ; int RightBracket; int LeftBrace ; int RightBrace ; int Minus ; int Dummy ; public: XTextToken(PCC pcc, int length) : TextTokenImpl(pcc, length) { Comma = AddToken("','"); SemiColon = AddToken("';'"); LeftParen = AddToken("'('"); RightParen = AddToken("')'"); LeftBracket = AddToken("'['"); RightBracket = AddToken("']'"); LeftBrace = AddToken("'{'"); RightBrace = AddToken("'}'"); Minus = AddToken("'-'"); Dummy = AddToken("dummy"); Next(); } int ParseToken(PCC& pcc) { switch (pcc[0]) { case '[': pcc++; return LeftBracket; case ']': pcc++; return RightBracket; case '{': pcc++; return LeftBrace; case '}': pcc++; return RightBrace; case '(': pcc++; return LeftParen; case ')': pcc++; return RightParen; case ',': pcc++; return Comma; case ';': pcc++; return SemiColon; case '-': pcc++; return Minus; case '<': case '>': pcc++; return Dummy; } return 0; } bool IsSymbolChar(char ch) { return TextTokenImpl::IsSymbolChar(ch) || ch == '-' || ch == '.'; } bool Error() { return TextTokenImpl::Error(); } void SetError(const ZString& str) { TextTokenImpl::SetError(str); } bool EnterSection(ZString& strType, ZString& strName) { if (IsSymbol(strType, false)) { IsSymbol(strName, false); return Is(LeftBrace, true); } return false; } bool LeaveSection() { int level = 1; while (MoreTokens() && level > 0) { if (Is(LeftBrace, false)) { level++; } else if (Is(RightBrace, false)) { level--; } else { Next(); } } return level == 0; } bool ReadFloat(float& value) { bool fMinus = Is(Minus, false); if (IsNumber(value, true)) { while (Is(Comma, false) || Is(SemiColon, false)); if (fMinus) { value = -value; } return true; } return false; } bool ReadInt(int& i) { float value; if (ReadFloat(value)) { i = (int)value; return true; } return false; } bool ReadString(ZString& str) { return IsString(str, true); } bool ReadReference(ZString& str) { if (Is(TOKEN_OBRACE, true)) { if (IsSymbol(str, true)) { if (Is(TOKEN_CBRACE, true)) { return true; } } } return false; } }; ////////////////////////////////////////////////////////////////////////////// // // Parser for X-Files // ////////////////////////////////////////////////////////////////////////////// class XParser { TRef m_pmodeler; TRef m_ptoken; TMap > m_namedFrames; int m_countMesh; int m_countFrame; public: XParser( Modeler* pmodeler, PCC pcc, int length ) : m_pmodeler(pmodeler), m_countMesh(0), m_countFrame(0) { if (pcc[8] == 't') { m_ptoken = new XTextToken(pcc + 17, length - 17); } else if (pcc[8] == 'b') { m_ptoken = new XBinaryToken(pcc + 16, length - 16); } } ~XParser() { } bool ReadTransform(TRef& ptrans) { ZString strType; ZString strName; if (m_ptoken->EnterSection(strType, strName)) { float m00, m01, m02, m03; float m10, m11, m12, m13; float m20, m21, m22, m23; float m30, m31, m32, m33; if ( m_ptoken->ReadFloat(m00) && m_ptoken->ReadFloat(m10) && m_ptoken->ReadFloat(m20) && m_ptoken->ReadFloat(m30) && m_ptoken->ReadFloat(m01) && m_ptoken->ReadFloat(m11) && m_ptoken->ReadFloat(m21) && m_ptoken->ReadFloat(m31) && m_ptoken->ReadFloat(m02) && m_ptoken->ReadFloat(m12) && m_ptoken->ReadFloat(m22) && m_ptoken->ReadFloat(m32) && m_ptoken->ReadFloat(m03) && m_ptoken->ReadFloat(m13) && m_ptoken->ReadFloat(m23) && m_ptoken->ReadFloat(m33) ) { Matrix mat( m00, m01, m02, m03, m10, m11, m12, m13, m20, m21, m22, m23, m30, m31, m32, m33 ); /* , this should change a LH Transform to a RH Transform mat.PreScale(Vector(1, 1, -1)); mat.Scale(Vector(1, 1, -1)); */ ptrans = new Transform(mat); m_ptoken->LeaveSection(); return true; } } return false; } bool ReadMesh(const ZString strMeshName, TRef& pgeo) { m_countMesh++; // // Read in the vertices // int vcount; if (!m_ptoken->ReadInt(vcount)) { return false; } TVector ppositions(vcount, vcount); int index; for(index = 0; index < vcount; index++) { Vertex& vertex = ppositions.Get(index); float z; if ( !m_ptoken->ReadFloat(vertex.x) || !m_ptoken->ReadFloat(vertex.y) || !m_ptoken->ReadFloat(z) ) { return false; } vertex.z = -z; vertex.u = 0; vertex.v = 0; } // // Read in the vertices for the faces // int countFace; if (!m_ptoken->ReadInt(countFace)) { return false; } TVector pvi; TVector pfaceVCounts(countFace, countFace); for(index = 0; index < countFace; index++) { int firstIndex ; int secondIndex; if ( !m_ptoken->ReadInt(pfaceVCounts.Get(index)) || !m_ptoken->ReadInt(firstIndex ) || !m_ptoken->ReadInt(secondIndex) ) { return false; } for (int faceVIndex = 0; faceVIndex < pfaceVCounts[index] - 2; faceVIndex++) { int thirdIndex; if (!m_ptoken->ReadInt(thirdIndex)) { return false; } pvi.PushEnd(firstIndex); pvi.PushEnd(secondIndex); pvi.PushEnd(thirdIndex); secondIndex = thirdIndex; } } // // Read in the materials // int mcount = 0; int matCountFace = 0; TVector > pmaterials; TVector > pimageTextures; TVector pmi; int ncount = 0; TVector pnormals; TVector pni; ZString strType; ZString strName; while (m_ptoken->EnterSection(strType, strName)) { if (strType == "MeshMaterialList") { if ( !m_ptoken->ReadInt(mcount) || !m_ptoken->ReadInt(matCountFace) ) { return false; } ZAssert(matCountFace == 1 || countFace == matCountFace); // // read in per face material indicies // for(index = 0; index < matCountFace; index++) { int mi; if (!m_ptoken->ReadInt(mi)) { return false; } pmi.PushEnd(mi); } // // read in the materials // for (index = 0; index < mcount; index++) { ZVerify(m_ptoken->EnterSection(strType, strName)); ZAssert(strType == "Material"); float rd, gd, bd, ad; float power; float rs, gs, bs; float re, ge, be; if ( !m_ptoken->ReadFloat(rd) || !m_ptoken->ReadFloat(gd) || !m_ptoken->ReadFloat(bd) || !m_ptoken->ReadFloat(ad) || !m_ptoken->ReadFloat(power) || !m_ptoken->ReadFloat(rs) || !m_ptoken->ReadFloat(gs) || !m_ptoken->ReadFloat(bs) || !m_ptoken->ReadFloat(re) || !m_ptoken->ReadFloat(ge) || !m_ptoken->ReadFloat(be) ) { return false; } Color colorDiffuse(rd, gd, bd, ad); Color colorSpecular(rs, gs, bs); Color colorEmissive(re, ge, be); TRef pimageTexture; if (m_ptoken->EnterSection(strType, strName)) { ZAssert(strType == "TextureFilename"); PathString strFilename; if (!m_ptoken->ReadString(strFilename)) { return false; } ZAssert( strFilename.GetExtension() == "ppm" || strFilename.GetExtension() == "bmp" ); strFilename = strFilename.LeftOf(4).ToLower() + "bmp"; pimageTexture = m_pmodeler->LoadImage(strFilename, false); m_ptoken->LeaveSection(); } else if (m_ptoken->Error()) { return false; } pmaterials.PushEnd( CreateMaterial( colorDiffuse, colorSpecular, colorEmissive, power ) ); pimageTextures.PushEnd(pimageTexture); m_ptoken->LeaveSection(); } } else if (strType == "MeshVertexColors" ) { // , parse this? } else if (strType == "MeshFaceWraps" ) { // , parse this? } else if (strType == "MeshTextureCoords") { int vcountTextureCoords; if (!m_ptoken->ReadInt(vcountTextureCoords)) { return false; } ZAssert(vcount == vcountTextureCoords); for(index = 0; index < vcount; index++) { Vertex& vertex = ppositions.Get(index); if ( !m_ptoken->ReadFloat(vertex.u) || !m_ptoken->ReadFloat(vertex.v) ) { return false; } } } else if (strType == "MeshNormals") { // // Read in the normals // if (!m_ptoken->ReadInt(ncount)) { return false; } pnormals.SetCount(ncount); for(index = 0; index < ncount; index++) { float x, y, z; if ( !m_ptoken->ReadFloat(x) || !m_ptoken->ReadFloat(y) || !m_ptoken->ReadFloat(z) ) { return false; } pnormals.Get(index) = Vector(x, y, -z); } // // Read in the normals for the faces // int countFaceNormals; if (!m_ptoken->ReadInt(countFaceNormals)) { return false; } ZAssert(countFace == countFaceNormals); for(index = 0; index < countFace; index++) { int faceVCount ; int firstIndex ; int secondIndex; if ( !m_ptoken->ReadInt(faceVCount ) || !m_ptoken->ReadInt(firstIndex ) || !m_ptoken->ReadInt(secondIndex) ) { return false; } for (int faceVIndex = 0; faceVIndex < faceVCount - 2; faceVIndex++) { int thirdIndex; if (!m_ptoken->ReadInt(thirdIndex)) { return false; } pni.PushEnd(firstIndex); pni.PushEnd(secondIndex); pni.PushEnd(thirdIndex); secondIndex = thirdIndex; } } } m_ptoken->LeaveSection(); } if (m_ptoken->Error()) { return false; } // // Create unique Position Normal pairs // TVector vertices; TVector indices; if (ncount > 0) { TVector > vertexTree(vcount, vcount); int count = pvi.GetCount(); for (index = 0; index < count; index++) { int iVertex = pvi[index]; int iNormal = pni[index]; TList::Iterator iter(vertexTree.Get(iVertex)); do { if (iter.End()) { // create a new vertex Vertex vertex; vertex.x = ppositions[iVertex].x; vertex.y = ppositions[iVertex].y; vertex.z = ppositions[iVertex].z; vertex.u = ppositions[iVertex].u; vertex.v = ppositions[iVertex].v; vertex.nx = pnormals[iNormal].X(); vertex.ny = pnormals[iNormal].Y(); vertex.nz = pnormals[iNormal].Z(); vertices.PushEnd(vertex); indices.PushEnd(vertices.GetCount() - 1); vertexTree.Get(iVertex).PushFront(vertices.GetCount() - 1); break; } Vertex& vertex = vertices.Get(iter.Value()); if ( vertex.nx == pnormals[iNormal].X() && vertex.ny == pnormals[iNormal].Y() && vertex.nz == pnormals[iNormal].Z() ) { indices.PushEnd(iter.Value()); break; } iter.Next(); } while (true); } } else { vertices = ppositions; indices = pvi; } // // Split up the mesh based on materials // if (mcount == 0) { pgeo = Geo::CreateMesh(vertices, indices); return true; } else if (matCountFace == 1 || mcount == 1) { if (pimageTextures[0]) { pgeo = new TextureGeo( new MaterialGeo( Geo::CreateMesh(vertices, indices), pmaterials[0] ), pimageTextures[0] ); return true; } else { pgeo = new MaterialGeo( Geo::CreateMesh(vertices, indices), pmaterials[0] ); return true; } } // // Multiple Materials // TRef pgroup = GroupGeo::Create(); TVector componentVertices; TVector componentIndices; TVector map(vertices.GetCount(), vertices.GetCount()); // // for each material create a smaller mesh for all the faces that have that material // for (int indexMaterial = 0; indexMaterial < mcount; indexMaterial++) { for (index = 0; index < map.GetCount(); index++) { map.Set(index, 0xffff); } // // set the small mesh to empty // componentVertices.SetCount(0); componentIndices.SetCount(0); // // add all of the verticies and faces for which the material matches // int indexStart = 0; for (int indexFace = 0; indexFace < countFace; indexFace++) { int indexEnd = indexStart + (pfaceVCounts[indexFace] - 2) * 3; if (pmi[indexFace] == indexMaterial) { for ( index = indexStart; index < indexEnd; index++ ) { int indexVertex = indices[index]; if (map[indexVertex] == 0xffff) { map.Set(indexVertex, componentVertices.GetCount()); componentVertices.PushEnd(vertices[indexVertex]); } componentIndices.PushEnd(map[indexVertex]); } } indexStart = indexEnd; } // // create the mesh and stick it into the group // if (pimageTextures[indexMaterial]) { pgroup->AddGeo( new TextureGeo( new MaterialGeo( Geo::CreateMesh(componentVertices, componentIndices), pmaterials[indexMaterial] ), pimageTextures[indexMaterial] ) ); } else { pgroup->AddGeo( new MaterialGeo( Geo::CreateMesh(componentVertices, componentIndices), pmaterials[indexMaterial] ) ); } } pgeo = pgroup; return true; } bool ReadFrame(const ZString& strFrameName, TRef& pgeo) { m_countFrame++; // // Create a group // TRef pgeoGroup = GroupGeo::Create(); pgeoGroup->SetName(strFrameName); // // Create the group transform // TRef ptrans; if (!ReadTransform(ptrans)) { if (m_ptoken->Error()) { return false; } ptrans = new Transform(); } TRef ptransformGeo = new TransformGeo(pgeoGroup, ptrans); // // return the transformGeo // pgeo = ptransformGeo; // // remember the the name of the transform so we can replace it // if it is animated // m_namedFrames.Set(strFrameName, ptransformGeo); // // read the children of this frame // ZString strType; ZString strName; while (m_ptoken->EnterSection(strType, strName)) { TRef pgeoChild; if (strType == "Frame") { if (!ReadFrame(strName, pgeoChild)) { return false; } } else if (strType == "Mesh") { if (!ReadMesh(strName, pgeoChild)) { return false; } } else { m_ptoken->SetError("Unknown Section Type"); return false; } pgeoGroup->AddGeo(pgeoChild); m_ptoken->LeaveSection(); } return !m_ptoken->Error(); } bool ReadAnimationKey( Number* pnumberFrame, TRef& ptransformTranslate, TRef& ptransformScale, TRef& ptransformRotate ) { int type ; int count; if ( !m_ptoken->ReadInt(type ) || !m_ptoken->ReadInt(count) ) { return false; } switch (type) { case 0: { // rotation keys TRef ptrans = CreateKeyFramedRotateTransform(pnumberFrame); ptransformRotate = ptrans; for (int index = 0; index < count; index++) { int time; int count; float s; float x; float y; float z; if ( !m_ptoken->ReadInt(time) || !m_ptoken->ReadInt(count) || !m_ptoken->ReadFloat(s) || !m_ptoken->ReadFloat(x) || !m_ptoken->ReadFloat(y) || !m_ptoken->ReadFloat(z) ) { return false; } ZAssert(count == 4); ptrans->AddKey((float)time, Quaternion(s, x, y, -z)); } } break; case 1: { // scale keys TRef ptrans = CreateKeyFramedScaleTransform(pnumberFrame); ptransformScale = ptrans; for (int index = 0; index < count; index++) { int time; int count; float x; float y; float z; if ( !m_ptoken->ReadInt(time) || !m_ptoken->ReadInt(count) || !m_ptoken->ReadFloat(x) || !m_ptoken->ReadFloat(y) || !m_ptoken->ReadFloat(z) ) { return false; } ZAssert(count == 3); ptrans->AddKey((float)time, Vector(x, y, z)); } } break; case 2: { // Translate keys TRef ptrans = CreateKeyFramedTranslateTransform(pnumberFrame); ptransformTranslate = ptrans; for (int index = 0; index < count; index++) { int time; int count; float x; float y; float z; if ( !m_ptoken->ReadInt(time) || !m_ptoken->ReadInt(count) || !m_ptoken->ReadFloat(x) || !m_ptoken->ReadFloat(y) || !m_ptoken->ReadFloat(z) ) { return false; } ZAssert(count == 3); ptrans->AddKey((float)time, Vector(x, y, -z)); } } break; default: m_ptoken->SetError("Invalid key type"); return false; } return true; } bool ReadAnimation(Number* pnumberFrame) { ZString strFrameName; if (m_ptoken->ReadReference(strFrameName)) { // // Read in any Animation Keys // TRef ptransformTranslate; TRef ptransformRotate; TRef ptransformScale; ZString strType; ZString strName; while (m_ptoken->EnterSection(strType, strName)) { if (strType == "AnimationKey") { if (!ReadAnimationKey(pnumberFrame, ptransformTranslate, ptransformScale, ptransformRotate)) { return false; } } else if (strType == "AnimationOptions") { ZError("Not Implemented"); } m_ptoken->LeaveSection(); } // // if there were any transforms replace the old transform // if (!m_ptoken->Error()) { if (ptransformRotate || ptransformScale || ptransformTranslate) { // // Find the frame we will be animating // TRef ptransformGeo; ZVerify(m_namedFrames.Find(strFrameName, ptransformGeo)); // // build the new transformed geo // TRef pgeo = ptransformGeo->GetGeo(); if (ptransformRotate) { pgeo = new TransformGeo(pgeo, ptransformRotate); } if (ptransformScale) { pgeo = new TransformGeo(pgeo, ptransformScale); } if (ptransformTranslate) { pgeo = new TransformGeo(pgeo, ptransformTranslate); } ptransformGeo->ChangeTo(pgeo); } return true; } } return false; } bool ReadAnimationSet(Number* pnumberFrame) { ZString strType; ZString strName; while (m_ptoken->EnterSection(strType, strName)) { if (strType == "Animation") { if (!ReadAnimation(pnumberFrame)) { return false; } } m_ptoken->LeaveSection(); } return !m_ptoken->Error(); } TRef ReadFile(Number* pnumberFrame, bool& bAnimation) { TRef pgroup = GroupGeo::Create(); ZString strType; ZString strName; while (m_ptoken->EnterSection(strType, strName)) { TRef pgeoChild; if (strType == "Frame") { if (!ReadFrame(strName, pgeoChild)) { return false; } } else if (strType == "Mesh") { if (!ReadMesh(strName, pgeoChild)) { return false; } } else if (strType == "AnimationSet") { ZAssert(!bAnimation); bAnimation = true; if (!ReadAnimationSet(pnumberFrame)) { return false; } } if (pgeoChild) { pgroup->AddGeo(pgeoChild); } m_ptoken->LeaveSection(); } if (m_ptoken->Error()) { return NULL; } else { return pgroup; } } }; ////////////////////////////////////////////////////////////////////////////// // // Parse Animation Data in X-File // ////////////////////////////////////////////////////////////////////////////// TRef ImportXFile(Modeler* pmodeler, ZFile* pfile, Number* pnumberFrame, bool& bAnimation) { XParser parser(pmodeler, (PCC)pfile->GetPointer(), pfile->GetLength()); bAnimation = false; return parser.ReadFile(pnumberFrame, bAnimation); }