Added StdMeshMaterial

stable-5.2
Armin Burgmeier 2009-07-06 23:45:44 +02:00
parent 4649547c97
commit 639b40bbb6
4 changed files with 546 additions and 0 deletions

View File

@ -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 \

View File

@ -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);

View File

@ -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 <Standard.h>
#include <StdBuf.h>
#include <StdPNG.h>
#include <StdSurface2.h>
#include <vector>
#include <map>
// 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<StdMeshMaterialTextureUnit> TextureUnits;
float Ambient[4];
float Diffuse[4];
float Specular[4];
float Emissive[4];
float Shininess;
};
class StdMeshMaterialTechnique
{
public:
void Load(StdMeshMaterialParserCtx& ctx);
std::vector<StdMeshMaterialPass> 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<StdMeshMaterialTechnique> 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<StdStrBuf, StdMeshMaterial> Materials;
};
#endif
// vim: et ts=2 sw=2

View File

@ -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 <StdMeshMaterial.h>
#include <cctype>
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<StdStrBuf, StdMeshMaterial>::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<StdStrBuf, StdMeshMaterial>::const_iterator iter = Materials.find(StdStrBuf(material_name));
if(iter == Materials.end()) return NULL;
return &iter->second;
}
// vim: et ts=2 sw=2