/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "core/geometry_template.h" #include "sort/sort.h" #include "log/log.h" using namespace GS; using namespace GS::Core; //------------------------------------------------------------------------------ void GeometryTemplate::ClearMaterials() { materials.Clear(); } void GeometryTemplate::PushMaterial(const char *uri) { materials.Add(uri); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ #define __AssertPolygon if (!polygon) __ERRRAW__(__LOG_E__ << "You must begin a polygon before pushing attributes.\n") //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void GeometryTemplate::BeginPolygon() { polygon = new Polygon; } void GeometryTemplate::PushVertex(const Vector4 &v) { __AssertPolygon polygon->vertex.Add(v); } void GeometryTemplate::PushNormal(const Vector4 &n) { __AssertPolygon polygon->normal.Add(n); } void GeometryTemplate::PushColor(const Color &c) { __AssertPolygon polygon->color.Add(c); } void GeometryTemplate::PushUV(uint channel, const Vector2 &uv) { __AssertPolygon polygon->uv[channel].Add(uv); } void GeometryTemplate::EndPolygon(ushort material) { if (polygon) { polygon->material = material; polygons.Add(polygon.Detach()); } else __LOG_E__ << "You must begin a polygon before ending it.\n"; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Vector4 GeometryTemplate::GetVertex(const PolygonVertex &pv) { return polygons[pv.ipoly]->vertex[pv.ivertex]; } Vector4 GeometryTemplate::GetNormal(const PolygonVertex &pv) { return polygons[pv.ipoly]->normal[pv.ivertex]; } Vector2 GeometryTemplate::GetUV(uint channel, const PolygonVertex &pv) { return polygons[pv.ipoly]->uv[channel][pv.ivertex]; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Geometry *GeometryTemplate::Instantiate(const char *name) { if (!materials.GetCount()) __ERR__(__LOG_E__ << "Cannot instantiate a geometry with no material array.\n", NULL) AutoPtr geo(new Geometry); if (geo.IsNull()) __ERR__(__LOG_E__ << "Geometry allocation error.\n", NULL) geo->name = name; // Build vertex merge table. uint vtx_per_poly_count = 0; ListForeachPtr(Polygon *, p, polygons) vtx_per_poly_count += p->vertex.GetCount(); typedef GS::Sort SortVPP; Array raw_v(vtx_per_poly_count); Array qs_e(vtx_per_poly_count); vtx_per_poly_count = 0; uint ipoly = 0; Vector4 &support = polygons[0]->vertex[0]; ListForeachPtr(Polygon *, p, polygons) { for (uint ivertex = 0; ivertex < p->vertex.GetCount(); ++ivertex) { // Track this vertex. raw_v[vtx_per_poly_count].ipoly = ipoly; raw_v[vtx_per_poly_count].ivertex = ivertex; // Quick-sort entry. qs_e[vtx_per_poly_count].o = vtx_per_poly_count; qs_e[vtx_per_poly_count].v = Vector4::Dist2(GetVertex(raw_v[vtx_per_poly_count]), support); ++vtx_per_poly_count; } ++ipoly; } // Compute polygon start offsets. Array poly_offset(polygons.GetCount()); int poly_binding_start_offset = 0; for (uint n = 0; n < polygons.GetCount(); ++n) { poly_offset[n] = poly_binding_start_offset; poly_binding_start_offset += polygons[n]->vertex.GetCount(); } // Count merged vertex. SortVPP::QuickSort(vtx_per_poly_count, qs_e); geo->binding.Allocate(vtx_per_poly_count); uint packed_vtx_count = 0; for (uint n = 0; n < vtx_per_poly_count; ) { PolygonVertex &pv = raw_v[qs_e[n].o]; geo->binding[poly_offset[pv.ipoly] + pv.ivertex] = packed_vtx_count; uint m = n + 1; for (; m < vtx_per_poly_count; ++m) { if (Vector4::Dist2(GetVertex(raw_v[qs_e[n].o]), GetVertex(raw_v[qs_e[m].o])) > merge_threshold) break; PolygonVertex &pv = raw_v[qs_e[m].o]; geo->binding[poly_offset[pv.ipoly] + pv.ivertex] = packed_vtx_count; } ++packed_vtx_count; n = m; } // Pack vertices. geo->vtx.Allocate(packed_vtx_count); packed_vtx_count = 0; for (uint n = 0; n < vtx_per_poly_count; ) { uint m = n + 1; for (; m < vtx_per_poly_count; ++m) if (Vector4::Dist2(GetVertex(raw_v[qs_e[n].o]), GetVertex(raw_v[qs_e[m].o])) > merge_threshold) break; geo->vtx[packed_vtx_count++] = GetVertex(raw_v[qs_e[n].o]); n = m; } // Build polygons. geo->pol.Allocate(polygons.GetCount()); uint pol_count = 0, pol_bind = 0; ListForeachPtr(Polygon *, p, polygons) { if (p->vertex.GetCount() > 65535) __LOG_W__ << "Too many vertices in polygon " << pol_count << ".\n"; geo->pol[pol_count].vtx_count = (ushort)p->vertex.GetCount(); geo->pol[pol_count].binding = &geo->binding[pol_bind]; pol_bind += p->vertex.GetCount(); if (p->material > materials.GetCount()) { p->material = 0; __LOG_W__ << "Polygon " << pol_count << " is referencing a material outside of the material table.\n"; } geo->pol[pol_count].material = p->material; ++pol_count; } // Output remaining attributes. Polygon *poly = polygons[0]; bool has_normal = asbool(poly->normal.GetCount()); bool has_color = asbool(poly->color.GetCount()); bool has_uv[__UV_PER_GEOMETRY__]; for (uint n = 0; n < __UV_PER_GEOMETRY__; ++n) has_uv[n] = asbool(poly->uv[n].GetCount()); //-------------------------------------------------------------------------- #define __CreateAttribute(__Destination, __Attrib)\ {\ if (__Destination.Allocate(vtx_per_poly_count))\ {\ uint cpol = 0, cvpl = 0;\ ListForeachPtr(Polygon *, p, polygons)\ {\ if (p->vertex.GetCount() != __Attrib.GetCount())\ __LOG_W__ << "Incoherent normal count in polygon " << cpol << ".\n";\ else\ for (uint n = 0; n < p->vertex.GetCount(); ++n)\ __Destination[cvpl + n] = __Attrib[n];\ \ cvpl += p->vertex.GetCount();\ ++cpol;\ }\ }\ else\ __LOG_E__ << "Failed to allocate attribute array while creating geometry '" << name << "'.\n";\ } //-------------------------------------------------------------------------- if (has_normal) __CreateAttribute(geo->vtx_normal, p->normal) if (has_color) __CreateAttribute(geo->rgb, p->color) for (uint i_uv = 0; i_uv < __UV_PER_GEOMETRY__; ++i_uv) if (has_uv[i_uv]) __CreateAttribute(geo->uv[i_uv], p->uv[i_uv]) if (!geo->material_table.Allocate(materials.GetCount())) __ERR__(__LOG_E__ << "Failed to allocate material array when creating geometry '" << name << "'.\n", NULL) uint slot_count = 0; ListForeach(String, n, materials) geo->material_table[slot_count++].name = n.Object(); return geo.Detach(); } void GeometryTemplate::Clear() { polygon = NULL; polygons.Clear(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ GeometryTemplate::GeometryTemplate() { merge_threshold = 0.0001f; } //------------------------------------------------------------------------------