From 639b40bbb624c0b791b8d63ae785062270d05b45 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Mon, 6 Jul 2009 23:45:44 +0200 Subject: [PATCH 01/18] Added StdMeshMaterial --- Makefile.am | 2 + standard/inc/StdBuf.h | 10 + standard/inc/StdMeshMaterial.h | 129 ++++++++++ standard/src/StdMeshMaterial.cpp | 405 +++++++++++++++++++++++++++++++ 4 files changed, 546 insertions(+) create mode 100644 standard/inc/StdMeshMaterial.h create mode 100644 standard/src/StdMeshMaterial.cpp diff --git a/Makefile.am b/Makefile.am index 2da972a24..eb57372f2 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/StdMeshMaterial.cpp \ standard/src/StdNoGfx.cpp \ standard/src/StdPNG.cpp \ standard/src/StdRegistry.cpp \ @@ -462,6 +463,7 @@ libstandard_a_SOURCES = \ standard/inc/StdFont.h \ standard/inc/StdGL.h \ standard/inc/StdMarkup.h \ + standard/inc/StdMeshMaterial.h \ standard/inc/StdNoGfx.h \ standard/inc/StdPNG.h \ standard/inc/StdRandom.h \ diff --git a/standard/inc/StdBuf.h b/standard/inc/StdBuf.h index e54e854d1..4333ca23c 100644 --- a/standard/inc/StdBuf.h +++ b/standard/inc/StdBuf.h @@ -676,6 +676,16 @@ public: }; +#if 0 +// const char* + StdStrBuf +inline StdStrBuf operator + (const char* szString, const StdStrBuf& Buf2) +{ + StdStrBuf Buf(szString); + Buf.Append(Buf2); + return Buf; +} +#endif + // Wrappers extern StdStrBuf FormatString(const char *szFmt, ...) GNUC_FORMAT_ATTRIBUTE; extern StdStrBuf FormatStringV(const char *szFmt, va_list args); diff --git a/standard/inc/StdMeshMaterial.h b/standard/inc/StdMeshMaterial.h new file mode 100644 index 000000000..216ff46f3 --- /dev/null +++ b/standard/inc/StdMeshMaterial.h @@ -0,0 +1,129 @@ +/* + * 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_StdMeshMaterial +#define INC_StdMeshMaterial + +#include +#include +#include +#include + +#include +#include + +// TODO: Support more features of OGRE material scripts +// Refer to http://www.ogre3d.org/docs/manual/manual_14.html + +class StdMeshMaterialParserCtx; + +class StdMeshMaterialError: public std::exception +{ +public: + StdMeshMaterialError(const StdStrBuf& message, const char* file, unsigned int line); + virtual ~StdMeshMaterialError() throw() {} + + virtual const char* what() const throw() { return Buf.getData(); } + +protected: + StdStrBuf Buf; +}; + +// Interface to load textures. Given a texture filename occuring in the +// material script, this should load the texture from wherever the material +// script is actually loaded, for example from a C4Group. +class StdMeshMaterialTextureLoader +{ +public: + virtual bool operator()(const char* filename, CPNGFile& dest) = 0; +}; + +class StdMeshMaterialTextureUnit +{ +public: + StdMeshMaterialTextureUnit(); + ~StdMeshMaterialTextureUnit(); + + void Load(StdMeshMaterialParserCtx& ctx); + + CTexRef* Texture; +}; + +class StdMeshMaterialPass +{ +public: + StdMeshMaterialPass(); + void Load(StdMeshMaterialParserCtx& ctx); + + std::vector TextureUnits; + + float Ambient[4]; + float Diffuse[4]; + float Specular[4]; + float Emissive[4]; + float Shininess; +}; + +class StdMeshMaterialTechnique +{ +public: + void Load(StdMeshMaterialParserCtx& ctx); + + std::vector Passes; +}; + +class StdMeshMaterial +{ +public: + StdMeshMaterial(); + void Load(StdMeshMaterialParserCtx& ctx); + + // Location the Material was loaded from + StdStrBuf FileName; + unsigned int Line; + + // Material name + StdStrBuf Name; + + // Not currently used in Clonk, but don't fail when we see this in a + // Material script: + bool ReceiveShadows; + + // Available techniques + std::vector Techniques; +}; + +class StdMeshMatManager +{ +public: + // Parse a material script file, and add the materials to the manager. + // filename may be NULL if the source is not a file. It will only be used + // for error messages. + // Throws StdMeshMaterialError. + void Parse(const char* mat_script, const char* filename, StdMeshMaterialTextureLoader& tex_loader); + + // Get material by name. NULL if there is no such material with this name. + const StdMeshMaterial* GetMaterial(const char* material_name) const; + +private: + std::map Materials; +}; + +#endif + +// vim: et ts=2 sw=2 diff --git a/standard/src/StdMeshMaterial.cpp b/standard/src/StdMeshMaterial.cpp new file mode 100644 index 000000000..6c0b10f9a --- /dev/null +++ b/standard/src/StdMeshMaterial.cpp @@ -0,0 +1,405 @@ +/* + * 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 + +#include + +StdMeshMaterialError::StdMeshMaterialError(const StdStrBuf& message, const char* file, unsigned int line) +{ + Buf.Format("%s[%u]: %s", file, line, message.getData()); +} + +enum Token +{ + TOKEN_IDTF, + TOKEN_BRACE_OPEN, + TOKEN_BRACE_CLOSE, + TOKEN_COLON, + TOKEN_EOF +}; + +class StdMeshMaterialParserCtx +{ +public: + StdMeshMaterialParserCtx(const char* mat_script, const char* filename, StdMeshMaterialTextureLoader& tex_loader); + + void SkipWhitespace(); + Token Peek(StdStrBuf& name); + Token Advance(StdStrBuf& name); + Token AdvanceNonEOF(StdStrBuf& name); + Token AdvanceRequired(StdStrBuf& name, Token expect); + Token AdvanceRequired(StdStrBuf& name, Token expect1, Token expect2); + float AdvanceFloat(); + bool AdvanceFloatOptional(float& value); + bool AdvanceBoolean(); + void Error(const StdStrBuf& message); + void ErrorUnexpectedIdentifier(const StdStrBuf& identifier); + + // Current parsing data + unsigned int Line; + const char* Script; + + StdStrBuf FileName; + StdMeshMaterialTextureLoader& TextureLoader; +}; + +StdMeshMaterialParserCtx::StdMeshMaterialParserCtx(const char* mat_script, const char* filename, StdMeshMaterialTextureLoader& tex_loader): + Line(0), Script(mat_script), FileName(filename), TextureLoader(tex_loader) +{ +} + +void StdMeshMaterialParserCtx::SkipWhitespace() +{ + while(isspace(*Script)) + { + if(*Script == '\n') ++Line; + ++Script; + } +} + +Token StdMeshMaterialParserCtx::Peek(StdStrBuf& name) +{ + SkipWhitespace(); + + const char* before = Script; + Token tok = Advance(name); + Script = before; + return tok; +} + +Token StdMeshMaterialParserCtx::Advance(StdStrBuf& name) +{ + SkipWhitespace(); + + switch(*Script) + { + case '\0': + name.Clear(); + return TOKEN_EOF; + case '{': + ++Script; + name = "{"; + return TOKEN_BRACE_OPEN; + case '}': + ++Script; + name = "}"; + return TOKEN_BRACE_CLOSE; + case ':': + ++Script; + name = ":"; + return TOKEN_COLON; + default: + const char* begin = Script; + // Advance to next whitespace + do { ++Script; } while(!isspace(*Script) && *Script != '{' && *Script != '}' && *Script != ':'); + name.Copy(begin, Script - begin); + return TOKEN_IDTF; + } +} + +Token StdMeshMaterialParserCtx::AdvanceNonEOF(StdStrBuf& name) +{ + Token token = Advance(name); + if(token == TOKEN_EOF) Error(StdStrBuf("Unexpected end of file")); + return token; +} + +Token StdMeshMaterialParserCtx::AdvanceRequired(StdStrBuf& name, Token expect) +{ + Token token = AdvanceNonEOF(name); + // TODO: Explain what was actually expected + if(token != expect) Error(StdStrBuf("'") + name + "' unexpected"); + return token; +} + +Token StdMeshMaterialParserCtx::AdvanceRequired(StdStrBuf& name, Token expect1, Token expect2) +{ + Token token = AdvanceNonEOF(name); + // TODO: Explain what was actually expected + if(token != expect1 && token != expect2) + Error(StdStrBuf("'") + name + "' unexpected"); + return token; +} + +float StdMeshMaterialParserCtx::AdvanceFloat() +{ + StdStrBuf buf; + AdvanceRequired(buf, TOKEN_IDTF); + char* end; + float f = strtof(buf.getData(), &end); + if(*end != '\0') Error(StdStrBuf("Floating point value expected")); + return f; +} + +bool StdMeshMaterialParserCtx::AdvanceFloatOptional(float& value) +{ + StdStrBuf buf; + Token tok = Peek(buf); + + if(tok == TOKEN_IDTF && isdigit(buf[0])) + { + value = AdvanceFloat(); + return true; + } + + return false; +} + +bool StdMeshMaterialParserCtx::AdvanceBoolean() +{ + StdStrBuf buf; + AdvanceRequired(buf, TOKEN_IDTF); + if(buf == "on") return true; + if(buf == "off") return false; + Error(StdStrBuf("Expected either 'on' or 'off', but not '") + buf + "'"); + return false; // Never reached +} + +void StdMeshMaterialParserCtx::Error(const StdStrBuf& message) +{ + throw StdMeshMaterialError(message, FileName.getData(), Line); +} + +void StdMeshMaterialParserCtx::ErrorUnexpectedIdentifier(const StdStrBuf& identifier) +{ + Error(StdStrBuf("Unexpected identifier: '") + identifier + "'"); +} + +StdMeshMaterialTextureUnit::StdMeshMaterialTextureUnit(): Texture(NULL) {} +StdMeshMaterialTextureUnit::~StdMeshMaterialTextureUnit() { delete Texture; } + +void StdMeshMaterialTextureUnit::Load(StdMeshMaterialParserCtx& ctx) +{ + Token token; + StdStrBuf token_name; + while((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF) + { + if(token_name == "texture") + { + ctx.AdvanceRequired(token_name, TOKEN_IDTF); + + CPNGFile png; + if(!ctx.TextureLoader(token_name.getData(), png)) + ctx.Error(StdStrBuf("Could not load texture '") + token_name + "'"); + + if(png.iWdt != png.iHgt) + ctx.Error(StdStrBuf("Texture '") + token_name + "' is not quadratic"); + + Texture = new CTexRef(png.iWdt, false); + Texture->Lock(); + for(unsigned int y = 0; y < png.iHgt; ++y) + for(unsigned int x = 0; x < png.iWdt; ++x) + Texture->SetPix4(x, y, png.GetPix(x, y)); + Texture->Unlock(); + } + else + ctx.ErrorUnexpectedIdentifier(token_name); + } + + if(token != TOKEN_BRACE_CLOSE) + ctx.Error(StdStrBuf("'") + token_name.getData() + "' unexpected"); +} + +StdMeshMaterialPass::StdMeshMaterialPass() +{ + Ambient[0] = Ambient[1] = Ambient[2] = Ambient[3] = 1.0f; + Diffuse[0] = Diffuse[1] = Diffuse[2] = Diffuse[3] = 1.0f; + Specular[0] = Specular[1] = Specular[2] = Specular[3] = 0.0f; + Emissive[0] = Emissive[1] = Emissive[2] = Emissive[3] = 0.0f; + Shininess = 0.0f; +} + +void StdMeshMaterialPass::Load(StdMeshMaterialParserCtx& ctx) +{ + Token token; + StdStrBuf token_name; + while((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF) + { + if(token_name == "texture_unit") + { + // TODO: Can there be an optional name? + ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN); + TextureUnits.push_back(StdMeshMaterialTextureUnit()); + TextureUnits.back().Load(ctx); + } + else if(token_name == "ambient") + { + Ambient[0] = ctx.AdvanceFloat(); + Ambient[1] = ctx.AdvanceFloat(); + Ambient[2] = ctx.AdvanceFloat(); + ctx.AdvanceFloatOptional(Ambient[3]); + } + else if(token_name == "diffuse") + { + Diffuse[0] = ctx.AdvanceFloat(); + Diffuse[1] = ctx.AdvanceFloat(); + Diffuse[2] = ctx.AdvanceFloat(); + ctx.AdvanceFloatOptional(Diffuse[3]); + } + else if(token_name == "specular") + { + Diffuse[0] = ctx.AdvanceFloat(); + Diffuse[1] = ctx.AdvanceFloat(); + Diffuse[2] = ctx.AdvanceFloat(); + + // The fourth argument is optional, not the fifth: + float diffuse3 = ctx.AdvanceFloat(); + + float shininess; + if(ctx.AdvanceFloatOptional(shininess)) + { + Diffuse[3] = diffuse3; + Shininess = shininess; + } + else + { + Shininess = diffuse3; + } + } + else if(token_name == "emissive") + { + Emissive[0] = ctx.AdvanceFloat(); + Emissive[1] = ctx.AdvanceFloat(); + Emissive[2] = ctx.AdvanceFloat(); + ctx.AdvanceFloatOptional(Emissive[3]); + } + else + ctx.ErrorUnexpectedIdentifier(token_name); + } + + if(token != TOKEN_BRACE_CLOSE) + ctx.Error(StdStrBuf("'") + token_name.getData() + "' unexpected"); +} + +void StdMeshMaterialTechnique::Load(StdMeshMaterialParserCtx& ctx) +{ + Token token; + StdStrBuf token_name; + while((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF) + { + if(token_name == "pass") + { + // TODO: Can there be an optional name? + ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN); + Passes.push_back(StdMeshMaterialPass()); + Passes.back().Load(ctx); + } + else + ctx.ErrorUnexpectedIdentifier(token_name); + } + + if(token != TOKEN_BRACE_CLOSE) + ctx.Error(StdStrBuf("'") + token_name.getData() + "' unexpected"); +} + +StdMeshMaterial::StdMeshMaterial(): + Line(0), ReceiveShadows(true) +{ +} + +void StdMeshMaterial::Load(StdMeshMaterialParserCtx& ctx) +{ + Token token; + StdStrBuf token_name; + while((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF) + { + if(token_name == "technique") + { + // TODO: Can there be an optional name? + ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN); + Techniques.push_back(StdMeshMaterialTechnique()); + Techniques.back().Load(ctx); + } + else if(token_name == "receive_shadows") + { + ReceiveShadows = ctx.AdvanceBoolean(); + } + else + ctx.ErrorUnexpectedIdentifier(token_name); + } + + if(token != TOKEN_BRACE_CLOSE) + ctx.Error(StdStrBuf("'") + token_name.getData() + "' unexpected"); +} + +void StdMeshMatManager::Parse(const char* mat_script, const char* filename, StdMeshMaterialTextureLoader& tex_loader) +{ + StdMeshMaterialParserCtx ctx(mat_script, filename, tex_loader); + + Token token; + StdStrBuf token_name; + while((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF) + { + if(token_name == "material") + { + // Read name + StdStrBuf material_name; + ctx.AdvanceRequired(material_name, TOKEN_IDTF); + + // Check for uniqueness + std::map::iterator iter = Materials.find(material_name); + if(iter != Materials.end()) + ctx.Error(FormatString("Material with name '%s' is already defined in %s:%u", material_name.getData(), iter->second.FileName.getData(), iter->second.Line)); + + // Check if there is a parent given + Token next = ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN, TOKEN_COLON); + // Read parent name, if any + StdMeshMaterial* parent = NULL; + if(next == TOKEN_COLON) + { + // Note that if there is a parent, then it needs to be loaded + // already. This currently makes only sense when its defined above + // in the same material script file or in a parent definition. + // We could later support material scripts in the System.c4g. + StdStrBuf parent_name; + ctx.AdvanceRequired(parent_name, TOKEN_IDTF); + ctx.AdvanceRequired(token_name, TOKEN_BRACE_OPEN); + + iter = Materials.find(parent_name); + if(iter == Materials.end()) + ctx.Error(StdStrBuf("Parent material '") + parent_name + "' does not exist (or is not yet loaded)"); + parent = &iter->second; + } + + // Copy properties from parent if one is given, otherwise + // default-construct the material. + StdMeshMaterial& mat = Materials.insert(std::make_pair(material_name, parent ? StdMeshMaterial(*parent) : StdMeshMaterial())).first->second; + // Set/Overwrite source and name + mat.Name = material_name; + mat.FileName = ctx.FileName; + mat.Line = ctx.Line; + + mat.Load(ctx); + } + else + ctx.ErrorUnexpectedIdentifier(token_name); + } + + if(token != TOKEN_EOF) + ctx.Error(StdStrBuf("'") + token_name.getData() + "' unexpected"); +} + +const StdMeshMaterial* StdMeshMatManager::GetMaterial(const char* material_name) const +{ + std::map::const_iterator iter = Materials.find(StdStrBuf(material_name)); + if(iter == Materials.end()) return NULL; + return &iter->second; +} + +// vim: et ts=2 sw=2 From dfcc0207b3cfa19642249a6a3ea2fcee3f4a582f Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Wed, 8 Jul 2009 22:27:29 +0200 Subject: [PATCH 02/18] Fixed inheriting materials using textures --- standard/inc/StdMeshMaterial.h | 20 +++++++++++++- standard/src/StdMeshMaterial.cpp | 47 ++++++++++++++++++++++++++++---- 2 files changed, 60 insertions(+), 7 deletions(-) diff --git a/standard/inc/StdMeshMaterial.h b/standard/inc/StdMeshMaterial.h index 216ff46f3..cc1def5cf 100644 --- a/standard/inc/StdMeshMaterial.h +++ b/standard/inc/StdMeshMaterial.h @@ -56,12 +56,30 @@ public: class StdMeshMaterialTextureUnit { public: + // Ref-counted texture. When a meterial inherits from one which contains + // a TextureUnit, then they will share the same CTexRef. + class TexRef + { + public: + TexRef(unsigned int size); + ~TexRef(); + + unsigned int RefCount; + CTexRef Tex; + }; + StdMeshMaterialTextureUnit(); + StdMeshMaterialTextureUnit(const StdMeshMaterialTextureUnit& other); ~StdMeshMaterialTextureUnit(); + StdMeshMaterialTextureUnit& operator=(const StdMeshMaterialTextureUnit&); + void Load(StdMeshMaterialParserCtx& ctx); - CTexRef* Texture; + const CTexRef& GetTexture() const { return Texture->Tex; } + +private: + TexRef* Texture; }; class StdMeshMaterialPass diff --git a/standard/src/StdMeshMaterial.cpp b/standard/src/StdMeshMaterial.cpp index 6c0b10f9a..482d60eb8 100644 --- a/standard/src/StdMeshMaterial.cpp +++ b/standard/src/StdMeshMaterial.cpp @@ -181,8 +181,42 @@ void StdMeshMaterialParserCtx::ErrorUnexpectedIdentifier(const StdStrBuf& identi Error(StdStrBuf("Unexpected identifier: '") + identifier + "'"); } -StdMeshMaterialTextureUnit::StdMeshMaterialTextureUnit(): Texture(NULL) {} -StdMeshMaterialTextureUnit::~StdMeshMaterialTextureUnit() { delete Texture; } +StdMeshMaterialTextureUnit::TexRef::TexRef(unsigned int size): + RefCount(1), Tex(size, false) +{ +} + +StdMeshMaterialTextureUnit::TexRef::~TexRef() +{ + assert(RefCount == 0); +} + +StdMeshMaterialTextureUnit::StdMeshMaterialTextureUnit(): + Texture(NULL) +{ +} + +StdMeshMaterialTextureUnit::StdMeshMaterialTextureUnit(const StdMeshMaterialTextureUnit& other): + Texture(other.Texture) +{ + if(Texture) + ++Texture->RefCount; +} + +StdMeshMaterialTextureUnit::~StdMeshMaterialTextureUnit() +{ + if(Texture && !--Texture->RefCount) + delete Texture; +} + +StdMeshMaterialTextureUnit& StdMeshMaterialTextureUnit::operator=(const StdMeshMaterialTextureUnit& other) +{ + if(this == &other) return *this; + if(Texture) if(!--Texture->RefCount) delete Texture; + Texture = other.Texture; + if(Texture) ++Texture->RefCount; + return *this; +} void StdMeshMaterialTextureUnit::Load(StdMeshMaterialParserCtx& ctx) { @@ -201,12 +235,13 @@ void StdMeshMaterialTextureUnit::Load(StdMeshMaterialParserCtx& ctx) if(png.iWdt != png.iHgt) ctx.Error(StdStrBuf("Texture '") + token_name + "' is not quadratic"); - Texture = new CTexRef(png.iWdt, false); - Texture->Lock(); + Texture = new TexRef(png.iWdt); + + Texture->Tex.Lock(); for(unsigned int y = 0; y < png.iHgt; ++y) for(unsigned int x = 0; x < png.iWdt; ++x) - Texture->SetPix4(x, y, png.GetPix(x, y)); - Texture->Unlock(); + Texture->Tex.SetPix4(x, y, png.GetPix(x, y)); + Texture->Tex.Unlock(); } else ctx.ErrorUnexpectedIdentifier(token_name); From 9f35ffe470c22087e64f67e2f1aa6f206467b7d7 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Wed, 8 Jul 2009 22:53:49 +0200 Subject: [PATCH 03/18] Added TinyXML --- CMakeLists.txt | 8 + Makefile.am | 8 +- standard/tinyxml/tinystr.cpp | 115 ++ standard/tinyxml/tinystr.h | 304 +++++ standard/tinyxml/tinyxml.cpp | 1732 ++++++++++++++++++++++++++++ standard/tinyxml/tinyxml.h | 1511 ++++++++++++++++++++++++ standard/tinyxml/tinyxmlerror.cpp | 52 + standard/tinyxml/tinyxmlparser.cpp | 1572 +++++++++++++++++++++++++ 8 files changed, 5301 insertions(+), 1 deletion(-) create mode 100644 standard/tinyxml/tinystr.cpp create mode 100644 standard/tinyxml/tinystr.h create mode 100644 standard/tinyxml/tinyxml.cpp create mode 100644 standard/tinyxml/tinyxml.h create mode 100644 standard/tinyxml/tinyxmlerror.cpp create mode 100644 standard/tinyxml/tinyxmlparser.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index b9ff2961f..63e9bb475 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/StdMeshMaterial.cpp standard/src/StdNoGfx.cpp standard/src/StdPNG.cpp standard/src/StdRegistry.cpp @@ -424,6 +425,7 @@ add_library(standard STATIC standard/inc/StdFont.h standard/inc/StdGL.h standard/inc/StdMarkup.h + standard/inc/StdMeshMaterial.h standard/inc/StdNoGfx.h standard/inc/StdPNG.h standard/inc/StdRandom.h @@ -438,6 +440,12 @@ add_library(standard STATIC standard/inc/StdWindow.h standard/zlib/gzio.c standard/zlib/zutil.h + standard/tinyxml/tinystr.cpp + standard/tinyxml/tinyxml.cpp + standard/tinyxml/tinyxmlparser.cpp + standard/tinyxml/tinyxmlerror.cpp + standard/tinyxml/tinyxml.h + standard/tinyxml/tinystr.h ) include_directories( diff --git a/Makefile.am b/Makefile.am index eb57372f2..571b1ee28 100644 --- a/Makefile.am +++ b/Makefile.am @@ -477,7 +477,13 @@ libstandard_a_SOURCES = \ standard/inc/StdVideo.h \ standard/inc/StdWindow.h \ standard/zlib/gzio.c \ - standard/zlib/zutil.h + standard/zlib/zutil.h \ + standard/tinyxml/tinystr.cpp \ + standard/tinyxml/tinyxml.cpp \ + standard/tinyxml/tinyxmlparser.cpp \ + standard/tinyxml/tinyxmlerror.cpp \ + standard/tinyxml/tinyxml.h \ + standard/tinyxml/tinystr.h if WIN32 libstandard_a_SOURCES += standard/src/StdWindow.cpp standard/src/StdJoystick.cpp standard/inc/StdJoystick.h diff --git a/standard/tinyxml/tinystr.cpp b/standard/tinyxml/tinystr.cpp new file mode 100644 index 000000000..41252422a --- /dev/null +++ b/standard/tinyxml/tinystr.cpp @@ -0,0 +1,115 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Løvset, 7. April 2005. + */ + + +#ifndef TIXML_USE_STL + +#include "tinystr.h" + +// Error value for find primitive +const TiXmlString::size_type TiXmlString::npos = static_cast< size_type >(-1); + +// Null rep. +TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, '\0' }; + + +void TiXmlString::reserve (size_type cap) +{ + if (cap > capacity()) + { + TiXmlString tmp; + tmp.init(length(), cap); + memcpy(tmp.start(), data(), length()); + swap(tmp); + } +} + + +TiXmlString& TiXmlString::assign(const char* str, size_type len) +{ + size_type cap = capacity(); + if (len > cap || cap > 3*(len + 8)) + { + TiXmlString tmp; + tmp.init(len); + memcpy(tmp.start(), str, len); + swap(tmp); + } + else + { + memmove(start(), str, len); + set_size(len); + } + return *this; +} + + +TiXmlString& TiXmlString::append(const char* str, size_type len) +{ + size_type newsize = length() + len; + if (newsize > capacity()) + { + reserve (newsize + capacity()); + } + memmove(finish(), str, len); + set_size(newsize); + return *this; +} + + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b) +{ + TiXmlString tmp; + tmp.reserve(a.length() + b.length()); + tmp += a; + tmp += b; + return tmp; +} + +TiXmlString operator + (const TiXmlString & a, const char* b) +{ + TiXmlString tmp; + TiXmlString::size_type b_len = static_cast( strlen(b) ); + tmp.reserve(a.length() + b_len); + tmp += a; + tmp.append(b, b_len); + return tmp; +} + +TiXmlString operator + (const char* a, const TiXmlString & b) +{ + TiXmlString tmp; + TiXmlString::size_type a_len = static_cast( strlen(a) ); + tmp.reserve(a_len + b.length()); + tmp.append(a, a_len); + tmp += b; + return tmp; +} + + +#endif // TIXML_USE_STL diff --git a/standard/tinyxml/tinystr.h b/standard/tinyxml/tinystr.h new file mode 100644 index 000000000..ef7601e1a --- /dev/null +++ b/standard/tinyxml/tinystr.h @@ -0,0 +1,304 @@ +/* +www.sourceforge.net/projects/tinyxml +Original file by Yves Berquin. + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +/* + * THIS FILE WAS ALTERED BY Tyge Lovset, 7. April 2005. + * + * - completely rewritten. compact, clean, and fast implementation. + * - sizeof(TiXmlString) = pointer size (4 bytes on 32-bit systems) + * - fixed reserve() to work as per specification. + * - fixed buggy compares operator==(), operator<(), and operator>() + * - fixed operator+=() to take a const ref argument, following spec. + * - added "copy" constructor with length, and most compare operators. + * - added swap(), clear(), size(), capacity(), operator+(). + */ + +#ifndef TIXML_USE_STL + +#ifndef TIXML_STRING_INCLUDED +#define TIXML_STRING_INCLUDED + +#include +#include + +/* + TiXmlString is an emulation of a subset of the std::string template. + Its purpose is to allow compiling TinyXML on compilers with no or poor STL support. + Only the member functions relevant to the TinyXML project have been implemented. + The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase + a string and there's no more room, we allocate a buffer twice as big as we need. +*/ +class TiXmlString +{ + public : + // The size type used + typedef unsigned int size_type; + + // Error value for find primitive + static const size_type npos; // = -1; + + + // TiXmlString empty constructor + TiXmlString () : rep_(&nullrep_) + { + } + + // TiXmlString copy constructor + TiXmlString (const TiXmlString & copy) + { + init(copy.length()); + memcpy(start(), copy.data(), length()); + } + + // TiXmlString constructor, based on a string + TiXmlString (const char * copy) + { + init( static_cast( strlen(copy) )); + memcpy(start(), copy, length()); + } + + // TiXmlString constructor, based on a string + TiXmlString (const char * str, size_type len) + { + init(len); + memcpy(start(), str, len); + } + + // TiXmlString destructor + ~TiXmlString () + { + quit(); + } + + // = operator + TiXmlString& operator = (const char * copy) + { + return assign( copy, (size_type)strlen(copy)); + } + + // = operator + TiXmlString& operator = (const TiXmlString & copy) + { + return assign(copy.start(), copy.length()); + } + + + // += operator. Maps to append + TiXmlString& operator += (const char * suffix) + { + return append(suffix, static_cast( strlen(suffix) )); + } + + // += operator. Maps to append + TiXmlString& operator += (char single) + { + return append(&single, 1); + } + + // += operator. Maps to append + TiXmlString& operator += (const TiXmlString & suffix) + { + return append(suffix.data(), suffix.length()); + } + + + // Convert a TiXmlString into a null-terminated char * + const char * c_str () const { return rep_->str; } + + // Convert a TiXmlString into a char * (need not be null terminated). + const char * data () const { return rep_->str; } + + // Return the length of a TiXmlString + size_type length () const { return rep_->size; } + + // Alias for length() + size_type size () const { return rep_->size; } + + // Checks if a TiXmlString is empty + bool empty () const { return rep_->size == 0; } + + // Return capacity of string + size_type capacity () const { return rep_->capacity; } + + + // single char extraction + const char& at (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // [] operator + char& operator [] (size_type index) const + { + assert( index < length() ); + return rep_->str[ index ]; + } + + // find a char in a string. Return TiXmlString::npos if not found + size_type find (char lookup) const + { + return find(lookup, 0); + } + + // find a char in a string from an offset. Return TiXmlString::npos if not found + size_type find (char tofind, size_type offset) const + { + if (offset >= length()) return npos; + + for (const char* p = c_str() + offset; *p != '\0'; ++p) + { + if (*p == tofind) return static_cast< size_type >( p - c_str() ); + } + return npos; + } + + void clear () + { + //Lee: + //The original was just too strange, though correct: + // TiXmlString().swap(*this); + //Instead use the quit & re-init: + quit(); + init(0,0); + } + + /* Function to reserve a big amount of data when we know we'll need it. Be aware that this + function DOES NOT clear the content of the TiXmlString if any exists. + */ + void reserve (size_type cap); + + TiXmlString& assign (const char* str, size_type len); + + TiXmlString& append (const char* str, size_type len); + + void swap (TiXmlString& other) + { + Rep* r = rep_; + rep_ = other.rep_; + other.rep_ = r; + } + + private: + + void init(size_type sz) { init(sz, sz); } + void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; } + char* start() const { return rep_->str; } + char* finish() const { return rep_->str + rep_->size; } + + struct Rep + { + size_type size, capacity; + char str[1]; + }; + + void init(size_type sz, size_type cap) + { + if (cap) + { + // Lee: the original form: + // rep_ = static_cast(operator new(sizeof(Rep) + cap)); + // doesn't work in some cases of new being overloaded. Switching + // to the normal allocation, although use an 'int' for systems + // that are overly picky about structure alignment. + const size_type bytesNeeded = sizeof(Rep) + cap; + const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int ); + rep_ = reinterpret_cast( new int[ intsNeeded ] ); + + rep_->str[ rep_->size = sz ] = '\0'; + rep_->capacity = cap; + } + else + { + rep_ = &nullrep_; + } + } + + void quit() + { + if (rep_ != &nullrep_) + { + // The rep_ is really an array of ints. (see the allocator, above). + // Cast it back before delete, so the compiler won't incorrectly call destructors. + delete [] ( reinterpret_cast( rep_ ) ); + } + } + + Rep * rep_; + static Rep nullrep_; + +} ; + + +inline bool operator == (const TiXmlString & a, const TiXmlString & b) +{ + return ( a.length() == b.length() ) // optimization on some platforms + && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare +} +inline bool operator < (const TiXmlString & a, const TiXmlString & b) +{ + return strcmp(a.c_str(), b.c_str()) < 0; +} + +inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); } +inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; } +inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); } +inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); } + +inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; } +inline bool operator == (const char* a, const TiXmlString & b) { return b == a; } +inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); } +inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); } + +TiXmlString operator + (const TiXmlString & a, const TiXmlString & b); +TiXmlString operator + (const TiXmlString & a, const char* b); +TiXmlString operator + (const char* a, const TiXmlString & b); + + +/* + TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString. + Only the operators that we need for TinyXML have been developped. +*/ +class TiXmlOutStream : public TiXmlString +{ +public : + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const TiXmlString & in) + { + *this += in; + return *this; + } + + // TiXmlOutStream << operator. + TiXmlOutStream & operator << (const char * in) + { + *this += in; + return *this; + } + +} ; + +#endif // TIXML_STRING_INCLUDED +#endif // TIXML_USE_STL diff --git a/standard/tinyxml/tinyxml.cpp b/standard/tinyxml/tinyxml.cpp new file mode 100644 index 000000000..87066833e --- /dev/null +++ b/standard/tinyxml/tinyxml.cpp @@ -0,0 +1,1732 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include +#include "tinyxml.h" + +#ifdef TIXML_USE_STL +#include +#endif + + +bool TiXmlBase::condenseWhiteSpace = true; + +void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_OSTREAM* stream ) +{ + TIXML_STRING buffer; + PutString( str, &buffer ); + (*stream) << buffer; +} + +void TiXmlBase::PutString( const TIXML_STRING& str, TIXML_STRING* outString ) +{ + int i=0; + + while( i<(int)str.length() ) + { + unsigned char c = (unsigned char) str[i]; + + if ( c == '&' + && i < ( (int)str.length() - 2 ) + && str[i+1] == '#' + && str[i+2] == 'x' ) + { + // Hexadecimal character reference. + // Pass through unchanged. + // © -- copyright symbol, for example. + // + // The -1 is a bug fix from Rob Laveaux. It keeps + // an overflow from happening if there is no ';'. + // There are actually 2 ways to exit this loop - + // while fails (error case) and break (semicolon found). + // However, there is no mechanism (currently) for + // this function to return an error. + while ( i<(int)str.length()-1 ) + { + outString->append( str.c_str() + i, 1 ); + ++i; + if ( str[i] == ';' ) + break; + } + } + else if ( c == '&' ) + { + outString->append( entity[0].str, entity[0].strLength ); + ++i; + } + else if ( c == '<' ) + { + outString->append( entity[1].str, entity[1].strLength ); + ++i; + } + else if ( c == '>' ) + { + outString->append( entity[2].str, entity[2].strLength ); + ++i; + } + else if ( c == '\"' ) + { + outString->append( entity[3].str, entity[3].strLength ); + ++i; + } + else if ( c == '\'' ) + { + outString->append( entity[4].str, entity[4].strLength ); + ++i; + } + else if ( c < 32 ) + { + // Easy pass at non-alpha/numeric/symbol + // Below 32 is symbolic. + char buf[ 32 ]; + + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) ); + #else + sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) ); + #endif + + //*ME: warning C4267: convert 'size_t' to 'int' + //*ME: Int-Cast to make compiler happy ... + outString->append( buf, (int)strlen( buf ) ); + ++i; + } + else + { + //char realc = (char) c; + //outString->append( &realc, 1 ); + *outString += (char) c; // somewhat more efficient function call. + ++i; + } + } +} + + +// <-- Strange class for a bug fix. Search for STL_STRING_BUG +TiXmlBase::StringToBuffer::StringToBuffer( const TIXML_STRING& str ) +{ + buffer = new char[ str.length()+1 ]; + if ( buffer ) + { + strcpy( buffer, str.c_str() ); + } +} + + +TiXmlBase::StringToBuffer::~StringToBuffer() +{ + delete [] buffer; +} +// End strange bug fix. --> + + +TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase() +{ + parent = 0; + type = _type; + firstChild = 0; + lastChild = 0; + prev = 0; + next = 0; +} + + +TiXmlNode::~TiXmlNode() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } +} + + +void TiXmlNode::CopyTo( TiXmlNode* target ) const +{ + target->SetValue (value.c_str() ); + target->userData = userData; +} + + +void TiXmlNode::Clear() +{ + TiXmlNode* node = firstChild; + TiXmlNode* temp = 0; + + while ( node ) + { + temp = node; + node = node->next; + delete temp; + } + + firstChild = 0; + lastChild = 0; +} + + +TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node ) +{ + node->parent = this; + + node->prev = lastChild; + node->next = 0; + + if ( lastChild ) + lastChild->next = node; + else + firstChild = node; // it was an empty list. + + lastChild = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis ) +{ + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + + return LinkEndChild( node ); +} + + +TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ) +{ + if ( !beforeThis || beforeThis->parent != this ) + return 0; + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->next = beforeThis; + node->prev = beforeThis->prev; + if ( beforeThis->prev ) + { + beforeThis->prev->next = node; + } + else + { + assert( firstChild == beforeThis ); + firstChild = node; + } + beforeThis->prev = node; + return node; +} + + +TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ) +{ + if ( !afterThis || afterThis->parent != this ) + return 0; + + TiXmlNode* node = addThis.Clone(); + if ( !node ) + return 0; + node->parent = this; + + node->prev = afterThis; + node->next = afterThis->next; + if ( afterThis->next ) + { + afterThis->next->prev = node; + } + else + { + assert( lastChild == afterThis ); + lastChild = node; + } + afterThis->next = node; + return node; +} + + +TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ) +{ + if ( replaceThis->parent != this ) + return 0; + + TiXmlNode* node = withThis.Clone(); + if ( !node ) + return 0; + + node->next = replaceThis->next; + node->prev = replaceThis->prev; + + if ( replaceThis->next ) + replaceThis->next->prev = node; + else + lastChild = node; + + if ( replaceThis->prev ) + replaceThis->prev->next = node; + else + firstChild = node; + + delete replaceThis; + node->parent = this; + return node; +} + + +bool TiXmlNode::RemoveChild( TiXmlNode* removeThis ) +{ + if ( removeThis->parent != this ) + { + assert( 0 ); + return false; + } + + if ( removeThis->next ) + removeThis->next->prev = removeThis->prev; + else + lastChild = removeThis->prev; + + if ( removeThis->prev ) + removeThis->prev->next = removeThis->next; + else + firstChild = removeThis->next; + + delete removeThis; + return true; +} + +const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +TiXmlNode* TiXmlNode::FirstChild( const char * _value ) +{ + TiXmlNode* node; + for ( node = firstChild; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + + +const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::LastChild( const char * _value ) +{ + TiXmlNode* node; + for ( node = lastChild; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + +const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + +TiXmlNode* TiXmlNode::IterateChildren( TiXmlNode* previous ) +{ + if ( !previous ) + { + return FirstChild(); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling(); + } +} + +const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + +TiXmlNode* TiXmlNode::IterateChildren( const char * val, TiXmlNode* previous ) +{ + if ( !previous ) + { + return FirstChild( val ); + } + else + { + assert( previous->parent == this ); + return previous->NextSibling( val ); + } +} + +const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::NextSibling( const char * _value ) +{ + TiXmlNode* node; + for ( node = next; node; node = node->next ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + +const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const +{ + const TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + +TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) +{ + TiXmlNode* node; + for ( node = prev; node; node = node->prev ) + { + if ( strcmp( node->Value(), _value ) == 0 ) + return node; + } + return 0; +} + +void TiXmlElement::RemoveAttribute( const char * name ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + attributeSet.Remove( node ); + delete node; + } +} + +const TiXmlElement* TiXmlNode::FirstChildElement() const +{ + const TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::FirstChildElement() +{ + TiXmlNode* node; + + for ( node = FirstChild(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) +{ + TiXmlNode* node; + + for ( node = FirstChild( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +const TiXmlElement* TiXmlNode::NextSiblingElement() const +{ + const TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::NextSiblingElement() +{ + TiXmlNode* node; + + for ( node = NextSibling(); + node; + node = node->NextSibling() ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const +{ + const TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + +TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) +{ + TiXmlNode* node; + + for ( node = NextSibling( _value ); + node; + node = node->NextSibling( _value ) ) + { + if ( node->ToElement() ) + return node->ToElement(); + } + return 0; +} + + +const TiXmlDocument* TiXmlNode::GetDocument() const +{ + const TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + +TiXmlDocument* TiXmlNode::GetDocument() +{ + TiXmlNode* node; + + for( node = this; node; node = node->parent ) + { + if ( node->ToDocument() ) + return node->ToDocument(); + } + return 0; +} + +TiXmlElement::TiXmlElement (const char * _value) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} + + +#ifdef TIXML_USE_STL +TiXmlElement::TiXmlElement( const std::string& _value ) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + value = _value; +} +#endif + + +TiXmlElement::TiXmlElement( const TiXmlElement& copy) + : TiXmlNode( TiXmlNode::ELEMENT ) +{ + firstChild = lastChild = 0; + copy.CopyTo( this ); +} + + +void TiXmlElement::operator=( const TiXmlElement& base ) +{ + ClearThis(); + base.CopyTo( this ); +} + + +TiXmlElement::~TiXmlElement() +{ + ClearThis(); +} + + +void TiXmlElement::ClearThis() +{ + Clear(); + while( attributeSet.First() ) + { + TiXmlAttribute* node = attributeSet.First(); + attributeSet.Remove( node ); + delete node; + } +} + + +const char * TiXmlElement::Attribute( const char * name ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + + if ( node ) + return node->Value(); + + return 0; +} + + +const char * TiXmlElement::Attribute( const char * name, int* i ) const +{ + const char * s = Attribute( name ); + if ( i ) + { + if ( s ) + *i = atoi( s ); + else + *i = 0; + } + return s; +} + + +const char * TiXmlElement::Attribute( const char * name, double* d ) const +{ + const char * s = Attribute( name ); + if ( d ) + { + if ( s ) + *d = atof( s ); + else + *d = 0; + } + return s; +} + + +int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + return node->QueryIntValue( ival ); +} + + +int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const +{ + const TiXmlAttribute* node = attributeSet.Find( name ); + if ( !node ) + return TIXML_NO_ATTRIBUTE; + + return node->QueryDoubleValue( dval ); +} + + +void TiXmlElement::SetAttribute( const char * name, int val ) +{ + char buf[64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%d", val ); + #else + sprintf( buf, "%d", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetDoubleAttribute( const char * name, double val ) +{ + char buf[256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%f", val ); + #else + sprintf( buf, "%f", val ); + #endif + SetAttribute( name, buf ); +} + + +void TiXmlElement::SetAttribute( const char * name, const char * _value ) +{ + TiXmlAttribute* node = attributeSet.Find( name ); + if ( node ) + { + node->SetValue( _value ); + return; + } + + TiXmlAttribute* attrib = new TiXmlAttribute( name, _value ); + if ( attrib ) + { + attributeSet.Add( attrib ); + } + else + { + TiXmlDocument* document = GetDocument(); + if ( document ) document->SetError( TIXML_ERROR_OUT_OF_MEMORY, 0, 0, TIXML_ENCODING_UNKNOWN ); + } +} + +void TiXmlElement::Print( FILE* cfile, int depth ) const +{ + int i; + for ( i=0; iNext() ) + { + fprintf( cfile, " " ); + attrib->Print( cfile, depth ); + } + + // There are 3 different formatting approaches: + // 1) An element without children is printed as a node + // 2) An element with only a text child is printed as text + // 3) An element with children is printed on multiple lines. + TiXmlNode* node; + if ( !firstChild ) + { + fprintf( cfile, " />" ); + } + else if ( firstChild == lastChild && firstChild->ToText() ) + { + fprintf( cfile, ">" ); + firstChild->Print( cfile, depth + 1 ); + fprintf( cfile, "", value.c_str() ); + } + else + { + fprintf( cfile, ">" ); + + for ( node = firstChild; node; node=node->NextSibling() ) + { + if ( !node->ToText() ) + { + fprintf( cfile, "\n" ); + } + node->Print( cfile, depth+1 ); + } + fprintf( cfile, "\n" ); + for( i=0; i", value.c_str() ); + } +} + +void TiXmlElement::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<" << value; + + const TiXmlAttribute* attrib; + for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() ) + { + (*stream) << " "; + attrib->StreamOut( stream ); + } + + // If this node has children, give it a closing tag. Else + // make it an empty tag. + TiXmlNode* node; + if ( firstChild ) + { + (*stream) << ">"; + + for ( node = firstChild; node; node=node->NextSibling() ) + { + node->StreamOut( stream ); + } + (*stream) << ""; + } + else + { + (*stream) << " />"; + } +} + + +void TiXmlElement::CopyTo( TiXmlElement* target ) const +{ + // superclass: + TiXmlNode::CopyTo( target ); + + // Element class: + // Clone the attributes, then clone the children. + const TiXmlAttribute* attribute = 0; + for( attribute = attributeSet.First(); + attribute; + attribute = attribute->Next() ) + { + target->SetAttribute( attribute->Name(), attribute->Value() ); + } + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlElement::Clone() const +{ + TiXmlElement* clone = new TiXmlElement( Value() ); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +const char* TiXmlElement::GetText() const +{ + const TiXmlNode* child = this->FirstChild(); + if ( child ) { + const TiXmlText* childText = child->ToText(); + if ( childText ) { + return childText->Value(); + } + } + return 0; +} + + +TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + ClearError(); +} + +TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} + + +#ifdef TIXML_USE_STL +TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + tabsize = 4; + useMicrosoftBOM = false; + value = documentName; + ClearError(); +} +#endif + + +TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::DOCUMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDocument::operator=( const TiXmlDocument& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +bool TiXmlDocument::LoadFile( TiXmlEncoding encoding ) +{ + // See STL_STRING_BUG below. + StringToBuffer buf( value ); + + if ( buf.buffer && LoadFile( buf.buffer, encoding ) ) + return true; + + return false; +} + + +bool TiXmlDocument::SaveFile() const +{ + // See STL_STRING_BUG below. + StringToBuffer buf( value ); + + if ( buf.buffer && SaveFile( buf.buffer ) ) + return true; + + return false; +} + +bool TiXmlDocument::LoadFile( const char* filename, TiXmlEncoding encoding ) +{ + // Delete the existing data: + Clear(); + location.Clear(); + + // There was a really terrifying little bug here. The code: + // value = filename + // in the STL case, cause the assignment method of the std::string to + // be called. What is strange, is that the std::string had the same + // address as it's c_str() method, and so bad things happen. Looks + // like a bug in the Microsoft STL implementation. + // See STL_STRING_BUG above. + // Fixed with the StringToBuffer class. + value = filename; + + // reading in binary mode so that tinyxml can normalize the EOL + FILE* file = fopen( value.c_str (), "rb" ); + + if ( file ) + { + // Get the file size, so we can pre-allocate the string. HUGE speed impact. + long length = 0; + fseek( file, 0, SEEK_END ); + length = ftell( file ); + fseek( file, 0, SEEK_SET ); + + // Strange case, but good to handle up front. + if ( length == 0 ) + { + fclose( file ); + return false; + } + + // If we have a file, assume it is all one big XML file, and read it in. + // The document parser may decide the document ends sooner than the entire file, however. + TIXML_STRING data; + data.reserve( length ); + + // Subtle bug here. TinyXml did use fgets. But from the XML spec: + // 2.11 End-of-Line Handling + // + // + // ...the XML processor MUST behave as if it normalized all line breaks in external + // parsed entities (including the document entity) on input, before parsing, by translating + // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to + // a single #xA character. + // + // + // It is not clear fgets does that, and certainly isn't clear it works cross platform. + // Generally, you expect fgets to translate from the convention of the OS to the c/unix + // convention, and not work generally. + + /* + while( fgets( buf, sizeof(buf), file ) ) + { + data += buf; + } + */ + + char* buf = new char[ length+1 ]; + buf[0] = 0; + + if ( fread( buf, length, 1, file ) != 1 ) { + //if ( fread( buf, 1, length, file ) != (size_t)length ) { + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + fclose( file ); + return false; + } + fclose( file ); + + const char* lastPos = buf; + const char* p = buf; + + buf[length] = 0; + while( *p ) { + assert( p < (buf+length) ); + if ( *p == 0xa ) { + // Newline character. No special rules for this. Append all the characters + // since the last string, and include the newline. + data.append( lastPos, p-lastPos+1 ); // append, include the newline + ++p; // move past the newline + lastPos = p; // and point to the new buffer (may be 0) + assert( p <= (buf+length) ); + } + else if ( *p == 0xd ) { + // Carriage return. Append what we have so far, then + // handle moving forward in the buffer. + if ( (p-lastPos) > 0 ) { + data.append( lastPos, p-lastPos ); // do not add the CR + } + data += (char)0xa; // a proper newline + + if ( *(p+1) == 0xa ) { + // Carriage return - new line sequence + p += 2; + lastPos = p; + assert( p <= (buf+length) ); + } + else { + // it was followed by something else...that is presumably characters again. + ++p; + lastPos = p; + assert( p <= (buf+length) ); + } + } + else { + ++p; + } + } + // Handle any left over characters. + if ( p-lastPos ) { + data.append( lastPos, p-lastPos ); + } + delete [] buf; + buf = 0; + + Parse( data.c_str(), 0, encoding ); + + if ( Error() ) + return false; + else + return true; + } + SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN ); + return false; +} + +bool TiXmlDocument::SaveFile( const char * filename ) const +{ + // The old c stuff lives on... + FILE* fp = fopen( filename, "w" ); + if ( fp ) + { + if ( useMicrosoftBOM ) + { + const unsigned char TIXML_UTF_LEAD_0 = 0xefU; + const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; + const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + + fputc( TIXML_UTF_LEAD_0, fp ); + fputc( TIXML_UTF_LEAD_1, fp ); + fputc( TIXML_UTF_LEAD_2, fp ); + } + Print( fp, 0 ); + fclose( fp ); + return true; + } + return false; +} + + +void TiXmlDocument::CopyTo( TiXmlDocument* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->error = error; + target->errorDesc = errorDesc.c_str (); + + TiXmlNode* node = 0; + for ( node = firstChild; node; node = node->NextSibling() ) + { + target->LinkEndChild( node->Clone() ); + } +} + + +TiXmlNode* TiXmlDocument::Clone() const +{ + TiXmlDocument* clone = new TiXmlDocument(); + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlDocument::Print( FILE* cfile, int depth ) const +{ + const TiXmlNode* node; + for ( node=FirstChild(); node; node=node->NextSibling() ) + { + node->Print( cfile, depth ); + fprintf( cfile, "\n" ); + } +} + +void TiXmlDocument::StreamOut( TIXML_OSTREAM * out ) const +{ + const TiXmlNode* node; + for ( node=FirstChild(); node; node=node->NextSibling() ) + { + node->StreamOut( out ); + + // Special rule for streams: stop after the root element. + // The stream in code will only read one element, so don't + // write more than one. + if ( node->ToElement() ) + break; + } +} + + +const TiXmlAttribute* TiXmlAttribute::Next() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +TiXmlAttribute* TiXmlAttribute::Next() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( next->value.empty() && next->name.empty() ) + return 0; + return next; +} + +const TiXmlAttribute* TiXmlAttribute::Previous() const +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +TiXmlAttribute* TiXmlAttribute::Previous() +{ + // We are using knowledge of the sentinel. The sentinel + // have a value or name. + if ( prev->value.empty() && prev->name.empty() ) + return 0; + return prev; +} + +void TiXmlAttribute::Print( FILE* cfile, int /*depth*/ ) const +{ + TIXML_STRING n, v; + + PutString( name, &n ); + PutString( value, &v ); + + if (value.find ('\"') == TIXML_STRING::npos) + fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() ); + else + fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() ); +} + + +void TiXmlAttribute::StreamOut( TIXML_OSTREAM * stream ) const +{ + if (value.find( '\"' ) != TIXML_STRING::npos) + { + PutString( name, stream ); + (*stream) << "=" << "'"; + PutString( value, stream ); + (*stream) << "'"; + } + else + { + PutString( name, stream ); + (*stream) << "=" << "\""; + PutString( value, stream ); + (*stream) << "\""; + } +} + +int TiXmlAttribute::QueryIntValue( int* ival ) const +{ + if ( sscanf( value.c_str(), "%d", ival ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +int TiXmlAttribute::QueryDoubleValue( double* dval ) const +{ + if ( sscanf( value.c_str(), "%lf", dval ) == 1 ) + return TIXML_SUCCESS; + return TIXML_WRONG_TYPE; +} + +void TiXmlAttribute::SetIntValue( int _value ) +{ + char buf [64]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value); + #else + sprintf (buf, "%d", _value); + #endif + SetValue (buf); +} + +void TiXmlAttribute::SetDoubleValue( double _value ) +{ + char buf [256]; + #if defined(TIXML_SNPRINTF) + TIXML_SNPRINTF( buf, sizeof(buf), "%lf", _value); + #else + sprintf (buf, "%lf", _value); + #endif + SetValue (buf); +} + +int TiXmlAttribute::IntValue() const +{ + return atoi (value.c_str ()); +} + +double TiXmlAttribute::DoubleValue() const +{ + return atof (value.c_str ()); +} + + +TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::COMMENT ) +{ + copy.CopyTo( this ); +} + + +void TiXmlComment::operator=( const TiXmlComment& base ) +{ + Clear(); + base.CopyTo( this ); +} + + +void TiXmlComment::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + +void TiXmlComment::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << ""; +} + + +void TiXmlComment::CopyTo( TiXmlComment* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlComment::Clone() const +{ + TiXmlComment* clone = new TiXmlComment(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlText::Print( FILE* cfile, int depth ) const +{ + if ( cdata ) + { + int i; + fprintf( cfile, "\n" ); + for ( i=0; i\n" ); + } + else + { + TIXML_STRING buffer; + PutString( value, &buffer ); + fprintf( cfile, "%s", buffer.c_str() ); + } +} + + +void TiXmlText::StreamOut( TIXML_OSTREAM * stream ) const +{ + if ( cdata ) + { + (*stream) << ""; + } + else + { + PutString( value, stream ); + } +} + + +void TiXmlText::CopyTo( TiXmlText* target ) const +{ + TiXmlNode::CopyTo( target ); + target->cdata = cdata; +} + + +TiXmlNode* TiXmlText::Clone() const +{ + TiXmlText* clone = 0; + clone = new TiXmlText( "" ); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlDeclaration::TiXmlDeclaration( const char * _version, + const char * _encoding, + const char * _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} + + +#ifdef TIXML_USE_STL +TiXmlDeclaration::TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + version = _version; + encoding = _encoding; + standalone = _standalone; +} +#endif + + +TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy ) + : TiXmlNode( TiXmlNode::DECLARATION ) +{ + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::operator=( const TiXmlDeclaration& copy ) +{ + Clear(); + copy.CopyTo( this ); +} + + +void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/ ) const +{ + fprintf (cfile, ""); +} + +void TiXmlDeclaration::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << ""; +} + + +void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const +{ + TiXmlNode::CopyTo( target ); + + target->version = version; + target->encoding = encoding; + target->standalone = standalone; +} + + +TiXmlNode* TiXmlDeclaration::Clone() const +{ + TiXmlDeclaration* clone = new TiXmlDeclaration(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +void TiXmlUnknown::Print( FILE* cfile, int depth ) const +{ + for ( int i=0; i", value.c_str() ); +} + + +void TiXmlUnknown::StreamOut( TIXML_OSTREAM * stream ) const +{ + (*stream) << "<" << value << ">"; // Don't use entities here! It is unknown. +} + + +void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const +{ + TiXmlNode::CopyTo( target ); +} + + +TiXmlNode* TiXmlUnknown::Clone() const +{ + TiXmlUnknown* clone = new TiXmlUnknown(); + + if ( !clone ) + return 0; + + CopyTo( clone ); + return clone; +} + + +TiXmlAttributeSet::TiXmlAttributeSet() +{ + sentinel.next = &sentinel; + sentinel.prev = &sentinel; +} + + +TiXmlAttributeSet::~TiXmlAttributeSet() +{ + assert( sentinel.next == &sentinel ); + assert( sentinel.prev == &sentinel ); +} + + +void TiXmlAttributeSet::Add( TiXmlAttribute* addMe ) +{ + assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set. + + addMe->next = &sentinel; + addMe->prev = sentinel.prev; + + sentinel.prev->next = addMe; + sentinel.prev = addMe; +} + +void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node == removeMe ) + { + node->prev->next = node->next; + node->next->prev = node->prev; + node->next = 0; + node->prev = 0; + return; + } + } + assert( 0 ); // we tried to remove a non-linked attribute. +} + +const TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) const +{ + const TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +TiXmlAttribute* TiXmlAttributeSet::Find( const char * name ) +{ + TiXmlAttribute* node; + + for( node = sentinel.next; node != &sentinel; node = node->next ) + { + if ( node->name == name ) + return node; + } + return 0; +} + +#ifdef TIXML_USE_STL +TIXML_ISTREAM & operator >> (TIXML_ISTREAM & in, TiXmlNode & base) +{ + TIXML_STRING tag; + tag.reserve( 8 * 1000 ); + base.StreamIn( &in, &tag ); + + base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING ); + return in; +} +#endif + + +TIXML_OSTREAM & operator<< (TIXML_OSTREAM & out, const TiXmlNode & base) +{ + base.StreamOut (& out); + return out; +} + + +#ifdef TIXML_USE_STL +std::string & operator<< (std::string& out, const TiXmlNode& base ) +{ + std::ostringstream os_stream( std::ostringstream::out ); + base.StreamOut( &os_stream ); + + out.append( os_stream.str() ); + return out; +} +#endif + + +TiXmlHandle TiXmlHandle::FirstChild() const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const +{ + if ( node ) + { + TiXmlNode* child = node->FirstChild( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement() const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement(); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const +{ + if ( node ) + { + TiXmlElement* child = node->FirstChildElement( value ); + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild(); + for ( i=0; + child && iNextSibling(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlNode* child = node->FirstChild( value ); + for ( i=0; + child && iNextSibling( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement(); + for ( i=0; + child && iNextSiblingElement(), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} + + +TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const +{ + if ( node ) + { + int i; + TiXmlElement* child = node->FirstChildElement( value ); + for ( i=0; + child && iNextSiblingElement( value ), ++i ) + { + // nothing + } + if ( child ) + return TiXmlHandle( child ); + } + return TiXmlHandle( 0 ); +} diff --git a/standard/tinyxml/tinyxml.h b/standard/tinyxml/tinyxml.h new file mode 100644 index 000000000..2475a3965 --- /dev/null +++ b/standard/tinyxml/tinyxml.h @@ -0,0 +1,1511 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + + +#ifndef TINYXML_INCLUDED +#define TINYXML_INCLUDED + +#ifdef _MSC_VER +#pragma warning( push ) +#pragma warning( disable : 4530 ) +#pragma warning( disable : 4786 ) +#endif + +#include +#include +#include +#include +#include + +// Help out windows: +#if defined( _DEBUG ) && !defined( DEBUG ) +#define DEBUG +#endif + +#if defined( DEBUG ) && defined( _MSC_VER ) +#include +#define TIXML_LOG OutputDebugString +#else +#define TIXML_LOG printf +#endif + +#ifdef TIXML_USE_STL + #include + #include + #define TIXML_STRING std::string + #define TIXML_ISTREAM std::istream + #define TIXML_OSTREAM std::ostream +#else + #include "tinystr.h" + #define TIXML_STRING TiXmlString + #define TIXML_OSTREAM TiXmlOutStream +#endif + +// Deprecated library function hell. Compilers want to use the +// new safe versions. This probably doesn't fully address the problem, +// but it gets closer. There are too many compilers for me to fully +// test. If you get compilation troubles, undefine TIXML_SAFE + +#define TIXML_SAFE // TinyXml isn't fully buffer overrun protected, safe code. This is work in progress. +#ifdef TIXML_SAFE + #if defined(_MSC_VER) && (_MSC_VER >= 1200 ) + // Microsoft visual studio, version 6 and higher. + //#pragma message( "Using _sn* functions." ) + #define TIXML_SNPRINTF _snprintf + #define TIXML_SNSCANF _snscanf + #elif defined(__GNUC__) && (__GNUC__ >= 3 ) + // GCC version 3 and higher.s + //#warning( "Using sn* functions." ) + #define TIXML_SNPRINTF snprintf + #define TIXML_SNSCANF snscanf + #endif +#endif + +class TiXmlDocument; +class TiXmlElement; +class TiXmlComment; +class TiXmlUnknown; +class TiXmlAttribute; +class TiXmlText; +class TiXmlDeclaration; +class TiXmlParsingData; + +const int TIXML_MAJOR_VERSION = 2; +const int TIXML_MINOR_VERSION = 4; +const int TIXML_PATCH_VERSION = 2; + +/* Internal structure for tracking location of items + in the XML file. +*/ +struct TiXmlCursor +{ + TiXmlCursor() { Clear(); } + void Clear() { row = col = -1; } + + int row; // 0 based. + int col; // 0 based. +}; + + +// Only used by Attribute::Query functions +enum +{ + TIXML_SUCCESS, + TIXML_NO_ATTRIBUTE, + TIXML_WRONG_TYPE +}; + + +// Used by the parsing routines. +enum TiXmlEncoding +{ + TIXML_ENCODING_UNKNOWN, + TIXML_ENCODING_UTF8, + TIXML_ENCODING_LEGACY +}; + +const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN; + +/** TiXmlBase is a base class for every class in TinyXml. + It does little except to establish that TinyXml classes + can be printed and provide some utility functions. + + In XML, the document and elements can contain + other elements and other types of nodes. + + @verbatim + A Document can contain: Element (container or leaf) + Comment (leaf) + Unknown (leaf) + Declaration( leaf ) + + An Element can contain: Element (container or leaf) + Text (leaf) + Attributes (not on tree) + Comment (leaf) + Unknown (leaf) + + A Decleration contains: Attributes (not on tree) + @endverbatim +*/ +class TiXmlBase +{ + friend class TiXmlNode; + friend class TiXmlElement; + friend class TiXmlDocument; + +public: + TiXmlBase() : userData(0) {} + virtual ~TiXmlBase() {} + + /** All TinyXml classes can print themselves to a filestream. + This is a formatted print, and will insert tabs and newlines. + + (For an unformatted stream, use the << operator.) + */ + virtual void Print( FILE* cfile, int depth ) const = 0; + + /** The world does not agree on whether white space should be kept or + not. In order to make everyone happy, these global, static functions + are provided to set whether or not TinyXml will condense all white space + into a single space or not. The default is to condense. Note changing this + values is not thread safe. + */ + static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; } + + /// Return the current white space setting. + static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; } + + /** Return the position, in the original source file, of this node or attribute. + The row and column are 1-based. (That is the first row and first column is + 1,1). If the returns values are 0 or less, then the parser does not have + a row and column value. + + Generally, the row and column value will be set when the TiXmlDocument::Load(), + TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set + when the DOM was created from operator>>. + + The values reflect the initial load. Once the DOM is modified programmatically + (by adding or changing nodes and attributes) the new values will NOT update to + reflect changes in the document. + + There is a minor performance cost to computing the row and column. Computation + can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value. + + @sa TiXmlDocument::SetTabSize() + */ + int Row() const { return location.row + 1; } + int Column() const { return location.col + 1; } ///< See Row() + + void SetUserData( void* user ) { userData = user; } + void* GetUserData() { return userData; } + + // Table that returs, for a given lead byte, the total number of bytes + // in the UTF-8 sequence. + static const int utf8ByteTable[256]; + + virtual const char* Parse( const char* p, + TiXmlParsingData* data, + TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0; + + enum + { + TIXML_NO_ERROR = 0, + TIXML_ERROR, + TIXML_ERROR_OPENING_FILE, + TIXML_ERROR_OUT_OF_MEMORY, + TIXML_ERROR_PARSING_ELEMENT, + TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, + TIXML_ERROR_READING_ELEMENT_VALUE, + TIXML_ERROR_READING_ATTRIBUTES, + TIXML_ERROR_PARSING_EMPTY, + TIXML_ERROR_READING_END_TAG, + TIXML_ERROR_PARSING_UNKNOWN, + TIXML_ERROR_PARSING_COMMENT, + TIXML_ERROR_PARSING_DECLARATION, + TIXML_ERROR_DOCUMENT_EMPTY, + TIXML_ERROR_EMBEDDED_NULL, + TIXML_ERROR_PARSING_CDATA, + + TIXML_ERROR_STRING_COUNT + }; + +protected: + + // See STL_STRING_BUG + // Utility class to overcome a bug. + class StringToBuffer + { + public: + StringToBuffer( const TIXML_STRING& str ); + ~StringToBuffer(); + char* buffer; + }; + + static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding ); + inline static bool IsWhiteSpace( char c ) + { + return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' ); + } + + virtual void StreamOut (TIXML_OSTREAM *) const = 0; + + #ifdef TIXML_USE_STL + static bool StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ); + static bool StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ); + #endif + + /* Reads an XML name into the string provided. Returns + a pointer just past the last character of the name, + or 0 if the function has an error. + */ + static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding ); + + /* Reads text. Returns a pointer past the given end tag. + Wickedly complex options, but it keeps the (sensitive) code in one place. + */ + static const char* ReadText( const char* in, // where to start + TIXML_STRING* text, // the string read + bool ignoreWhiteSpace, // whether to keep the white space + const char* endTag, // what ends this text + bool ignoreCase, // whether to ignore case in the end tag + TiXmlEncoding encoding ); // the current encoding + + // If an entity has been found, transform it into a character. + static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding ); + + // Get a character, while interpreting entities. + // The length can be from 0 to 4 bytes. + inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding ) + { + assert( p ); + if ( encoding == TIXML_ENCODING_UTF8 ) + { + *length = utf8ByteTable[ *((unsigned char*)p) ]; + assert( *length >= 0 && *length < 5 ); + } + else + { + *length = 1; + } + + if ( *length == 1 ) + { + if ( *p == '&' ) + return GetEntity( p, _value, length, encoding ); + *_value = *p; + return p+1; + } + else if ( *length ) + { + //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe), + // and the null terminator isn't needed + for( int i=0; p[i] && i<*length; ++i ) { + _value[i] = p[i]; + } + return p + (*length); + } + else + { + // Not valid text. + return 0; + } + } + + // Puts a string to a stream, expanding entities as it goes. + // Note this should not contian the '<', '>', etc, or they will be transformed into entities! + static void PutString( const TIXML_STRING& str, TIXML_OSTREAM* out ); + + static void PutString( const TIXML_STRING& str, TIXML_STRING* out ); + + // Return true if the next characters in the stream are any of the endTag sequences. + // Ignore case only works for english, and should only be relied on when comparing + // to English words: StringEqual( p, "version", true ) is fine. + static bool StringEqual( const char* p, + const char* endTag, + bool ignoreCase, + TiXmlEncoding encoding ); + + static const char* errorString[ TIXML_ERROR_STRING_COUNT ]; + + TiXmlCursor location; + + /// Field containing a generic user pointer + void* userData; + + // None of these methods are reliable for any language except English. + // Good for approximation, not great for accuracy. + static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding ); + static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding ); + inline static int ToLower( int v, TiXmlEncoding encoding ) + { + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( v < 128 ) return tolower( v ); + return v; + } + else + { + return tolower( v ); + } + } + static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ); + +private: + TiXmlBase( const TiXmlBase& ); // not implemented. + void operator=( const TiXmlBase& base ); // not allowed. + + struct Entity + { + const char* str; + unsigned int strLength; + char chr; + }; + enum + { + NUM_ENTITY = 5, + MAX_ENTITY_LENGTH = 6 + + }; + static Entity entity[ NUM_ENTITY ]; + static bool condenseWhiteSpace; +}; + + +/** The parent class for everything in the Document Object Model. + (Except for attributes). + Nodes have siblings, a parent, and children. A node can be + in a document, or stand on its own. The type of a TiXmlNode + can be queried, and it can be cast to its more defined type. +*/ +class TiXmlNode : public TiXmlBase +{ + friend class TiXmlDocument; + friend class TiXmlElement; + +public: + #ifdef TIXML_USE_STL + + /** An input stream operator, for every class. Tolerant of newlines and + formatting, but doesn't expect them. + */ + friend std::istream& operator >> (std::istream& in, TiXmlNode& base); + + /** An output stream operator, for every class. Note that this outputs + without any newlines or formatting, as opposed to Print(), which + includes tabs and new lines. + + The operator<< and operator>> are not completely symmetric. Writing + a node to a stream is very well defined. You'll get a nice stream + of output, without any extra whitespace or newlines. + + But reading is not as well defined. (As it always is.) If you create + a TiXmlElement (for example) and read that from an input stream, + the text needs to define an element or junk will result. This is + true of all input streams, but it's worth keeping in mind. + + A TiXmlDocument will read nodes until it reads a root element, and + all the children of that root element. + */ + friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base); + + /// Appends the XML node or attribute to a std::string. + friend std::string& operator<< (std::string& out, const TiXmlNode& base ); + + #else + // Used internally, not part of the public API. + friend TIXML_OSTREAM& operator<< (TIXML_OSTREAM& out, const TiXmlNode& base); + #endif + + /** The types of XML nodes supported by TinyXml. (All the + unsupported types are picked up by UNKNOWN.) + */ + enum NodeType + { + DOCUMENT, + ELEMENT, + COMMENT, + UNKNOWN, + TEXT, + DECLARATION, + TYPECOUNT + }; + + virtual ~TiXmlNode(); + + /** The meaning of 'value' changes for the specific type of + TiXmlNode. + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + + The subclasses will wrap this function. + */ + const char *Value() const { return value.c_str (); } + + #ifdef TIXML_USE_STL + /** Return Value() as a std::string. If you only use STL, + this is more efficient than calling Value(). + Only available in STL mode. + */ + const std::string& ValueStr() const { return value; } + #endif + + /** Changes the value of the node. Defined as: + @verbatim + Document: filename of the xml file + Element: name of the element + Comment: the comment text + Unknown: the tag contents + Text: the text string + @endverbatim + */ + void SetValue(const char * _value) { value = _value;} + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetValue( const std::string& _value ) + { + StringToBuffer buf( _value ); + SetValue( buf.buffer ? buf.buffer : "" ); + } + #endif + + /// Delete all the children of this node. Does not affect 'this'. + void Clear(); + + /// One step up the DOM. + TiXmlNode* Parent() { return parent; } + const TiXmlNode* Parent() const { return parent; } + + const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children. + TiXmlNode* FirstChild() { return firstChild; } + const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found. + TiXmlNode* FirstChild( const char * value ); ///< The first child of this node with the matching 'value'. Will be null if none found. + + const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children. + TiXmlNode* LastChild() { return lastChild; } + const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children. + TiXmlNode* LastChild( const char * value ); + + #ifdef TIXML_USE_STL + const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form. + #endif + + /** An alternate way to walk the children of a node. + One way to iterate over nodes is: + @verbatim + for( child = parent->FirstChild(); child; child = child->NextSibling() ) + @endverbatim + + IterateChildren does the same thing with the syntax: + @verbatim + child = 0; + while( child = parent->IterateChildren( child ) ) + @endverbatim + + IterateChildren takes the previous child as input and finds + the next one. If the previous child is null, it returns the + first. IterateChildren will return null when done. + */ + const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( TiXmlNode* previous ); + + /// This flavor of IterateChildren searches for children with a particular 'value' + const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const; + TiXmlNode* IterateChildren( const char * value, TiXmlNode* previous ); + + #ifdef TIXML_USE_STL + const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + TiXmlNode* IterateChildren( const std::string& _value, TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form. + #endif + + /** Add a new node related to this. Adds a child past the LastChild. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertEndChild( const TiXmlNode& addThis ); + + + /** Add a new node related to this. Adds a child past the LastChild. + + NOTE: the node to be added is passed by pointer, and will be + henceforth owned (and deleted) by tinyXml. This method is efficient + and avoids an extra copy, but should be used with care as it + uses a different memory model than the other insert functions. + + @sa InsertEndChild + */ + TiXmlNode* LinkEndChild( TiXmlNode* addThis ); + + /** Add a new node related to this. Adds a child before the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis ); + + /** Add a new node related to this. Adds a child after the specified child. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis ); + + /** Replace a child of this node. + Returns a pointer to the new object or NULL if an error occured. + */ + TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis ); + + /// Delete a child of this node. + bool RemoveChild( TiXmlNode* removeThis ); + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling() const { return prev; } + TiXmlNode* PreviousSibling() { return prev; } + + /// Navigate to a sibling node. + const TiXmlNode* PreviousSibling( const char * ) const; + TiXmlNode* PreviousSibling( const char * ); + + #ifdef TIXML_USE_STL + const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form. + const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form. + TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Navigate to a sibling node. + const TiXmlNode* NextSibling() const { return next; } + TiXmlNode* NextSibling() { return next; } + + /// Navigate to a sibling node with the given 'value'. + const TiXmlNode* NextSibling( const char * ) const; + TiXmlNode* NextSibling( const char * ); + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement() const; + TiXmlElement* NextSiblingElement(); + + /** Convenience function to get through elements. + Calls NextSibling and ToElement. Will skip all non-Element + nodes. Returns 0 if there is not another element. + */ + const TiXmlElement* NextSiblingElement( const char * ) const; + TiXmlElement* NextSiblingElement( const char * ); + + #ifdef TIXML_USE_STL + const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement() const; + TiXmlElement* FirstChildElement(); + + /// Convenience function to get through elements. + const TiXmlElement* FirstChildElement( const char * value ) const; + TiXmlElement* FirstChildElement( const char * value ); + + #ifdef TIXML_USE_STL + const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form. + #endif + + /** Query the type (as an enumerated value, above) of this node. + The possible types are: DOCUMENT, ELEMENT, COMMENT, + UNKNOWN, TEXT, and DECLARATION. + */ + int Type() const { return type; } + + /** Return a pointer to the Document this node lives in. + Returns null if not in a document. + */ + const TiXmlDocument* GetDocument() const; + TiXmlDocument* GetDocument(); + + /// Returns true if this node has no children. + bool NoChildren() const { return !firstChild; } + + const TiXmlDocument* ToDocument() const { return ( this && type == DOCUMENT ) ? (const TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlElement* ToElement() const { return ( this && type == ELEMENT ) ? (const TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlComment* ToComment() const { return ( this && type == COMMENT ) ? (const TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlUnknown* ToUnknown() const { return ( this && type == UNKNOWN ) ? (const TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlText* ToText() const { return ( this && type == TEXT ) ? (const TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + const TiXmlDeclaration* ToDeclaration() const { return ( this && type == DECLARATION ) ? (const TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + + TiXmlDocument* ToDocument() { return ( this && type == DOCUMENT ) ? (TiXmlDocument*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlElement* ToElement() { return ( this && type == ELEMENT ) ? (TiXmlElement*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlComment* ToComment() { return ( this && type == COMMENT ) ? (TiXmlComment*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlUnknown* ToUnknown() { return ( this && type == UNKNOWN ) ? (TiXmlUnknown*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlText* ToText() { return ( this && type == TEXT ) ? (TiXmlText*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + TiXmlDeclaration* ToDeclaration() { return ( this && type == DECLARATION ) ? (TiXmlDeclaration*) this : 0; } ///< Cast to a more defined type. Will return null not of the requested type. + + /** Create an exact duplicate of this node and return it. The memory must be deleted + by the caller. + */ + virtual TiXmlNode* Clone() const = 0; + +protected: + TiXmlNode( NodeType _type ); + + // Copy to the allocated object. Shared functionality between Clone, Copy constructor, + // and the assignment operator. + void CopyTo( TiXmlNode* target ) const; + + #ifdef TIXML_USE_STL + // The real work of the input operator. + virtual void StreamIn( TIXML_ISTREAM* in, TIXML_STRING* tag ) = 0; + #endif + + // Figure out what is at *p, and parse it. Returns null if it is not an xml node. + TiXmlNode* Identify( const char* start, TiXmlEncoding encoding ); + + TiXmlNode* parent; + NodeType type; + + TiXmlNode* firstChild; + TiXmlNode* lastChild; + + TIXML_STRING value; + + TiXmlNode* prev; + TiXmlNode* next; + +private: + TiXmlNode( const TiXmlNode& ); // not implemented. + void operator=( const TiXmlNode& base ); // not allowed. +}; + + +/** An attribute is a name-value pair. Elements have an arbitrary + number of attributes, each with a unique name. + + @note The attributes are not TiXmlNodes, since they are not + part of the tinyXML document object model. There are other + suggested ways to look at this problem. +*/ +class TiXmlAttribute : public TiXmlBase +{ + friend class TiXmlAttributeSet; + +public: + /// Construct an empty attribute. + TiXmlAttribute() : TiXmlBase() + { + document = 0; + prev = next = 0; + } + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlAttribute( const std::string& _name, const std::string& _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + #endif + + /// Construct an attribute with a name and value. + TiXmlAttribute( const char * _name, const char * _value ) + { + name = _name; + value = _value; + document = 0; + prev = next = 0; + } + + const char* Name() const { return name.c_str (); } ///< Return the name of this attribute. + const char* Value() const { return value.c_str (); } ///< Return the value of this attribute. + int IntValue() const; ///< Return the value of this attribute, converted to an integer. + double DoubleValue() const; ///< Return the value of this attribute, converted to a double. + + /** QueryIntValue examines the value string. It is an alternative to the + IntValue() method with richer error checking. + If the value is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. + + A specialized but useful call. Note that for success it returns 0, + which is the opposite of almost all other TinyXml calls. + */ + int QueryIntValue( int* _value ) const; + /// QueryDoubleValue examines the value string. See QueryIntValue(). + int QueryDoubleValue( double* _value ) const; + + void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute. + void SetValue( const char* _value ) { value = _value; } ///< Set the value. + + void SetIntValue( int _value ); ///< Set the value from an integer. + void SetDoubleValue( double _value ); ///< Set the value from a double. + + #ifdef TIXML_USE_STL + /// STL std::string form. + void SetName( const std::string& _name ) + { + StringToBuffer buf( _name ); + SetName ( buf.buffer ? buf.buffer : "error" ); + } + /// STL std::string form. + void SetValue( const std::string& _value ) + { + StringToBuffer buf( _value ); + SetValue( buf.buffer ? buf.buffer : "error" ); + } + #endif + + /// Get the next sibling attribute in the DOM. Returns null at end. + const TiXmlAttribute* Next() const; + TiXmlAttribute* Next(); + /// Get the previous sibling attribute in the DOM. Returns null at beginning. + const TiXmlAttribute* Previous() const; + TiXmlAttribute* Previous(); + + bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; } + bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; } + bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; } + + /* Attribute parsing starts: first letter of the name + returns: the next char after the value end quote + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + + // Prints this Attribute to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual void StreamOut( TIXML_OSTREAM * out ) const; + // [internal use] + // Set the document pointer so the attribute can report errors. + void SetDocument( TiXmlDocument* doc ) { document = doc; } + +private: + TiXmlAttribute( const TiXmlAttribute& ); // not implemented. + void operator=( const TiXmlAttribute& base ); // not allowed. + + TiXmlDocument* document; // A pointer back to a document, for error reporting. + TIXML_STRING name; + TIXML_STRING value; + TiXmlAttribute* prev; + TiXmlAttribute* next; +}; + + +/* A class used to manage a group of attributes. + It is only used internally, both by the ELEMENT and the DECLARATION. + + The set can be changed transparent to the Element and Declaration + classes that use it, but NOT transparent to the Attribute + which has to implement a next() and previous() method. Which makes + it a bit problematic and prevents the use of STL. + + This version is implemented with circular lists because: + - I like circular lists + - it demonstrates some independence from the (typical) doubly linked list. +*/ +class TiXmlAttributeSet +{ +public: + TiXmlAttributeSet(); + ~TiXmlAttributeSet(); + + void Add( TiXmlAttribute* attribute ); + void Remove( TiXmlAttribute* attribute ); + + const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; } + const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; } + + const TiXmlAttribute* Find( const char * name ) const; + TiXmlAttribute* Find( const char * name ); + +private: + //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element), + //*ME: this class must be also use a hidden/disabled copy-constructor !!! + TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed + void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute) + + TiXmlAttribute sentinel; +}; + + +/** The element is a container class. It has a value, the element name, + and can contain other elements, text, comments, and unknowns. + Elements also contain an arbitrary number of attributes. +*/ +class TiXmlElement : public TiXmlNode +{ +public: + /// Construct an element. + TiXmlElement (const char * in_value); + + #ifdef TIXML_USE_STL + /// std::string constructor. + TiXmlElement( const std::string& _value ); + #endif + + TiXmlElement( const TiXmlElement& ); + + void operator=( const TiXmlElement& base ); + + virtual ~TiXmlElement(); + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + */ + const char* Attribute( const char* name ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an integer, + the integer value will be put in the return 'i', if 'i' + is non-null. + */ + const char* Attribute( const char* name, int* i ) const; + + /** Given an attribute name, Attribute() returns the value + for the attribute of that name, or null if none exists. + If the attribute exists and can be converted to an double, + the double value will be put in the return 'd', if 'd' + is non-null. + */ + const char* Attribute( const char* name, double* d ) const; + + /** QueryIntAttribute examines the attribute - it is an alternative to the + Attribute() method with richer error checking. + If the attribute is an integer, it is stored in 'value' and + the call returns TIXML_SUCCESS. If it is not + an integer, it returns TIXML_WRONG_TYPE. If the attribute + does not exist, then TIXML_NO_ATTRIBUTE is returned. + */ + int QueryIntAttribute( const char* name, int* _value ) const; + /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute(). + int QueryDoubleAttribute( const char* name, double* _value ) const; + /// QueryFloatAttribute examines the attribute - see QueryIntAttribute(). + int QueryFloatAttribute( const char* name, float* _value ) const { + double d; + int result = QueryDoubleAttribute( name, &d ); + if ( result == TIXML_SUCCESS ) { + *_value = (float)d; + } + return result; + } + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char* name, const char * _value ); + + #ifdef TIXML_USE_STL + const char* Attribute( const std::string& name ) const { return Attribute( name.c_str() ); } + const char* Attribute( const std::string& name, int* i ) const { return Attribute( name.c_str(), i ); } + const char* Attribute( const std::string& name, double* d ) const { return Attribute( name.c_str(), d ); } + int QueryIntAttribute( const std::string& name, int* _value ) const { return QueryIntAttribute( name.c_str(), _value ); } + int QueryDoubleAttribute( const std::string& name, double* _value ) const { return QueryDoubleAttribute( name.c_str(), _value ); } + + /// STL std::string form. + void SetAttribute( const std::string& name, const std::string& _value ) + { + StringToBuffer n( name ); + StringToBuffer v( _value ); + if ( n.buffer && v.buffer ) + SetAttribute (n.buffer, v.buffer ); + } + ///< STL std::string form. + void SetAttribute( const std::string& name, int _value ) + { + StringToBuffer n( name ); + if ( n.buffer ) + SetAttribute (n.buffer, _value); + } + #endif + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetAttribute( const char * name, int value ); + + /** Sets an attribute of name to a given value. The attribute + will be created if it does not exist, or changed if it does. + */ + void SetDoubleAttribute( const char * name, double value ); + + /** Deletes an attribute with the given name. + */ + void RemoveAttribute( const char * name ); + #ifdef TIXML_USE_STL + void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form. + #endif + + const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element. + TiXmlAttribute* FirstAttribute() { return attributeSet.First(); } + const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element. + TiXmlAttribute* LastAttribute() { return attributeSet.Last(); } + + /** Convenience function for easy access to the text inside an element. Although easy + and concise, GetText() is limited compared to getting the TiXmlText child + and accessing it directly. + + If the first child of 'this' is a TiXmlText, the GetText() + returns the character string of the Text node, else null is returned. + + This is a convenient method for getting the text of simple contained text: + @verbatim + This is text + const char* str = fooElement->GetText(); + @endverbatim + + 'str' will be a pointer to "This is text". + + Note that this function can be misleading. If the element foo was created from + this XML: + @verbatim + This is text + @endverbatim + + then the value of str would be null. The first child node isn't a text node, it is + another element. From this XML: + @verbatim + This is text + @endverbatim + GetText() will return "This is ". + + WARNING: GetText() accesses a child node - don't become confused with the + similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are + safe type casts on the referenced node. + */ + const char* GetText() const; + + /// Creates a new Element and returns it - the returned element is a copy. + virtual TiXmlNode* Clone() const; + // Print the Element to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: next char past '<' + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + + void CopyTo( TiXmlElement* target ) const; + void ClearThis(); // like clear, but initializes 'this' object as well + + // Used to be public [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut( TIXML_OSTREAM * out ) const; + + /* [internal use] + Reads the "value" of the element -- another element, or text. + This should terminate with the current end tag. + */ + const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +private: + + TiXmlAttributeSet attributeSet; +}; + + +/** An XML comment. +*/ +class TiXmlComment : public TiXmlNode +{ +public: + /// Constructs an empty comment. + TiXmlComment() : TiXmlNode( TiXmlNode::COMMENT ) {} + TiXmlComment( const TiXmlComment& ); + void operator=( const TiXmlComment& base ); + + virtual ~TiXmlComment() {} + + /// Returns a copy of this Comment. + virtual TiXmlNode* Clone() const; + /// Write this Comment to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /* Attribtue parsing starts: at the ! of the !-- + returns: next char past '>' + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlComment* target ) const; + + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** XML text. A text node can have 2 ways to output the next. "normal" output + and CDATA. It will default to the mode it was parsed from the XML file and + you generally want to leave it alone, but you can change the output mode with + SetCDATA() and query it with CDATA(). +*/ +class TiXmlText : public TiXmlNode +{ + friend class TiXmlElement; +public: + /** Constructor for text element. By default, it is treated as + normal, encoded text. If you want it be output as a CDATA text + element, set the parameter _cdata to 'true' + */ + TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + virtual ~TiXmlText() {} + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TEXT) + { + SetValue( initValue ); + cdata = false; + } + #endif + + TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TEXT ) { copy.CopyTo( this ); } + void operator=( const TiXmlText& base ) { base.CopyTo( this ); } + + /// Write this text object to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + /// Queries whether this represents text using a CDATA section. + bool CDATA() { return cdata; } + /// Turns on or off a CDATA representation of text. + void SetCDATA( bool _cdata ) { cdata = _cdata; } + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected : + /// [internal use] Creates a new Element and returns it. + virtual TiXmlNode* Clone() const; + void CopyTo( TiXmlText* target ) const; + + virtual void StreamOut ( TIXML_OSTREAM * out ) const; + bool Blank() const; // returns true if all white space and new lines + // [internal use] + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + +private: + bool cdata; // true if this should be input and output as a CDATA style text element +}; + + +/** In correct XML the declaration is the first entry in the file. + @verbatim + + @endverbatim + + TinyXml will happily read or write files without a declaration, + however. There are 3 possible attributes to the declaration: + version, encoding, and standalone. + + Note: In this version of the code, the attributes are + handled as special cases, not generic attributes, simply + because there can only be at most 3 and they are always the same. +*/ +class TiXmlDeclaration : public TiXmlNode +{ +public: + /// Construct an empty declaration. + TiXmlDeclaration() : TiXmlNode( TiXmlNode::DECLARATION ) {} + +#ifdef TIXML_USE_STL + /// Constructor. + TiXmlDeclaration( const std::string& _version, + const std::string& _encoding, + const std::string& _standalone ); +#endif + + /// Construct. + TiXmlDeclaration( const char* _version, + const char* _encoding, + const char* _standalone ); + + TiXmlDeclaration( const TiXmlDeclaration& copy ); + void operator=( const TiXmlDeclaration& copy ); + + virtual ~TiXmlDeclaration() {} + + /// Version. Will return an empty string if none was found. + const char *Version() const { return version.c_str (); } + /// Encoding. Will return an empty string if none was found. + const char *Encoding() const { return encoding.c_str (); } + /// Is this a standalone document? + const char *Standalone() const { return standalone.c_str (); } + + /// Creates a copy of this Declaration and returns it. + virtual TiXmlNode* Clone() const; + /// Print this declaration to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlDeclaration* target ) const; + // used to be public + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut ( TIXML_OSTREAM * out) const; + +private: + + TIXML_STRING version; + TIXML_STRING encoding; + TIXML_STRING standalone; +}; + + +/** Any tag that tinyXml doesn't recognize is saved as an + unknown. It is a tag of text, but should not be modified. + It will be written back to the XML, unchanged, when the file + is saved. + + DTD tags get thrown into TiXmlUnknowns. +*/ +class TiXmlUnknown : public TiXmlNode +{ +public: + TiXmlUnknown() : TiXmlNode( TiXmlNode::UNKNOWN ) {} + virtual ~TiXmlUnknown() {} + + TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::UNKNOWN ) { copy.CopyTo( this ); } + void operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); } + + /// Creates a copy of this Unknown and returns it. + virtual TiXmlNode* Clone() const; + /// Print this Unknown to a FILE stream. + virtual void Print( FILE* cfile, int depth ) const; + + virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ); + +protected: + void CopyTo( TiXmlUnknown* target ) const; + + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + virtual void StreamOut ( TIXML_OSTREAM * out ) const; + +private: + +}; + + +/** Always the top level node. A document binds together all the + XML pieces. It can be saved, loaded, and printed to the screen. + The 'value' of a document node is the xml file name. +*/ +class TiXmlDocument : public TiXmlNode +{ +public: + /// Create an empty document, that has no name. + TiXmlDocument(); + /// Create a document with a name. The name of the document is also the filename of the xml. + TiXmlDocument( const char * documentName ); + + #ifdef TIXML_USE_STL + /// Constructor. + TiXmlDocument( const std::string& documentName ); + #endif + + TiXmlDocument( const TiXmlDocument& copy ); + void operator=( const TiXmlDocument& copy ); + + virtual ~TiXmlDocument() {} + + /** Load a file using the current document value. + Returns true if successful. Will delete any existing + document data before loading. + */ + bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the current document value. Returns true if successful. + bool SaveFile() const; + /// Load a file using the given filename. Returns true if successful. + bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + /// Save a file using the given filename. Returns true if successful. + bool SaveFile( const char * filename ) const; + + #ifdef TIXML_USE_STL + bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version. + { + StringToBuffer f( filename ); + return ( f.buffer && LoadFile( f.buffer, encoding )); + } + bool SaveFile( const std::string& filename ) const ///< STL std::string version. + { + StringToBuffer f( filename ); + return ( f.buffer && SaveFile( f.buffer )); + } + #endif + + /** Parse the given null terminated block of xml data. Passing in an encoding to this + method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml + to use that encoding, regardless of what TinyXml might otherwise try to detect. + */ + virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ); + + /** Get the root element -- the only top level element -- of the document. + In well formed XML, there should only be one. TinyXml is tolerant of + multiple elements at the document level. + */ + const TiXmlElement* RootElement() const { return FirstChildElement(); } + TiXmlElement* RootElement() { return FirstChildElement(); } + + /** If an error occurs, Error will be set to true. Also, + - The ErrorId() will contain the integer identifier of the error (not generally useful) + - The ErrorDesc() method will return the name of the error. (very useful) + - The ErrorRow() and ErrorCol() will return the location of the error (if known) + */ + bool Error() const { return error; } + + /// Contains a textual (english) description of the error if one occurs. + const char * ErrorDesc() const { return errorDesc.c_str (); } + + /** Generally, you probably want the error string ( ErrorDesc() ). But if you + prefer the ErrorId, this function will fetch it. + */ + int ErrorId() const { return errorId; } + + /** Returns the location (if known) of the error. The first column is column 1, + and the first row is row 1. A value of 0 means the row and column wasn't applicable + (memory errors, for example, have no row/column) or the parser lost the error. (An + error in the error reporting, in that case.) + + @sa SetTabSize, Row, Column + */ + int ErrorRow() { return errorLocation.row+1; } + int ErrorCol() { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow() + + /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol()) + to report the correct values for row and column. It does not change the output + or input in any way. + + By calling this method, with a tab size + greater than 0, the row and column of each node and attribute is stored + when the file is loaded. Very useful for tracking the DOM back in to + the source file. + + The tab size is required for calculating the location of nodes. If not + set, the default of 4 is used. The tabsize is set per document. Setting + the tabsize to 0 disables row/column tracking. + + Note that row and column tracking is not supported when using operator>>. + + The tab size needs to be enabled before the parse or load. Correct usage: + @verbatim + TiXmlDocument doc; + doc.SetTabSize( 8 ); + doc.Load( "myfile.xml" ); + @endverbatim + + @sa Row, Column + */ + void SetTabSize( int _tabsize ) { tabsize = _tabsize; } + + int TabSize() const { return tabsize; } + + /** If you have handled the error, it can be reset with this call. The error + state is automatically cleared if you Parse a new XML block. + */ + void ClearError() { error = false; + errorId = 0; + errorDesc = ""; + errorLocation.row = errorLocation.col = 0; + //errorLocation.last = 0; + } + + /** Dump the document to standard out. */ + void Print() const { Print( stdout, 0 ); } + + /// Print this Document to a FILE stream. + virtual void Print( FILE* cfile, int depth = 0 ) const; + // [internal use] + void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding ); + +protected : + virtual void StreamOut ( TIXML_OSTREAM * out) const; + // [internal use] + virtual TiXmlNode* Clone() const; + #ifdef TIXML_USE_STL + virtual void StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ); + #endif + +private: + void CopyTo( TiXmlDocument* target ) const; + + bool error; + int errorId; + TIXML_STRING errorDesc; + int tabsize; + TiXmlCursor errorLocation; + bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write. +}; + + +/** + A TiXmlHandle is a class that wraps a node pointer with null checks; this is + an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml + DOM structure. It is a separate utility class. + + Take an example: + @verbatim + + + + + + + @endverbatim + + Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very + easy to write a *lot* of code that looks like: + + @verbatim + TiXmlElement* root = document.FirstChildElement( "Document" ); + if ( root ) + { + TiXmlElement* element = root->FirstChildElement( "Element" ); + if ( element ) + { + TiXmlElement* child = element->FirstChildElement( "Child" ); + if ( child ) + { + TiXmlElement* child2 = child->NextSiblingElement( "Child" ); + if ( child2 ) + { + // Finally do something useful. + @endverbatim + + And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity + of such code. A TiXmlHandle checks for null pointers so it is perfectly safe + and correct to use: + + @verbatim + TiXmlHandle docHandle( &document ); + TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).Element(); + if ( child2 ) + { + // do something useful + @endverbatim + + Which is MUCH more concise and useful. + + It is also safe to copy handles - internally they are nothing more than node pointers. + @verbatim + TiXmlHandle handleCopy = handle; + @endverbatim + + What they should not be used for is iteration: + + @verbatim + int i=0; + while ( true ) + { + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).Element(); + if ( !child ) + break; + // do something + ++i; + } + @endverbatim + + It seems reasonable, but it is in fact two embedded while loops. The Child method is + a linear walk to find the element, so this code would iterate much more than it needs + to. Instead, prefer: + + @verbatim + TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).Element(); + + for( child; child; child=child->NextSiblingElement() ) + { + // do something + } + @endverbatim +*/ +class TiXmlHandle +{ +public: + /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. + TiXmlHandle( TiXmlNode* _node ) { this->node = _node; } + /// Copy constructor + TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; } + TiXmlHandle operator=( const TiXmlHandle& ref ) { this->node = ref.node; return *this; } + + /// Return a handle to the first child node. + TiXmlHandle FirstChild() const; + /// Return a handle to the first child node with the given name. + TiXmlHandle FirstChild( const char * value ) const; + /// Return a handle to the first child element. + TiXmlHandle FirstChildElement() const; + /// Return a handle to the first child element with the given name. + TiXmlHandle FirstChildElement( const char * value ) const; + + /** Return a handle to the "index" child with the given name. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( const char* value, int index ) const; + /** Return a handle to the "index" child. + The first child is 0, the second 1, etc. + */ + TiXmlHandle Child( int index ) const; + /** Return a handle to the "index" child element with the given name. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( const char* value, int index ) const; + /** Return a handle to the "index" child element. + The first child element is 0, the second 1, etc. Note that only TiXmlElements + are indexed: other types are not counted. + */ + TiXmlHandle ChildElement( int index ) const; + + #ifdef TIXML_USE_STL + TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); } + TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); } + + TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); } + TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); } + #endif + + /// Return the handle as a TiXmlNode. This may return null. + TiXmlNode* Node() const { return node; } + /// Return the handle as a TiXmlElement. This may return null. + TiXmlElement* Element() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); } + /// Return the handle as a TiXmlText. This may return null. + TiXmlText* Text() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); } + /// Return the handle as a TiXmlUnknown. This may return null; + TiXmlUnknown* Unknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); } + +private: + TiXmlNode* node; +}; + +#ifdef _MSC_VER +#pragma warning( pop ) +#endif + +#endif + diff --git a/standard/tinyxml/tinyxmlerror.cpp b/standard/tinyxml/tinyxmlerror.cpp new file mode 100644 index 000000000..5788438ea --- /dev/null +++ b/standard/tinyxml/tinyxmlerror.cpp @@ -0,0 +1,52 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" + +// The goal of the seperate error file is to make the first +// step towards localization. tinyxml (currently) only supports +// english error messages, but the could now be translated. +// +// It also cleans up the code a bit. +// + +const char* TiXmlBase::errorString[ TIXML_ERROR_STRING_COUNT ] = +{ + "No error", + "Error", + "Failed to open file", + "Memory allocation failed.", + "Error parsing Element.", + "Failed to read Element name", + "Error reading Element value.", + "Error reading Attributes.", + "Error: empty tag.", + "Error reading end tag.", + "Error parsing Unknown.", + "Error parsing Comment.", + "Error parsing Declaration.", + "Error document empty.", + "Error null (0) or unexpected EOF found in input stream.", + "Error parsing CDATA.", +}; diff --git a/standard/tinyxml/tinyxmlparser.cpp b/standard/tinyxml/tinyxmlparser.cpp new file mode 100644 index 000000000..67d0a9e54 --- /dev/null +++ b/standard/tinyxml/tinyxmlparser.cpp @@ -0,0 +1,1572 @@ +/* +www.sourceforge.net/projects/tinyxml +Original code (2.0 and earlier )copyright (c) 2000-2002 Lee Thomason (www.grinninglizard.com) + +This software is provided 'as-is', without any express or implied +warranty. In no event will the authors be held liable for any +damages arising from the use of this software. + +Permission is granted to anyone to use this software for any +purpose, including commercial applications, and to alter it and +redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must +not claim that you wrote the original software. If you use this +software in a product, an acknowledgment in the product documentation +would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and +must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source +distribution. +*/ + +#include "tinyxml.h" +#include +#include + +//#define DEBUG_PARSER + +// Note tha "PutString" hardcodes the same list. This +// is less flexible than it appears. Changing the entries +// or order will break putstring. +TiXmlBase::Entity TiXmlBase::entity[ NUM_ENTITY ] = +{ + { "&", 5, '&' }, + { "<", 4, '<' }, + { ">", 4, '>' }, + { """, 6, '\"' }, + { "'", 6, '\'' } +}; + +// Bunch of unicode info at: +// http://www.unicode.org/faq/utf_bom.html +// Including the basic of this table, which determines the #bytes in the +// sequence from the lead byte. 1 placed for invalid sequences -- +// although the result will be junk, pass it through as much as possible. +// Beware of the non-characters in UTF-8: +// ef bb bf (Microsoft "lead bytes") +// ef bf be +// ef bf bf + +const unsigned char TIXML_UTF_LEAD_0 = 0xefU; +const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; +const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; + +const int TiXmlBase::utf8ByteTable[256] = +{ + // 0 1 2 3 4 5 6 7 8 9 a b c d e f + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0 + 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte + 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid +}; + + +void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) +{ + const unsigned long BYTE_MASK = 0xBF; + const unsigned long BYTE_MARK = 0x80; + const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; + + if (input < 0x80) + *length = 1; + else if ( input < 0x800 ) + *length = 2; + else if ( input < 0x10000 ) + *length = 3; + else if ( input < 0x200000 ) + *length = 4; + else + { *length = 0; return; } // This code won't covert this correctly anyway. + + output += *length; + + // Scary scary fall throughs. + switch (*length) + { + case 4: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 3: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 2: + --output; + *output = (char)((input | BYTE_MARK) & BYTE_MASK); + input >>= 6; + case 1: + --output; + *output = (char)(input | FIRST_BYTE_MARK[*length]); + } +} + + +/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalpha( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalpha( anyByte ); +// } +} + + +/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ ) +{ + // This will only work for low-ascii, everything else is assumed to be a valid + // letter. I'm not sure this is the best approach, but it is quite tricky trying + // to figure out alhabetical vs. not across encoding. So take a very + // conservative approach. + +// if ( encoding == TIXML_ENCODING_UTF8 ) +// { + if ( anyByte < 127 ) + return isalnum( anyByte ); + else + return 1; // What else to do? The unicode set is huge...get the english ones right. +// } +// else +// { +// return isalnum( anyByte ); +// } +} + + +class TiXmlParsingData +{ + friend class TiXmlDocument; + public: + void Stamp( const char* now, TiXmlEncoding encoding ); + + const TiXmlCursor& Cursor() { return cursor; } + + private: + // Only used by the document! + TiXmlParsingData( const char* start, int _tabsize, int row, int col ) + { + assert( start ); + stamp = start; + tabsize = _tabsize; + cursor.row = row; + cursor.col = col; + } + + TiXmlCursor cursor; + const char* stamp; + int tabsize; +}; + + +void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding ) +{ + assert( now ); + + // Do nothing if the tabsize is 0. + if ( tabsize < 1 ) + { + return; + } + + // Get the current row, column. + int row = cursor.row; + int col = cursor.col; + const char* p = stamp; + assert( p ); + + while ( p < now ) + { + // Treat p as unsigned, so we have a happy compiler. + const unsigned char* pU = (const unsigned char*)p; + + // Code contributed by Fletcher Dunn: (modified by lee) + switch (*pU) { + case 0: + // We *should* never get here, but in case we do, don't + // advance past the terminating null character, ever + return; + + case '\r': + // bump down to the next line + ++row; + col = 0; + // Eat the character + ++p; + + // Check for \r\n sequence, and treat this as a single character + if (*p == '\n') { + ++p; + } + break; + + case '\n': + // bump down to the next line + ++row; + col = 0; + + // Eat the character + ++p; + + // Check for \n\r sequence, and treat this as a single + // character. (Yes, this bizarre thing does occur still + // on some arcane platforms...) + if (*p == '\r') { + ++p; + } + break; + + case '\t': + // Eat the character + ++p; + + // Skip to next tab stop + col = (col / tabsize + 1) * tabsize; + break; + + case TIXML_UTF_LEAD_0: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + if ( *(p+1) && *(p+2) ) + { + // In these cases, don't advance the column. These are + // 0-width spaces. + if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU ) + p += 3; + else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU ) + p += 3; + else + { p +=3; ++col; } // A normal character. + } + } + else + { + ++p; + ++col; + } + break; + + default: + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // Eat the 1 to 4 byte utf8 character. + int step = TiXmlBase::utf8ByteTable[*((unsigned char*)p)]; + if ( step == 0 ) + step = 1; // Error case from bad encoding, but handle gracefully. + p += step; + + // Just advance one column, of course. + ++col; + } + else + { + ++p; + ++col; + } + break; + } + } + cursor.row = row; + cursor.col = col; + assert( cursor.row >= -1 ); + assert( cursor.col >= -1 ); + stamp = p; + assert( stamp ); +} + + +const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding ) +{ + if ( !p || !*p ) + { + return 0; + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + while ( *p ) + { + const unsigned char* pU = (const unsigned char*)p; + + // Skip the stupid Microsoft UTF-8 Byte order marks + if ( *(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==TIXML_UTF_LEAD_1 + && *(pU+2)==TIXML_UTF_LEAD_2 ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbeU ) + { + p += 3; + continue; + } + else if(*(pU+0)==TIXML_UTF_LEAD_0 + && *(pU+1)==0xbfU + && *(pU+2)==0xbfU ) + { + p += 3; + continue; + } + + if ( IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) // Still using old rules for white space. + ++p; + else + break; + } + } + else + { + while ( *p && IsWhiteSpace( *p ) || *p == '\n' || *p =='\r' ) + ++p; + } + + return p; +} + +#ifdef TIXML_USE_STL +/*static*/ bool TiXmlBase::StreamWhiteSpace( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + for( ;; ) + { + if ( !in->good() ) return false; + + int c = in->peek(); + // At this scope, we can't get to a document. So fail silently. + if ( !IsWhiteSpace( c ) || c <= 0 ) + return true; + + *tag += (char) in->get(); + } +} + +/*static*/ bool TiXmlBase::StreamTo( TIXML_ISTREAM * in, int character, TIXML_STRING * tag ) +{ + //assert( character > 0 && character < 128 ); // else it won't work in utf-8 + while ( in->good() ) + { + int c = in->peek(); + if ( c == character ) + return true; + if ( c <= 0 ) // Silent failure: can't get document at this scope + return false; + + in->get(); + *tag += (char) c; + } + return false; +} +#endif + +const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding ) +{ + *name = ""; + assert( p ); + + // Names start with letters or underscores. + // Of course, in unicode, tinyxml has no idea what a letter *is*. The + // algorithm is generous. + // + // After that, they can be letters, underscores, numbers, + // hyphens, or colons. (Colons are valid ony for namespaces, + // but tinyxml can't tell namespaces from names.) + if ( p && *p + && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) ) + { + while( p && *p + && ( IsAlphaNum( (unsigned char ) *p, encoding ) + || *p == '_' + || *p == '-' + || *p == '.' + || *p == ':' ) ) + { + (*name) += *p; + ++p; + } + return p; + } + return 0; +} + +const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding ) +{ + // Presume an entity, and pull it out. + TIXML_STRING ent; + int i; + *length = 0; + + if ( *(p+1) && *(p+1) == '#' && *(p+2) ) + { + unsigned long ucs = 0; + ptrdiff_t delta = 0; + unsigned mult = 1; + + if ( *(p+2) == 'x' ) + { + // Hexadecimal. + if ( !*(p+3) ) return 0; + + const char* q = p+3; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != 'x' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else if ( *q >= 'a' && *q <= 'f' ) + ucs += mult * (*q - 'a' + 10); + else if ( *q >= 'A' && *q <= 'F' ) + ucs += mult * (*q - 'A' + 10 ); + else + return 0; + mult *= 16; + --q; + } + } + else + { + // Decimal. + if ( !*(p+2) ) return 0; + + const char* q = p+2; + q = strchr( q, ';' ); + + if ( !q || !*q ) return 0; + + delta = q-p; + --q; + + while ( *q != '#' ) + { + if ( *q >= '0' && *q <= '9' ) + ucs += mult * (*q - '0'); + else + return 0; + mult *= 10; + --q; + } + } + if ( encoding == TIXML_ENCODING_UTF8 ) + { + // convert the UCS to UTF-8 + ConvertUTF32ToUTF8( ucs, value, length ); + } + else + { + *value = (char)ucs; + *length = 1; + } + return p + delta + 1; + } + + // Now try to match it. + for( i=0; iappend( cArr, len ); + } + } + else + { + bool whitespace = false; + + // Remove leading white space: + p = SkipWhiteSpace( p, encoding ); + while ( p && *p + && !StringEqual( p, endTag, caseInsensitive, encoding ) ) + { + if ( *p == '\r' || *p == '\n' ) + { + whitespace = true; + ++p; + } + else if ( IsWhiteSpace( *p ) ) + { + whitespace = true; + ++p; + } + else + { + // If we've found whitespace, add it before the + // new character. Any whitespace just becomes a space. + if ( whitespace ) + { + (*text) += ' '; + whitespace = false; + } + int len; + char cArr[4] = { 0, 0, 0, 0 }; + p = GetChar( p, cArr, &len, encoding ); + if ( len == 1 ) + (*text) += cArr[0]; // more efficient + else + text->append( cArr, len ); + } + } + } + return p + strlen( endTag ); +} + +#ifdef TIXML_USE_STL + +void TiXmlDocument::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + // The basic issue with a document is that we don't know what we're + // streaming. Read something presumed to be a tag (and hope), then + // identify it, and call the appropriate stream method on the tag. + // + // This "pre-streaming" will never read the closing ">" so the + // sub-tag can orient itself. + + if ( !StreamTo( in, '<', tag ) ) + { + SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + while ( in->good() ) + { + int tagIndex = (int) tag->length(); + while ( in->good() && in->peek() != '>' ) + { + int c = in->get(); + if ( c <= 0 ) + { + SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + break; + } + (*tag) += (char) c; + } + + if ( in->good() ) + { + // We now have something we presume to be a node of + // some sort. Identify it, and call the node to + // continue streaming. + TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING ); + + if ( node ) + { + node->StreamIn( in, tag ); + bool isElement = node->ToElement() != 0; + delete node; + node = 0; + + // If this is the root element, we're done. Parsing will be + // done by the >> operator. + if ( isElement ) + { + return; + } + } + else + { + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + } + } + // We should have returned sooner. + SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN ); +} + +#endif + +const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding ) +{ + ClearError(); + + // Parse away, at the document level. Since a document + // contains nothing but other tags, most of what happens + // here is skipping white space. + if ( !p || !*p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + // Note that, for a document, this needs to come + // before the while space skip, so that parsing + // starts from the pointer we are given. + location.Clear(); + if ( prevData ) + { + location.row = prevData->cursor.row; + location.col = prevData->cursor.col; + } + else + { + location.row = 0; + location.col = 0; + } + TiXmlParsingData data( p, TabSize(), location.row, location.col ); + location = data.Cursor(); + + if ( encoding == TIXML_ENCODING_UNKNOWN ) + { + // Check for the Microsoft UTF-8 lead bytes. + const unsigned char* pU = (const unsigned char*)p; + if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0 + && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1 + && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 ) + { + encoding = TIXML_ENCODING_UTF8; + useMicrosoftBOM = true; + } + } + + p = SkipWhiteSpace( p, encoding ); + if ( !p ) + { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN ); + return 0; + } + + while ( p && *p ) + { + TiXmlNode* node = Identify( p, encoding ); + if ( node ) + { + p = node->Parse( p, &data, encoding ); + LinkEndChild( node ); + } + else + { + break; + } + + // Did we get encoding info? + if ( encoding == TIXML_ENCODING_UNKNOWN + && node->ToDeclaration() ) + { + TiXmlDeclaration* dec = node->ToDeclaration(); + const char* enc = dec->Encoding(); + assert( enc ); + + if ( *enc == 0 ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; + else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) ) + encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice + else + encoding = TIXML_ENCODING_LEGACY; + } + + p = SkipWhiteSpace( p, encoding ); + } + + // Was this empty? + if ( !firstChild ) { + SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding ); + return 0; + } + + // All is well. + return p; +} + +void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + // The first error in a chain is more accurate - don't set again! + if ( error ) + return; + + assert( err > 0 && err < TIXML_ERROR_STRING_COUNT ); + error = true; + errorId = err; + errorDesc = errorString[ errorId ]; + + errorLocation.Clear(); + if ( pError && data ) + { + data->Stamp( pError, encoding ); + errorLocation = data->Cursor(); + } +} + + +TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding ) +{ + TiXmlNode* returnNode = 0; + + p = SkipWhiteSpace( p, encoding ); + if( !p || !*p || *p != '<' ) + { + return 0; + } + + TiXmlDocument* doc = GetDocument(); + p = SkipWhiteSpace( p, encoding ); + + if ( !p || !*p ) + { + return 0; + } + + // What is this thing? + // - Elements start with a letter or underscore, but xml is reserved. + // - Comments: "; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + p = ReadText( p, &value, false, endTag, false, encoding ); + return p; +} + + +const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) return 0; + + int tabsize = 4; + if ( document ) + tabsize = document->TabSize(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + // Read the name, the '=' and the value. + const char* pErr = p; + p = ReadName( p, &name, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding ); + return 0; + } + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p || *p != '=' ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + ++p; // skip '=' + p = SkipWhiteSpace( p, encoding ); + if ( !p || !*p ) + { + if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding ); + return 0; + } + + const char* end; + + if ( *p == '\'' ) + { + ++p; + end = "\'"; + p = ReadText( p, &value, false, end, false, encoding ); + } + else if ( *p == '"' ) + { + ++p; + end = "\""; + p = ReadText( p, &value, false, end, false, encoding ); + } + else + { + // All attribute values should be in single or double quotes. + // But this is such a common error that the parser will try + // its best, even without them. + value = ""; + while ( p && *p // existence + && !IsWhiteSpace( *p ) && *p != '\n' && *p != '\r' // whitespace + && *p != '/' && *p != '>' ) // tag end + { + value += *p; + ++p; + } + } + return p; +} + +#ifdef TIXML_USE_STL +void TiXmlText::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + if ( cdata ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + + if ( c == '>' + && tag->at( tag->length() - 2 ) == ']' + && tag->at( tag->length() - 3 ) == ']' ) + { + // All is well. + return; + } + } + else + { + while ( in->good() ) + { + int c = in->peek(); + if ( c == '<' ) + return; + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + + (*tag) += (char) c; + in->get(); + } + } +} +#endif + +const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding ) +{ + value = ""; + TiXmlDocument* document = GetDocument(); + + if ( data ) + { + data->Stamp( p, encoding ); + location = data->Cursor(); + } + + const char* const startTag = ""; + + if ( cdata || StringEqual( p, startTag, false, encoding ) ) + { + cdata = true; + + if ( !StringEqual( p, startTag, false, encoding ) ) + { + document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding ); + return 0; + } + p += strlen( startTag ); + + // Keep all the white space, ignore the encoding, etc. + while ( p && *p + && !StringEqual( p, endTag, false, encoding ) + ) + { + value += *p; + ++p; + } + + TIXML_STRING dummy; + p = ReadText( p, &dummy, false, endTag, false, encoding ); + return p; + } + else + { + bool ignoreWhite = true; + + const char* end = "<"; + p = ReadText( p, &value, ignoreWhite, end, false, encoding ); + if ( p ) + return p-1; // don't truncate the '<' + return 0; + } +} + +#ifdef TIXML_USE_STL +void TiXmlDeclaration::StreamIn( TIXML_ISTREAM * in, TIXML_STRING * tag ) +{ + while ( in->good() ) + { + int c = in->get(); + if ( c <= 0 ) + { + TiXmlDocument* document = GetDocument(); + if ( document ) + document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN ); + return; + } + (*tag) += (char) c; + + if ( c == '>' ) + { + // All is well. + return; + } + } +} +#endif + +const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding ) +{ + p = SkipWhiteSpace( p, _encoding ); + // Find the beginning, find the end, and look for + // the stuff in-between. + TiXmlDocument* document = GetDocument(); + if ( !p || !*p || !StringEqual( p, "SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding ); + return 0; + } + if ( data ) + { + data->Stamp( p, _encoding ); + location = data->Cursor(); + } + p += 5; + + version = ""; + encoding = ""; + standalone = ""; + + while ( p && *p ) + { + if ( *p == '>' ) + { + ++p; + return p; + } + + p = SkipWhiteSpace( p, _encoding ); + if ( StringEqual( p, "version", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + version = attrib.Value(); + } + else if ( StringEqual( p, "encoding", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + encoding = attrib.Value(); + } + else if ( StringEqual( p, "standalone", true, _encoding ) ) + { + TiXmlAttribute attrib; + p = attrib.Parse( p, data, _encoding ); + standalone = attrib.Value(); + } + else + { + // Read over whatever it is. + while( p && *p && *p != '>' && !IsWhiteSpace( *p ) ) + ++p; + } + } + return 0; +} + +bool TiXmlText::Blank() const +{ + for ( unsigned i=0; i Date: Thu, 9 Jul 2009 00:01:15 +0200 Subject: [PATCH 04/18] Added StdMesh data structures Loading code is still to be written --- CMakeLists.txt | 2 + Makefile.am | 2 + standard/inc/StdMesh.h | 220 +++++++++++++++++++++++++++++ standard/inc/StdMeshMaterial.h | 2 +- standard/src/StdMesh.cpp | 229 +++++++++++++++++++++++++++++++ standard/src/StdMeshMaterial.cpp | 2 +- 6 files changed, 455 insertions(+), 2 deletions(-) create mode 100644 standard/inc/StdMesh.h create mode 100644 standard/src/StdMesh.cpp 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) From 2a3382b4ea4af90315ffa9b778f02949aade56fa Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Fri, 10 Jul 2009 00:18:26 +0200 Subject: [PATCH 05/18] Added mesh loading (from OGRE XML file) --- standard/inc/StdMesh.h | 27 ++- standard/src/StdMesh.cpp | 388 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 406 insertions(+), 9 deletions(-) diff --git a/standard/inc/StdMesh.h b/standard/inc/StdMesh.h index 14ca97ead..e190c4835 100644 --- a/standard/inc/StdMesh.h +++ b/standard/inc/StdMesh.h @@ -37,8 +37,8 @@ protected: // 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. +// was loaded from, for example from a C4Group. Return default-construted +// StdStrBuf with NULL data in case of error. class StdMeshSkeletonLoader { public: @@ -73,14 +73,16 @@ class StdMeshBone { friend class StdMesh; public: - unsigned int Index; // Index in master bone array + StdMeshBone() {} + + unsigned int Index; // Index in master bone table int ID; // Bone ID StdStrBuf Name; // Bone name // Bone transformation - StdMeshMatrix trans; + StdMeshMatrix Trans; // Inverse transformation - StdMeshMatrix inverse_trans; + StdMeshMatrix InverseTrans; const StdMeshBone* GetParent() const { return Parent; } @@ -149,8 +151,12 @@ class StdMeshAnimation { friend class StdMesh; public: + StdMeshAnimation() {} + StdMeshAnimation(const StdMeshAnimation& other); ~StdMeshAnimation(); + StdMeshAnimation& operator=(const StdMeshAnimation& other); + StdStrBuf Name; float Length; @@ -164,8 +170,10 @@ public: StdMesh(); ~StdMesh(); - // Throws StdMeshError - void InitXML(const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager); + // filename is only used to show it in error messages. The model is + // loaded from xml_data. + // Throws StdMeshError. + void InitXML(const char* filename, 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(); } @@ -180,6 +188,11 @@ public: const StdMeshMaterial& GetMaterial() const { return *Material; } private: + void AddMasterBone(StdMeshBone* bone); + + StdMesh(const StdMesh& other); // non-copyable + StdMesh& operator=(const StdMesh& other); // non-assignable + // Remember bone assignments for vertices class Vertex: public StdMeshVertex { diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp index 614e8fd34..9e3d0b5e9 100644 --- a/standard/src/StdMesh.cpp +++ b/standard/src/StdMesh.cpp @@ -18,11 +18,88 @@ #include +#include + StdMeshError::StdMeshError(const StdStrBuf& message, const char* file, unsigned int line) { Buf.Format("%s[%u]: %s", file, line, message.getData()); } +// Helper class to load things from an XML file with error checking +class StdMeshXML +{ +public: + StdMeshXML(const char* filename, const char* xml_data); + + const char* RequireStrAttribute(TiXmlElement* element, const char* attribute) const; + int RequireIntAttribute(TiXmlElement* element, const char* attribute) const; + float RequireFloatAttribute(TiXmlElement* element, const char* attribute) const; + + TiXmlElement* RequireFirstChild(TiXmlElement* element, const char* child); + + void Error(const StdStrBuf& message, TiXmlElement* element) const; + +private: + TiXmlDocument Document; + StdStrBuf FileName; +}; + +StdMeshXML::StdMeshXML(const char* filename, const char* xml_data): + FileName(filename) +{ + Document.Parse(filename); + if(Document.Error()) + throw StdMeshError(StdStrBuf(Document.ErrorDesc()), FileName.getData(), Document.ErrorRow()); +} + +const char* StdMeshXML::RequireStrAttribute(TiXmlElement* element, const char* attribute) const +{ + const char* value = element->Attribute(attribute); + if(!value) Error(FormatString("Element '%s' does not have attribute '%s'", element->Value(), attribute), element); + return value; +} + +int StdMeshXML::RequireIntAttribute(TiXmlElement* element, const char* attribute) const +{ + int retval; + if(element->QueryIntAttribute(attribute, &retval) != TIXML_SUCCESS) + Error(FormatString("Element '%s' does not have integer attribute '%s'", element->Value(), attribute), element); + return retval; +} + +float StdMeshXML::RequireFloatAttribute(TiXmlElement* element, const char* attribute) const +{ + float retval; + if(element->QueryFloatAttribute(attribute, &retval) != TIXML_SUCCESS) + Error(FormatString("Element '%s' does not have integer attribute '%s'", element->Value(), attribute), element); + return retval; +} + +TiXmlElement* StdMeshXML::RequireFirstChild(TiXmlElement* element, const char* child) +{ + TiXmlElement* retval; + + if(element) + { + retval = element->FirstChildElement(child); + if(!retval) + Error(FormatString("Element '%s' does not contain '%s' child", element->Value(), child), element); + } + else + { + retval = Document.RootElement(); + if(strcmp(retval->Value(), child) != 0) + Error(FormatString("Root element is not '%s'", child), retval); + } + + return retval; +} + +void StdMeshXML::Error(const StdStrBuf& message, TiXmlElement* element) const +{ + throw StdMeshError(message, FileName.getData(), element->Row()); +} + void StdMeshMatrix::SetIdentity() { a[0][0] = 1.0f; a[0][1] = 0.0f; a[0][2] = 0.0f; a[0][3] = 0.0f; @@ -206,12 +283,40 @@ StdMeshMatrix StdMeshTrack::GetTransformAt(float time) const return trans1; } +StdMeshAnimation::StdMeshAnimation(const StdMeshAnimation& other): + Name(other.Name), Length(other.Length), Tracks(other.Tracks.size()) +{ + // Note that all Tracks are already default-initialized to zero + for(unsigned int i = 0; i < Tracks.size(); ++i) + if(other.Tracks[i]) + Tracks[i] = new StdMeshTrack(*other.Tracks[i]); +} + StdMeshAnimation::~StdMeshAnimation() { for(unsigned int i = 0; i < Tracks.size(); ++i) delete Tracks[i]; } +StdMeshAnimation& StdMeshAnimation::operator=(const StdMeshAnimation& other) +{ + if(this == &other) return *this; + + Name = other.Name; + Length = other.Length; + + for(unsigned int i = 0; i < Tracks.size(); ++i) + delete Tracks[i]; + + Tracks.resize(other.Tracks.size()); + + for(unsigned int i = 0; i < Tracks.size(); ++i) + if(other.Tracks[i]) + Tracks[i] = new StdMeshTrack(*other.Tracks[i]); + + return *this; +} + StdMesh::StdMesh(): Material(NULL) { @@ -223,7 +328,286 @@ StdMesh::~StdMesh() delete Bones[i]; } -void StdMesh::InitXML(const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager) +void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeletonLoader& skel_loader, const StdMeshMatManager& manager) { - + StdMeshXML mesh(filename, xml_data); + + TiXmlElement* mesh_elem = mesh.RequireFirstChild(NULL, "mesh"); + TiXmlElement* submeshes_elem = mesh.RequireFirstChild(mesh_elem, "submeshes"); + // Load first submesh only for now + TiXmlElement* submesh_elem = mesh.RequireFirstChild(submeshes_elem, "submesh"); + TiXmlElement* geometry_elem = mesh.RequireFirstChild(submesh_elem, "geometry"); + + int VertexCount = mesh.RequireIntAttribute(geometry_elem, "vertexcount"); + Vertices.resize(VertexCount); + + TiXmlElement* buffer_elem = mesh.RequireFirstChild(geometry_elem, "vertexbuffer"); + + unsigned int i = 0; + for(TiXmlElement* vertex_elem = buffer_elem->FirstChildElement("vertex"); vertex_elem != NULL && i < Vertices.size(); vertex_elem = vertex_elem->NextSiblingElement("vertex"), ++i) + { + TiXmlElement* position_elem = mesh.RequireFirstChild(vertex_elem, "position"); + TiXmlElement* normal_elem = mesh.RequireFirstChild(vertex_elem, "normal"); + TiXmlElement* texcoord_elem = mesh.RequireFirstChild(vertex_elem, "texcoord"); + + Vertices[i].x = mesh.RequireFloatAttribute(position_elem, "x"); + Vertices[i].y = mesh.RequireFloatAttribute(position_elem, "y"); + Vertices[i].z = mesh.RequireFloatAttribute(position_elem, "z"); + Vertices[i].nx = mesh.RequireFloatAttribute(normal_elem, "x"); + Vertices[i].ny = mesh.RequireFloatAttribute(normal_elem, "y"); + Vertices[i].nz = mesh.RequireFloatAttribute(normal_elem, "z"); + Vertices[i].u = mesh.RequireFloatAttribute(texcoord_elem, "u"); + Vertices[i].v = mesh.RequireFloatAttribute(texcoord_elem, "v"); + } + + TiXmlElement* faces_elem = mesh.RequireFirstChild(submesh_elem, "faces"); + int FaceCount = mesh.RequireIntAttribute(faces_elem, "count"); + Faces.resize(FaceCount); + + i = 0; + for(TiXmlElement* face_elem = faces_elem->FirstChildElement("face"); face_elem != NULL && i < Faces.size(); face_elem = face_elem->NextSiblingElement("face"), ++i) + { + int v[3]; + + v[0] = mesh.RequireIntAttribute(face_elem, "v1"); + v[1] = mesh.RequireIntAttribute(face_elem, "v2"); + v[2] = mesh.RequireIntAttribute(face_elem, "v3"); + + for(unsigned int j = 0; j < 3; ++j) + { + if(v[j] < 0 || static_cast(v[j]) >= Vertices.size()) + mesh.Error(FormatString("Vertex index v%u (%d) is out of range", j+1, v[j]), face_elem); + Faces[i].Vertices[j] = v[j]; + } + } + + // Read skeleton + TiXmlElement* skeletonlink_elem = mesh.RequireFirstChild(mesh_elem, "skeletonlink"); + const char* name = mesh.RequireStrAttribute(skeletonlink_elem, "name"); + StdStrBuf xml_filename(name); xml_filename.Append(".xml"); + + StdStrBuf skeleton_xml_data = skel_loader.LoadSkeleton(xml_filename.getData()); + if(skeleton_xml_data.isNull()) mesh.Error(FormatString("Failed to load '%s'", xml_filename.getData()), skeletonlink_elem); + + StdMeshXML skeleton(xml_filename.getData(), skeleton_xml_data.getData()); + TiXmlElement* skeleton_elem = skeleton.RequireFirstChild(NULL, "skeleton"); + TiXmlElement* bones_elem = skeleton.RequireFirstChild(skeleton_elem, "bones"); + + // Read bones. Don't insert into Master bone table yet, as the master bone + // table is sorted hierarchically, and we will read the hierarchy only + // afterwards. + std::vector bones; + for(TiXmlElement* bone_elem = bones_elem->FirstChildElement("bone"); bone_elem != NULL; bone_elem = bone_elem->NextSiblingElement("bone")) + { + StdMeshBone* bone = new StdMeshBone; + bones.push_back(bone); + + bone->ID = skeleton.RequireIntAttribute(bone_elem, "id"); + bone->Name = skeleton.RequireStrAttribute(bone_elem, "name"); + + // TODO: Make sure ID and name are unique + + TiXmlElement* position_elem = skeleton.RequireFirstChild(bone_elem, "position"); + TiXmlElement* rotation_elem = skeleton.RequireFirstChild(bone_elem, "rotation"); + TiXmlElement* axis_elem = skeleton.RequireFirstChild(rotation_elem, "axis"); + + float dx = skeleton.RequireFloatAttribute(position_elem, "x"); + float dy = skeleton.RequireFloatAttribute(position_elem, "y"); + float dz = skeleton.RequireFloatAttribute(position_elem, "z"); + float angle = skeleton.RequireFloatAttribute(rotation_elem, "angle"); + float rx = skeleton.RequireFloatAttribute(axis_elem, "x"); + float ry = skeleton.RequireFloatAttribute(axis_elem, "y"); + float rz = skeleton.RequireFloatAttribute(axis_elem, "z"); + + StdMeshMatrix helper; + helper.SetTranslate(dx, dy, dz); + bone->Trans.SetRotate(angle, rx, ry, rz); + bone->Trans.Transform(helper); + + helper.SetRotate(-angle, rx, ry, rz); + bone->InverseTrans.SetTranslate(-dx, -dy, -dz); + bone->InverseTrans.Transform(helper); + + bone->Parent = NULL; + + // Index of bone will be set when building Master Bone Table later + } + + // Bone hierarchy + TiXmlElement* bonehierarchy_elem = skeleton.RequireFirstChild(skeleton_elem, "bonehierarchy"); + for(TiXmlElement* boneparent_elem = bonehierarchy_elem->FirstChildElement("boneparent"); boneparent_elem != NULL; boneparent_elem = boneparent_elem->NextSiblingElement("boneparent")) + { + const char* child_name = skeleton.RequireStrAttribute(boneparent_elem, "bone"); + const char* parent_name = skeleton.RequireStrAttribute(boneparent_elem, "parent"); + + // Lookup the two bones + StdMeshBone* child = NULL; + StdMeshBone* parent = NULL; + for(unsigned int i = 0; i < bones.size() && (!child || !parent); ++i) + { + if(!child && bones[i]->Name == child_name) + child = bones[i]; + if(!parent && bones[i]->Name == parent_name) + parent = bones[i]; + } + + if(!child) skeleton.Error(FormatString("There is no such bone with name '%s'", child_name), boneparent_elem); + if(!parent) skeleton.Error(FormatString("There is no such bone with name '%s'", parent_name), boneparent_elem); + + child->Parent = parent; + parent->Children.push_back(child); + + // Apply parent transformation + child->Trans.Transform(parent->Trans); + child->InverseTrans.Mul(parent->InverseTrans); + } + + // Fill master bone table in hierarchical order: + for(unsigned int i = 0; i < bones.size(); ++i) + if(bones[i]->Parent == NULL) + AddMasterBone(bones[i]); + + // Vertex<->Bone assignments + TiXmlElement* boneassignments_elem = mesh.RequireFirstChild(submesh_elem, "boneassignments"); + for(TiXmlElement* vertexboneassignment_elem = boneassignments_elem->FirstChildElement("vertexboneassignment"); vertexboneassignment_elem != NULL; vertexboneassignment_elem = vertexboneassignment_elem->NextSiblingElement("vertexboneassignment")) + { + int BoneID = mesh.RequireIntAttribute(vertexboneassignment_elem, "boneindex"); + int VertexIndex = mesh.RequireIntAttribute(vertexboneassignment_elem, "vertexindex"); + float weight = mesh.RequireFloatAttribute(vertexboneassignment_elem, "weight"); + + if(VertexIndex < 0 || static_cast(VertexIndex) >= Vertices.size()) + mesh.Error(FormatString("Vertex index in bone assignment (%d) is out of range", VertexIndex), vertexboneassignment_elem); + + StdMeshBone* bone = NULL; + for(unsigned int i = 0; !bone && i < bones.size(); ++i) + if(bones[i]->ID == BoneID) + bone = bones[i]; + + if(!bone) mesh.Error(FormatString("There is no such bone with index %d", BoneID), vertexboneassignment_elem); + + Vertex& vertex = Vertices[VertexIndex]; + vertex.BoneAssignments.push_back(StdMeshVertexBoneAssignment()); + StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments.back(); + assignment.BoneIndex = bone->Index; + assignment.Weight = weight; + } + + // Normalize vertex bone assignment weights (this is not guaranteed in the + // Ogre file format). + for(unsigned int i = 0; i < Vertices.size(); ++i) + { + Vertex& vertex = Vertices[i]; + float sum = 0.0f; + for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j) + sum += vertex.BoneAssignments[j].Weight; + for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j) + vertex.BoneAssignments[j].Weight /= sum; + } + + // Load Animations + TiXmlElement* animations_elem = skeleton.RequireFirstChild(skeleton_elem, "animations"); + for(TiXmlElement* animation_elem = animations_elem->FirstChildElement("animation"); animation_elem != NULL; animation_elem = animation_elem->NextSiblingElement("animation")) + { + StdStrBuf name(skeleton.RequireStrAttribute(animation_elem, "name")); + if(Animations.find(name) != Animations.end()) + skeleton.Error(FormatString("There is already an animation with name '%s'", name.getData()), animation_elem); + + StdMeshAnimation& animation = Animations.insert(std::make_pair(name, StdMeshAnimation())).first->second; + animation.Name = name; + animation.Length = skeleton.RequireFloatAttribute(animation_elem, "length"); + animation.Tracks.resize(Bones.size()); + + TiXmlElement* tracks_elem = skeleton.RequireFirstChild(animation_elem, "tracks"); + for(TiXmlElement* track_elem = tracks_elem->FirstChildElement("track"); track_elem != NULL; track_elem = track_elem->NextSiblingElement("track")) + { + const char* bone_name = skeleton.RequireStrAttribute(track_elem, "bone"); + StdMeshBone* bone = NULL; + for(unsigned int i = 0; !bone && i < Bones.size(); ++i) + if(Bones[i]->Name == bone_name) + bone = Bones[i]; + if(!bone) skeleton.Error(FormatString("There is no such bone with name '%s'", bone_name), track_elem); + + if(animation.Tracks[bone->Index] != NULL) skeleton.Error(FormatString("There is already a track for bone '%s' in animation '%s'", bone_name, animation.Name.getData()), track_elem); + + StdMeshTrack* track = new StdMeshTrack; + animation.Tracks[bone->Index] = track; + + TiXmlElement* keyframes_elem = skeleton.RequireFirstChild(track_elem, "keyframes"); + for(TiXmlElement* keyframe_elem = keyframes_elem->FirstChildElement("keyframe"); keyframe_elem != NULL; keyframe_elem = keyframe_elem->NextSiblingElement("keyframe")) + { + float time = skeleton.RequireFloatAttribute(keyframe_elem, "time"); + StdMeshKeyFrame& frame = track->Frames[time]; + + TiXmlElement* translate_elem = skeleton.RequireFirstChild(keyframe_elem, "translate"); + TiXmlElement* rotate_elem = skeleton.RequireFirstChild(keyframe_elem, "rotate"); + TiXmlElement* scale_elem = skeleton.RequireFirstChild(scale_elem, "scale"); + TiXmlElement* axis_elem = skeleton.RequireFirstChild(rotate_elem, "axis"); + + float dx = skeleton.RequireFloatAttribute(translate_elem, "x"); + float dy = skeleton.RequireFloatAttribute(translate_elem, "y"); + float dz = skeleton.RequireFloatAttribute(translate_elem, "z"); + float sx = skeleton.RequireFloatAttribute(scale_elem, "x"); + float sy = skeleton.RequireFloatAttribute(scale_elem, "y"); + float sz = skeleton.RequireFloatAttribute(scale_elem, "z"); + float angle = skeleton.RequireFloatAttribute(rotate_elem, "angle"); + float rx = skeleton.RequireFloatAttribute(axis_elem, "x"); + float ry = skeleton.RequireFloatAttribute(axis_elem, "y"); + float rz = skeleton.RequireFloatAttribute(axis_elem, "z"); + + StdMeshMatrix helper; + frame.Trans.SetRotate(angle, rx, ry, rz); + helper.SetScale(sx, sy, sz); + frame.Trans.Transform(helper); + helper.SetTranslate(-dx, -dy, -dz); + frame.Trans.Transform(helper); + } + } + + // Apply bone transformation on animation frames. We need to do this + // after the actual loading because we need to walk the bone list + // hierarchically. + for(unsigned int i = 0; i < Bones.size(); ++i) + { + if(animation.Tracks[i]) + { + StdMeshTrack& track = *animation.Tracks[i]; + + // Get next parent track + StdMeshTrack* parent_track = NULL; + StdMeshBone* parent_bone = Bones[i]->Parent; + while(parent_bone && !(parent_track = animation.Tracks[parent_bone->Index])) + parent_bone = parent_bone->Parent; + assert(!parent_bone || parent_track); + + for(std::map::iterator iter = track.Frames.begin(); iter != track.Frames.end(); ++iter) + { + // TODO: If this bone's track is not as smooth as the parent animation + // (which means if there is more than one keyframe in the parent + // animation for each keyframe pair in the this bone's animation), + // then we need to insert additional child keyframes, so that the + // transformation for the child does not skip parent keyframes. + StdMeshKeyFrame& frame = iter->second; + + // Apply transformation of parent tracks (for which we computed + // already the bone transformations, as we walk the bone list + // hierarchically) in bone's coordinate system. + frame.Trans.Mul(Bones[i]->InverseTrans); + frame.Trans.Transform(Bones[i]->Trans); + + if(parent_bone) + frame.Trans.Transform(parent_track->GetTransformAt(iter->first)); + } + } + } + } } + +void StdMesh::AddMasterBone(StdMeshBone* bone) +{ + bone->Index = Bones.size(); // Remember index in master bone table + Bones.push_back(bone); + for(unsigned int i = 0; i < bone->Children.size(); ++i) + AddMasterBone(bone->Children[i]); +} + +// vim: et ts=2 sw=2 From 16bd81ffad8a1d4ea9a9c62a468185d543826c10 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Fri, 10 Jul 2009 21:57:11 +0200 Subject: [PATCH 06/18] Implemented StdMeshInstance --- standard/inc/StdMesh.h | 19 ++++++--- standard/src/StdMesh.cpp | 91 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 103 insertions(+), 7 deletions(-) diff --git a/standard/inc/StdMesh.h b/standard/inc/StdMesh.h index e190c4835..8195652d4 100644 --- a/standard/inc/StdMesh.h +++ b/standard/inc/StdMesh.h @@ -114,12 +114,10 @@ public: // *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 @@ -150,6 +148,7 @@ private: class StdMeshAnimation { friend class StdMesh; + friend class StdMeshInstance; public: StdMeshAnimation() {} StdMeshAnimation(const StdMeshAnimation& other); @@ -166,6 +165,7 @@ private: class StdMesh { + friend class StdMeshInstance; public: StdMesh(); ~StdMesh(); @@ -184,7 +184,7 @@ public: const StdMeshBone& GetBone(unsigned int i) const { return *Bones[i]; } unsigned int GetNumBones() const { return Bones.size(); } - const StdMeshAnimation& GetAnimationByName(const char* name); + const StdMeshAnimation* GetAnimationByName(const StdStrBuf& name) const; const StdMeshMaterial& GetMaterial() const { return *Material; } private: @@ -214,17 +214,26 @@ class StdMeshInstance public: StdMeshInstance(const StdMesh& mesh); - void SetAnimation(const StdStrBuf& animation); - const StdMeshAnimation& GetAnimation() const; + void SetAnimation(const StdStrBuf& animation_name); + void UnsetAnimation(); + + const StdMeshAnimation* GetAnimation() const { return Animation; } void SetPosition(float position); + float GetPosition() const { return 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(); } + const StdMesh& Mesh; + protected: + const StdMeshAnimation* Animation; + float Position; + + std::vector BoneTransforms; std::vector Vertices; }; diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp index 9e3d0b5e9..a583ca714 100644 --- a/standard/src/StdMesh.cpp +++ b/standard/src/StdMesh.cpp @@ -224,7 +224,6 @@ void StdMeshVertex::Transform(const StdMeshMatrix& trans) 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; @@ -249,7 +248,6 @@ void StdMeshVertex::Add(const StdMeshVertex& other) ny += other.ny; nz += other.nz; } -#endif StdMeshMatrix StdMeshTrack::GetTransformAt(float time) const { @@ -610,4 +608,93 @@ void StdMesh::AddMasterBone(StdMeshBone* bone) AddMasterBone(bone->Children[i]); } +const StdMeshAnimation* StdMesh::GetAnimationByName(const StdStrBuf& name) const +{ + std::map::const_iterator iter = Animations.find(name); + if(iter == Animations.end()) return NULL; + return &iter->second; +} + +StdMeshInstance::StdMeshInstance(const StdMesh& mesh): + Mesh(mesh), Animation(NULL), Position(0.0f), + BoneTransforms(Mesh.GetNumBones()), Vertices(Mesh.GetNumVertices()) +{ + for(unsigned int i = 0; i < Mesh.GetNumVertices(); ++i) + Vertices[i] = Mesh.GetVertex(i); +} + +void StdMeshInstance::SetAnimation(const StdStrBuf& animation_name) +{ + Animation = Mesh.GetAnimationByName(animation_name); + SetPosition(0.0f); +} + +void StdMeshInstance::UnsetAnimation() +{ + Animation = NULL; + + // Reset instance vertices + for(unsigned int i = 0; i < Mesh.GetNumVertices(); ++i) + Vertices[i] = Mesh.GetVertex(i); +} + +void StdMeshInstance::SetPosition(float position) +{ + assert(Animation); + Position = position; + + // Compute transformation matrix for each bone. + for(unsigned int i = 0; i < BoneTransforms.size(); ++i) + { + StdMeshTrack* track = Animation->Tracks[i]; + if(track) + { + BoneTransforms[i] = track->GetTransformAt(position); + } + else + { + // No track for this bone, so use parent transformation + const StdMeshBone* parent = Mesh.GetBone(i).GetParent(); + if(parent) + { + // Parent should already have been processed, because the bone indices + // are supposed to be hierarchically ordered. + assert(parent->Index < i); + BoneTransforms[i] = BoneTransforms[parent->Index]; + } + else + { + BoneTransforms[i].SetIdentity(); + } + } + } + + // Compute transformation for each vertex. We could later think about + // doing this on the GPU using a vertex shader. This would then probably + // need to go to CStdGL::PerformMesh and CStdD3D::PerformMesh. + for(unsigned int i = 0; i < Vertices.size(); ++i) + { + const StdMesh::Vertex& vertex = Mesh.Vertices[i]; + if(!vertex.BoneAssignments.empty()) + { + Vertices[i].x = Vertices[i].y = Vertices[i].z = 0.0f; + Vertices[i].nx = Vertices[i].ny = Vertices[i].nz = 0.0f; + Vertices[i].u = vertex.u; Vertices[i].v = vertex.v; + + for(unsigned int j = 0; j < vertex.BoneAssignments.size(); ++j) + { + const StdMeshVertexBoneAssignment& assignment = vertex.BoneAssignments[j]; + StdMeshVertex vtx = vertex; + vtx.Transform(BoneTransforms[assignment.BoneIndex]); + vtx.Mul(assignment.Weight); + Vertices[i].Add(vtx); + } + } + else + { + Vertices[i] = vertex; + } + } +} + // vim: et ts=2 sw=2 From 8c15d08a180b866ac0e2c71d92e4b1507817fd2a Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Fri, 10 Jul 2009 22:02:32 +0200 Subject: [PATCH 07/18] Added BoundingBox to StdMesh --- standard/inc/StdMesh.h | 9 +++++++++ standard/src/StdMesh.cpp | 19 +++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/standard/inc/StdMesh.h b/standard/inc/StdMesh.h index 8195652d4..74a3f1b3f 100644 --- a/standard/inc/StdMesh.h +++ b/standard/inc/StdMesh.h @@ -163,6 +163,12 @@ private: std::vector Tracks; // bone-indexed }; +struct StdMeshBox +{ + float x1, y1, z1; + float x2, y2, z2; +}; + class StdMesh { friend class StdMeshInstance; @@ -187,6 +193,8 @@ public: const StdMeshAnimation* GetAnimationByName(const StdStrBuf& name) const; const StdMeshMaterial& GetMaterial() const { return *Material; } + const StdMeshBox& GetBoundingBox() const { return BoundingBox; } + private: void AddMasterBone(StdMeshBone* bone); @@ -206,6 +214,7 @@ private: std::map Animations; + StdMeshBox BoundingBox; const StdMeshMaterial* Material; }; diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp index a583ca714..6e65ef5cf 100644 --- a/standard/src/StdMesh.cpp +++ b/standard/src/StdMesh.cpp @@ -318,6 +318,8 @@ StdMeshAnimation& StdMeshAnimation::operator=(const StdMeshAnimation& other) StdMesh::StdMesh(): Material(NULL) { + BoundingBox.x1 = BoundingBox.y1 = BoundingBox.z1 = 0.0f; + BoundingBox.x2 = BoundingBox.y2 = BoundingBox.z2 = 0.0f; } StdMesh::~StdMesh() @@ -356,6 +358,23 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto Vertices[i].nz = mesh.RequireFloatAttribute(normal_elem, "z"); Vertices[i].u = mesh.RequireFloatAttribute(texcoord_elem, "u"); Vertices[i].v = mesh.RequireFloatAttribute(texcoord_elem, "v"); + + // Construct BoundingBox + if(i == 0) + { + BoundingBox.x1 = BoundingBox.x2 = Vertices[i].x; + BoundingBox.y1 = BoundingBox.y2 = Vertices[i].y; + BoundingBox.z1 = BoundingBox.z2 = Vertices[i].z; + } + else + { + BoundingBox.x1 = Min(Vertices[i].x, BoundingBox.x1); + BoundingBox.x2 = Max(Vertices[i].x, BoundingBox.x2); + BoundingBox.y1 = Min(Vertices[i].y, BoundingBox.y1); + BoundingBox.y2 = Max(Vertices[i].y, BoundingBox.y2); + BoundingBox.z1 = Min(Vertices[i].z, BoundingBox.z1); + BoundingBox.z2 = Max(Vertices[i].z, BoundingBox.z2); + } } TiXmlElement* faces_elem = mesh.RequireFirstChild(submesh_elem, "faces"); From 3d359840a111c4e7eace1dc0391e2a8b05221df8 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sat, 11 Jul 2009 00:31:47 +0200 Subject: [PATCH 08/18] Declare StdBuf::operator< as const so that it is used in map lookups --- standard/inc/StdBuf.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/inc/StdBuf.h b/standard/inc/StdBuf.h index 4333ca23c..53a08cad0 100644 --- a/standard/inc/StdBuf.h +++ b/standard/inc/StdBuf.h @@ -539,7 +539,7 @@ public: operator const void *() const { return getData(); } // less-than operation for map - inline bool operator <(const StdStrBuf &v2) + inline bool operator <(const StdStrBuf &v2) const { int iLen = getLength(), iLen2 = v2.getLength(); if (iLen == iLen2) From a3f1ccf66e9bcd6145943e2158a463177e355867 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sat, 11 Jul 2009 01:09:48 +0200 Subject: [PATCH 09/18] Fixed mesh loading --- standard/inc/StdMeshMaterial.h | 4 ++++ standard/src/StdMesh.cpp | 9 +++++++-- standard/src/StdMeshMaterial.cpp | 19 ++++++++++++------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/standard/inc/StdMeshMaterial.h b/standard/inc/StdMeshMaterial.h index 74590a8bc..87e78fe37 100644 --- a/standard/inc/StdMeshMaterial.h +++ b/standard/inc/StdMeshMaterial.h @@ -129,6 +129,10 @@ public: class StdMeshMatManager { public: + // Remove all materials from manager. Make sure there is no StdMesh + // referencing any out there before calling this. + void Clear(); + // Parse a material script file, and add the materials to the manager. // filename may be NULL if the source is not a file. It will only be used // for error messages. diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp index 6e65ef5cf..a1ba3f1f6 100644 --- a/standard/src/StdMesh.cpp +++ b/standard/src/StdMesh.cpp @@ -47,7 +47,7 @@ private: StdMeshXML::StdMeshXML(const char* filename, const char* xml_data): FileName(filename) { - Document.Parse(filename); + Document.Parse(xml_data); if(Document.Error()) throw StdMeshError(StdStrBuf(Document.ErrorDesc()), FileName.getData(), Document.ErrorRow()); } @@ -338,6 +338,11 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto TiXmlElement* submesh_elem = mesh.RequireFirstChild(submeshes_elem, "submesh"); TiXmlElement* geometry_elem = mesh.RequireFirstChild(submesh_elem, "geometry"); + const char* material = mesh.RequireStrAttribute(submesh_elem, "material"); + Material = manager.GetMaterial(material); + if(!Material) + mesh.Error(FormatString("There is no such material named '%s'", material), submesh_elem); + int VertexCount = mesh.RequireIntAttribute(geometry_elem, "vertexcount"); Vertices.resize(VertexCount); @@ -557,7 +562,7 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto TiXmlElement* translate_elem = skeleton.RequireFirstChild(keyframe_elem, "translate"); TiXmlElement* rotate_elem = skeleton.RequireFirstChild(keyframe_elem, "rotate"); - TiXmlElement* scale_elem = skeleton.RequireFirstChild(scale_elem, "scale"); + TiXmlElement* scale_elem = skeleton.RequireFirstChild(keyframe_elem, "scale"); TiXmlElement* axis_elem = skeleton.RequireFirstChild(rotate_elem, "axis"); float dx = skeleton.RequireFloatAttribute(translate_elem, "x"); diff --git a/standard/src/StdMeshMaterial.cpp b/standard/src/StdMeshMaterial.cpp index 3c470698d..7700751ec 100644 --- a/standard/src/StdMeshMaterial.cpp +++ b/standard/src/StdMeshMaterial.cpp @@ -289,22 +289,22 @@ void StdMeshMaterialPass::Load(StdMeshMaterialParserCtx& ctx) } else if(token_name == "specular") { - Diffuse[0] = ctx.AdvanceFloat(); - Diffuse[1] = ctx.AdvanceFloat(); - Diffuse[2] = ctx.AdvanceFloat(); + Specular[0] = ctx.AdvanceFloat(); + Specular[1] = ctx.AdvanceFloat(); + Specular[2] = ctx.AdvanceFloat(); // The fourth argument is optional, not the fifth: - float diffuse3 = ctx.AdvanceFloat(); + float specular3 = ctx.AdvanceFloat(); float shininess; if(ctx.AdvanceFloatOptional(shininess)) { - Diffuse[3] = diffuse3; + Specular[3] = specular3; Shininess = shininess; } else { - Shininess = diffuse3; + Shininess = specular3; } } else if(token_name == "emissive") @@ -373,13 +373,18 @@ void StdMeshMaterial::Load(StdMeshMaterialParserCtx& ctx) ctx.Error(StdStrBuf("'") + token_name.getData() + "' unexpected"); } +void StdMeshMatManager::Clear() +{ + Materials.clear(); +} + void StdMeshMatManager::Parse(const char* mat_script, const char* filename, StdMeshMaterialTextureLoader& tex_loader) { StdMeshMaterialParserCtx ctx(mat_script, filename, tex_loader); Token token; StdStrBuf token_name; - while((token = ctx.AdvanceNonEOF(token_name)) == TOKEN_IDTF) + while((token = ctx.Advance(token_name)) == TOKEN_IDTF) { if(token_name == "material") { From 01f2da1366abf7ea393da3cda49578375a6fd042 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sat, 11 Jul 2009 01:10:18 +0200 Subject: [PATCH 10/18] Allow objects to use a mesh instead of pixel-based graphics --- engine/inc/C4Components.h | 2 + engine/inc/C4DefGraphics.h | 20 ++++++- engine/inc/C4Game.h | 5 +- engine/inc/C4Object.h | 2 + engine/src/C4Def.cpp | 24 ++++++-- engine/src/C4DefGraphics.cpp | 111 +++++++++++++++++++++++++++++++++-- engine/src/C4Game.cpp | 1 + engine/src/C4Object.cpp | 64 ++++++++++++++++---- standard/inc/StdDDraw2.h | 3 + standard/inc/StdGL.h | 3 +- standard/inc/StdNoGfx.h | 1 + standard/src/StdDDraw2.cpp | 20 +++++++ standard/src/StdGL.cpp | 76 ++++++++++++++++++++++++ standard/src/StdGLCtx.cpp | 2 + 14 files changed, 307 insertions(+), 27 deletions(-) diff --git a/engine/inc/C4Components.h b/engine/inc/C4Components.h index a061e31cd..fd8a8e4b5 100644 --- a/engine/inc/C4Components.h +++ b/engine/inc/C4Components.h @@ -85,6 +85,8 @@ #define C4CFN_IconPNG "Icon.png" #define C4CFN_ScenarioObjects "Objects.txt" #define C4CFN_ScenarioDesc "Desc%s.rtf" +#define C4CFN_DefMaterials "*.material" +#define C4CFN_DefMesh "Graphics.mesh.xml" #define C4CFN_DefGraphics "Graphics.bmp" #define C4CFN_DefGraphicsPNG "Graphics.png" #define C4CFN_ClrByOwnerPNG "Overlay.png" diff --git a/engine/inc/C4DefGraphics.h b/engine/inc/C4DefGraphics.h index 8536c095f..0a00fa06d 100644 --- a/engine/inc/C4DefGraphics.h +++ b/engine/inc/C4DefGraphics.h @@ -44,16 +44,30 @@ class C4DefGraphics C4DefGraphics *GetLast(); // get last graphics in list public: - C4Surface *Bitmap, *BitmapClr; + enum GraphicsType { + TYPE_Bitmap, + TYPE_Mesh + }; + + GraphicsType Type; + + union { + struct { + C4Surface *Bitmap, *BitmapClr; + }; + StdMesh *Mesh; + }; + bool fColorBitmapAutoCreated; // if set, the color-by-owner-bitmap has been created automatically by all blue shades of the bitmap - inline C4Surface *GetBitmap(DWORD dwClr=0) { if (BitmapClr) { BitmapClr->SetClr(dwClr); return BitmapClr; } else return Bitmap; } + inline C4Surface *GetBitmap(DWORD dwClr=0) { if(Type != TYPE_Bitmap) return NULL; if (BitmapClr) { BitmapClr->SetClr(dwClr); return BitmapClr; } else return Bitmap; } C4DefGraphics(C4Def *pOwnDef=NULL); // ctor virtual ~C4DefGraphics() { Clear(); }; // dtor bool LoadBitmap(C4Group &hGroup, const char *szFilename, const char *szFilenamePNG, const char *szOverlayPNG, bool fColorByOwner); // load specified graphics from group - bool LoadBitmaps(C4Group &hGroup, bool fColorByOwner); // load graphics from group + bool LoadMesh(C4Group &hGroup, StdMeshSkeletonLoader& loader); + bool Load(C4Group &hGroup, bool fColorByOwner); // load graphics from group bool ColorizeByMaterial(int32_t iMat, C4MaterialMap &rMats, BYTE bGBM); // colorize all graphics by material C4DefGraphics *Get(const char *szGrpName); // get graphics by name void Clear(); // clear fields; delete additional graphics diff --git a/engine/inc/C4Game.h b/engine/inc/C4Game.h index 798f7965c..ab5d6a240 100644 --- a/engine/inc/C4Game.h +++ b/engine/inc/C4Game.h @@ -24,7 +24,7 @@ #ifndef INC_C4Game #define INC_C4Game - +#include #include #include #include @@ -82,7 +82,7 @@ class C4Game C4PathFinder PathFinder; C4TransferZones TransferZones; - C4Group ScenarioFile; + C4Group ScenarioFile; C4GroupSet GroupSet; C4Group *pParentGroup; C4Extra Extra; @@ -94,6 +94,7 @@ class C4Game #endif C4Scoreboard Scoreboard; C4VideoPlayer VideoPlayer; + StdMeshMatManager MaterialManager; class C4Network2Stats *pNetworkStatistics; // may be NULL if no statistics are recorded class C4KeyboardInput &KeyboardInput; class C4FileMonitor *pFileMonitor; diff --git a/engine/inc/C4Object.h b/engine/inc/C4Object.h index 073ea4c43..5ab236592 100644 --- a/engine/inc/C4Object.h +++ b/engine/inc/C4Object.h @@ -178,6 +178,7 @@ class C4Object C4NotifyingObjectList Contents; C4MaterialList *MaterialContents; // SyncClearance-NoSave // C4DefGraphics *pGraphics; // currently set object graphics + StdMeshInstance* pMeshInstance; // Instance for mesh-type objects C4Effect *pEffects; // linked list of effects C4ParticleList FrontParticles, BackParticles; // lists of object local particles @@ -260,6 +261,7 @@ class C4Object void DrawTopFace(C4TargetFacet &cgo, int32_t iByPlayer = -1, DrawMode eDrawMode=ODM_Normal); void DrawActionFace(C4TargetFacet &cgo, float offX, float offY); void DrawFace(C4TargetFacet &cgo, float offX, float offY, int32_t iPhaseX=0, int32_t iPhaseY=0); + void DrawFaceImpl(C4TargetFacet &cgo, bool action, float fx, float fy, float fwdt, float fhgt, float tx, float ty, float twdt, float thgt, C4DrawTransform* transform); void Execute(); void ClearPointers(C4Object *ptr); BOOL ExecMovement(); diff --git a/engine/src/C4Def.cpp b/engine/src/C4Def.cpp index fc074b43e..098c81ceb 100644 --- a/engine/src/C4Def.cpp +++ b/engine/src/C4Def.cpp @@ -605,7 +605,7 @@ BOOL C4Def::Load(C4Group &hGroup, // Read surface bitmap if (dwLoadWhat & C4D_Load_Bitmap) - if (!Graphics.LoadBitmaps(hGroup, !!ColorByOwner)) + if (!Graphics.Load(hGroup, !!ColorByOwner)) { DebugLogF(" Error loading graphics of %s (%s)", hGroup.GetFullName().getData(), C4IdText(id)); return FALSE; @@ -866,14 +866,30 @@ void C4Def::Draw(C4Facet &cgo, BOOL fSelected, DWORD iColor, C4Object *pObj, int // if assigned: use object specific rect and graphics if (pObj) if(pObj->PictureRect.Wdt) fctPicRect = pObj->PictureRect; - fctPicture.Set((pObj ? *pObj->GetGraphics() : Graphics).GetBitmap(iColor),fctPicRect.x,fctPicRect.y,fctPicRect.Wdt,fctPicRect.Hgt); - if (fSelected) Application.DDraw->DrawBox(cgo.Surface,cgo.X,cgo.Y,cgo.X+cgo.Wdt-1,cgo.Y+cgo.Hgt-1,CRed); + C4DefGraphics* graphics = pObj ? pObj->GetGraphics() : &Graphics; + // specific object color? if (pObj) pObj->PrepareDrawing(); - fctPicture.Draw(cgo,TRUE,iPhaseX,iPhaseY,TRUE); + + switch(graphics->Type) + { + case C4DefGraphics::TYPE_Bitmap: + fctPicture.Set((pObj ? *pObj->GetGraphics() : Graphics).GetBitmap(iColor),fctPicRect.x,fctPicRect.y,fctPicRect.Wdt,fctPicRect.Hgt); + fctPicture.Draw(cgo,TRUE,iPhaseX,iPhaseY,TRUE); + break; + case C4DefGraphics::TYPE_Mesh: + { + // TODO: Allow rendering of a mesh directly, without instance + StdMeshInstance dummy(*graphics->Mesh); + // TODO: Keep aspect ratio of mesh dimensions + lpDDraw->RenderMesh(dummy, cgo.Surface, cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt); + } + break; + } + if (pObj) pObj->FinishedDrawing(); // draw overlays diff --git a/engine/src/C4DefGraphics.cpp b/engine/src/C4DefGraphics.cpp index f98853568..41097dcee 100644 --- a/engine/src/C4DefGraphics.cpp +++ b/engine/src/C4DefGraphics.cpp @@ -42,6 +42,35 @@ #include #endif +// Helper class to load additional ressources required for meshes from +// a C4Group. +class AdditionalRessourcesLoader: + public StdMeshMaterialTextureLoader, public StdMeshSkeletonLoader +{ +public: + AdditionalRessourcesLoader(C4Group& hGroup): Group(hGroup) {} + + virtual bool LoadTexture(const char* filename, CPNGFile& dest) + { + char* buf; + size_t size; + if(!Group.LoadEntry(filename, &buf, &size, 1)) return false; + bool ret = dest.Load(reinterpret_cast(buf), size); + delete[] buf; + return ret; + } + + virtual StdStrBuf LoadSkeleton(const char* filename) + { + StdStrBuf ret; + if(!Group.LoadEntryString(filename, ret)) return StdStrBuf(); + return ret; + } + +private: + C4Group& Group; +}; + //-------------------------------- C4DefGraphics ----------------------------------------------- C4DefGraphics::C4DefGraphics(C4Def *pOwnDef) @@ -49,6 +78,7 @@ C4DefGraphics::C4DefGraphics(C4Def *pOwnDef) // store def pDef = pOwnDef; // zero fields + Type = TYPE_Bitmap; Bitmap = BitmapClr = NULL; pNext = NULL; fColorBitmapAutoCreated = false; @@ -64,8 +94,17 @@ C4DefGraphics *C4DefGraphics::GetLast() void C4DefGraphics::Clear() { // zero own fields - if (BitmapClr) { delete BitmapClr; BitmapClr=NULL; } - if (Bitmap) { delete Bitmap; Bitmap=NULL; } + switch (Type) + { + case TYPE_Bitmap: + if (BitmapClr) { delete BitmapClr; BitmapClr=NULL; } + if (Bitmap) { delete Bitmap; Bitmap=NULL; } + break; + case TYPE_Mesh: + if (Mesh) { delete Mesh; Mesh = NULL; } + break; + } + // delete additonal graphics C4AdditionalDefGraphics *pGrp2N = pNext, *pGrp2; while (pGrp2=pGrp2N) { pGrp2N = pGrp2->pNext; pGrp2->pNext = NULL; delete pGrp2; } @@ -116,17 +155,74 @@ bool C4DefGraphics::LoadBitmap(C4Group &hGroup, const char *szFilename, const ch if (!BitmapClr->CreateColorByOwner(Bitmap)) return false; fColorBitmapAutoCreated = true; } + Type = TYPE_Bitmap; // success return true; } -bool C4DefGraphics::LoadBitmaps(C4Group &hGroup, bool fColorByOwner) +bool C4DefGraphics::LoadMesh(C4Group &hGroup, StdMeshSkeletonLoader& loader) +{ + char* buf; + size_t size; + if(!hGroup.LoadEntry(C4CFN_DefMesh, &buf, &size, 1)) return false; + + Mesh = new StdMesh; + + bool result; + try { + Mesh->InitXML(C4CFN_DefMesh, buf, loader, Game.MaterialManager); + result = true; + } + catch(const StdMeshError& ex) + { + DebugLogF("Failed to load mesh: %s\n", ex.what()); + result = false; + } + + delete[] buf; + if(!result) + { + delete Mesh; + Mesh = NULL; + + return false; + } + + Type = TYPE_Mesh; + return true; +} + +bool C4DefGraphics::Load(C4Group &hGroup, bool fColorByOwner) + { + char Filename[_MAX_PATH+1]; *Filename=0; + AdditionalRessourcesLoader loader(hGroup); + + // Load all materials for this definition: + hGroup.ResetSearch(); + while (hGroup.FindNextEntry(C4CFN_DefMaterials, Filename, NULL, NULL, !!*Filename)) + { + StdStrBuf material; + if(hGroup.LoadEntryString(Filename, material)) + { + try + { + Game.MaterialManager.Parse(material.getData(), Filename, loader); + } + catch(const StdMeshMaterialError& ex) + { + DebugLogF("Failed to read material script: %s\n", ex.what()); + } + } + } + + // Try from Mesh first + if (LoadMesh(hGroup, loader)) return true; // load basic graphics if (!LoadBitmap(hGroup, C4CFN_DefGraphics, C4CFN_DefGraphicsPNG, C4CFN_ClrByOwnerPNG, fColorByOwner)) return false; + // load additional graphics // first, search all png-graphics in NewGfx - char Filename[_MAX_PATH+1]; *Filename=0; C4DefGraphics *pLastGraphics = this; int32_t iWildcardPos; iWildcardPos = SCharPos('*', C4CFN_DefGraphicsExPNG); @@ -229,6 +325,8 @@ bool C4DefGraphics::LoadBitmaps(C4Group &hGroup, bool fColorByOwner) bool C4DefGraphics::ColorizeByMaterial(int32_t iMat, C4MaterialMap &rMats, BYTE bGBM) { + if(Type != TYPE_Bitmap) return false; + SURFACE sfcBitmap = GetBitmap(); // first bitmap only if (sfcBitmap) { @@ -265,6 +363,7 @@ C4PortraitGraphics *C4PortraitGraphics::Get(const char *szGrpName) bool C4DefGraphics::CopyGraphicsFrom(C4DefGraphics &rSource) { + if (Type != TYPE_Bitmap) return false; // TODO! // clear previous if (BitmapClr) { delete BitmapClr; BitmapClr=NULL; } if (Bitmap) { delete Bitmap; Bitmap=NULL; } @@ -291,6 +390,7 @@ bool C4DefGraphics::CopyGraphicsFrom(C4DefGraphics &rSource) void C4DefGraphics::DrawClr(C4Facet &cgo, BOOL fAspect, DWORD dwClr) { + if(Type != TYPE_Bitmap) return; // TODO // create facet and draw it C4Surface *pSfc = BitmapClr ? BitmapClr : Bitmap; if (!pSfc) return; C4Facet fct(pSfc, 0,0,pSfc->Wdt, pSfc->Hgt); @@ -507,7 +607,7 @@ bool C4Portrait::Link(C4DefGraphics *pGfxPortrait) bool C4Portrait::SavePNG(C4Group &rGroup, const char *szFilename, const char *szOverlayFN) { // safety - if (!pGfxPortrait || !szFilename || !pGfxPortrait->Bitmap) return false; + if (!pGfxPortrait || !szFilename || pGfxPortrait->Type != C4DefGraphics::TYPE_Bitmap || !pGfxPortrait->Bitmap) return false; // save files if (pGfxPortrait->fColorBitmapAutoCreated) { @@ -616,6 +716,7 @@ void C4GraphicsOverlay::UpdateFacet() if (eMode == MODE_Object) return; // otherwise, source graphics must be specified if (!pSourceGfx) return; + if (pSourceGfx->Type != C4DefGraphics::TYPE_Bitmap) return; C4Def *pDef = pSourceGfx->pDef; assert(pDef); fZoomToShape = false; diff --git a/engine/src/C4Game.cpp b/engine/src/C4Game.cpp index 09b231ea7..4946fdd7c 100644 --- a/engine/src/C4Game.cpp +++ b/engine/src/C4Game.cpp @@ -594,6 +594,7 @@ void C4Game::Clear() KeyboardInput.Clear(); SetMusicLevel(100); PlayList.Clear(); + MaterialManager.Clear(); // global fullscreen class is not cleared, because it holds the carrier window // but the menu must be cleared (maybe move Fullscreen.Menu somewhere else?) diff --git a/engine/src/C4Object.cpp b/engine/src/C4Object.cpp index 728a83215..1a81d8e77 100644 --- a/engine/src/C4Object.cpp +++ b/engine/src/C4Object.cpp @@ -157,6 +157,7 @@ void C4Object::Default() pLayer=NULL; pSolidMaskData=NULL; pGraphics=NULL; + pMeshInstance=NULL; pDrawTransform=NULL; pEffects=NULL; FirstRef=NULL; @@ -186,6 +187,10 @@ BOOL C4Object::Init(C4Def *pDef, C4Object *pCreator, // graphics pGraphics = &Def->Graphics; + if(pGraphics->Type == C4DefGraphics::TYPE_Mesh) + pMeshInstance = new StdMeshInstance(*pGraphics->Mesh); + else + pMeshInstance = NULL; BlitMode = Def->BlitMode; // Position @@ -418,6 +423,13 @@ void C4Object::UpdateGraphics(bool fGraphicsChanged, bool fTemp) // ensure SolidMask-rect lies within new graphics-rect CheckSolidMaskRect(); } + + delete pMeshInstance; + if(pGraphics->Type == C4DefGraphics::TYPE_Mesh) + pMeshInstance = new StdMeshInstance(*pGraphics->Mesh); + else + pMeshInstance = NULL; + // update face - this also puts any SolidMask UpdateFace(false); } @@ -459,6 +471,25 @@ void C4Object::UpdateFlipDir() } } +void C4Object::DrawFaceImpl(C4TargetFacet &cgo, bool action, float fx, float fy, float fwdt, float fhgt, float tx, float ty, float twdt, float thgt, C4DrawTransform* transform) + { + CSurface* sfc; + switch(GetGraphics()->Type) + { + case C4DefGraphics::TYPE_Bitmap: + sfc = action ? Action.Facet.Surface : GetGraphics()->GetBitmap(Color); + + lpDDraw->Blit(sfc, + fx, fy, fwdt, fhgt, + cgo.Surface, tx, ty, twdt, thgt, + TRUE, transform); + break; + case C4DefGraphics::TYPE_Mesh: + lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, tx, ty, twdt, thgt); + break; + } + } + void C4Object::DrawFace(C4TargetFacet &cgo, float offX, float offY, int32_t iPhaseX, int32_t iPhaseY) { const float swdt = float(Def->Shape.Wdt); @@ -488,10 +519,11 @@ void C4Object::DrawFace(C4TargetFacet &cgo, float offX, float offY, int32_t iPha // Straight if ((!Def->Rotateable || (r==0)) && !pDrawTransform) { - lpDDraw->Blit(GetGraphics()->GetBitmap(Color), + DrawFaceImpl(cgo, false, fx, fy, fwdt, fhgt, tx, ty, twdt, thgt, NULL); +/* lpDDraw->Blit(GetGraphics()->GetBitmap(Color), fx, fy, fwdt, fhgt, cgo.Surface, tx, ty, twdt, thgt, - TRUE, NULL); + TRUE, NULL);*/ } // Rotated or transformed else @@ -506,10 +538,11 @@ void C4Object::DrawFace(C4TargetFacet &cgo, float offX, float offY, int32_t iPha { rot.SetRotate(r * 100, offX, offY); } - lpDDraw->Blit(GetGraphics()->GetBitmap(Color), + DrawFaceImpl(cgo, false, fx, fy, fwdt, fhgt, tx, ty, twdt, thgt, &rot); +/* lpDDraw->Blit(GetGraphics()->GetBitmap(Color), fx, fy, fwdt, fhgt, cgo.Surface, tx, ty, twdt, thgt, - TRUE, &rot); + TRUE, &rot);*/ } } @@ -554,10 +587,11 @@ void C4Object::DrawActionFace(C4TargetFacet &cgo, float offX, float offY) // Straight if ((!Def->Rotateable || (r==0)) && !pDrawTransform) { - lpDDraw->Blit(Action.Facet.Surface, + DrawFaceImpl(cgo, true, fx, fy, fwdt, fhgt, tx, ty, twdt, thgt, NULL); + /*lpDDraw->Blit(Action.Facet.Surface, fx, fy, fwdt, fhgt, cgo.Surface, tx, ty, twdt, thgt, - TRUE, NULL); + TRUE, NULL);*/ } // Rotated or transformed else @@ -574,10 +608,11 @@ void C4Object::DrawActionFace(C4TargetFacet &cgo, float offX, float offY) { rot.SetRotate(r * 100, offX, offY); } - lpDDraw->Blit(Action.Facet.Surface, + DrawFaceImpl(cgo, true, fx, fy, fwdt, fhgt, tx, ty, twdt, thgt, &rot); +/* lpDDraw->Blit(Action.Facet.Surface, fx, fy, fwdt, fhgt, cgo.Surface, tx, ty, twdt, thgt, - TRUE, &rot); + TRUE, &rot);*/ } } @@ -2264,7 +2299,8 @@ void C4Object::Draw(C4TargetFacet &cgo, int32_t iByPlayer, DrawMode eDrawMode) { if (FrontParticles && !Contained) FrontParticles.Draw(cgo,this); return; } // ensure correct color is set - if (GetGraphics()->BitmapClr) GetGraphics()->BitmapClr->SetClr(Color); + if (GetGraphics()->Type == C4DefGraphics::TYPE_Bitmap) + if (GetGraphics()->BitmapClr) GetGraphics()->BitmapClr->SetClr(Color); // Debug Display ////////////////////////////////////////////////////////////////////// if (::GraphicsSystem.ShowCommand && !eDrawMode) @@ -2416,7 +2452,7 @@ void C4Object::Draw(C4TargetFacet &cgo, int32_t iByPlayer, DrawMode eDrawMode) (fixtof(Action.Target->fix_y) + Action.Target->Shape.GetY()) - (fixtof(fix_y) + Shape.GetY() + Action.FacetY), TRUE); } - else if (Action.Facet.Surface) + else if (Action.Facet.Surface || GetGraphics()->Type != C4DefGraphics::TYPE_Bitmap) DrawActionFace(cgo, offX, offY); } @@ -3185,7 +3221,7 @@ void C4Object::Clear() if (pEffects) { delete pEffects; pEffects=NULL; } if (FrontParticles) FrontParticles.Clear(); if (BackParticles) BackParticles.Clear(); - if (pSolidMaskData) { delete pSolidMaskData; pSolidMaskData=NULL; } + if (pSolidMaskData) { delete pSolidMaskData; pSolidMaskData=NULL; } if (Menu) delete Menu; Menu=NULL; if (MaterialContents) delete MaterialContents; MaterialContents=NULL; // clear commands! @@ -3196,7 +3232,8 @@ void C4Object::Clear() } if (pDrawTransform) { delete pDrawTransform; pDrawTransform=NULL; } if (pGfxOverlay) { delete pGfxOverlay; pGfxOverlay=NULL; } - while (FirstRef) FirstRef->Set(0); + if (pMeshInstance) { delete pMeshInstance; pMeshInstance = NULL; } + while (FirstRef) FirstRef->Set(0); } BOOL C4Object::ContainedControl(BYTE byCom) @@ -3756,6 +3793,9 @@ void C4Object::SetSolidMask(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, bool C4Object::CheckSolidMaskRect() { + // SolidMasks are only supported for bitmap graphics + if(GetGraphics()->Type != C4DefGraphics::TYPE_Bitmap) return false; + // check NewGfx only, because invalid SolidMask-rects are OK in OldGfx // the bounds-check is done in CStdDDraw::GetPixel() CSurface *sfcGraphics = GetGraphics()->GetBitmap(); diff --git a/standard/inc/StdDDraw2.h b/standard/inc/StdDDraw2.h index 6ed286228..c51739dfe 100644 --- a/standard/inc/StdDDraw2.h +++ b/standard/inc/StdDDraw2.h @@ -27,6 +27,7 @@ #include #include #include +#include // texref-predef class CStdDDraw; @@ -295,7 +296,9 @@ class CStdDDraw BOOL Blit(SURFACE sfcSource, float fx, float fy, float fwdt, float fhgt, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt, BOOL fSrcColKey=FALSE, CBltTransform *pTransform=NULL); + BOOL RenderMesh(StdMeshInstance &instance, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt); virtual void PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool fMod2, bool fExact) = 0; + virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt) = 0; BOOL Blit8(SURFACE sfcSource, int fx, int fy, int fwdt, int fhgt, // force 8bit-blit (inline) SURFACE sfcTarget, int tx, int ty, int twdt, int thgt, BOOL fSrcColKey=FALSE, CBltTransform *pTransform=NULL); diff --git a/standard/inc/StdGL.h b/standard/inc/StdGL.h index ead465ead..8d8bd340e 100644 --- a/standard/inc/StdGL.h +++ b/standard/inc/StdGL.h @@ -109,7 +109,8 @@ class CStdGL : public CStdDDraw virtual CStdGLCtx *CreateContext(HWND hWindow, CStdApp *pApp); #endif // Blit - void PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool fMod2, bool fExact); + virtual void PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool fMod2, bool fExact); + virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt); virtual void BlitLandscape(SURFACE sfcSource, SURFACE sfcSource2, SURFACE sfcLiquidAnimation, float fx, float fy, SURFACE sfcTarget, float tx, float ty, float wdt, float hgt); void FillBG(DWORD dwClr=0); diff --git a/standard/inc/StdNoGfx.h b/standard/inc/StdNoGfx.h index c55ced412..e0029a38d 100644 --- a/standard/inc/StdNoGfx.h +++ b/standard/inc/StdNoGfx.h @@ -41,6 +41,7 @@ public: virtual bool PrepareRendering(SURFACE) { return true; } virtual void FillBG(DWORD dwClr=0) { } virtual void PerformBlt(CBltData &, CTexRef *, DWORD, bool, bool) { } + virtual void PerformMesh(StdMeshInstance &, float, float, float, float) { } virtual void PerformLine(SURFACE, float, float, float, float, DWORD) { } virtual void DrawQuadDw(SURFACE, float *, DWORD, DWORD, DWORD, DWORD) { } virtual void PerformPix(SURFACE, float, float, DWORD) { } diff --git a/standard/src/StdDDraw2.cpp b/standard/src/StdDDraw2.cpp index 202878d13..b4fbc4f3b 100644 --- a/standard/src/StdDDraw2.cpp +++ b/standard/src/StdDDraw2.cpp @@ -1050,6 +1050,26 @@ BOOL CStdDDraw::Blit(SURFACE sfcSource, float fx, float fy, float fwdt, float fh return TRUE; } +BOOL CStdDDraw::RenderMesh(StdMeshInstance &instance, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt) +{ + // TODO: Emulate rendering + if (!sfcTarget->IsRenderTarget()) return FALSE; + + // TODO: Clip + + // prepare rendering to surface + if (!PrepareRendering(sfcTarget)) return FALSE; + // store current state + StoreStateBlock(); + + PerformMesh(instance, tx, ty, twdt, thgt); + + // restore state + RestoreStateBlock(); + // success + return TRUE; +} + BOOL CStdDDraw::Blit8(SURFACE sfcSource, int fx, int fy, int fwdt, int fhgt, SURFACE sfcTarget, int tx, int ty, int twdt, int thgt, BOOL fSrcColKey, CBltTransform *pTransform) diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index 5f79aaf05..f471fbe03 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -306,6 +306,82 @@ void CStdGL::PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool } } +void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt) +{ + const StdMesh& mesh = instance.Mesh; + const StdMeshBox& box = mesh.GetBoundingBox(); + + glPushMatrix(); + glEnable(GL_DEPTH_TEST); + glEnable(GL_NORMALIZE); + glDisable(GL_BLEND); // TODO: Invert alpha instead in material loader + glEnable(GL_LIGHTING); + + // TODO: Zoom, ClrMod, ... + + //glColor4f(1.0f, 1.0f, 1.0f, 0.0f); + + // Scale so that the mesh fits in (tx,ty,twdt,thgt) + double rx = -box.x1 / (box.x2 - box.x1); + double ry = -box.y1 / (box.y2 - box.y1); + glTranslatef(tx + rx*twdt, ty + ry*thgt, 0.0f); + glScalef(twdt/(box.x2 - box.x1), thgt/(box.y2 - box.y1), 1.0f); + + // Put a light source in front of the object + GLfloat light_position[] = { 0.0f, 0.0f, 6.0f, 1.0f }; + glLightfv(GL_LIGHT0, GL_POSITION, light_position); + glEnable(GL_LIGHT0); + + // TODO: Find a working technique, we currently always use the + // first one: + const StdMeshMaterial& material = mesh.GetMaterial(); + const StdMeshMaterialTechnique& technique = material.Techniques[0]; + + // Render each pass + for(unsigned int i = 0; i < technique.Passes.size(); ++i) + { + const StdMeshMaterialPass& pass = technique.Passes[i]; + + // Set up material + glMaterialfv(GL_FRONT, GL_AMBIENT, pass.Ambient); + glMaterialfv(GL_FRONT, GL_DIFFUSE, pass.Diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, pass.Specular); + glMaterialfv(GL_FRONT, GL_EMISSION, pass.Emissive); + glMaterialf(GL_FRONT, GL_SHININESS, pass.Shininess); + // TODO: Set up texture units + // Render mesh + glBegin(GL_TRIANGLES); + for(unsigned int j = 0; j < mesh.GetNumFaces(); ++j) + { + const StdMeshFace& face = mesh.GetFace(j); + const StdMeshVertex& vtx1 = instance.GetVertex(face.Vertices[0]); + const StdMeshVertex& vtx2 = instance.GetVertex(face.Vertices[1]); + const StdMeshVertex& vtx3 = instance.GetVertex(face.Vertices[2]); + + glTexCoord2f(vtx1.u, vtx1.v); + glNormal3f(vtx1.nx, vtx1.ny, vtx1.nz); + glVertex3f(vtx1.x, vtx1.y, vtx1.z); + glTexCoord2f(vtx2.u, vtx2.v); + glNormal3f(vtx2.nx, vtx2.ny, vtx2.nz); + glVertex3f(vtx2.x, vtx2.y, vtx2.z); + glTexCoord2f(vtx3.u, vtx3.v); + glNormal3f(vtx3.nx, vtx3.ny, vtx3.nz); + glVertex3f(vtx3.x, vtx3.y, vtx3.z); + } + glEnd(); // GL_TRIANGLES + } + + glDisable(GL_LIGHT0); + glDisable(GL_LIGHTING); + glDisable(GL_DEPTH_TEST); + glDisable(GL_NORMALIZE); + glEnable(GL_BLEND); + glPopMatrix(); + + // TODO: glScissor, so that we only clear the area the mesh covered. + glClear(GL_DEPTH_BUFFER_BIT); +} + void CStdGL::BlitLandscape(SURFACE sfcSource, SURFACE sfcSource2, SURFACE sfcLiquidAnimation, float fx, float fy, SURFACE sfcTarget, float tx, float ty, float wdt, float hgt) { diff --git a/standard/src/StdGLCtx.cpp b/standard/src/StdGLCtx.cpp index 5d7b667d4..333be7a0a 100644 --- a/standard/src/StdGLCtx.cpp +++ b/standard/src/StdGLCtx.cpp @@ -116,6 +116,7 @@ bool CStdGLCtx::Select(bool verbose) pGL->lpPrimary->Wdt=cx; pGL->lpPrimary->Hgt=cy; // set some default states glDisable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); glShadeModel(GL_FLAT); glDisable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); @@ -266,6 +267,7 @@ bool CStdGLCtx::Select(bool verbose) pGL->lpPrimary->Wdt=cx; pGL->lpPrimary->Hgt=cy; // set some default states glDisable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); glShadeModel(GL_FLAT); glDisable(GL_ALPHA_TEST); glDisable(GL_CULL_FACE); From 41f5a3d069a4655ad2f0da9b3f280c9981d30add Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sat, 11 Jul 2009 01:11:29 +0200 Subject: [PATCH 11/18] Added Monster as a test object --- planet/Objects.c4d/Monster.c4d/ActMap.txt | 81 + planet/Objects.c4d/Monster.c4d/DefCore.txt | 44 + planet/Objects.c4d/Monster.c4d/DescDE.txt | 1 + planet/Objects.c4d/Monster.c4d/DescUS.txt | 1 + .../Objects.c4d/Monster.c4d/Graphics.mesh.xml | 8708 +++++++++++++++++ .../Monster.c4d/Graphics.skeleton.xml | 2643 +++++ planet/Objects.c4d/Monster.c4d/Names.txt | 2 + planet/Objects.c4d/Monster.c4d/Scene.material | 14 + planet/Objects.c4d/Monster.c4d/Script.c | 210 + .../Objects.c4d/Monster.c4d/StringTblDE.txt | 2 + .../Objects.c4d/Monster.c4d/StringTblUS.txt | 2 + 11 files changed, 11708 insertions(+) create mode 100644 planet/Objects.c4d/Monster.c4d/ActMap.txt create mode 100644 planet/Objects.c4d/Monster.c4d/DefCore.txt create mode 100644 planet/Objects.c4d/Monster.c4d/DescDE.txt create mode 100644 planet/Objects.c4d/Monster.c4d/DescUS.txt create mode 100644 planet/Objects.c4d/Monster.c4d/Graphics.mesh.xml create mode 100644 planet/Objects.c4d/Monster.c4d/Graphics.skeleton.xml create mode 100644 planet/Objects.c4d/Monster.c4d/Names.txt create mode 100644 planet/Objects.c4d/Monster.c4d/Scene.material create mode 100644 planet/Objects.c4d/Monster.c4d/Script.c create mode 100644 planet/Objects.c4d/Monster.c4d/StringTblDE.txt create mode 100644 planet/Objects.c4d/Monster.c4d/StringTblUS.txt diff --git a/planet/Objects.c4d/Monster.c4d/ActMap.txt b/planet/Objects.c4d/Monster.c4d/ActMap.txt new file mode 100644 index 000000000..acbdd816b --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/ActMap.txt @@ -0,0 +1,81 @@ +[Action] +Name=Walk +Procedure=WALK +Directions=2 +FlipDir=1 +Length=16 +Delay=5 +Facet=0,0,48,34 +NextAction=Walk +TurnAction=Turn +StartCall=HitCheck +InLiquidAction=Swim + +[Action] +Name=Turn +Procedure=NONE +Directions=2 +FlipDir=1 +Length=7 +Delay=2 +Facet=0,68,48,34 +NextAction=Walk + +[Action] +Name=Jump +Procedure=FLIGHT +Directions=2 +FlipDir=1 +Length=17 +Delay=1 +Facet=0,34,48,34 +NextAction=Hold +InLiquidAction=Swim + +[Action] +Name=Swim +Procedure=SWIM +Directions=2 +FlipDir=1 +Length=16 +Delay=5 +Facet=0,0,48,34 +NextAction=Swim +TurnAction=Turn +StartCall=HitCheck + +[Action] +Name=LookUp +Procedure=NONE +Attach=8 +Directions=2 +FlipDir=1 +Length=12 +Delay=1 +Facet=0,102,48,34 +NextAction=Look +InLiquidAction=Swim + +[Action] +Name=Look +Attach=8 +Procedure=NONE +Directions=2 +FlipDir=1 +Delay=15 +Facet=528,102,48,34 +NextAction=LookAway +InLiquidAction=Swim + +[Action] +Name=LookAway +Attach=8 +Procedure=NONE +Directions=2 +FlipDir=1 +Length=12 +Delay=1 +Reverse=1 +Facet=0,102,48,34 +NextAction=Walk +InLiquidAction=Swim \ No newline at end of file diff --git a/planet/Objects.c4d/Monster.c4d/DefCore.txt b/planet/Objects.c4d/Monster.c4d/DefCore.txt new file mode 100644 index 000000000..af7510bea --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/DefCore.txt @@ -0,0 +1,44 @@ +[DefCore] +id=MONS +Version=4,9,8 +Name=Monster +Category=C4D_Living|C4D_SelectAnimal +MaxUserSelect=5 +TimerCall=Activity +ContactCalls=1 +Width=48 +Height=34 +Offset=-24,-17 +Vertices=7 +VertexX=-1,1,0,-6,-2,7,2 +VertexY=-7,-7,12,-3,6,-3,6 +VertexCNAT=5,6,8,1,1,2,2 +VertexFriction=100,100,100,100,100,100 +Value=35 +Mass=100 +Components=MONS=1 +Picture=576,68,64,64 +Growth=15 +Float=1 +BorderBound=1 +StretchGrowth=1 +NoBurnDecay=1 +IncompleteActivity=1 +VehicleControl=2 +Pathfinder=1 +ClosedContainer=2 +NoFight=1 + +[Physical] +Energy=250000 +Breath=50000 +Walk=71000 +Jump=50000 +Scale=30000 +Hangle=30000 +Dig=40000 +Swim=60000 +Throw=50000 +Push=40000 +Fight=50000 +CorrosionResist=1 diff --git a/planet/Objects.c4d/Monster.c4d/DescDE.txt b/planet/Objects.c4d/Monster.c4d/DescDE.txt new file mode 100644 index 000000000..4f4fe8345 --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/DescDE.txt @@ -0,0 +1 @@ +Friedliebende grüne Zeitgenossen. diff --git a/planet/Objects.c4d/Monster.c4d/DescUS.txt b/planet/Objects.c4d/Monster.c4d/DescUS.txt new file mode 100644 index 000000000..b58d2f4bc --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/DescUS.txt @@ -0,0 +1 @@ +They want no harm. diff --git a/planet/Objects.c4d/Monster.c4d/Graphics.mesh.xml b/planet/Objects.c4d/Monster.c4d/Graphics.mesh.xml new file mode 100644 index 000000000..d969a8252 --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/Graphics.mesh.xml @@ -0,0 +1,8708 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/planet/Objects.c4d/Monster.c4d/Graphics.skeleton.xml b/planet/Objects.c4d/Monster.c4d/Graphics.skeleton.xml new file mode 100644 index 000000000..295740981 --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/Graphics.skeleton.xml @@ -0,0 +1,2643 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/planet/Objects.c4d/Monster.c4d/Names.txt b/planet/Objects.c4d/Monster.c4d/Names.txt new file mode 100644 index 000000000..de54cd58c --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/Names.txt @@ -0,0 +1,2 @@ +DE:Monster +US:Monster diff --git a/planet/Objects.c4d/Monster.c4d/Scene.material b/planet/Objects.c4d/Monster.c4d/Scene.material new file mode 100644 index 000000000..a3c76b64d --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/Scene.material @@ -0,0 +1,14 @@ +material Material.002 +{ + receive_shadows on + technique + { + pass + { + ambient 0.500000 0.500000 0.500000 1.000000 + diffuse 0.000000 0.800000 0.169670 1.000000 + specular 0.342542 0.500000 0.000000 1.000000 12.500000 + emissive 0.000000 0.000000 0.000000 1.000000 + } + } +} diff --git a/planet/Objects.c4d/Monster.c4d/Script.c b/planet/Objects.c4d/Monster.c4d/Script.c new file mode 100644 index 000000000..68eec90ef --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/Script.c @@ -0,0 +1,210 @@ +/*-- Monster --*/ + +#strict 2 +//#include ANIM + +public func IsPossessible() { return 1; } + +/* Initialisierung */ + +protected func Initialize() { SetAction("Walk"); } + +/* TimerCall mit KI-Steuerung */ + +private func Activity() +{ + // Die KI-Steuerung wird bei Besessenheit nicht gebraucht + if (GetEffect("PossessionSpell", this)) return; + + // Nichts machen + if (Random(2) || (GetAction() != "Walk" && GetAction() != "Swim")) return 1; + + // Springen + if (GetAction() == "Walk") + if (!Random(3)) return DoJump(); + + // Umsehen + if (GetAction() == "Walk") + if (!Random(8)) return SetAction("LookUp"); + + // Umdrehen + if (Random(2)) return TurnRight(); + return TurnLeft(); +} + +/* Kontakte */ + +protected func ContactLeft() +{ + // Die KI-Steuerung wird bei Besessenheit nicht gebraucht + if (GetEffect("PossessionSpell", this)) return; + + return TurnRight(); +} + +protected func ContactRight() +{ + // Die KI-Steuerung wird bei Besessenheit nicht gebraucht + if (GetEffect("PossessionSpell", this)) return; + + return TurnLeft(); +} + +/* Aktionen */ + +public func TurnRight() +{ + if (Stuck() || (GetAction() != "Walk" && GetAction() != "Swim")) return; + if (GetXDir() < 0) SetXDir(0); + SetDir(DIR_Right); + SetComDir(COMD_Right); + return 1; +} + +public func TurnLeft() +{ + if (Stuck() || (GetAction() != "Walk" && GetAction() != "Swim")) return; + if (GetXDir() > 0) SetXDir(0); + SetDir(DIR_Left); + SetComDir(COMD_Left); + return 1; +} + +private func HitCheck() +{ + var obj; + if (obj = FindObject(0, +1,0,0,0, OCF_Prey, 0,0, NoContainer())) + Punch(obj, 10); + return 1; +} + +public func DoJump() +{ + if (GetAction() != "Walk") return; + if (Random(2)) Sound("Growl*"); + Jump(); +} + +/* Einwirkungen */ + +protected func Death() +{ + Sound("DeathGrowl"); + SetDir(DIR_Left); + ChangeDef(DMNS); + SetAction("Dead"); + return 1; +} + +/* Vermehrung */ + +private func ReproductionRate() { return 2000; } // Die Chance, dass in einem Timerintervall eine Vermehrung stattfindet +private func MaxAnimalCount() { return 4; } // Maximale Tieranzahl im Umkreis + +/* Steuerung durch Besessenheit */ + +protected func ControlCommand(szCommand, pTarget, iTx, iTy) +{ + // Bewegungskommando + if (szCommand == "MoveTo") + return SetCommand(this,szCommand, pTarget, iTx, iTy); + return 0; +} + +protected func ContainedLeft(object caller) +{ + [$TxtMovement$] + SetCommand(this, "None"); + if(!GetPlrCoreJumpAndRunControl(caller->GetController())) + TurnLeft(); + return 1; +} + +protected func ContainedRight(object caller) +{ + [$TxtMovement$] + SetCommand(this, "None"); + if(!GetPlrCoreJumpAndRunControl(caller->GetController())) + TurnRight(); + return 1; +} + +protected func ContainedUp(object caller) +{ + [$TxtMovement$] + SetCommand(this, "None"); + if(Contained()) return SetCommand(this, "Exit"); + if (GetAction() == "Swim") + { + if(!GetPlrCoreJumpAndRunControl(caller->GetController())) + SetComDir(COMD_Up); + + return 1; + } + + DoJump(); + return 1; +} + +protected func ContainedDown(object caller) +{ + [$TxtMovement$] + SetCommand(this, "None"); + + if (GetAction() == "Swim") + { + if(!GetPlrCoreJumpAndRunControl(caller->GetController())) + SetComDir(COMD_Down); + return 1; + } + + if (GetAction() == "Walk") + SetAction("LookUp"); + + return 1; +} + +/* JumpAndRun Steuerung */ + +private func ClearDir(bool fX) +{ + if(fX && GetXDir()) + { + if(GetXDir() > 0) SetXDir(Max(GetXDir() - 2, 0)); + else SetXDir(Min(GetXDir() + 2, 0)); + } + if(!fX && GetYDir()) + { + if(GetYDir() > 0) SetYDir(Max(GetYDir() - 2, 0)); + else SetYDir(Min(GetYDir() + 2, 0)); + } +} + +public func ContainedUpdate(object self, int comdir) +{ + if(GetAction() == "Swim") + { + SetComDir(comdir); + ClearScheduleCall(this, "ClearDir"); + if(comdir == COMD_Down || comdir == COMD_Up) ScheduleCall(this, "ClearDir", 1, (Abs(GetXDir())+1)/2, true); + if(comdir == COMD_Left || comdir == COMD_Right) ScheduleCall(this, "ClearDir", 1, (Abs(GetYDir())+1)/2, false); + } + else + { + if(comdir == COMD_UpRight || comdir == COMD_DownRight) comdir = COMD_Right; + if(comdir == COMD_Up || comdir == COMD_Down) comdir = COMD_Stop; + if(comdir == COMD_UpLeft || comdir == COMD_DownLeft) comdir = COMD_Left; + SetComDir(comdir); + } + + return 1; +} + +protected func ContainedThrow() { return 1; } + +protected func ContainedDigDouble() +{ + [$TxtLeave$] + RemoveEffect("PossessionSpell", this); + return 1; +} diff --git a/planet/Objects.c4d/Monster.c4d/StringTblDE.txt b/planet/Objects.c4d/Monster.c4d/StringTblDE.txt new file mode 100644 index 000000000..ea482265d --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/StringTblDE.txt @@ -0,0 +1,2 @@ +TxtMovement=Bewegen +TxtLeave=Zurückverwandeln \ No newline at end of file diff --git a/planet/Objects.c4d/Monster.c4d/StringTblUS.txt b/planet/Objects.c4d/Monster.c4d/StringTblUS.txt new file mode 100644 index 000000000..459bf7457 --- /dev/null +++ b/planet/Objects.c4d/Monster.c4d/StringTblUS.txt @@ -0,0 +1,2 @@ +TxtMovement=Move +TxtLeave=Retransform \ No newline at end of file From 3760771ee5e56e1cdf55d8d96deb0469ab586230 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sat, 11 Jul 2009 12:36:53 +0200 Subject: [PATCH 12/18] Read material alpha components in the way clonk needs them --- planet/Worlds.c4f/Outset.c4s/Scenario.txt | 1 + standard/src/StdGL.cpp | 2 -- standard/src/StdMesh.cpp | 2 +- standard/src/StdMeshMaterial.cpp | 21 ++++++++++++--------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/planet/Worlds.c4f/Outset.c4s/Scenario.txt b/planet/Worlds.c4f/Outset.c4s/Scenario.txt index c448eab34..a4d074c63 100755 --- a/planet/Worlds.c4f/Outset.c4s/Scenario.txt +++ b/planet/Worlds.c4f/Outset.c4s/Scenario.txt @@ -72,6 +72,7 @@ Liquid=Water-Smooth LiquidLevel=20,30,0,100 Layers=Rock=7;Rock=7;Gold=7;Granite=4;Water=5;Earth-earth=50;Earth-earth_dry=50 SkyScrollMode=2 +Animals=MONS=3 [Weather] Climate=40,10,0,100 diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index f471fbe03..aec891d04 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -314,7 +314,6 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glPushMatrix(); glEnable(GL_DEPTH_TEST); glEnable(GL_NORMALIZE); - glDisable(GL_BLEND); // TODO: Invert alpha instead in material loader glEnable(GL_LIGHTING); // TODO: Zoom, ClrMod, ... @@ -375,7 +374,6 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glDisable(GL_NORMALIZE); - glEnable(GL_BLEND); glPopMatrix(); // TODO: glScissor, so that we only clear the area the mesh covered. diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp index a1ba3f1f6..37233a7e5 100644 --- a/standard/src/StdMesh.cpp +++ b/standard/src/StdMesh.cpp @@ -22,7 +22,7 @@ StdMeshError::StdMeshError(const StdStrBuf& message, const char* file, unsigned int line) { - Buf.Format("%s[%u]: %s", file, line, message.getData()); + Buf.Format("%s:%u: %s", file, line, message.getData()); } // Helper class to load things from an XML file with error checking diff --git a/standard/src/StdMeshMaterial.cpp b/standard/src/StdMeshMaterial.cpp index 7700751ec..1aecb7475 100644 --- a/standard/src/StdMeshMaterial.cpp +++ b/standard/src/StdMeshMaterial.cpp @@ -22,7 +22,7 @@ StdMeshMaterialError::StdMeshMaterialError(const StdStrBuf& message, const char* file, unsigned int line) { - Buf.Format("%s[%u]: %s", file, line, message.getData()); + Buf.Format("%s:%u: %s", file, line, message.getData()); } enum Token @@ -253,10 +253,10 @@ void StdMeshMaterialTextureUnit::Load(StdMeshMaterialParserCtx& ctx) StdMeshMaterialPass::StdMeshMaterialPass() { - Ambient[0] = Ambient[1] = Ambient[2] = Ambient[3] = 1.0f; - Diffuse[0] = Diffuse[1] = Diffuse[2] = Diffuse[3] = 1.0f; - Specular[0] = Specular[1] = Specular[2] = Specular[3] = 0.0f; - Emissive[0] = Emissive[1] = Emissive[2] = Emissive[3] = 0.0f; + Ambient[0] = Ambient[1] = Ambient[2] = 1.0f; Ambient[3] = 0.0f; + Diffuse[0] = Diffuse[1] = Diffuse[2] = 1.0f; Diffuse[3] = 0.0f; + Specular[0] = Specular[1] = Specular[2] = 0.0f; Specular[3] = 1.0f; + Emissive[0] = Emissive[1] = Emissive[2] = 0.0f; Emissive[3] = 1.0f; Shininess = 0.0f; } @@ -278,14 +278,16 @@ void StdMeshMaterialPass::Load(StdMeshMaterialParserCtx& ctx) Ambient[0] = ctx.AdvanceFloat(); Ambient[1] = ctx.AdvanceFloat(); Ambient[2] = ctx.AdvanceFloat(); - ctx.AdvanceFloatOptional(Ambient[3]); + if(ctx.AdvanceFloatOptional(Ambient[3])) + Ambient[3] = 1 - Ambient[3]; } else if(token_name == "diffuse") { Diffuse[0] = ctx.AdvanceFloat(); Diffuse[1] = ctx.AdvanceFloat(); Diffuse[2] = ctx.AdvanceFloat(); - ctx.AdvanceFloatOptional(Diffuse[3]); + if(ctx.AdvanceFloatOptional(Diffuse[3])) + Diffuse[3] = 1 - Diffuse[3]; } else if(token_name == "specular") { @@ -299,7 +301,7 @@ void StdMeshMaterialPass::Load(StdMeshMaterialParserCtx& ctx) float shininess; if(ctx.AdvanceFloatOptional(shininess)) { - Specular[3] = specular3; + Specular[3] = 1 - specular3; Shininess = shininess; } else @@ -312,7 +314,8 @@ void StdMeshMaterialPass::Load(StdMeshMaterialParserCtx& ctx) Emissive[0] = ctx.AdvanceFloat(); Emissive[1] = ctx.AdvanceFloat(); Emissive[2] = ctx.AdvanceFloat(); - ctx.AdvanceFloatOptional(Emissive[3]); + if(ctx.AdvanceFloatOptional(Emissive[3])) + Emissive[3] = 1 - Emissive[3]; } else ctx.ErrorUnexpectedIdentifier(token_name); From 8501d99173a71d2231a91331656b907b7c275886 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sat, 11 Jul 2009 21:43:49 +0200 Subject: [PATCH 13/18] Require a depth buffer size when choosing the glx visual --- standard/src/StdXWindow.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/standard/src/StdXWindow.cpp b/standard/src/StdXWindow.cpp index b4c5a7247..3bc07feba 100644 --- a/standard/src/StdXWindow.cpp +++ b/standard/src/StdXWindow.cpp @@ -202,11 +202,13 @@ bool CStdWindow::FindInfo() // attributes for a single buffered visual in RGBA format with at least 4 bits per color static int attrListSgl[] = { GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, + GLX_DEPTH_SIZE, 8, None }; // attributes for a double buffered visual in RGBA format with at least 4 bits per color static int attrListDbl[] = { GLX_RGBA, GLX_DOUBLEBUFFER, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_DEPTH_SIZE, 8, None }; // doublebuffered is the best Info = glXChooseVisual(dpy, DefaultScreen(dpy), attrListDbl); From bbc8fa98db8506132c679dc7d4dda770c755150b Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sun, 12 Jul 2009 16:27:04 +0200 Subject: [PATCH 14/18] Added animations for mesh objects --- engine/inc/C4Def.h | 1 + engine/src/C4Def.cpp | 1 + engine/src/C4Object.cpp | 32 +++++++++ planet/Objects.c4d/Monster.c4d/ActMap.txt | 3 +- planet/Objects.c4d/Monster.c4d/Script.c | 4 +- planet/Worlds.c4f/Outset.c4s/Scenario.txt | 1 - planet/Worlds.c4f/Outset.c4s/Script.c | 5 ++ standard/inc/StdMesh.h | 3 +- standard/src/StdGL.cpp | 20 ++++-- standard/src/StdMesh.cpp | 82 ++++++++++++++++++++--- 10 files changed, 133 insertions(+), 19 deletions(-) create mode 100644 planet/Worlds.c4f/Outset.c4s/Script.c diff --git a/engine/inc/C4Def.h b/engine/inc/C4Def.h index f4493dda2..6bf96c0df 100644 --- a/engine/inc/C4Def.h +++ b/engine/inc/C4Def.h @@ -176,6 +176,7 @@ class C4ActionDef C4AulScriptFunc *EndCall; C4AulScriptFunc *AbortCall; C4AulScriptFunc *PhaseCall; + StdStrBuf Animation; public: void Default(); void CompileFunc(StdCompiler *pComp); diff --git a/engine/src/C4Def.cpp b/engine/src/C4Def.cpp index 098c81ceb..91adcfeb2 100644 --- a/engine/src/C4Def.cpp +++ b/engine/src/C4Def.cpp @@ -132,6 +132,7 @@ void C4ActionDef::CompileFunc(StdCompiler *pComp) pComp->Value(mkNamingAdapt(toC4CStr(TurnAction), "TurnAction", "" )); pComp->Value(mkNamingAdapt(Reverse, "Reverse", 0 )); pComp->Value(mkNamingAdapt(Step, "Step", 1 )); + pComp->Value(mkNamingAdapt(Animation, "Animation", StdStrBuf() )); } //--------------------------------- C4DefCore ---------------------------------------------- diff --git a/engine/src/C4Object.cpp b/engine/src/C4Object.cpp index 1a81d8e77..7d84f3a18 100644 --- a/engine/src/C4Object.cpp +++ b/engine/src/C4Object.cpp @@ -2251,6 +2251,16 @@ BOOL C4Object::SetPhase(int32_t iPhase) if (Action.Act<=ActIdle) return FALSE; C4ActionDef *actdef=&(Def->ActMap[Action.Act]); Action.Phase=BoundBy(iPhase,0,actdef->Length); + Action.PhaseDelay = 0; + + if(pMeshInstance) + { + if(actdef->Delay) + pMeshInstance->SetPosition(static_cast(Action.Phase * actdef->Delay + Action.PhaseDelay) / (actdef->Delay * actdef->Length) * pMeshInstance->GetAnimation()->Length); + else + pMeshInstance->SetPosition(static_cast(Action.Phase) / actdef->Length * pMeshInstance->GetAnimation()->Length); + } + return TRUE; } @@ -4103,6 +4113,16 @@ BOOL C4Object::SetAction(int32_t iAct, C4Object *pTarget, C4Object *pTarget2, in if (Def && !Inside(iAct,ActIdle,Def->ActNum-1)) return FALSE; + // Set animation on instance. Abort if the mesh does not have + // such an animation. + if(pMeshInstance) + { + if(iAct == ActIdle || Def->ActMap[iAct].Animation == "") + pMeshInstance->UnsetAnimation(); + else if(!pMeshInstance->SetAnimationByName(Def->ActMap[iAct].Animation)) + return FALSE; + } + // Stop previous act sound if (iLastAction>ActIdle) if (iAct!=iLastAction) @@ -5372,6 +5392,7 @@ void C4Object::ExecAction() if (pAction->Delay) { Action.PhaseDelay+=iPhaseAdvance; + bool set_new_action = false; if (Action.PhaseDelay>=pAction->Delay) { // Advance Phase @@ -5387,12 +5408,23 @@ void C4Object::ExecAction() { // set new action if it's not Hold if (pAction->NextAction==ActHold) + { Action.Phase = pAction->Length-1; + Action.PhaseDelay = pAction->Delay-1; + } else + { // Set new action + set_new_action = true; SetAction(pAction->NextAction, NULL, NULL, SAC_StartCall | SAC_EndCall); + } } } + + // Update animation on mesh instance. If a new action was set, + // then will already have happened for the new action. + if(pMeshInstance && pMeshInstance->GetAnimation() && !set_new_action) + pMeshInstance->SetPosition(static_cast(Action.Phase * pAction->Delay + Action.PhaseDelay) / (pAction->Delay * pAction->Length) * pMeshInstance->GetAnimation()->Length); } return; diff --git a/planet/Objects.c4d/Monster.c4d/ActMap.txt b/planet/Objects.c4d/Monster.c4d/ActMap.txt index acbdd816b..0e4e630e0 100644 --- a/planet/Objects.c4d/Monster.c4d/ActMap.txt +++ b/planet/Objects.c4d/Monster.c4d/ActMap.txt @@ -10,6 +10,7 @@ NextAction=Walk TurnAction=Turn StartCall=HitCheck InLiquidAction=Swim +Animation=Walk [Action] Name=Turn @@ -78,4 +79,4 @@ Delay=1 Reverse=1 Facet=0,102,48,34 NextAction=Walk -InLiquidAction=Swim \ No newline at end of file +InLiquidAction=Swim diff --git a/planet/Objects.c4d/Monster.c4d/Script.c b/planet/Objects.c4d/Monster.c4d/Script.c index 68eec90ef..35485458a 100644 --- a/planet/Objects.c4d/Monster.c4d/Script.c +++ b/planet/Objects.c4d/Monster.c4d/Script.c @@ -20,12 +20,12 @@ private func Activity() if (Random(2) || (GetAction() != "Walk" && GetAction() != "Swim")) return 1; // Springen - if (GetAction() == "Walk") +/* if (GetAction() == "Walk") if (!Random(3)) return DoJump(); // Umsehen if (GetAction() == "Walk") - if (!Random(8)) return SetAction("LookUp"); + if (!Random(8)) return SetAction("LookUp");*/ // Umdrehen if (Random(2)) return TurnRight(); diff --git a/planet/Worlds.c4f/Outset.c4s/Scenario.txt b/planet/Worlds.c4f/Outset.c4s/Scenario.txt index a4d074c63..c448eab34 100755 --- a/planet/Worlds.c4f/Outset.c4s/Scenario.txt +++ b/planet/Worlds.c4f/Outset.c4s/Scenario.txt @@ -72,7 +72,6 @@ Liquid=Water-Smooth LiquidLevel=20,30,0,100 Layers=Rock=7;Rock=7;Gold=7;Granite=4;Water=5;Earth-earth=50;Earth-earth_dry=50 SkyScrollMode=2 -Animals=MONS=3 [Weather] Climate=40,10,0,100 diff --git a/planet/Worlds.c4f/Outset.c4s/Script.c b/planet/Worlds.c4f/Outset.c4s/Script.c new file mode 100644 index 000000000..1bfcefd8a --- /dev/null +++ b/planet/Worlds.c4f/Outset.c4s/Script.c @@ -0,0 +1,5 @@ +#strict 2 +func Initialize() +{ + CreateObject(MONS, LandscapeWidth()/2, LandscapeHeight()/2); +} diff --git a/standard/inc/StdMesh.h b/standard/inc/StdMesh.h index 74a3f1b3f..583180dcf 100644 --- a/standard/inc/StdMesh.h +++ b/standard/inc/StdMesh.h @@ -223,7 +223,8 @@ class StdMeshInstance public: StdMeshInstance(const StdMesh& mesh); - void SetAnimation(const StdStrBuf& animation_name); + bool SetAnimationByName(const StdStrBuf& animation_name); + void SetAnimation(const StdMeshAnimation& animation); void UnsetAnimation(); const StdMeshAnimation* GetAnimation() const { return Animation; } diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index aec891d04..90d9ea48c 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -312,8 +312,9 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw const StdMeshBox& box = mesh.GetBoundingBox(); glPushMatrix(); + glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); - glEnable(GL_NORMALIZE); + //glEnable(GL_NORMALIZE); glEnable(GL_LIGHTING); // TODO: Zoom, ClrMod, ... @@ -321,16 +322,22 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw //glColor4f(1.0f, 1.0f, 1.0f, 0.0f); // Scale so that the mesh fits in (tx,ty,twdt,thgt) - double rx = -box.x1 / (box.x2 - box.x1); - double ry = -box.y1 / (box.y2 - box.y1); + float rx = -box.x1 / (box.x2 - box.x1); + float ry = -box.y1 / (box.y2 - box.y1); glTranslatef(tx + rx*twdt, ty + ry*thgt, 0.0f); - glScalef(twdt/(box.x2 - box.x1), thgt/(box.y2 - box.y1), 1.0f); // Put a light source in front of the object - GLfloat light_position[] = { 0.0f, 0.0f, 6.0f, 1.0f }; + GLfloat light_position[] = { 0.0f, 0.0f, 15.0f, 1.0f }; glLightfv(GL_LIGHT0, GL_POSITION, light_position); glEnable(GL_LIGHT0); + float scx = twdt/(box.x2 - box.x1); + float scy = thgt/(box.y2 - box.y1); + // Keep aspect ratio: + //if(scx < scy) scy = scx; + //else scx = scy; + glScalef(scx, scy, 1.0f); + // TODO: Find a working technique, we currently always use the // first one: const StdMeshMaterial& material = mesh.GetMaterial(); @@ -347,6 +354,7 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glMaterialfv(GL_FRONT, GL_SPECULAR, pass.Specular); glMaterialfv(GL_FRONT, GL_EMISSION, pass.Emissive); glMaterialf(GL_FRONT, GL_SHININESS, pass.Shininess); + // TODO: Set up texture units // Render mesh glBegin(GL_TRIANGLES); @@ -373,7 +381,7 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glDisable(GL_LIGHT0); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); - glDisable(GL_NORMALIZE); + glShadeModel(GL_FLAT); glPopMatrix(); // TODO: glScissor, so that we only clear the area the mesh covered. diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp index 37233a7e5..47136fd34 100644 --- a/standard/src/StdMesh.cpp +++ b/standard/src/StdMesh.cpp @@ -20,6 +20,51 @@ #include +namespace +{ + // Generate matrix to convert the mesh from Ogre coordinate system to Clonk + // coordinate system. When making changes here, don't forget to make + // corresponding changes for the inverse matrix below. + StdMeshMatrix CoordCorrectionMatrix() + { + StdMeshMatrix matrix; + StdMeshMatrix helper; + + //matrix.SetIdentity(); + matrix.SetScale(-1.0f, 1.0f, 1.0f); + + //helper.SetRotate(M_PI/2.0f, 1.0f, 0.0f, 0.0f); + helper.SetRotate(M_PI/2.0f, 1.0f, 0.0f, 0.0f); + matrix.Mul(helper); + + helper.SetRotate(M_PI/2.0f, 0.0f, 0.0f, 1.0f); + matrix.Mul(helper); + + return matrix; + } + + StdMeshMatrix CoordCorrectionMatrixInverse() + { + StdMeshMatrix matrix; + StdMeshMatrix helper; + + matrix.SetRotate(-M_PI/2.0f, 0.0f, 0.0f, 1.0f); + + //helper.SetRotate(-M_PI/2.0f, 1.0f, 0.0f, 0.0f); + helper.SetRotate(-M_PI/2.0f, 1.0f, 0.0f, 0.0f); + matrix.Mul(helper); + + //helper.SetIdentity(); + helper.SetScale(-1.0f, 1.0f, 1.0f); + matrix.Mul(helper); + + return matrix; + } + + StdMeshMatrix CoordCorrection = CoordCorrectionMatrix(); + StdMeshMatrix CoordCorrectionInverse = CoordCorrectionMatrixInverse(); +} + StdMeshError::StdMeshError(const StdStrBuf& message, const char* file, unsigned int line) { Buf.Format("%s:%u: %s", file, line, message.getData()); @@ -189,10 +234,6 @@ void StdMeshMatrix::Add(const StdMeshMatrix& other) 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]; @@ -217,8 +258,8 @@ 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); + y = trans(1,0)*old.x + trans(1,1)*old.y + trans(1,2)*old.z + trans(1,3); + z = trans(2,0)*old.x + trans(2,1)*old.y + trans(2,2)*old.z + trans(2,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; @@ -364,6 +405,9 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto Vertices[i].u = mesh.RequireFloatAttribute(texcoord_elem, "u"); Vertices[i].v = mesh.RequireFloatAttribute(texcoord_elem, "v"); + // Convert to Clonk coordinate system + Vertices[i].Transform(CoordCorrection); + // Construct BoundingBox if(i == 0) { @@ -446,10 +490,18 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto bone->Trans.SetRotate(angle, rx, ry, rz); bone->Trans.Transform(helper); + // Transform to Clonk coordinate system + bone->Trans.Mul(CoordCorrectionInverse); + bone->Trans.Transform(CoordCorrection); + helper.SetRotate(-angle, rx, ry, rz); bone->InverseTrans.SetTranslate(-dx, -dy, -dz); bone->InverseTrans.Transform(helper); + // Transform to Clonk coordinate system + bone->InverseTrans.Mul(CoordCorrectionInverse); + bone->InverseTrans.Transform(CoordCorrection); + bone->Parent = NULL; // Index of bone will be set when building Master Bone Table later @@ -576,12 +628,17 @@ void StdMesh::InitXML(const char* filename, const char* xml_data, StdMeshSkeleto float ry = skeleton.RequireFloatAttribute(axis_elem, "y"); float rz = skeleton.RequireFloatAttribute(axis_elem, "z"); + // TODO: Make sure the order is correct here - I am not sure about scale StdMeshMatrix helper; frame.Trans.SetRotate(angle, rx, ry, rz); helper.SetScale(sx, sy, sz); frame.Trans.Transform(helper); helper.SetTranslate(-dx, -dy, -dz); frame.Trans.Transform(helper); + + // Transform into Clonk coordinate system + frame.Trans.Transform(CoordCorrection); + frame.Trans.Mul(CoordCorrectionInverse); } } @@ -647,9 +704,18 @@ StdMeshInstance::StdMeshInstance(const StdMesh& mesh): Vertices[i] = Mesh.GetVertex(i); } -void StdMeshInstance::SetAnimation(const StdStrBuf& animation_name) +bool StdMeshInstance::SetAnimationByName(const StdStrBuf& animation_name) { - Animation = Mesh.GetAnimationByName(animation_name); + const StdMeshAnimation* animation = Mesh.GetAnimationByName(animation_name); + if(!animation) return false; + SetAnimation(*animation); + return true; +} + +void StdMeshInstance::SetAnimation(const StdMeshAnimation& animation) +{ + // TODO: Make sure the animation belongs to this mesh + Animation = &animation; SetPosition(0.0f); } From 76bc924f465a954e1f7105b328ad95606a0d222c Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sun, 12 Jul 2009 19:06:31 +0200 Subject: [PATCH 15/18] Take zoom into account when rendering a mesh --- standard/src/StdGL.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index 90d9ea48c..b4317baee 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -317,7 +317,14 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw //glEnable(GL_NORMALIZE); glEnable(GL_LIGHTING); - // TODO: Zoom, ClrMod, ... +/* float twdt = wdt; + float thgt = hgt;*/ + tx = (tx - ZoomX) * Zoom + ZoomX; + ty = (ty - ZoomY) * Zoom + ZoomY; + twdt *= Zoom; + thgt *= Zoom; + + // TODO: ClrMod, ... //glColor4f(1.0f, 1.0f, 1.0f, 0.0f); @@ -327,7 +334,7 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glTranslatef(tx + rx*twdt, ty + ry*thgt, 0.0f); // Put a light source in front of the object - GLfloat light_position[] = { 0.0f, 0.0f, 15.0f, 1.0f }; + GLfloat light_position[] = { 0.0f, 0.0f, 15.0f*Zoom, 1.0f }; glLightfv(GL_LIGHT0, GL_POSITION, light_position); glEnable(GL_LIGHT0); From 5b033036fcf6bbd91fd0106d5d206f72bf7261b6 Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Sat, 18 Jul 2009 21:36:08 -0400 Subject: [PATCH 16/18] Apply transformation to mesh renderings --- engine/src/C4Def.cpp | 4 ++-- engine/src/C4Object.cpp | 2 +- standard/inc/StdDDraw2.h | 4 ++-- standard/inc/StdGL.h | 2 +- standard/inc/StdNoGfx.h | 2 +- standard/src/StdDDraw2.cpp | 4 ++-- standard/src/StdGL.cpp | 36 +++++++++++++++++++----------------- 7 files changed, 28 insertions(+), 26 deletions(-) diff --git a/engine/src/C4Def.cpp b/engine/src/C4Def.cpp index 91adcfeb2..3aede5506 100644 --- a/engine/src/C4Def.cpp +++ b/engine/src/C4Def.cpp @@ -883,10 +883,10 @@ void C4Def::Draw(C4Facet &cgo, BOOL fSelected, DWORD iColor, C4Object *pObj, int break; case C4DefGraphics::TYPE_Mesh: { - // TODO: Allow rendering of a mesh directly, without instance + // TODO: Allow rendering of a mesh directly, without instance (to render pose; no animation) StdMeshInstance dummy(*graphics->Mesh); // TODO: Keep aspect ratio of mesh dimensions - lpDDraw->RenderMesh(dummy, cgo.Surface, cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt); + lpDDraw->RenderMesh(dummy, cgo.Surface, cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt, NULL); } break; } diff --git a/engine/src/C4Object.cpp b/engine/src/C4Object.cpp index 7d84f3a18..b8c88daa9 100644 --- a/engine/src/C4Object.cpp +++ b/engine/src/C4Object.cpp @@ -485,7 +485,7 @@ void C4Object::DrawFaceImpl(C4TargetFacet &cgo, bool action, float fx, float fy, TRUE, transform); break; case C4DefGraphics::TYPE_Mesh: - lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, tx, ty, twdt, thgt); + lpDDraw->RenderMesh(*pMeshInstance, cgo.Surface, tx, ty, twdt, thgt, transform); break; } } diff --git a/standard/inc/StdDDraw2.h b/standard/inc/StdDDraw2.h index c51739dfe..9e286cb23 100644 --- a/standard/inc/StdDDraw2.h +++ b/standard/inc/StdDDraw2.h @@ -296,9 +296,9 @@ class CStdDDraw BOOL Blit(SURFACE sfcSource, float fx, float fy, float fwdt, float fhgt, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt, BOOL fSrcColKey=FALSE, CBltTransform *pTransform=NULL); - BOOL RenderMesh(StdMeshInstance &instance, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt); + BOOL RenderMesh(StdMeshInstance &instance, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt, CBltTransform* pTransform); virtual void PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool fMod2, bool fExact) = 0; - virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt) = 0; + virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, CBltTransform* pTransform) = 0; BOOL Blit8(SURFACE sfcSource, int fx, int fy, int fwdt, int fhgt, // force 8bit-blit (inline) SURFACE sfcTarget, int tx, int ty, int twdt, int thgt, BOOL fSrcColKey=FALSE, CBltTransform *pTransform=NULL); diff --git a/standard/inc/StdGL.h b/standard/inc/StdGL.h index 8d8bd340e..cf243ed1e 100644 --- a/standard/inc/StdGL.h +++ b/standard/inc/StdGL.h @@ -110,7 +110,7 @@ class CStdGL : public CStdDDraw #endif // Blit virtual void PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool fMod2, bool fExact); - virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt); + virtual void PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, CBltTransform* pTransform); virtual void BlitLandscape(SURFACE sfcSource, SURFACE sfcSource2, SURFACE sfcLiquidAnimation, float fx, float fy, SURFACE sfcTarget, float tx, float ty, float wdt, float hgt); void FillBG(DWORD dwClr=0); diff --git a/standard/inc/StdNoGfx.h b/standard/inc/StdNoGfx.h index e0029a38d..c6c9dff0d 100644 --- a/standard/inc/StdNoGfx.h +++ b/standard/inc/StdNoGfx.h @@ -41,7 +41,7 @@ public: virtual bool PrepareRendering(SURFACE) { return true; } virtual void FillBG(DWORD dwClr=0) { } virtual void PerformBlt(CBltData &, CTexRef *, DWORD, bool, bool) { } - virtual void PerformMesh(StdMeshInstance &, float, float, float, float) { } + virtual void PerformMesh(StdMeshInstance &, float, float, float, float, CBltTransform* pTransform) { } virtual void PerformLine(SURFACE, float, float, float, float, DWORD) { } virtual void DrawQuadDw(SURFACE, float *, DWORD, DWORD, DWORD, DWORD) { } virtual void PerformPix(SURFACE, float, float, DWORD) { } diff --git a/standard/src/StdDDraw2.cpp b/standard/src/StdDDraw2.cpp index b4fbc4f3b..47f25f9e5 100644 --- a/standard/src/StdDDraw2.cpp +++ b/standard/src/StdDDraw2.cpp @@ -1050,7 +1050,7 @@ BOOL CStdDDraw::Blit(SURFACE sfcSource, float fx, float fy, float fwdt, float fh return TRUE; } -BOOL CStdDDraw::RenderMesh(StdMeshInstance &instance, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt) +BOOL CStdDDraw::RenderMesh(StdMeshInstance &instance, SURFACE sfcTarget, float tx, float ty, float twdt, float thgt, CBltTransform* pTransform) { // TODO: Emulate rendering if (!sfcTarget->IsRenderTarget()) return FALSE; @@ -1062,7 +1062,7 @@ BOOL CStdDDraw::RenderMesh(StdMeshInstance &instance, SURFACE sfcTarget, float t // store current state StoreStateBlock(); - PerformMesh(instance, tx, ty, twdt, thgt); + PerformMesh(instance, tx, ty, twdt, thgt, pTransform); // restore state RestoreStateBlock(); diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index b4317baee..576e9c578 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -306,7 +306,7 @@ void CStdGL::PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool } } -void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt) +void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, CBltTransform* pTransform) { const StdMesh& mesh = instance.Mesh; const StdMeshBox& box = mesh.GetBoundingBox(); @@ -314,37 +314,37 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glPushMatrix(); glShadeModel(GL_SMOOTH); glEnable(GL_DEPTH_TEST); - //glEnable(GL_NORMALIZE); glEnable(GL_LIGHTING); -/* float twdt = wdt; - float thgt = hgt;*/ - tx = (tx - ZoomX) * Zoom + ZoomX; - ty = (ty - ZoomY) * Zoom + ZoomY; - twdt *= Zoom; - thgt *= Zoom; + // Apply zoom + glTranslatef(ZoomX, ZoomY, 0.0f); + glScalef(Zoom, Zoom, 1.0f); + glTranslatef(-ZoomX, -ZoomY, 0.0f); + + if(pTransform) + { + const GLfloat transform[16] = { pTransform->mat[0], pTransform->mat[3], 0, pTransform->mat[6], pTransform->mat[1], pTransform->mat[4], 0, pTransform->mat[7], 0, 0, 1, 0, pTransform->mat[2], pTransform->mat[5], 0, pTransform->mat[8] }; + glMultMatrixf(transform); + } // TODO: ClrMod, ... - //glColor4f(1.0f, 1.0f, 1.0f, 0.0f); - // Scale so that the mesh fits in (tx,ty,twdt,thgt) float rx = -box.x1 / (box.x2 - box.x1); float ry = -box.y1 / (box.y2 - box.y1); - glTranslatef(tx + rx*twdt, ty + ry*thgt, 0.0f); - - // Put a light source in front of the object - GLfloat light_position[] = { 0.0f, 0.0f, 15.0f*Zoom, 1.0f }; - glLightfv(GL_LIGHT0, GL_POSITION, light_position); - glEnable(GL_LIGHT0); - float scx = twdt/(box.x2 - box.x1); float scy = thgt/(box.y2 - box.y1); // Keep aspect ratio: //if(scx < scy) scy = scx; //else scx = scy; + glTranslatef(tx + rx*twdt, ty + ry*thgt, 0.0f); glScalef(scx, scy, 1.0f); + // Put a light source in front of the object + GLfloat light_position[] = { 0.0f, 0.0f, 15.0f*Zoom, 1.0f }; + glLightfv(GL_LIGHT0, GL_POSITION, light_position); + glEnable(GL_LIGHT0); + // TODO: Find a working technique, we currently always use the // first one: const StdMeshMaterial& material = mesh.GetMaterial(); @@ -363,7 +363,9 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glMaterialf(GL_FRONT, GL_SHININESS, pass.Shininess); // TODO: Set up texture units + // Render mesh + // TODO: Use glInterleavedArrays? glBegin(GL_TRIANGLES); for(unsigned int j = 0; j < mesh.GetNumFaces(); ++j) { From a4d094034ddaadd0763a0c68a0a9d21014494dad Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Wed, 22 Jul 2009 21:11:39 -0400 Subject: [PATCH 17/18] Apply ClrMod to mesh graphics --- planet/Objects.c4d/Monster.c4d/DefCore.txt | 1 + standard/src/StdGL.cpp | 91 +++++++++++++++++----- 2 files changed, 73 insertions(+), 19 deletions(-) diff --git a/planet/Objects.c4d/Monster.c4d/DefCore.txt b/planet/Objects.c4d/Monster.c4d/DefCore.txt index af7510bea..585094957 100644 --- a/planet/Objects.c4d/Monster.c4d/DefCore.txt +++ b/planet/Objects.c4d/Monster.c4d/DefCore.txt @@ -28,6 +28,7 @@ VehicleControl=2 Pathfinder=1 ClosedContainer=2 NoFight=1 +Rotate=1 [Physical] Energy=250000 diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index 576e9c578..563294f68 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -306,6 +306,47 @@ void CStdGL::PerformBlt(CBltData &rBltData, CTexRef *pTex, DWORD dwModClr, bool } } +namespace +{ + void RenderMeshVertex(const StdMeshVertex& vtx, const StdMeshMaterialPass& pass, CBltTransform* pTransform, CClrModAddMap* pClrModMap, DWORD dwModClr) + { + // TODO: We might also want to modulate emissive + float Ambient[4]; + float Diffuse[4]; + float Specular[4]; + + float x = vtx.x; + float y = vtx.y; + pTransform->TransformPoint(x, y); + DWORD dwClr = pClrModMap ? pClrModMap->GetModAt(x, y) : 0xffffff; + ModulateClr(dwClr, dwModClr); + + Ambient[0] = pass.Ambient[0] * ((dwClr >> 16) & 0xff) / 255.0f; + Ambient[1] = pass.Ambient[1] * ((dwClr >> 8) & 0xff) / 255.0f; + Ambient[2] = pass.Ambient[2] * ((dwClr ) & 0xff) / 255.0f; + Ambient[3] = 1 - ((1-pass.Ambient[3]) * (1-((dwClr >> 24) & 0xff) / 255.0f)); + + Diffuse[0] = pass.Diffuse[0] * ((dwClr >> 16) & 0xff) / 255.0f; + Diffuse[1] = pass.Diffuse[1] * ((dwClr >> 8) & 0xff) / 255.0f; + Diffuse[2] = pass.Diffuse[2] * ((dwClr ) & 0xff) / 255.0f; + Diffuse[3] = 1 - ((1-pass.Diffuse[3]) * (1-((dwClr >> 24) & 0xff) / 255.0f)); + + Specular[0] = pass.Specular[0] * ((dwClr >> 16) & 0xff) / 255.0f; + Specular[1] = pass.Specular[1] * ((dwClr >> 8) & 0xff) / 255.0f; + Specular[2] = pass.Specular[2] * ((dwClr ) & 0xff) / 255.0f; + Specular[3] = 1 - ((1-pass.Specular[3]) * (1-((dwClr >> 24) & 0xff) / 255.0f)); + + glMaterialfv(GL_FRONT, GL_AMBIENT, Ambient); + glMaterialfv(GL_FRONT, GL_DIFFUSE, Diffuse); + glMaterialfv(GL_FRONT, GL_SPECULAR, Specular); + + glTexCoord2f(vtx.u, vtx.v); + glTexCoord2f(vtx.u, vtx.v); + glNormal3f(vtx.nx, vtx.ny, vtx.nz); + glVertex3f(vtx.x, vtx.y, vtx.z); + } +} + void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float twdt, float thgt, CBltTransform* pTransform) { const StdMesh& mesh = instance.Mesh; @@ -327,22 +368,21 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw glMultMatrixf(transform); } - // TODO: ClrMod, ... - // Scale so that the mesh fits in (tx,ty,twdt,thgt) - float rx = -box.x1 / (box.x2 - box.x1); - float ry = -box.y1 / (box.y2 - box.y1); - float scx = twdt/(box.x2 - box.x1); - float scy = thgt/(box.y2 - box.y1); + const float rx = -box.x1 / (box.x2 - box.x1); + const float ry = -box.y1 / (box.y2 - box.y1); + const float dx = tx + rx*twdt; + const float dy = ty + ry*thgt; + const float scx = twdt/(box.x2 - box.x1); + const float scy = thgt/(box.y2 - box.y1); // Keep aspect ratio: //if(scx < scy) scy = scx; //else scx = scy; - glTranslatef(tx + rx*twdt, ty + ry*thgt, 0.0f); + glTranslatef(dx, dy, 0.0f); glScalef(scx, scy, 1.0f); // Put a light source in front of the object - GLfloat light_position[] = { 0.0f, 0.0f, 15.0f*Zoom, 1.0f }; - glLightfv(GL_LIGHT0, GL_POSITION, light_position); + const GLfloat light_position[] = { 0.0f, 0.0f, 15.0f*Zoom, 1.0f }; glEnable(GL_LIGHT0); // TODO: Find a working technique, we currently always use the @@ -350,22 +390,41 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw const StdMeshMaterial& material = mesh.GetMaterial(); const StdMeshMaterialTechnique& technique = material.Techniques[0]; + // Create a transformation which transfers a vertex from mesh + // coordinates to screen coordinates. This is basically the same + // as the current GL modelview matrix, but we need it to access the + // ClrModMap for each vertex with the correct coordinates. + CBltTransform Transform; + Transform.SetMoveScale(dx, dy, scx, scy); + if(pTransform) Transform *= *pTransform; + + CClrModAddMap* ClrModMap = fUseClrModMap ? pClrModMap : NULL; + DWORD dwModClr = BlitModulated ? BlitModulateClr : 0xffffff; + // Render each pass for(unsigned int i = 0; i < technique.Passes.size(); ++i) { const StdMeshMaterialPass& pass = technique.Passes[i]; // Set up material +#if 0 glMaterialfv(GL_FRONT, GL_AMBIENT, pass.Ambient); glMaterialfv(GL_FRONT, GL_DIFFUSE, pass.Diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, pass.Specular); +#endif glMaterialfv(GL_FRONT, GL_EMISSION, pass.Emissive); glMaterialf(GL_FRONT, GL_SHININESS, pass.Shininess); // TODO: Set up texture units // Render mesh - // TODO: Use glInterleavedArrays? + // TODO: Use glInterleavedArrays? Hm, might be impossible as + // we need to set material for each vertex. Can't use + // glMaterialColor either, because we set all diffuse, ambient + // and specular material... + // TODO: We might not want to calculate the material for each + // vertex separately. This looks odd when the mesh is moving + // at FoW borders anyway. glBegin(GL_TRIANGLES); for(unsigned int j = 0; j < mesh.GetNumFaces(); ++j) { @@ -374,15 +433,9 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw const StdMeshVertex& vtx2 = instance.GetVertex(face.Vertices[1]); const StdMeshVertex& vtx3 = instance.GetVertex(face.Vertices[2]); - glTexCoord2f(vtx1.u, vtx1.v); - glNormal3f(vtx1.nx, vtx1.ny, vtx1.nz); - glVertex3f(vtx1.x, vtx1.y, vtx1.z); - glTexCoord2f(vtx2.u, vtx2.v); - glNormal3f(vtx2.nx, vtx2.ny, vtx2.nz); - glVertex3f(vtx2.x, vtx2.y, vtx2.z); - glTexCoord2f(vtx3.u, vtx3.v); - glNormal3f(vtx3.nx, vtx3.ny, vtx3.nz); - glVertex3f(vtx3.x, vtx3.y, vtx3.z); + RenderMeshVertex(vtx1, pass, &Transform, ClrModMap, dwModClr); + RenderMeshVertex(vtx2, pass, &Transform, ClrModMap, dwModClr); + RenderMeshVertex(vtx3, pass, &Transform, ClrModMap, dwModClr); } glEnd(); // GL_TRIANGLES } From 6e49936f7de1f65e2dfaea695e84d0c098c439ec Mon Sep 17 00:00:00 2001 From: Armin Burgmeier Date: Thu, 23 Jul 2009 19:52:18 -0400 Subject: [PATCH 18/18] Order faces from nearest to farthest for mesh rendering This supports ClrMod with alpha on a mesh --- engine/src/C4Def.cpp | 1 + engine/src/C4Object.cpp | 10 +++++++ standard/inc/StdMesh.h | 19 ++++++++++++++ standard/src/StdGL.cpp | 4 +-- standard/src/StdMesh.cpp | 57 ++++++++++++++++++++++++++++++++++++++-- 5 files changed, 87 insertions(+), 4 deletions(-) diff --git a/engine/src/C4Def.cpp b/engine/src/C4Def.cpp index 3aede5506..daa0f8a4c 100644 --- a/engine/src/C4Def.cpp +++ b/engine/src/C4Def.cpp @@ -885,6 +885,7 @@ void C4Def::Draw(C4Facet &cgo, BOOL fSelected, DWORD iColor, C4Object *pObj, int { // TODO: Allow rendering of a mesh directly, without instance (to render pose; no animation) StdMeshInstance dummy(*graphics->Mesh); + dummy.SetFaceOrdering(StdMeshInstance::FO_NearestToFarthest); // TODO: Keep aspect ratio of mesh dimensions lpDDraw->RenderMesh(dummy, cgo.Surface, cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt, NULL); } diff --git a/engine/src/C4Object.cpp b/engine/src/C4Object.cpp index b8c88daa9..b309f8d52 100644 --- a/engine/src/C4Object.cpp +++ b/engine/src/C4Object.cpp @@ -188,9 +188,14 @@ BOOL C4Object::Init(C4Def *pDef, C4Object *pCreator, // graphics pGraphics = &Def->Graphics; if(pGraphics->Type == C4DefGraphics::TYPE_Mesh) + { pMeshInstance = new StdMeshInstance(*pGraphics->Mesh); + pMeshInstance->SetFaceOrdering(StdMeshInstance::FO_NearestToFarthest); + } else + { pMeshInstance = NULL; + } BlitMode = Def->BlitMode; // Position @@ -426,9 +431,14 @@ void C4Object::UpdateGraphics(bool fGraphicsChanged, bool fTemp) delete pMeshInstance; if(pGraphics->Type == C4DefGraphics::TYPE_Mesh) + { pMeshInstance = new StdMeshInstance(*pGraphics->Mesh); + pMeshInstance->SetFaceOrdering(StdMeshInstance::FO_NearestToFarthest); + } else + { pMeshInstance = NULL; + } // update face - this also puts any SolidMask UpdateFace(false); diff --git a/standard/inc/StdMesh.h b/standard/inc/StdMesh.h index 583180dcf..266fa8254 100644 --- a/standard/inc/StdMesh.h +++ b/standard/inc/StdMesh.h @@ -223,6 +223,15 @@ class StdMeshInstance public: StdMeshInstance(const StdMesh& mesh); + enum FaceOrdering { + FO_Fixed, // don't reorder, keep faces as in mesh + FO_FarthestToNearest, + FO_NearestToFarthest + }; + + FaceOrdering GetFaceOrdering() const { return CurrentFaceOrdering; } + void SetFaceOrdering(FaceOrdering ordering); + bool SetAnimationByName(const StdStrBuf& animation_name); void SetAnimation(const StdMeshAnimation& animation); void UnsetAnimation(); @@ -237,14 +246,24 @@ public: const StdMeshVertex& GetVertex(unsigned int i) const { return Vertices[i]; } unsigned int GetNumVertices() const { return Vertices.size(); } + // Get face of instance. The instance faces are the same as the mesh faces, + // with the exception that they are differently ordered, depending on the + // current FaceOrdering. See also SetFaceOrdering. + const StdMeshFace& GetFace(unsigned int i) const { return *Faces[i]; } + unsigned int GetNumFaces() const { return Faces.size(); } + const StdMesh& Mesh; protected: + void ReorderFaces(); + + FaceOrdering CurrentFaceOrdering; const StdMeshAnimation* Animation; float Position; std::vector BoneTransforms; std::vector Vertices; + std::vector Faces; }; #endif diff --git a/standard/src/StdGL.cpp b/standard/src/StdGL.cpp index 563294f68..4a97168db 100644 --- a/standard/src/StdGL.cpp +++ b/standard/src/StdGL.cpp @@ -426,9 +426,9 @@ void CStdGL::PerformMesh(StdMeshInstance &instance, float tx, float ty, float tw // vertex separately. This looks odd when the mesh is moving // at FoW borders anyway. glBegin(GL_TRIANGLES); - for(unsigned int j = 0; j < mesh.GetNumFaces(); ++j) + for(unsigned int j = 0; j < instance.GetNumFaces(); ++j) { - const StdMeshFace& face = mesh.GetFace(j); + const StdMeshFace& face = instance.GetFace(j); const StdMeshVertex& vtx1 = instance.GetVertex(face.Vertices[0]); const StdMeshVertex& vtx2 = instance.GetVertex(face.Vertices[1]); const StdMeshVertex& vtx3 = instance.GetVertex(face.Vertices[2]); diff --git a/standard/src/StdMesh.cpp b/standard/src/StdMesh.cpp index 47136fd34..cec4e9fd6 100644 --- a/standard/src/StdMesh.cpp +++ b/standard/src/StdMesh.cpp @@ -20,8 +20,41 @@ #include +#include + namespace { + // Helper to sort faces for FaceOrdering + struct StdMeshInstanceFaceOrderingCmpPred + { + const StdMeshInstance& m_inst; + StdMeshInstanceFaceOrderingCmpPred(const StdMeshInstance& inst): + m_inst(inst) {} + + bool operator()(const StdMeshFace* face1, const StdMeshFace* face2) const + { + switch(m_inst.GetFaceOrdering()) + { + case StdMeshInstance::FO_Fixed: + // Faces are in a vector, thus contiuous in memory + return face1 < face2; // TODO: face1 > face2? + case StdMeshInstance::FO_FarthestToNearest: + case StdMeshInstance::FO_NearestToFarthest: + { + float z1 = m_inst.GetVertex(face1->Vertices[0]).z + m_inst.GetVertex(face1->Vertices[1]).z + m_inst.GetVertex(face1->Vertices[2]).z; + float z2 = m_inst.GetVertex(face2->Vertices[0]).z + m_inst.GetVertex(face2->Vertices[1]).z + m_inst.GetVertex(face2->Vertices[2]).z; + if(m_inst.GetFaceOrdering() == StdMeshInstance::FO_FarthestToNearest) + return z1 < z2; + else + return z2 < z1; + } + default: + assert(false); + return false; + } + } + }; + // Generate matrix to convert the mesh from Ogre coordinate system to Clonk // coordinate system. When making changes here, don't forget to make // corresponding changes for the inverse matrix below. @@ -697,11 +730,22 @@ const StdMeshAnimation* StdMesh::GetAnimationByName(const StdStrBuf& name) const } StdMeshInstance::StdMeshInstance(const StdMesh& mesh): - Mesh(mesh), Animation(NULL), Position(0.0f), - BoneTransforms(Mesh.GetNumBones()), Vertices(Mesh.GetNumVertices()) + Mesh(mesh), CurrentFaceOrdering(FO_Fixed), Animation(NULL), Position(0.0f), + BoneTransforms(Mesh.GetNumBones()), Vertices(Mesh.GetNumVertices()), + Faces(Mesh.GetNumFaces()) { for(unsigned int i = 0; i < Mesh.GetNumVertices(); ++i) Vertices[i] = Mesh.GetVertex(i); + + // This is FO_Fixed actually + for(unsigned int i = 0; i < Mesh.GetNumFaces(); ++i) + Faces[i] = &Mesh.GetFace(i); +} + +void StdMeshInstance::SetFaceOrdering(FaceOrdering ordering) +{ + CurrentFaceOrdering = ordering; + ReorderFaces(); } bool StdMeshInstance::SetAnimationByName(const StdStrBuf& animation_name) @@ -785,6 +829,15 @@ void StdMeshInstance::SetPosition(float position) Vertices[i] = vertex; } } + + if(CurrentFaceOrdering != FO_Fixed) + ReorderFaces(); +} + +void StdMeshInstance::ReorderFaces() +{ + StdMeshInstanceFaceOrderingCmpPred pred(*this); + std::sort(Faces.begin(), Faces.end(), pred); } // vim: et ts=2 sw=2