854 lines
23 KiB
C++
854 lines
23 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "core/geometry.h"
|
|
#include "core/embedded_resource_handler_interface.h"
|
|
#include "timing/benchmark.h"
|
|
#include "metafile/nml.h"
|
|
#include "math/matrix4.h"
|
|
#include "ascii/parser.h"
|
|
#include "memory/endian.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS::Core;
|
|
using namespace GS::AsciiParser;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParseVertex(const Tag *pt)
|
|
{
|
|
NMLTagForeach(vt, *pt)
|
|
{
|
|
if (vt->name == "Count")
|
|
vtx.Allocate(vt->GetInteger());
|
|
|
|
else if (vt->name == "Data")
|
|
{
|
|
float *fbff = (float *)vt->GetValue().GetBinaryBuffer();
|
|
|
|
if (vtx)
|
|
for (uint n = 0; n < vtx.GetCount(); ++n)
|
|
{
|
|
for (int m = 0; m < 3; ++m)
|
|
Endian::ToHost(&fbff[n * 3 + m], 4, Endian::Intel);
|
|
vtx[n].Set(fbff[n * 3 + 0], fbff[n * 3 + 1], fbff[n * 3 + 2]);
|
|
}
|
|
else __LOG_E__ << "Incomplete vertex chunk loading '" << name << "', expected vertex count integer tag.\n";
|
|
}
|
|
}
|
|
}
|
|
void Geometry::ParseAsciiVertex(const Tag *pt)
|
|
{
|
|
NMLTagForeach(vt, *pt)
|
|
{
|
|
if (vt->name == "Count")
|
|
vtx.Allocate(vt->GetInteger());
|
|
|
|
else if (vt->name == "Data")
|
|
{
|
|
if (vtx)
|
|
{
|
|
uint cvtx = 0;
|
|
|
|
NMLTagForeach(vx, *vt)
|
|
{
|
|
if (cvtx == vtx.GetCount())
|
|
{
|
|
__LOG_E__ << "Corrupter <AVertex> tag, more vertices than expected.\n";
|
|
break;
|
|
}
|
|
vtx[cvtx++].FromMetaTag(*vx);
|
|
}
|
|
}
|
|
else __LOG_E__ << "Incomplete vertex chunk loading '" << name << "', expected vertex count integer tag.\n";
|
|
}
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParseSkin(const Tag *pt)
|
|
{
|
|
// Retrieve bone id list.
|
|
Tag *bonetag = pt->GetTag("Bones"),
|
|
*bindtag = pt->GetTag("Binds");
|
|
|
|
skin.Free();
|
|
bone_name.Free();
|
|
bone_bind_matrix.Free();
|
|
|
|
if (bonetag)
|
|
{
|
|
AllocateBone(bonetag->GetChildCount());
|
|
|
|
if (!bone_name)
|
|
__LOG_E__ << "Failed to allocate bone id list.\n";
|
|
|
|
else
|
|
{
|
|
int n = 0;
|
|
NMLTagForeach(b, *bonetag)
|
|
bone_name[n++] = b->GetString();
|
|
}
|
|
|
|
if (bindtag && (bindtag->GetChildCount() == bone_name.GetCount()))
|
|
{
|
|
int n = 0;
|
|
NMLTagForeach(m, *bindtag)
|
|
bone_bind_matrix[n++].FromMetaTag(*m);
|
|
}
|
|
}
|
|
|
|
// Retrieve skin weights.
|
|
Tag *weighttag = pt->GetTag("Weights");
|
|
|
|
if (bone_name.GetCount() && weighttag)
|
|
{
|
|
if (weighttag->GetChildCount() != vtx.GetCount())
|
|
__LOG_W__ << "Incoherent weight count (" << weighttag->GetChildCount() <<" for " << vtx.GetCount() << " vertice (is the skin being declared before the vertice?)).\n";
|
|
|
|
if (!skin.Allocate(weighttag->GetChildCount()))
|
|
__LOG_E__ << "Failed to allocate skin weights array.\n";
|
|
|
|
else
|
|
{
|
|
int n = 0;
|
|
NMLTagForeach(b, *weighttag)
|
|
{
|
|
const char *p = b->GetString(), *e = p + String::strlen(p);
|
|
|
|
skin[n].bone_index[0] = (ushort)String::atoi(p);
|
|
p = NextEntry(p, e) + 1;
|
|
skin[n].bone_index[1] = (ushort)String::atoi(p);
|
|
p = NextEntry(p, e) + 1;
|
|
skin[n].bone_index[2] = (ushort)String::atoi(p);
|
|
p = NextEntry(p, e) + 1;
|
|
skin[n].bone_index[3] = (ushort)String::atoi(p);
|
|
p = NextEntry(p, e) + 1;
|
|
|
|
skin[n].w[0] = ((float)String::atoi(p)) / 255.f;
|
|
p = NextEntry(p, e) + 1;
|
|
skin[n].w[1] = ((float)String::atoi(p)) / 255.f;
|
|
p = NextEntry(p, e) + 1;
|
|
skin[n].w[2] = ((float)String::atoi(p)) / 255.f;
|
|
p = NextEntry(p, e) + 1;
|
|
skin[n].w[3] = ((float)String::atoi(p)) / 255.f;
|
|
|
|
// Re-normalize weights.
|
|
float tw = 0.f;
|
|
for (int w = 0; w < 4; ++w)
|
|
tw += skin[n].w[w];
|
|
|
|
float k = 1.f / tw;
|
|
for (int w = 0; w < 4; ++w)
|
|
skin[n].w[w] *= k;
|
|
|
|
++n;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParsePolygon(const Tag *pt)
|
|
{
|
|
NMLTagForeach(vt, *pt)
|
|
{
|
|
if (vt->name == "Count")
|
|
pol.Allocate(vt->GetInteger());
|
|
|
|
else if (vt->name == "BindingCount")
|
|
binding.Allocate(vt->GetInteger());
|
|
|
|
else if (vt->name == "Data")
|
|
{
|
|
if (pol && binding)
|
|
{
|
|
uint *pbuffer = (uint *)vt->GetValue().GetBinaryBuffer();
|
|
uint *pbinding = binding;
|
|
|
|
for (uint m = 0; m < pol.GetCount(); m++)
|
|
{
|
|
Endian::ToHost(pbuffer, 4, Endian::Intel);
|
|
pol[m].vtx_count = (ushort)*pbuffer++;
|
|
|
|
pol[m].binding = pbinding;
|
|
for (uint j = 0; j < pol[m].vtx_count; j++)
|
|
{
|
|
Endian::ToHost(pbuffer, 4, Endian::Intel);
|
|
*pbinding++ = *pbuffer++;
|
|
}
|
|
|
|
Endian::ToHost(pbuffer, 4, Endian::Intel);
|
|
pol[m].material = (ushort)*pbuffer++;
|
|
}
|
|
}
|
|
else __LOG_E__ << "Could not allocate polygon/binding memory.\n";
|
|
}
|
|
}
|
|
}
|
|
void Geometry::ParseAsciiPolygon(const Tag *pt)
|
|
{
|
|
NMLTagForeach(vt, *pt)
|
|
{
|
|
if (vt->name == "Count")
|
|
pol.Allocate(vt->GetInteger());
|
|
|
|
else if (vt->name == "BindingCount")
|
|
binding.Allocate(vt->GetInteger());
|
|
|
|
else if (vt->name == "Data")
|
|
{
|
|
if (binding && pol)
|
|
{
|
|
static String _PolyIndex("Index");
|
|
|
|
uint *pbinding = binding;
|
|
uint cpoly = 0;
|
|
|
|
NMLTagForeach(poly, *vt)
|
|
{
|
|
if (cpoly == pol.GetCount())
|
|
{
|
|
__LOG_E__ << "Corrupted <APolygon> tag, too many polygons.\n";
|
|
break;
|
|
}
|
|
|
|
Tag *count_tag = poly->GetTag("Count;"),
|
|
*material_tag = poly->GetTag("Material;");
|
|
|
|
pol[cpoly].vtx_count = 0;
|
|
pol[cpoly].binding = pbinding;
|
|
pol[cpoly].material = 0;
|
|
|
|
if (count_tag && material_tag)
|
|
{
|
|
pol[cpoly].vtx_count = (ushort)count_tag->GetInteger();
|
|
pol[cpoly].material = (ushort)material_tag->GetInteger();
|
|
|
|
NMLTagForeach(tag, *poly)
|
|
if (tag->name == _PolyIndex)
|
|
*pbinding++ = tag->GetInteger();
|
|
}
|
|
else __LOG_E__ << "Corrupt <Polygon> tag in <APolygon>.\n";
|
|
|
|
++cpoly;
|
|
}
|
|
}
|
|
else __LOG_E__ << "Could not allocate polygon/binding memory.\n";
|
|
}
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParsePolygonNormal(const Tag *pt)
|
|
{
|
|
NMLTagForeach(vt, *pt)
|
|
{
|
|
if (vt->name == "Count")
|
|
pol_normal.Allocate(vt->GetInteger());
|
|
|
|
else if (vt->name == "Data")
|
|
{
|
|
if (pol_normal)
|
|
{
|
|
char *pbuffer = (char *)vt->GetValue().GetBinaryBuffer();
|
|
|
|
for (uint m = 0; m < pol.GetCount(); m++)
|
|
{
|
|
pol_normal[m].x = (float)(pbuffer[0]) / 127.f;
|
|
pol_normal[m].y = (float)(pbuffer[1]) / 127.f;
|
|
pol_normal[m].z = (float)(pbuffer[2]) / 127.f;
|
|
pol_normal[m].Normalize();
|
|
pbuffer += 3;
|
|
}
|
|
}
|
|
else __LOG_E__ << "Incomplete polygon normal chunk loading '" << name << "'.\n";
|
|
}
|
|
}
|
|
}
|
|
void Geometry::ParseVertexNormal(const Tag *pt)
|
|
{
|
|
Tag *vt = pt->GetTag("Count;");
|
|
if (!vt)
|
|
__ERRRAW__(__LOG_E__ << "No count tag in <VNormal>.\n")
|
|
vtx_normal.Allocate(vt->GetInteger());
|
|
|
|
vt = pt->GetTag("Data;");
|
|
if (!vt)
|
|
__ERRRAW__(__LOG_E__ << "No data tag in <VNormal>.\n")
|
|
|
|
if (vtx_normal)
|
|
{
|
|
signed char *pbuffer = (signed char *)vt->GetValue().GetBinaryBuffer();
|
|
|
|
for (uint m = 0; m < vtx_normal.GetCount(); ++m)
|
|
{
|
|
vtx_normal[m].x = ((float)pbuffer[0]) / 127.f;
|
|
vtx_normal[m].y = ((float)pbuffer[1]) / 127.f;
|
|
vtx_normal[m].z = ((float)pbuffer[2]) / 127.f;
|
|
|
|
vtx_normal[m].Normalize();
|
|
pbuffer += 3;
|
|
}
|
|
}
|
|
else __LOG_E__ << "Incomplete vertex normal chunk loading '" << name << "'.\n";
|
|
}
|
|
void Geometry::ParseVertexTangent(const Tag *pt)
|
|
{
|
|
Tag *vt = pt->GetTag("Count;");
|
|
if (!vt)
|
|
__ERRRAW__(__LOG_E__ << "No count tag in <VTangent>.\n")
|
|
vtx_tangent.Allocate(vt->GetInteger());
|
|
|
|
vt = pt->GetTag("Data;");
|
|
if (!vt)
|
|
__ERRRAW__(__LOG_E__ << "No data tag in <VTangent>.\n")
|
|
|
|
// Allocate vertex normals.
|
|
if (vtx_tangent)
|
|
{
|
|
signed char *pbuffer = (signed char *)vt->GetValue().GetBinaryBuffer();
|
|
|
|
for (uint m = 0; m < vtx_tangent.GetCount(); ++m)
|
|
{
|
|
Vector4 &T = vtx_tangent[m].T, &B = vtx_tangent[m].B;
|
|
|
|
T.x = ((float)pbuffer[0]) / 127.f;
|
|
T.y = ((float)pbuffer[1]) / 127.f;
|
|
T.z = ((float)pbuffer[2]) / 127.f;
|
|
T.Normalize();
|
|
|
|
B.x = ((float)pbuffer[3]) / 127.f;
|
|
B.y = ((float)pbuffer[4]) / 127.f;
|
|
B.z = ((float)pbuffer[5]) / 127.f;
|
|
B.Normalize();
|
|
|
|
pbuffer += 6;
|
|
}
|
|
}
|
|
else __LOG_E__ << "Incomplete vertex tangent chunk loading '" << name << "'.\n";
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParseAsciiRGB(const Tag *pt)
|
|
{
|
|
Tag *vt = pt->GetTag("Count;");
|
|
if (!vt)
|
|
__ERRRAW__(__LOG_E__ << "No count tag in <ARGB>.\n")
|
|
rgb.Allocate(vt->GetInteger());
|
|
|
|
vt = pt->GetTag("Data;");
|
|
if (!vt)
|
|
__ERRRAW__(__LOG_E__ << "No data tag in <ARGB>.\n")
|
|
|
|
if (rgb)
|
|
{
|
|
uint rgb_count = 0;
|
|
|
|
NMLTagForeach(rt, *vt)
|
|
{
|
|
if (rgb_count == rgb.GetCount())
|
|
{
|
|
__LOG_E__ << "Too many <Value> tags in RGB list of geometry '" << name << "'.\n";
|
|
break;
|
|
}
|
|
Tag *r_tag = rt->GetTypedTag("R;", Variant::VariantFloat),
|
|
*g_tag = rt->GetTypedTag("G;", Variant::VariantFloat),
|
|
*b_tag = rt->GetTypedTag("B;", Variant::VariantFloat),
|
|
*a_tag = rt->GetTypedTag("A;", Variant::VariantFloat);
|
|
|
|
rgb[rgb_count].x = r_tag ? r_tag->GetReal() : 0;
|
|
rgb[rgb_count].y = g_tag ? g_tag->GetReal() : 0;
|
|
rgb[rgb_count].z = b_tag ? b_tag->GetReal() : 0;
|
|
rgb[rgb_count].w = a_tag ? a_tag->GetReal() : 1;
|
|
++rgb_count;
|
|
}
|
|
|
|
if (rgb_count != rgb.GetCount())
|
|
__LOG_W__ << "Not enough <Value> tags in RGB list of geometry '" << name << "'.\n";
|
|
}
|
|
else __LOG_E__ << "Could not allocate RGB for geometry '" << name << "'.\n";
|
|
}
|
|
void Geometry::ParseRGB(const Tag *pt)
|
|
{
|
|
if (Tag *vt = pt->GetTag("Data"))
|
|
{
|
|
float *pbuffer = (float *)vt->GetValue().GetBinaryBuffer();
|
|
size_t size = vt->GetValue().GetBinarySize();
|
|
|
|
if (rgb.Allocate(binding.GetCount()))
|
|
{
|
|
uint components = (size == binding.GetCount() * 4) ? 4 : 3;
|
|
|
|
for (uint m = 0; m < rgb.GetCount(); m++)
|
|
{
|
|
for (uint n = 0; n < components; ++n)
|
|
Endian::ToHost(&pbuffer[n], 4, Endian::Intel);
|
|
|
|
rgb[m].Set(pbuffer[0], pbuffer[1], pbuffer[2]);
|
|
rgb[m].w = components == 4 ? pbuffer[3] : 1.f;
|
|
|
|
pbuffer += components;
|
|
}
|
|
}
|
|
else __LOG_E__ << "Could not allocate RGB channel for geometry '" << name << "'.\n";
|
|
}
|
|
else __LOG_E__ << "Expected <Data> tag under <RGB> tag.\n";
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParseUV(const Tag *pt)
|
|
{
|
|
uint current_uv = 0;
|
|
|
|
NMLTagForeach(vt, *pt)
|
|
{
|
|
if (vt->name != "Data")
|
|
continue;
|
|
|
|
if (current_uv == __UV_PER_GEOMETRY__)
|
|
{
|
|
__LOG_W__ << "Geometry '" << name << "' requires more UV channels than this build can handle.\n";
|
|
__LOG_W__ << "Please increase nUVMapCount and recompile to fully import this object.\n";
|
|
break;
|
|
}
|
|
|
|
float *pbuffer = (float *)vt->GetValue().GetBinaryBuffer();
|
|
|
|
if (uv[current_uv].Allocate(binding.GetCount()))
|
|
for (uint m = 0; m < binding.GetCount(); ++m)
|
|
{
|
|
for (int n = 0; n < 2; ++n)
|
|
Endian::ToHost(&pbuffer[n], 4, Endian::Intel);
|
|
uv[current_uv][m].x = pbuffer[0];
|
|
uv[current_uv][m].y = pbuffer[1];
|
|
pbuffer += 2;
|
|
}
|
|
else
|
|
__LOG_E__ << "could not allocate UV channel for geometry '" << name << "'.\n";
|
|
|
|
++current_uv;
|
|
}
|
|
}
|
|
void Geometry::ParseAsciiUV(const Tag *pt)
|
|
{
|
|
uint current_uv = 0;
|
|
|
|
NMLTagForeach(vt, *pt)
|
|
{
|
|
if (vt->name != "Data")
|
|
continue;
|
|
|
|
if (uv[current_uv].Allocate(binding.GetCount()))
|
|
{
|
|
uint uv_index = 0;
|
|
|
|
NMLTagForeach(uvt, *vt)
|
|
{
|
|
if (uv_index == binding.GetCount())
|
|
{
|
|
__LOG_E__ << "Too many <Value> tags in UV channel " << current_uv << " of geometry '" << name << "'.\n";
|
|
break;
|
|
}
|
|
Tag *u_tag = uvt->GetTypedTag("U;", Variant::VariantFloat),
|
|
*v_tag = uvt->GetTypedTag("V;", Variant::VariantFloat);
|
|
|
|
uv[current_uv][uv_index].x = u_tag ? u_tag->GetReal() : 0;
|
|
uv[current_uv][uv_index].y = v_tag ? v_tag->GetReal() : 0;
|
|
uv_index++;
|
|
}
|
|
}
|
|
else __LOG_E__ << "could not allocate UV channel for geometry '" << name << "'.\n";
|
|
|
|
if (++current_uv == __UV_PER_GEOMETRY__)
|
|
{
|
|
__LOG_W__ << "'" << name << "' requires more UV channels than this build can handle.\n";
|
|
__LOG_W__ << " Please increase nUVMapCount and recompile nEngine to fully import this object.\n";
|
|
}
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParseMaterials(const Tag *pt)
|
|
{
|
|
// First pass, material count.
|
|
uint slot_count = 0;
|
|
NMLTagForeach(mt, *pt)
|
|
if ((mt->name == "Material") || (mt->name == "MaterialRef") || (mt->name == "MaterialRefEx"))
|
|
slot_count++;
|
|
|
|
// Allocate slots.
|
|
if (material_table.Allocate(slot_count))
|
|
{
|
|
slot_count = 0;
|
|
|
|
NMLTagForeach(mt, *pt)
|
|
{
|
|
String mref;
|
|
bool use_cache = true;
|
|
|
|
if (mt->name == "Material")
|
|
IEmbeddedResourceHandler::Get()->ExtractEmbeddedMaterial(mref, *mt, name, slot_count);
|
|
else if (mt->name == "MaterialRef")
|
|
mref = mt->GetString();
|
|
else if (mt->name == "MaterialRefEx")
|
|
{
|
|
if (Tag *t = mt->GetTypedTag("Name", Variant::VariantString))
|
|
mref = t->GetString();
|
|
if (Tag *t = mt->GetTypedTag("UseCache", Variant::VariantBool))
|
|
use_cache = t->GetBool();
|
|
}
|
|
|
|
material_table[slot_count].name = mref;
|
|
material_table[slot_count].use_cache = use_cache;
|
|
|
|
++slot_count;
|
|
}
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Geometry::ParseMisc(const Tag &tag)
|
|
{
|
|
NMLTagForeach(pt, tag)
|
|
{
|
|
if (pt->name == "LodDistance")
|
|
lod_distance = pt->GetReal();
|
|
else if (pt->name == "LodNull")
|
|
flag.Raise(FlagNullLodProxy, pt->GetBool());
|
|
|
|
else if (pt->name == "ShadowNull")
|
|
flag.Raise(FlagNullShadowProxy, pt->GetBool());
|
|
|
|
else if (pt->name == "CopyLock")
|
|
copy_lock = pt->GetString();
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Geometry::FromMetaTag(const Tag &tag)
|
|
{
|
|
Benchmark bench(true);
|
|
|
|
// Build binary objects base name.
|
|
if (tag.name != "Geometry")
|
|
__ERR__(__LOG_E__ << "Could not parse material, incorrect root tag (" << tag.name << ")...\n", false)
|
|
Free();
|
|
|
|
// Parse root tags.
|
|
Tag *pt;
|
|
|
|
if ((pt = tag.GetTag("Materials")) != NULL)
|
|
ParseMaterials(pt);
|
|
|
|
if ((pt = tag.GetTag("Vertex")) != NULL)
|
|
ParseVertex(pt);
|
|
else
|
|
if ((pt = tag.GetTag("AVertex")) != NULL)
|
|
ParseAsciiVertex(pt);
|
|
|
|
if ((pt = tag.GetTag("Polygon")) != NULL)
|
|
ParsePolygon(pt);
|
|
else
|
|
if ((pt = tag.GetTag("APolygon")) != NULL)
|
|
ParseAsciiPolygon(pt);
|
|
|
|
if ((pt = tag.GetTag("PNormal")) != NULL)
|
|
ParsePolygonNormal(pt);
|
|
|
|
if ((pt = tag.GetTag("VNormal")) != NULL)
|
|
ParseVertexNormal(pt);
|
|
|
|
if ((pt = tag.GetTag("VTangent")) != NULL)
|
|
ParseVertexTangent(pt);
|
|
|
|
if ((pt = tag.GetTag("Skin")) != NULL)
|
|
ParseSkin(pt);
|
|
|
|
if ((pt = tag.GetTag("RGB")) != NULL)
|
|
ParseRGB(pt);
|
|
else
|
|
if ((pt = tag.GetTag("ARGB")) != NULL)
|
|
ParseAsciiRGB(pt);
|
|
|
|
if ((pt = tag.GetTag("UV")) != NULL)
|
|
ParseUV(pt);
|
|
else
|
|
if ((pt = tag.GetTag("AUV")) != NULL)
|
|
ParseAsciiUV(pt);
|
|
|
|
if ((pt = tag.GetTag("LodProxy")) != NULL)
|
|
lod_proxy = pt->GetString();
|
|
|
|
if ((pt = tag.GetTag("ShadowProxy")) != NULL)
|
|
shadow_proxy = pt->GetString();
|
|
|
|
ParseMisc(tag);
|
|
|
|
// Quick integrity check.
|
|
if (!vtx.GetCount())
|
|
__ERR__(__LOG_E__ << "No vertice in geometry '" << name << "'.\n", false)
|
|
if (!pol.GetCount())
|
|
__ERR__(__LOG_E__ << "No polygon in geometry '" << name << "'.\n", false)
|
|
|
|
if (vtx_tangent.IsNull())
|
|
ComputeVertexTangent();
|
|
|
|
bench.Stop();
|
|
__LOG__ << "Geometry::FromMetaTag(Tag &tag) done in " << bench.GetMs() << "ms. " << vtx.GetCount() << " vertice, " << pol.GetCount() << " polygon(s), " << material_table.GetCount() << " material(s).\n";
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Tag *Geometry::AsMetaTag() const
|
|
{
|
|
// Integrity check.
|
|
if (!vtx.GetCount() || !pol.GetCount() || !material_table.GetCount())
|
|
__ERR__(__LOG_E__ << "Cannot serialize '" << name << "', geometry is empty.\n", NULL)
|
|
|
|
Tag *root = new Tag("Geometry");
|
|
if (!root)
|
|
__ERR__(__LOG_E__ << "Could not create root tag to serialize geometry '" << name << "'.\n", NULL)
|
|
|
|
uint n;
|
|
|
|
// Vertice.
|
|
if (Tag *vertex = root->AddChild("Vertex"))
|
|
{
|
|
vertex->AddChild("Count", (int)vtx.GetCount());
|
|
|
|
if (float *vtx_rl = new float[vtx.GetCount() * 3])
|
|
{
|
|
for (n = 0; n < vtx.GetCount(); n++)
|
|
{
|
|
vtx_rl[n * 3 + 0] = vtx[n].x;
|
|
vtx_rl[n * 3 + 1] = vtx[n].y;
|
|
vtx_rl[n * 3 + 2] = vtx[n].z;
|
|
}
|
|
vertex->AddChild("Data", (uchar *)vtx_rl, vtx.GetCount() * 3 * sizeof(float));
|
|
_safe_delete_array(vtx_rl);
|
|
}
|
|
else
|
|
__LOG_E__ << "Failed to allocate raw vertice buffer.\n";
|
|
}
|
|
|
|
// Polygons.
|
|
if (Tag *polygon = root->AddChild("Polygon"))
|
|
{
|
|
polygon->AddChild("Count", (int)pol.GetCount());
|
|
polygon->AddChild("BindingCount", (int)binding.GetCount());
|
|
|
|
if (uint *pol_u4 = new uint[binding.GetCount() + pol.GetCount() * 2])
|
|
{
|
|
uint *ptr = pol_u4;
|
|
for (n = 0; n < pol.GetCount(); n++)
|
|
{
|
|
*ptr++ = pol[n].vtx_count;
|
|
for (uint m = 0; m < pol[n].vtx_count; m++)
|
|
*ptr++ = pol[n].binding[m];
|
|
*ptr++ = (uint)pol[n].material;
|
|
}
|
|
polygon->AddChild("Data", (uchar *)pol_u4, (binding.GetCount() + pol.GetCount() * 2) * sizeof(uint));
|
|
_safe_delete_array(pol_u4);
|
|
}
|
|
else
|
|
__LOG_E__ << "Failed to allocate raw polygon buffer.\n";
|
|
}
|
|
|
|
// Polygon normals.
|
|
if (pol_normal)
|
|
if (Tag *pnormal = root->AddChild("PNormal"))
|
|
{
|
|
pnormal->AddChild("Count", (int)pol.GetCount());
|
|
|
|
if (char *pnrm = new char[pol.GetCount() * 3])
|
|
{
|
|
char *ptr = pnrm;
|
|
for (n = 0; n < pol.GetCount(); n++)
|
|
{
|
|
*ptr++ = (char)(pol_normal[n].x * 127.f);
|
|
*ptr++ = (char)(pol_normal[n].y * 127.f);
|
|
*ptr++ = (char)(pol_normal[n].z * 127.f);
|
|
}
|
|
pnormal->AddChild("Data", (uchar *)pnrm, pol.GetCount() * 3);
|
|
_safe_delete_array(pnrm);
|
|
}
|
|
else
|
|
__LOG_E__ << "Failed to allocate raw normal buffer.\n";
|
|
}
|
|
|
|
// Vertex normals.
|
|
if (vtx_normal)
|
|
if (Tag *vnormal = root->AddChild("VNormal"))
|
|
{
|
|
vnormal->AddChild("Count", (int)binding.GetCount());
|
|
|
|
if (char *vnrm = new char[binding.GetCount() * 3])
|
|
{
|
|
char *ptr = vnrm;
|
|
for (n = 0; n < binding.GetCount(); n++)
|
|
{
|
|
*ptr++ = (char)(vtx_normal[n].x * 127.f);
|
|
*ptr++ = (char)(vtx_normal[n].y * 127.f);
|
|
*ptr++ = (char)(vtx_normal[n].z * 127.f);
|
|
}
|
|
vnormal->AddChild("Data", (uchar *)vnrm, binding.GetCount() * 3);
|
|
_safe_delete_array(vnrm);
|
|
}
|
|
else
|
|
__LOG_E__ << "Failed to allocate raw normal buffer.\n";
|
|
}
|
|
|
|
// Vertex tangent frames.
|
|
if (vtx_tangent)
|
|
if (Tag *vtangent = root->AddChild("VTangent"))
|
|
{
|
|
vtangent->AddChild("Count", (int)binding.GetCount());
|
|
|
|
if (char *vfrm = new char[binding.GetCount() * 6])
|
|
{
|
|
char *ptr = vfrm;
|
|
for (n = 0; n < binding.GetCount(); n++)
|
|
{
|
|
*ptr++ = (char)(vtx_tangent[n].T.x * 127.f);
|
|
*ptr++ = (char)(vtx_tangent[n].T.y * 127.f);
|
|
*ptr++ = (char)(vtx_tangent[n].T.z * 127.f);
|
|
*ptr++ = (char)(vtx_tangent[n].B.x * 127.f);
|
|
*ptr++ = (char)(vtx_tangent[n].B.y * 127.f);
|
|
*ptr++ = (char)(vtx_tangent[n].B.z * 127.f);
|
|
}
|
|
vtangent->AddChild("Data", (uchar *)vfrm, binding.GetCount() * 6);
|
|
_safe_delete_array(vfrm);
|
|
}
|
|
else
|
|
__LOG_E__ << "Failed to allocate raw tangent frame buffer.\n";
|
|
}
|
|
|
|
// Output RGB.
|
|
if (rgb)
|
|
if (Tag *rgbtag = root->AddChild("RGB"))
|
|
{
|
|
// Test if we need to output alpha.
|
|
uint num_comp = 3;
|
|
for (uint m = 0; m < binding.GetCount(); ++m)
|
|
if (rgb[m].w < 1.f)
|
|
{
|
|
num_comp = 4;
|
|
break;
|
|
}
|
|
|
|
// Output buffer.
|
|
if (float *rgbbuf = new float[binding.GetCount() * num_comp])
|
|
{
|
|
float *prgb = rgbbuf;
|
|
for (uint m = 0; m < binding.GetCount(); ++m)
|
|
{
|
|
prgb[0] = rgb[m].x;
|
|
prgb[1] = rgb[m].y;
|
|
prgb[2] = rgb[m].z;
|
|
if (num_comp == 4)
|
|
prgb[3] = rgb[m].w;
|
|
prgb += num_comp;
|
|
}
|
|
rgbtag->AddChild("Data", (uchar *)rgbbuf, binding.GetCount() * num_comp * sizeof(float));
|
|
_safe_delete_array(rgbbuf);
|
|
}
|
|
}
|
|
|
|
// Output UVs.
|
|
if (GetUVCount())
|
|
if (Tag *uvtag = root->AddChild("UV"))
|
|
{
|
|
uvtag->AddChild("Count", (int)GetUVCount()); // LEGACY
|
|
|
|
if (float *uvbuf = new float[binding.GetCount() * 2])
|
|
{
|
|
for (n = 0; n < __UV_PER_GEOMETRY__; ++n)
|
|
if (uv[n])
|
|
{
|
|
for (uint m = 0; m < binding.GetCount(); m++)
|
|
{
|
|
uvbuf[m * 2 + 0] = uv[n][m].x;
|
|
uvbuf[m * 2 + 1] = uv[n][m].y;
|
|
}
|
|
uvtag->AddChild("Data", (uchar *)uvbuf, binding.GetCount() * 2 * sizeof(float));
|
|
}
|
|
_safe_delete_array(uvbuf);
|
|
}
|
|
else
|
|
__LOG_E__ << "Failed to allocate raw UVs buffer.\n";
|
|
}
|
|
|
|
// Output vertex weights.
|
|
if (bone_name.GetCount())
|
|
{
|
|
Tag *skintag = root->AddChild("Skin");
|
|
|
|
// Save bone id list.
|
|
if (Tag *bonetag = skintag->AddChild("Bones"))
|
|
for (uint n = 0; n < bone_name.GetCount(); ++n)
|
|
bonetag->AddChild("Id", bone_name[n]);
|
|
else
|
|
__LOG_E__ << "Failed to allocate bone id tag.\n";
|
|
|
|
// Save bone binding matrix.
|
|
if (Tag *bindtag = skintag->AddChild("Binds"))
|
|
for (uint n = 0; n < bone_name.GetCount(); ++n)
|
|
bindtag->AddChild(bone_bind_matrix[n].AsMetaTag("Matrix"));
|
|
else
|
|
__LOG_E__ << "Failed to allocate bone binding tag.\n";
|
|
|
|
// Save bone association/weights.
|
|
Tag *weighttag = skintag->AddChild("Weights");
|
|
for (uint n = 0; n < vtx.GetCount(); ++n)
|
|
weighttag->AddChild
|
|
(
|
|
"W", String::Format
|
|
( "%d,%d,%d,%d:%d,%d,%d,%d",
|
|
skin[n].bone_index[0], skin[n].bone_index[1], skin[n].bone_index[2], skin[n].bone_index[3],
|
|
(int)(skin[n].w[0] * 255.f), (int)(skin[n].w[1] * 255.f), (int)(skin[n].w[2] * 255.f), (int)(skin[n].w[3] * 255.f)).toUtf8()
|
|
);
|
|
}
|
|
|
|
// Proxies.
|
|
if (!lod_proxy.IsEmpty())
|
|
root->AddChild("LodProxy", lod_proxy.c_str());
|
|
if (lod_distance != Units::Mtr(100.f))
|
|
root->AddChild("LodDistance", lod_distance);
|
|
if (flag.IsSet(FlagNullLodProxy))
|
|
root->AddChild("LodNull", true);
|
|
if (!shadow_proxy.IsEmpty())
|
|
root->AddChild("ShadowProxy", shadow_proxy.c_str());
|
|
if (flag.IsSet(FlagNullShadowProxy))
|
|
root->AddChild("ShadowNull", true);
|
|
|
|
// Serialize materials.
|
|
if (Tag *mtag = root->AddChild("Materials"))
|
|
for (n = 0; n < material_table.GetCount(); n++)
|
|
if (Tag *extag = mtag->AddChild("MaterialRefEx"))
|
|
{
|
|
extag->AddChild("Name", material_table[n].name.c_str());
|
|
if (!material_table[n].use_cache)
|
|
extag->AddChild("UseCache", material_table[n].use_cache);
|
|
}
|
|
|
|
// Copyright lock.
|
|
if (!copy_lock.IsEmpty())
|
|
root->AddChild("CopyLock", copy_lock);
|
|
|
|
return root;
|
|
}
|
|
//------------------------------------------------------------------------------
|