forked from Mirrors/openclonk
Added StdMeshMaterial
parent
4649547c97
commit
639b40bbb6
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue