/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "tools/geometry_merge.h" #include "core/geometry.h" #include "math/matrix4.h" #include "log/log.h" using namespace GS::Core; //------------------------------------------------------------------------------ Geometry *GS::Core::MergeGeometry(Geometry *geo_a, Geometry *geo_b, const Matrix4 *mtx_a, const Matrix4 *mtx_b) { if (!geo_a || !geo_b) __ERR__(__LOG_E__ << "Cannot merge NULL geometry.\n", NULL) if (!mtx_a) mtx_a = &Matrix4::IdentityMatrix(); if (!mtx_b) mtx_b = &Matrix4::IdentityMatrix(); // Allocate a new geometry. Geometry *geo_o = new Geometry; if (!geo_o) __ERR__(__LOG_E__ << "Failed to allocate merged geometry.\n", NULL) geo_o->name = geo_a->name; // Nothing fancy here, the string length would blow up on large merge. // Update data for all geometries. if (geo_a->pol_normal || geo_b->pol_normal) { geo_a->ComputePolygonNormal(); geo_b->ComputePolygonNormal(); } if (geo_a->vtx_normal || geo_b->vtx_normal) { geo_a->ComputeVertexNormal(); geo_b->ComputeVertexNormal(); } // Append vertex. if (geo_o->vtx.Allocate(geo_a->vtx.GetCount() + geo_b->vtx.GetCount())) { Vector4 *pvtx = &geo_o->vtx[0]; for (uint n = 0; n < geo_a->vtx.GetCount(); ++n) *pvtx++ = geo_a->vtx[n] * *mtx_a; for (uint n = 0; n < geo_b->vtx.GetCount(); ++n) *pvtx++ = geo_b->vtx[n] * *mtx_b; } // Append polygon. if (geo_o->binding.Allocate(geo_a->binding.GetCount() + geo_b->binding.GetCount())) { uint *pbind = &geo_o->binding[0]; for (uint n = 0; n < geo_a->binding.GetCount(); ++n) *pbind++ = geo_a->binding[n]; for (uint n = 0; n < geo_b->binding.GetCount(); ++n) *pbind++ = geo_b->binding[n] + geo_a->vtx.GetCount(); } if (geo_o->pol.Allocate(geo_a->pol.GetCount() + geo_b->pol.GetCount())) { uint total_binding = 0; Polygon *ppol = &geo_o->pol[0]; for (uint n = 0; n < geo_a->pol.GetCount(); ++n) { ppol->vtx_count = geo_a->pol[n].vtx_count; ppol->material = geo_a->pol[n].material; ppol->binding = &geo_o->binding[total_binding]; total_binding += ppol->vtx_count; ppol++; } for (uint n = 0; n < geo_b->pol.GetCount(); ++n) { ppol->vtx_count = geo_b->pol[n].vtx_count; ppol->material = (ushort)(geo_b->pol[n].material + geo_a->material_table.GetCount()); ppol->binding = &geo_o->binding[total_binding]; total_binding += ppol->vtx_count; ppol++; } } // Append polygon normal. static Vector4 default_normal(0, 0, 1); if (geo_a->pol_normal || geo_b->pol_normal) if (geo_o->pol_normal.Allocate(geo_o->pol.GetCount())) { Vector4 *ppnrm = &geo_o->pol_normal[0]; for (uint n = 0; n < geo_a->pol.GetCount(); ++n) { if (geo_a->pol_normal) { mtx_a->ApplyRotation(ppnrm, &geo_a->pol_normal[n]); *ppnrm++ = ppnrm->Normalized(); } else *ppnrm++ = default_normal; } for (uint n = 0; n < geo_b->pol.GetCount(); ++n) { if (geo_b->pol_normal) { mtx_b->ApplyRotation(ppnrm, &geo_b->pol_normal[n]); *ppnrm++ = ppnrm->Normalized(); } else *ppnrm++ = default_normal; } } // Append vertex normal. if (geo_a->vtx_normal || geo_b->vtx_normal) if (geo_o->vtx_normal.Allocate(geo_o->binding.GetCount())) { Vector4 *pvnrm = &geo_o->vtx_normal[0]; for (uint n = 0; n < geo_a->binding.GetCount(); ++n) { if (geo_a->vtx_normal) { mtx_a->ApplyRotation(pvnrm, &geo_a->vtx_normal[n]); *pvnrm++ = pvnrm->Normalized(); } else *pvnrm++ = Vector4(0, 0, 1); } for (uint n = 0; n < geo_b->binding.GetCount(); ++n) { if (geo_b->vtx_normal) { mtx_b->ApplyRotation(pvnrm, &geo_b->vtx_normal[n]); *pvnrm++ = pvnrm->Normalized(); } else *pvnrm++ = Vector4(0, 0, 1); } } // Append RGB. if (geo_a->rgb || geo_b->rgb) if (geo_o->rgb.Allocate(geo_o->binding.GetCount())) { Color *prgb = &geo_o->rgb[0]; for (uint n = 0; n < geo_a->binding.GetCount(); ++n) *prgb++ = geo_a->rgb ? geo_a->rgb[n] : Color::White; for (uint n = 0; n < geo_b->binding.GetCount(); ++n) *prgb++ = geo_b->rgb ? geo_b->rgb[n] : Color::White; } // Append all UV channels. static Vector2 default_uv(0.5, 0.5); for (uint n = 0; n < __UV_PER_GEOMETRY__; ++n) if (geo_a->uv[n] || geo_b->uv[n]) if (geo_o->uv[n].Allocate(geo_o->binding.GetCount())) { Vector2 *puv = &geo_o->uv[n][0]; if (geo_a->uv[n]) for (uint v = 0; v < geo_a->binding.GetCount(); ++v) *puv++ = geo_a->uv[n][v]; else for (uint v = 0; v < geo_a->binding.GetCount(); ++v) *puv++ = default_uv; if (geo_b->uv[n]) for (uint v = 0; v < geo_b->binding.GetCount(); ++v) *puv++ = geo_b->uv[n][v]; else for (uint v = 0; v < geo_b->binding.GetCount(); ++v) *puv++ = default_uv; } // Append all materials. geo_o->material_table.Allocate(geo_a->material_table.GetCount() + geo_b->material_table.GetCount()); Geometry::MaterialSlot *pslot = geo_o->material_table.c_ptr(); for (uint n = 0; n < geo_a->material_table.GetCount(); ++n) { pslot->name = geo_a->material_table[n].name; pslot->use_cache = geo_a->material_table[n].use_cache; ++pslot; } for (uint n = 0; n < geo_b->material_table.GetCount(); ++n) { pslot->name = geo_b->material_table[n].name; pslot->use_cache = geo_b->material_table[n].use_cache; ++pslot; } // Merge materials. geo_o->MergeDuplicateMaterials(); return geo_o; } //------------------------------------------------------------------------------