diff --git a/CMakeLists.txt b/CMakeLists.txt index 63e9bb475..fe63f798c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -394,6 +394,7 @@ add_library(standard STATIC standard/src/StdGL.cpp standard/src/StdGLCtx.cpp standard/src/StdMarkup.cpp + standard/src/StdMesh.cpp standard/src/StdMeshMaterial.cpp standard/src/StdNoGfx.cpp standard/src/StdPNG.cpp @@ -425,6 +426,7 @@ add_library(standard STATIC standard/inc/StdFont.h standard/inc/StdGL.h standard/inc/StdMarkup.h + standard/inc/StdMesh.h standard/inc/StdMeshMaterial.h standard/inc/StdNoGfx.h standard/inc/StdPNG.h diff --git a/Makefile.am b/Makefile.am index 571b1ee28..65aee9695 100644 --- a/Makefile.am +++ b/Makefile.am @@ -432,6 +432,7 @@ libstandard_a_SOURCES = \ standard/src/StdGL.cpp \ standard/src/StdGLCtx.cpp \ standard/src/StdMarkup.cpp \ + standard/src/StdMesh.cpp \ standard/src/StdMeshMaterial.cpp \ standard/src/StdNoGfx.cpp \ standard/src/StdPNG.cpp \ @@ -463,6 +464,7 @@ libstandard_a_SOURCES = \ standard/inc/StdFont.h \ standard/inc/StdGL.h \ standard/inc/StdMarkup.h \ + standard/inc/StdMesh.h \ standard/inc/StdMeshMaterial.h \ standard/inc/StdNoGfx.h \ standard/inc/StdPNG.h \ diff --git a/standard/inc/StdMesh.h b/standard/inc/StdMesh.h new file mode 100644 index 000000000..14ca97ead --- /dev/null +++ b/standard/inc/StdMesh.h @@ -0,0 +1,220 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2009 Armin Burgmeier + * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de + * + * Portions might be copyrighted by other authors who have contributed + * to OpenClonk. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * See isc_license.txt for full license and disclaimer. + * + * "Clonk" is a registered trademark of Matthes Bender. + * See clonk_trademark_license.txt for full license. + */ + +#ifndef INC_StdMesh +#define INC_StdMesh + +#include + +// Loader for OGRE meshes. Currently supports XML files only. + +class StdMeshError: public std::exception +{ +public: + StdMeshError(const StdStrBuf& message, const char* file, unsigned int line); + virtual ~StdMeshError() throw() {} + + virtual const char* what() const throw() { return Buf.getData(); } + +protected: + StdStrBuf Buf; +}; + +// Interface to load skeleton files. Given a filename occuring in the +// mesh file, this should load the skeleton file from wherever the mesh file +// was loaded from, for example from a C4Group. Return an empty string if +// the loading failed. +class StdMeshSkeletonLoader +{ +public: + virtual StdStrBuf LoadSkeleton(const char* filename) = 0; +}; + +class StdMeshMatrix +{ +public: + void SetIdentity(); + void SetTranslate(float dx, float dy, float dz); + void SetScale(float sx, float sy, float sz); + void SetRotate(float angle, float rx, float ry, float rz); + + float& operator()(int i, int j) { return a[i][j]; } + float operator()(int i, int j) const { return a[i][j]; } + + // *this *= other + void Mul(const StdMeshMatrix& other); + void Mul(float f); + // *this += other + void Add(const StdMeshMatrix& other); + + // *this = other * *this + void Transform(const StdMeshMatrix& other); +private: + // 3x3 orthogonal + translation in last column + float a[3][4]; +}; + +class StdMeshBone +{ + friend class StdMesh; +public: + unsigned int Index; // Index in master bone array + int ID; // Bone ID + StdStrBuf Name; // Bone name + + // Bone transformation + StdMeshMatrix trans; + // Inverse transformation + StdMeshMatrix inverse_trans; + + const StdMeshBone* GetParent() const { return Parent; } + + const StdMeshBone& GetChild(unsigned int i) const { return *Children[i]; } + unsigned int GetNumChildren() const { return Children.size(); } + +private: + StdMeshBone* Parent; // Parent bone + std::vector Children; // Children. Not owned. + + StdMeshBone(const StdMeshBone&); // non-copyable + StdMeshBone& operator=(const StdMeshBone&); // non-assignable +}; + +class StdMeshVertexBoneAssignment +{ +public: + unsigned int BoneIndex; + float Weight; +}; + +class StdMeshVertex +{ +public: + float x, y, z; + float nx, ny, nz; + float u, v; + + // *this = trans * *this + void Transform(const StdMeshMatrix& trans); + +#if 0 + // *this *= f; + void Mul(float f); + // *this += other; + void Add(const StdMeshVertex& other); +#endif +}; + +class StdMeshFace +{ +public: + unsigned int Vertices[3]; +}; + +// Keyframe, specifies transformation for one bone in a particular frame +class StdMeshKeyFrame +{ +public: + StdMeshMatrix Trans; +}; + +// Animation track, specifies transformation for one bone for each keyframe +class StdMeshTrack +{ + friend class StdMesh; +public: + StdMeshMatrix GetTransformAt(float time) const; + +private: + std::map Frames; +}; + +// Animation, consists of one Track for each animated Bone +class StdMeshAnimation +{ + friend class StdMesh; +public: + ~StdMeshAnimation(); + + StdStrBuf Name; + float Length; + +private: + std::vector Tracks; // bone-indexed +}; + +class StdMesh +{ +public: + StdMesh(); + ~StdMesh(); + + // Throws StdMeshError + void InitXML(const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager); + + const StdMeshVertex& GetVertex(unsigned int i) const { return Vertices[i]; } + unsigned int GetNumVertices() const { return Vertices.size(); } + + const StdMeshFace& GetFace(unsigned int i) const { return Faces[i]; } + unsigned int GetNumFaces() const { return Faces.size(); } + + const StdMeshBone& GetBone(unsigned int i) const { return *Bones[i]; } + unsigned int GetNumBones() const { return Bones.size(); } + + const StdMeshAnimation& GetAnimationByName(const char* name); + const StdMeshMaterial& GetMaterial() const { return *Material; } + +private: + // Remember bone assignments for vertices + class Vertex: public StdMeshVertex + { + public: + std::vector BoneAssignments; + }; + + std::vector Vertices; + std::vector Faces; + std::vector Bones; // Master Bone Table + + std::map Animations; + + const StdMeshMaterial* Material; +}; + +class StdMeshInstance +{ +public: + StdMeshInstance(const StdMesh& mesh); + + void SetAnimation(const StdStrBuf& animation); + const StdMeshAnimation& GetAnimation() const; + + void SetPosition(float position); + + // Get vertex of instance, with current animation applied. This needs to + // go elsewhere if/when we want to calculate this on the hardware. + const StdMeshVertex& GetVertex(unsigned int i) const { return Vertices[i]; } + unsigned int GetNumVertices() const { return Vertices.size(); } + +protected: + std::vector Vertices; +}; + +#endif + +// vim: et ts=2 sw=2 diff --git a/standard/inc/StdMeshMaterial.h b/standard/inc/StdMeshMaterial.h index cc1def5cf..74590a8bc 100644 --- a/standard/inc/StdMeshMaterial.h +++ b/standard/inc/StdMeshMaterial.h @@ -50,7 +50,7 @@ protected: class StdMeshMaterialTextureLoader { public: - virtual bool operator()(const char* filename, CPNGFile& dest) = 0; + virtual bool LoadTexture(const char* filename, CPNGFile& dest) = 0; }; class StdMeshMaterialTextureUnit diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp new file mode 100644 index 000000000..614e8fd34 --- /dev/null +++ b/standard/src/StdMesh.cpp @@ -0,0 +1,229 @@ +/* + * OpenClonk, http://www.openclonk.org + * + * Copyright (c) 2009 Armin Burgmeier + * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de + * + * Portions might be copyrighted by other authors who have contributed + * to OpenClonk. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * See isc_license.txt for full license and disclaimer. + * + * "Clonk" is a registered trademark of Matthes Bender. + * See clonk_trademark_license.txt for full license. + */ + +#include + +StdMeshError::StdMeshError(const StdStrBuf& message, const char* file, unsigned int line) +{ + Buf.Format("%s[%u]: %s", file, line, message.getData()); +} + +void StdMeshMatrix::SetIdentity() +{ + a[0][0] = 1.0f; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = 0.0f; + a[1][0] = 0.0f; a[1][1] = 1.0f; a[1][2] = 0.0f; a[1][3] = 0.0f; + a[2][0] = 0.0f; a[2][1] = 0.0f; a[2][2] = 1.0f; a[2][3] = 0.0f; +} + +void StdMeshMatrix::SetTranslate(float dx, float dy, float dz) +{ + a[0][0] = 1.0f; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = dx; + a[1][0] = 0.0f; a[1][1] = 1.0f; a[1][2] = 0.0f; a[1][3] = dy; + a[2][0] = 0.0f; a[2][1] = 0.0f; a[2][2] = 1.0f; a[2][3] = dz; +} + +void StdMeshMatrix::SetScale(float sx, float sy, float sz) +{ + a[0][0] = sx; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = 0.0f; + a[1][0] = 0.0f; a[1][1] = sy; a[1][2] = 0.0f; a[1][3] = 0.0f; + a[2][0] = 0.0f; a[2][1] = 0.0f; a[2][2] = sz; a[2][3] = 0.0f; +} + +void StdMeshMatrix::SetRotate(float angle, float rx, float ry, float rz) +{ + // We do normalize the rx,ry,rz vector here: This is only required for + // precalculations anyway, thus not time-critical. + float abs = sqrt(rx*rx+ry*ry+rz*rz); + rx/=abs; ry/=abs; rz/=abs; + float c = cos(angle), s = sin(angle); + + a[0][0] = rx*rx*(1-c)+c; a[0][1] = rx*ry*(1-c)-rz*s; a[0][2] = rx*rz*(1-c)+ry*s; a[0][3] = 0.0f; + a[1][0] = ry*rx*(1-c)+rz*s; a[1][1] = ry*ry*(1-c)+c; a[1][2] = ry*rz*(1-c)-rx*s; a[1][3] = 0.0f; + a[2][0] = rz*rx*(1-c)-ry*s; a[2][1] = ry*rz*(1-c)+rx*s; a[2][2] = rz*rz*(1-c)+c; a[2][3] = 0.0f; +} + +void StdMeshMatrix::Mul(const StdMeshMatrix& other) +{ + StdMeshMatrix old(*this); + + a[0][0] = old.a[0][0]*other.a[0][0] + old.a[0][1]*other.a[1][0] + old.a[0][2]*other.a[2][0]; + a[1][0] = old.a[1][0]*other.a[0][0] + old.a[1][1]*other.a[1][0] + old.a[1][2]*other.a[2][0]; + a[2][0] = old.a[2][0]*other.a[0][0] + old.a[2][1]*other.a[1][0] + old.a[2][2]*other.a[2][0]; + + a[0][1] = old.a[0][0]*other.a[0][1] + old.a[0][1]*other.a[1][1] + old.a[0][2]*other.a[2][1]; + a[1][1] = old.a[1][0]*other.a[0][1] + old.a[1][1]*other.a[1][1] + old.a[1][2]*other.a[2][1]; + a[2][1] = old.a[2][0]*other.a[0][1] + old.a[2][1]*other.a[1][1] + old.a[2][2]*other.a[2][1]; + + a[0][2] = old.a[0][0]*other.a[0][2] + old.a[0][1]*other.a[1][2] + old.a[0][2]*other.a[2][2]; + a[1][2] = old.a[1][0]*other.a[0][2] + old.a[1][1]*other.a[1][2] + old.a[1][2]*other.a[2][2]; + a[2][2] = old.a[2][0]*other.a[0][2] + old.a[2][1]*other.a[1][2] + old.a[2][2]*other.a[2][2]; + + a[0][3] = old.a[0][0]*other.a[0][3] + old.a[0][1]*other.a[1][3] + old.a[0][2]*other.a[2][3] + old.a[0][3]; + a[1][3] = old.a[1][0]*other.a[0][3] + old.a[1][1]*other.a[1][3] + old.a[1][2]*other.a[2][3] + old.a[1][3]; + a[2][3] = old.a[2][0]*other.a[0][3] + old.a[2][1]*other.a[1][3] + old.a[2][2]*other.a[2][3] + old.a[2][3]; +} + +void StdMeshMatrix::Mul(float f) +{ + a[0][0] *= f; + a[0][1] *= f; + a[0][2] *= f; + a[0][3] *= f; + a[1][0] *= f; + a[1][1] *= f; + a[1][2] *= f; + a[1][3] *= f; + a[2][0] *= f; + a[2][1] *= f; + a[2][2] *= f; + a[2][3] *= f; +} + +void StdMeshMatrix::Add(const StdMeshMatrix& other) +{ + a[0][0] += other.a[0][0]; + a[0][1] += other.a[0][1]; + a[0][2] += other.a[0][2]; + a[0][3] += other.a[0][3]; + a[1][0] += other.a[1][0]; + a[1][1] += other.a[1][1]; + a[1][2] += other.a[1][2]; + a[1][3] += other.a[1][3]; + a[2][0] += other.a[2][0]; + a[2][1] += other.a[2][1]; + a[2][2] += other.a[2][2]; + a[2][3] += other.a[2][3]; +} + +void StdMeshMatrix::Transform(const StdMeshMatrix& other) +{ + // StdMeshMatrix blah(other); + // bla.Mul(*this); + // *this = bla; + + StdMeshMatrix old(*this); + + a[0][0] = other.a[0][0]*old.a[0][0] + other.a[0][1]*old.a[1][0] + other.a[0][2]*old.a[2][0]; + a[1][0] = other.a[1][0]*old.a[0][0] + other.a[1][1]*old.a[1][0] + other.a[1][2]*old.a[2][0]; + a[2][0] = other.a[2][0]*old.a[0][0] + other.a[2][1]*old.a[1][0] + other.a[2][2]*old.a[2][0]; + + a[0][1] = other.a[0][0]*old.a[0][1] + other.a[0][1]*old.a[1][1] + other.a[0][2]*old.a[2][1]; + a[1][1] = other.a[1][0]*old.a[0][1] + other.a[1][1]*old.a[1][1] + other.a[1][2]*old.a[2][1]; + a[2][1] = other.a[2][0]*old.a[0][1] + other.a[2][1]*old.a[1][1] + other.a[2][2]*old.a[2][1]; + + a[0][2] = other.a[0][0]*old.a[0][2] + other.a[0][1]*old.a[1][2] + other.a[0][2]*old.a[2][2]; + a[1][2] = other.a[1][0]*old.a[0][2] + other.a[1][1]*old.a[1][2] + other.a[1][2]*old.a[2][2]; + a[2][2] = other.a[2][0]*old.a[0][2] + other.a[2][1]*old.a[1][2] + other.a[2][2]*old.a[2][2]; + + a[0][3] = other.a[0][0]*old.a[0][3] + other.a[0][1]*old.a[1][3] + other.a[0][2]*old.a[2][3] + other.a[0][3]; + a[1][3] = other.a[1][0]*old.a[0][3] + other.a[1][1]*old.a[1][3] + other.a[1][2]*old.a[2][3] + other.a[1][3]; + a[2][3] = other.a[2][0]*old.a[0][3] + other.a[2][1]*old.a[1][3] + other.a[2][2]*old.a[2][3] + other.a[2][3]; +} + +void StdMeshVertex::Transform(const StdMeshMatrix& trans) +{ + StdMeshVertex old(*this); + + x = trans(0,0)*old.x + trans(0,1)*old.y + trans(0,2)*old.z + trans(0,3); + y = trans(1,0)*old.x + trans(1,1)*old.y + trans(1,2)*old.z + trans(0,3); + z = trans(2,0)*old.x + trans(2,1)*old.y + trans(2,2)*old.z + trans(0,3); + nx = trans(0,0)*old.nx + trans(0,1)*old.ny + trans(0,2)*old.nz; + ny = trans(1,0)*old.nx + trans(1,1)*old.ny + trans(0,2)*old.nz; + nz = trans(2,0)*old.nx + trans(2,1)*old.ny + trans(2,2)*old.nz; +} + +#if 0 +void StdMeshVertex::Mul(float f) +{ + x *= f; + y *= f; + z *= f; + + // We also multiplicate normals because we expect this to happen in + // an expression such as a*v1 + (1-a)*v2 which would ensure normalization + // of the normals again. + nx *= f; + ny *= f; + nz *= f; +} + +void StdMeshVertex::Add(const StdMeshVertex& other) +{ + x += other.x; + y += other.y; + z += other.z; + + nx += other.nx; + ny += other.ny; + nz += other.nz; +} +#endif + +StdMeshMatrix StdMeshTrack::GetTransformAt(float time) const +{ + std::map::const_iterator iter = Frames.lower_bound(time); + + // If this points to end(), then either + // a) time > animation length + // b) The track does not include a frame for the very end of the animation + // Both is considered an error + assert(iter != Frames.end()); + + if(iter == Frames.begin()) + return iter->second.Trans; + + std::map::const_iterator prev_iter = iter; + -- prev_iter; + + float dt = iter->first - prev_iter->first; + float weight1 = (time - prev_iter->first) / dt; + float weight2 = (iter->first - time) / dt; + + assert(weight1 >= 0 && weight2 >= 0 && weight1 <= 1 && weight2 <= 1); + assert(fabs(weight1 + weight2 - 1) < 1e-6); + + StdMeshMatrix trans1 = iter->second.Trans; + StdMeshMatrix trans2 = prev_iter->second.Trans; + trans1.Mul(weight1); + trans2.Mul(weight2); + + trans1.Add(trans2); + return trans1; +} + +StdMeshAnimation::~StdMeshAnimation() +{ + for(unsigned int i = 0; i < Tracks.size(); ++i) + delete Tracks[i]; +} + +StdMesh::StdMesh(): + Material(NULL) +{ +} + +StdMesh::~StdMesh() +{ + for(unsigned int i = 0; i < Bones.size(); ++i) + delete Bones[i]; +} + +void StdMesh::InitXML(const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager) +{ + +} diff --git a/standard/src/StdMeshMaterial.cpp b/standard/src/StdMeshMaterial.cpp index 482d60eb8..3c470698d 100644 --- a/standard/src/StdMeshMaterial.cpp +++ b/standard/src/StdMeshMaterial.cpp @@ -229,7 +229,7 @@ void StdMeshMaterialTextureUnit::Load(StdMeshMaterialParserCtx& ctx) ctx.AdvanceRequired(token_name, TOKEN_IDTF); CPNGFile png; - if(!ctx.TextureLoader(token_name.getData(), png)) + if(!ctx.TextureLoader.LoadTexture(token_name.getData(), png)) ctx.Error(StdStrBuf("Could not load texture '") + token_name + "'"); if(png.iWdt != png.iHgt)