// copyright (c) 2001 Lev Povalahev #include "StdAfx.h" #include "l3ds.h" #include #include #include #include //using namespace std; //------------------------------------------------------- // generic stuff //------------------------------------------------------- typedef unsigned long ulong; #define SEEK_START 1900 #define SEEK_CURSOR 1901 // common chunks // colors #define COLOR_F 0x0010 #define COLOR_24 0x0011 #define LIN_COLOR_24 0x0012 #define LIN_COLOR_F 0x0013 // percentage #define INT_PERCENTAGE 0x0030 #define FLOAT_PERCENTAGE 0x0031 // ambient light #define AMBIENT_LIGHT 0x2100 #define MAIN3DS 0x4D4D #define EDIT3DS 0x3D3D // this is the start of the editor config // keyframer chunk ids #define KFDATA 0xB000 // the keyframer section #define KFHDR 0xB00A #define OBJECT_NODE_TAG 0xB002 #define NODE_HDR 0xB010 #define PIVOT 0xB013 #define POS_TRACK_TAG 0xB020 #define ROT_TRACK_TAG 0xB021 #define SCL_TRACK_TAG 0xB022 // material entries #define MAT_ENTRY 0xAFFF #define MAT_NAME 0xA000 #define MAT_AMBIENT 0xA010 #define MAT_DIFFUSE 0xA020 #define MAT_SPECULAR 0xA030 #define MAT_SHININESS 0xA040 #define MAT_SHIN2PCT 0xA041 #define MAT_TRANSPARENCY 0xA050 #define MAT_SHADING 0xA100 #define MAT_TWO_SIDE 0xA081 #define MAT_ADDITIVE 0xA083 #define MAT_WIRE 0xA085 #define MAT_FACEMAP 0xA088 #define MAT_WIRESIZE 0xA087 #define MAT_DECAL 0xA082 #define MAT_TEXMAP 0xA200 #define MAT_MAPNAME 0xA300 #define MAT_MAP_TILING 0xA351 #define MAT_MAP_USCALE 0xA354 #define MAT_MAP_VSCALE 0xA356 #define MAT_MAP_UOFFSET 0xA358 #define MAT_MAP_VOFFSET 0xA35A #define MAT_MAP_ANG 0xA35C #define MAT_TEX2MAP 0xA33A #define MAT_OPACMAP 0xA210 #define MAT_BUMPMAP 0xA230 #define MAT_SPECMAP 0xA204 #define MAT_SHINMAP 0xA33C #define MAT_REFLMAP 0xA220 #define MAT_ACUBIC 0xA310 #define EDIT_OBJECT 0x4000 #define OBJ_TRIMESH 0x4100 #define OBJ_LIGHT 0x4600 #define OBJ_CAMERA 0x4700 #define LIT_OFF 0x4620 #define LIT_SPOT 0x4610 #define TRI_VERTEXLIST 0x4110 #define TRI_VERTEXOPTIONS 0x4111 #define TRI_FACELIST 0x4120 #define TRI_MAT_GROUP 0x4130 #define TRI_SMOOTH_GROUP 0x4150 #define TRI_FACEMAPPING 0x4140 #define TRI_MATRIX 0x4160 #define SPOTLIGHT 0x4610 //---------------------------------- #define MAX_SHARED_TRIS 100 // the error reporting routine void ErrorMsg(const char *msg) { } struct LChunk { unsigned short id; uint start; uint end; }; struct LTri { unsigned short a; unsigned short b; unsigned short c; ulong smoothingGroups; LVector3 normal; LVector3 tangent; LVector3 binormal; uint materialId; }; // globals LColor3 black = {0, 0, 0}; LVector3 zero3 = {0, 0, 0}; LVector4 zero4 = {0, 0, 0, 0}; LMap emptyMap = {0, "", 1, 1, 0, 0, 0}; LVector3 _4to3(const LVector4 &vec) { LVector3 t; t.x = vec.x; t.y = vec.y; t.z = vec.z; return t; } LVector3 AddVectors(const LVector3 &a, const LVector3 &b) { LVector3 t; t.x = a.x+b.x; t.y = a.y+b.y; t.z = a.z+b.z; return t; } LVector3 SubtractVectors(const LVector3 &a, const LVector3 &b) { LVector3 t; t.x = a.x-b.x; t.y = a.y-b.y; t.z = a.z-b.z; return t; } float VectorLength(const LVector3 &vec) { return (float)sqrt(vec.x*vec.x + vec.y*vec.y+vec.z*vec.z); } LVector3 NormalizeVector(const LVector3 &vec) { float a = VectorLength(vec); if (a == 0) return vec; float b = 1/a; LVector3 v; v.x = vec.x*b; v.y = vec.y*b; v.z = vec.z*b; return v; } LVector3 CrossProduct(const LVector3 &a, const LVector3 &b) { LVector3 v; v.x = a.y*b.z - a.z*b.y; v.y = a.z*b.x - a.x*b.z; v.z = a.x*b.y - a.y*b.x; return v; } void LoadIdentityMatrix(LMatrix4 &m) { for (int i = 0; i < 4; i++) for (int j = 0; j < 4; j++) m.m[i][j] = (i==j) ? 1.0f : 0.0f; } LVector4 VectorByMatrix(const LMatrix4 &m, const LVector4 &vec) { LVector4 res; res.x = m.m[0][0]*vec.x + m.m[1][0]*vec.y + m.m[2][0]*vec.z + m.m[3][0]*vec.w; res.y = m.m[0][1]*vec.x + m.m[1][1]*vec.y + m.m[2][1]*vec.z + m.m[3][1]*vec.w; res.z = m.m[0][2]*vec.x + m.m[1][2]*vec.y + m.m[2][2]*vec.z + m.m[3][2]*vec.w; res.w = m.m[0][3]*vec.x + m.m[1][3]*vec.y + m.m[2][3]*vec.z + m.m[3][3]*vec.w; if (res.w != 0) { float b = 1/res.w; res.x *= b; res.y *= b; res.z *= b; res.w = 1; } else { res.w = 1; } return res; } void QuatToMatrix(const LVector4 &quat, LMatrix4 &m) { } //------------------------------------------------------- // LObject implementation //------------------------------------------------------- LObject::LObject() { m_name = "";//.clear(); } LObject::~LObject() { // nothing here } void LObject::SetName(const std::string& value) { m_name = value; } const std::string& LObject::GetName() { return m_name; } bool LObject::IsObject(const std::string &name) { return (m_name == name); } //------------------------------------------------------- // LMaterial implementation //------------------------------------------------------- LMaterial::LMaterial() : LObject() { m_id = 0; m_texMap1 = emptyMap; m_texMap2 = emptyMap; m_opacMap = emptyMap; m_bumpMap = emptyMap; m_reflMap = emptyMap; m_specMap = emptyMap; m_ambient = black; m_diffuse = black; m_specular = black; m_shading = sGouraud; m_shininess = 0; m_transparency = 0; } LMaterial::~LMaterial() { } uint LMaterial::GetID() { return m_id; } LMap& LMaterial::GetTextureMap1() { return m_texMap1; } LMap& LMaterial::GetTextureMap2() { return m_texMap2; } LMap& LMaterial::GetOpacityMap() { return m_opacMap; } LMap& LMaterial::GetSpecularMap() { return m_specMap; } LMap& LMaterial::GetBumpMap() { return m_bumpMap; } LMap& LMaterial::GetReflectionMap() { return m_reflMap; } LColor3 LMaterial::GetAmbientColor() { return m_ambient; } LColor3 LMaterial::GetDiffuseColor() { return m_diffuse; } LColor3 LMaterial::GetSpecularColor() { return m_specular; } float LMaterial::GetShininess() { return m_shininess; } float LMaterial::GetTransparency() { return m_transparency; } LShading LMaterial::GetShadingType() { return m_shading; } void LMaterial::SetID(uint value) { m_id = value; } void LMaterial::SetAmbientColor(const LColor3 &color) { m_ambient = color; } void LMaterial::SetDiffuseColor(const LColor3 &color) { m_diffuse = color; } void LMaterial::SetSpecularColor(const LColor3 &color) { m_specular = color; } void LMaterial::SetShininess(float value) { m_shininess = value; if (m_shininess < 0) m_shininess = 0; if (m_shininess > 1) m_shininess = 1; } void LMaterial::SetTransparency(float value) { m_transparency = value; if (m_transparency < 0) m_transparency = 0; if (m_transparency > 1) m_transparency = 1; } void LMaterial::SetShadingType(LShading shading) { m_shading = shading; } //------------------------------------------------------- // LMesh implementation //------------------------------------------------------- LMesh::LMesh() : LObject() { Clear(); } LMesh::~LMesh() { Clear(); } void LMesh::Clear() { m_vertices.clear(); m_normals.clear(); m_uv.clear(); m_tangents.clear(); m_binormals.clear(); m_triangles.clear(); m_tris.clear(); m_materials.clear(); LoadIdentityMatrix(m_matrix); } uint LMesh::GetVertexCount() { return m_vertices.size(); } void LMesh::SetVertexArraySize(uint value) { m_vertices.resize(value); m_normals.resize(value); m_uv.resize(value); m_tangents.resize(value); m_binormals.resize(value); } uint LMesh::GetTriangleCount() { return m_triangles.size(); } void LMesh::SetTriangleArraySize(uint value) { m_triangles.resize(value); m_tris.resize(value); } const LVector4& LMesh::GetVertex(uint index) { return m_vertices[index]; } const LVector3& LMesh::GetNormal(uint index) { return m_normals[index]; } const LVector2& LMesh::GetUV(uint index) { return m_uv[index]; } const LVector3& LMesh::GetTangent(uint index) { return m_tangents[index]; } const LVector3& LMesh::GetBinormal(uint index) { return m_binormals[index]; } void LMesh::SetVertex(const LVector4 &vec, uint index) { if (index >= m_vertices.size()) return; m_vertices[index] = vec; } void LMesh::SetNormal(const LVector3 &vec, uint index) { if (index >= m_vertices.size()) return; m_normals[index] = vec; } void LMesh::SetUV(const LVector2 &vec, uint index) { if (index >= m_vertices.size()) return; m_uv[index] = vec; } void LMesh::SetTangent(const LVector3 &vec, uint index) { if (index >= m_vertices.size()) return; m_tangents[index] = vec; } void LMesh::SetBinormal(const LVector3 &vec, uint index) { if (index >= m_vertices.size()) return; m_binormals[index] = vec; } const LTriangle& LMesh::GetTriangle(uint index) { return m_triangles[index]; } LTriangle2 LMesh::GetTriangle2(uint index) { LTriangle2 f; LTriangle t = GetTriangle(index); f.vertices[0] = GetVertex(t.a); f.vertices[1] = GetVertex(t.b); f.vertices[2] = GetVertex(t.c); f.vertexNormals[0] = GetNormal(t.a); f.vertexNormals[1] = GetNormal(t.b); f.vertexNormals[2] = GetNormal(t.c); f.textureCoords[0] = GetUV(t.a); f.textureCoords[1] = GetUV(t.b); f.textureCoords[2] = GetUV(t.c); LVector3 a, b; a = SubtractVectors(_4to3(f.vertices[1]), _4to3(f.vertices[0])); b = SubtractVectors(_4to3(f.vertices[1]), _4to3(f.vertices[2])); f.faceNormal = CrossProduct(b, a); f.faceNormal = NormalizeVector(f.faceNormal); f.materialId = m_tris[index].materialId; return f; } LMatrix4 LMesh::GetMatrix() { return m_matrix; } void LMesh::SetMatrix(LMatrix4 m) { m_matrix = m; } void LMesh::TransformVertices() { for (uint i=0; i > array; array.resize(m_vertices.size()); for (i=0; i smGroups; std::vector< std::vector > smList; uint loop_size = m_vertices.size(); for (i=0; i 1) for (uint j=1; j< smGroups.size(); j++) { m_vertices.push_back(m_vertices[i]); m_normals.push_back(m_normals[i]); m_uv.push_back(m_uv[i]); m_tangents.push_back(m_tangents[i]); m_binormals.push_back(m_binormals[i]); uint t = m_vertices.size()-1; for (uint h=0; h > array; array.resize(m_vertices.size()); for (int i=0; i<(int)m_triangles.size(); i++) { uint k = m_tris[i].a; array[k].push_back(i); k = m_tris[i].b; array[k].push_back(i); k = m_tris[i].c; array[k].push_back(i); } // now average the tangents and compute the binormals as (tangent X normal) for (int i=0; i<(int)m_vertices.size(); i++) { v1 = zero3; v2 = zero3; int t = array[i].size(); for (int k=0; k= m_triangles.size()) return; m_tris[index] = tri; } LTri& LMesh::GetTri(uint index) { return m_tris[index]; } uint LMesh::GetMaterial(uint index) { return m_materials[index]; } uint LMesh::AddMaterial(uint id) { m_materials.push_back(id); return m_materials.size()-1; } uint LMesh::GetMaterialCount() { return m_materials.size(); } //------------------------------------------------------- // LLight implementation //------------------------------------------------------- LLight::LLight() : LObject() { Clear(); } LLight::~LLight() { } void LLight::Clear() { m_pos.x = m_pos.y = m_pos.z = 0.0f; m_color.r = m_color.g = m_color.b = 0.0f; m_spotlight = false; } void LLight::SetPosition(LVector3 vec) { m_pos = vec; } LVector3 LLight::GetPosition() { return m_pos; } void LLight::SetColor(LColor3 color) { m_color = color; } LColor3 LLight::GetColor() { return m_color; } void LLight::SetSpotlight(bool value) { m_spotlight = value; } bool LLight::GetSpotlight() { return m_spotlight; } void LLight::SetTarget(LVector3 target) { m_target = target; } LVector3 LLight::GetTarget() { return m_target; } void LLight::SetHotspot(float value) { m_hotspot = value; } float LLight::GetHotspot() { return m_hotspot; } void LLight::SetFalloff(float value) { m_falloff = value; } float LLight::GetFalloff() { return m_falloff; } //------------------------------------------------------- // LImporter implementation //------------------------------------------------------- LImporter::LImporter() { Clear(); } LImporter::~LImporter() { Clear(); } uint LImporter::GetMeshCount() { return m_meshes.size(); } uint LImporter::GetLightCount() { return m_lights.size(); } uint LImporter::GetMaterialCount() { return m_materials.size(); } LMesh& LImporter::GetMesh(uint index) { return m_meshes[index]; } LLight& LImporter::GetLight(uint index) { return m_lights[index]; } LMaterial& LImporter::GetMaterial(uint index) { return m_materials[index]; } LMaterial* LImporter::FindMaterial(const std::string& name) { for (uint i=0; i 0) free(m_buffer); } bool L3DS::LoadFile(const char *filename) { FILE *f; f = fopen(filename, "rb"); if (f == 0) { ErrorMsg("L3DS::LoadFile - cannot open file"); return false; } fseek(f, 0, SEEK_END); m_bufferSize = ftell(f); fseek(f, 0, SEEK_SET); m_buffer = (unsigned char*) calloc(m_bufferSize, 1); if (m_buffer == 0) { ErrorMsg("L3DS::LoadFile - not enough memory (malloc failed)"); return false; } if (fread(m_buffer, m_bufferSize, 1, f) != 1) { fclose(f); free(m_buffer); m_bufferSize = 0; ErrorMsg("L3DS::LoadFile - error reading from file"); return false; } fclose(f); Clear(); bool res = Read3DS(); free(m_buffer); m_buffer = 0; m_bufferSize = 0; return res; } short L3DS::ReadShort() { if ((m_buffer!=0) && (m_bufferSize != 0) && ((m_pos+2)=m_bufferSize)) { count = 0; m_eof = true; return count; } count = 0; char c = ReadChar(); while ((c!=0) && (count= m_bufferSize) m_pos = m_bufferSize-1; m_eof = false; } uint L3DS::Pos() { return m_pos; } LChunk L3DS::ReadChunk() { LChunk chunk; chunk.id = ReadShort(); int a = ReadInt(); chunk.start = Pos(); chunk.end = chunk.start+a-6; return chunk; } bool L3DS::FindChunk(LChunk &target, const LChunk &parent) { if (Pos() >= parent.end) return false; LChunk chunk; chunk = ReadChunk(); while (( chunk.id != target.id) && (chunk.end <= parent.end)) { SkipChunk(chunk); if (chunk.end >= parent.end) break; chunk = ReadChunk(); } if (chunk.id == target.id) { target.start = chunk.start; target.end = chunk.end; return true; } return false; } void L3DS::SkipChunk(const LChunk &chunk) { Seek(chunk.end, SEEK_START); } void L3DS::GotoChunk(const LChunk &chunk) { Seek(chunk.start, SEEK_START); } LColor3 L3DS::ReadColor(const LChunk &chunk) { LColor3 col = black; GotoChunk(chunk); switch (chunk.id) { case COLOR_F: col.r = ReadFloat(); col.g = ReadFloat(); col.b = ReadFloat(); break; case COLOR_24: col.r = ReadByte()/255.0f; col.g = ReadByte()/255.0f; col.b = ReadByte()/255.0f; break; case LIN_COLOR_F: col.r = ReadFloat(); col.g = ReadFloat(); col.b = ReadFloat(); break; case LIN_COLOR_24: col.r = ReadByte()/255.0f; col.g = ReadByte()/255.0f; col.b = ReadByte()/255.0f; break; default: ErrorMsg("L3DS::ReadColor - error this is not a color chunk"); } return col; } float L3DS::ReadPercentage(const LChunk &chunk) { GotoChunk(chunk); switch (chunk.id) { case INT_PERCENTAGE: return (ReadShort()/100.0f); case FLOAT_PERCENTAGE: return ReadFloat(); } ErrorMsg("L3DS::ReadPercentage - error, the chunk is not a percentage chunk"); return 0; } bool L3DS::Read3DS() { LChunk mainchunk; LChunk edit; edit.id = EDIT3DS; mainchunk = ReadChunk(); if (mainchunk.id != MAIN3DS) { ErrorMsg("L3DS::Read3DS - wrong file format"); return false; } if (!FindChunk(edit, mainchunk)) return false; LChunk obj; LChunk ml; GotoChunk(edit); obj.id = MAT_ENTRY; while (FindChunk(obj, edit)) { ReadMaterial(obj); SkipChunk(obj); } GotoChunk(edit); obj.id = EDIT_OBJECT; { while (FindChunk(obj, edit)) { ReadASCIIZ(m_objName, 99); ml = ReadChunk(); if (ml.id == OBJ_TRIMESH) ReadMesh(ml); if (ml.id == OBJ_LIGHT) ReadLight(ml); SkipChunk(obj); } } // read the keyframer data here to find out correct object orientation LChunk keyframer; keyframer.id = KFDATA; LChunk objtrack; objtrack.id = OBJECT_NODE_TAG; GotoChunk(mainchunk); if (FindChunk(keyframer, mainchunk)) { // keyframer chunk is present GotoChunk(keyframer); while (FindChunk(objtrack, keyframer)) { ReadKeyframeData(objtrack); SkipChunk(objtrack); } } for (uint i=0; i= parent.end) break; chunk = ReadChunk(); } } m_lights.push_back(light); } void L3DS::ReadMesh(const LChunk &parent) { unsigned short count, i; LVector4 p; LMatrix4 m; LVector2 t; p.w = 1.0f; LMesh mesh; mesh.SetName(m_objName); GotoChunk(parent); LChunk chunk = ReadChunk(); while (chunk.end <= parent.end) { switch (chunk.id) { case TRI_VERTEXLIST: count = ReadShort(); mesh.SetVertexArraySize(count); for (i=0; i < count; i++) { p.x = ReadFloat(); p.y = ReadFloat(); p.z = ReadFloat(); mesh.SetVertex(p, i); } break; case TRI_FACEMAPPING: count = ReadShort(); if (mesh.GetVertexCount() == 0) mesh.SetVertexArraySize(count); for (i=0; i < count; i++) { t.x = ReadFloat(); t.y = ReadFloat(); mesh.SetUV(t, i); } break; case TRI_FACELIST: ReadFaceList(chunk, mesh); break; case TRI_MATRIX: { for (int i = 0; i < 4; i++) for (int j = 0; j < 3; j++) m.m[i][j] = ReadFloat(); m.m[0][3] = 0.0f; m.m[1][3] = 0.0f; m.m[2][3] = 0.0f; m.m[3][3] = 1.0f; mesh.SetMatrix(m); } break; default: break; } SkipChunk(chunk); if (chunk.end >= parent.end) break; chunk = ReadChunk(); } m_meshes.push_back(mesh); } void L3DS::ReadFaceList(const LChunk &chunk, LMesh &mesh) { // variables unsigned short count, t; uint i; LTri tri; LChunk ch; char str[20]; //uint mat; // consistency checks if (chunk.id != TRI_FACELIST) { ErrorMsg("L3DS::ReadFaceList - internal error: wrong chunk passed as parameter"); return; } GotoChunk(chunk); tri.smoothingGroups = 1; // read the number of faces count = ReadShort(); mesh.SetTriangleArraySize(count); for (i=0; iGetID(); mesh.AddMaterial(mat_id); count = ReadShort(); for (i=0; i 0) { ReadKeyheader(); pos.x = ReadFloat(); pos.y = ReadFloat(); pos.z = ReadFloat(); } } GotoChunk(parent); // now read the rotation track LVector4 rot = zero4; LChunk rotchunk; rotchunk.id = ROT_TRACK_TAG; frames = 0; if (FindChunk(rotchunk, parent)) { GotoChunk(rotchunk); // read the trackheader structure ReadShort(); ReadInt(); ReadInt(); frames = ReadInt(); if (frames > 0) { ReadKeyheader(); rot.x = ReadFloat(); rot.y = ReadFloat(); rot.z = ReadFloat(); rot.w = ReadFloat(); } } GotoChunk(parent); // now read the scaling chunk LVector3 scale; scale.x = 1; scale.y = 1; scale.z = 1; LChunk scalechunk; scalechunk.id = SCL_TRACK_TAG; frames = 0; if (FindChunk(scalechunk, parent)) { GotoChunk(scalechunk); // read the trackheader structure ReadShort(); ReadInt(); ReadInt(); frames = ReadInt(); if (frames > 0) { ReadKeyheader(); scale.x = ReadFloat(); scale.y = ReadFloat(); scale.z = ReadFloat(); } } GotoChunk(parent); } long L3DS::ReadKeyheader() { long frame; frame = ReadInt(); short opts = ReadShort(); if (opts & 32768) // 32768 is 1000000000000000 binary { // tension is present ReadFloat(); } if (opts & 16384) // 16384 is 0100000000000000 binary { // continuity is present ReadFloat(); } if (opts & 8192) { // bias info present ReadFloat(); } if (opts & 4096) { // "ease to" present ReadFloat(); } if (opts & 2048) { // "ease from" present ReadFloat(); } return frame; }