/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "core/geometry.h" #include "math/matrix4.h" #include "log/log.h" using namespace GS; using namespace GS::Core; static const float HomogeneousDistance = Units::Mm(0.01f); //------------------------------------------------------------------------------ bool Core::ComputeVertexArrayMinMax(const Array &vtx, MinMax &mm, const Matrix4 *mtx) { if (!vtx.GetCount()) return false; Vector4 tvt = mtx ? vtx[0] * mtx[0] : vtx[0], mn = tvt, mx = tvt; if (mtx) for (uint n = 0; n < vtx.GetCount(); ++n) { tvt = vtx[n] * mtx[0]; if (tvt.x > mx.x) mx.x = tvt.x; if (tvt.y > mx.y) mx.y = tvt.y; if (tvt.z > mx.z) mx.z = tvt.z; if (tvt.x < mn.x) mn.x = tvt.x; if (tvt.y < mn.y) mn.y = tvt.y; if (tvt.z < mn.z) mn.z = tvt.z; } else for (uint n = 0; n < vtx.GetCount(); ++n) { tvt = vtx[n]; if (tvt.x > mx.x) mx.x = tvt.x; if (tvt.y > mx.y) mx.y = tvt.y; if (tvt.z > mx.z) mx.z = tvt.z; if (tvt.x < mn.x) mn.x = tvt.x; if (tvt.y < mn.y) mn.y = tvt.y; if (tvt.z < mn.z) mn.z = tvt.z; } mm.mn = mn; mm.mx = mx; return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint Geometry::GetUVCount() const { uint c = 0; for (uint n = 0; n < __UV_PER_GEOMETRY__; ++n) if (uv[n]) ++c; return c; } uint Geometry::GetBoneCount() const { return bone_name.GetCount(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Geometry::ComputeBoneBoundingVolumes(Array &bone_mm) const { uint bone_count = bone_bind_matrix.GetCount(); if (skin.IsNull() || !bone_count) return false; if (!bone_mm.Allocate(bone_count)) __ERR__(__LOG_E__ << "Failed to allocate bon minmax array.\n", false) Array bone_mm_init(bone_count); for (uint n = 0; n < bone_count; ++n) bone_mm_init[n] = false; for (uint v = 0; v < vtx.GetCount(); ++v) for (int b = 0; b < __PV_BONE_LIMIT__; ++b) if (skin[v].w[b] > 0.f) { int idx = skin[v].bone_index[b]; if (!bone_mm_init[idx]) { bone_mm[idx].Set(vtx[v], vtx[v]); bone_mm_init[idx] = true; } else bone_mm[idx].Grow(vtx[v]); } return true; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint Geometry::ComputePolygonBindingCount() const { uint c = 0; for (uint n = 0; n < pol.GetCount(); ++n) c += pol[n].vtx_count; return c; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ MinMax Geometry::ComputeMinMax(const Matrix4 *mtx) const { MinMax mm; ComputeVertexArrayMinMax(vtx, mm, mtx); return mm; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool Geometry::AllocateVertex(uint count) { return vtx.Allocate(count); } bool Geometry::AllocatePolygon(uint count) { binding.Free(); return pol.Allocate(count); } bool Geometry::AllocatePolygonBinding() { uint count = ComputePolygonBindingCount(); if (!binding.Allocate(count)) return false; count = 0; for (uint n = 0; n < pol.GetCount(); ++n) { pol[n].binding = &binding[count]; count += pol[n].vtx_count; } return true; } bool Geometry::AllocateBone(uint count) { return bone_name.Allocate(count) && bone_bind_matrix.Allocate(count); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Geometry::ComputePolygonIndex(Array &i) const { if (i.Allocate(pol.GetCount())) for (uint pc = 0, ci = 0; pc < pol.GetCount(); ++pc) { i[pc] = ci; ci += pol[pc].vtx_count; } } void Geometry::ComputeVertexToPolygon(Array &vtx_to_pol) const { if (!pol || !vtx) return; Array pol_per_vtx(vtx.GetCount()); if (!pol_per_vtx) return; Memory::Set(&pol_per_vtx[0], 0, pol_per_vtx.GetSize()); uint p, v; for (p = 0; p < pol.GetCount(); ++p) for (v = 0; v < pol[p].vtx_count; ++v) pol_per_vtx[pol[p].binding[v]]++; vtx_to_pol.Allocate(vtx.GetCount()); for (v = 0; v < vtx.GetCount(); ++v) { vtx_to_pol[v].pol_count = 0; vtx_to_pol[v].pol_index.Allocate(pol_per_vtx[v]); } for (p = 0; p < pol.GetCount(); ++p) for (v = 0; v < pol[p].vtx_count; ++v) vtx_to_pol[pol[p].binding[v]].pol_index[vtx_to_pol[pol[p].binding[v]].pol_count++] = p; } void Geometry::ComputeVertexToVertex(Array &vtx_to_vtx, const Array *vtx_to_pol) const { if (!pol || !vtx) return; // Allocate vertex to vertex buffer. if (!vtx_to_vtx.Allocate(vtx.GetCount())) __ERRRAW__(__LOG_E__ << "Could not allocate memory.\n") // Allocate work area. #define __VertexToVertexTempListSize 1024 PolygonVertex tmp_vtx_to_vtx[__VertexToVertexTempListSize]; // Compute vertex to polygon if not provided. Array _vtx_to_pol; if (!vtx_to_pol) { vtx_to_pol = &_vtx_to_pol; ComputeVertexToPolygon(_vtx_to_pol); } for (int pass = 0; pass < 2; ++pass) for (uint v = 0; v < vtx.GetCount(); ++v) { vtx_to_vtx[v].vtx_count = 0; uint vtx_vtx_count = 0; for (uint p = 0; p < (*vtx_to_pol)[v].pol_count; ++p) { uint pol_index = (*vtx_to_pol)[v].pol_index[p]; Polygon *poly = &pol[pol_index]; int ci; for (ci = 0; ci < poly->vtx_count; ++ci) if (poly->binding[ci] == v) break; for (int _c = (ci - 1); _c <= (ci + 1); _c += 2) { int vtx_index = _c; if (vtx_index < 0) vtx_index += poly->vtx_count; if (vtx_index >= poly->vtx_count) vtx_index -= poly->vtx_count; // Invalidate already registered candidate. bool insert = true; for (uint nl = 0; nl < vtx_vtx_count; ++nl) if ((tmp_vtx_to_vtx[nl].pol_index == pol_index) && (tmp_vtx_to_vtx[nl].vtx_index == (uint)vtx_index)) { insert = false; break; } if (insert) { tmp_vtx_to_vtx[vtx_vtx_count].pol_index = pol_index; tmp_vtx_to_vtx[vtx_vtx_count].vtx_index = vtx_index; if (vtx_vtx_count == __VertexToVertexTempListSize) { __LOG_E__ << "Temporary list exceeded, vertex to vertex LUT corrupted.\n"; vtx_vtx_count = __VertexToVertexTempListSize - 1; } if (pass == 1) { vtx_to_vtx[v].vtx[vtx_vtx_count].pol_index = pol_index; vtx_to_vtx[v].vtx[vtx_vtx_count].vtx_index = vtx_index; } ++vtx_vtx_count; } } } // Allocate vertex container for this vertex. if (pass == 0) vtx_to_vtx[v].vtx.Allocate(vtx_vtx_count); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Geometry::FlagHomogeneousVertex(Array &flag, const Array &pol_index, const Array &vtx_to_pol, int mat) const { if (!flag.Allocate(vtx.GetCount())) __ERRRAW__(__LOG_E__ << "Could not allocate homogeneous vertex table.\n") for (uint n = 0; n < vtx.GetCount(); ++n) flag[n] = true; #if 0 // Test for early exit in case no polygon uses this material. for (uint n = 0; n < pol.GetCount(); n++) if (pol[n].material == n) break; if (n == pol.GetCount()) return; #endif for (uint m = 0; m < vtx.GetCount(); ++m) { uint ngeo = vtx_to_pol[m].pol_count; for (uint ac = 0; ac < ngeo; ++ac) for (uint bc = 0; bc < ngeo; ++bc) { uint i_ac = vtx_to_pol[m].pol_index[ac], i_bc = vtx_to_pol[m].pol_index[bc]; Polygon *apoly = &pol[i_ac], *bpoly = &pol[i_bc]; if (apoly == bpoly) continue; if (apoly->material == bpoly->material) { if ((mat == -1) || (apoly->material == (uint)mat)) { uint _u, _v; for (_u = 0; _u < apoly->vtx_count; ++_u) if ( apoly->binding[_u] == m ) break; for (_v = 0; _v < bpoly->vtx_count; ++_v) if ( bpoly->binding[_v] == m ) break; if (vtx_normal) if (Vector4::Dist2(vtx_normal[pol_index[i_ac] + _u], vtx_normal[pol_index[i_bc] + _v]) > HomogeneousDistance) flag[m] = false; /* if (vtx_tangent) if ( (nVector::Dist2(vtx_tangent[pol_index[i_ac] + _u].B, vtx_tangent[pol_index[i_bc] + _v].B) > HomogeneousDistance) || (nVector::Dist2(vtx_tangent[pol_index[i_ac] + _u].T, vtx_tangent[pol_index[i_bc] + _v].T) > HomogeneousDistance) ) vtx_homogeneous[m] = false; */ if (rgb) if (Vector4::Dist2(rgb[pol_index[i_ac] + _u], rgb[pol_index[i_bc] + _v]) > HomogeneousDistance) flag[m] = false; for (uint cuv = 0; cuv < __UV_PER_GEOMETRY__; ++cuv) if (uv[cuv]) if (Vector2::Dist2(uv[cuv][pol_index[i_ac] + _u], uv[cuv][pol_index[i_bc] + _v]) > HomogeneousDistance) flag[m] = false; } } else flag[m] = false; if (!flag[m]) goto nxth; } nxth:; } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ uint Geometry::MergeDuplicateMaterials() { if (material_table.GetCount() < 2) return 0; __LOG_H__ << "Merging materials in geometry '" << name << "'...\n"; // Build the drop table. uint old_slot_count = material_table.GetCount(); Array drop(material_table.GetCount()); for (uint n = 0; n < material_table.GetCount(); ++n) drop[n] = false; Array material_remap(material_table.GetCount()); for (uint n = 0; n < material_table.GetCount(); ++n) material_remap[n] = n; // Flag materials to drop. for (uint n = 0; n < material_table.GetCount(); ++n) { if (drop[n]) // Already dropped. continue; for (uint m = n + 1; m < material_table.GetCount(); ++m) { // Check by material name. if (material_table[n].name != material_table[m].name) goto skip_material_drop; // Drop material. drop[m] = true; material_remap[m] = n; skip_material_drop:; } } // Create the new material array. uint new_material_slot_count = 0; for (uint n = 0; n < material_table.GetCount(); ++n) if (!drop[n]) new_material_slot_count++; Array new_material_slot(new_material_slot_count); new_material_slot_count = 0; for (uint n = 0; n < material_table.GetCount(); ++n) if (!drop[n]) { for (uint m = 0; m < material_table.GetCount(); ++m) if (material_remap[m] == n) material_remap[m] = new_material_slot_count; new_material_slot[new_material_slot_count].name = material_table[n].name; new_material_slot[new_material_slot_count].use_cache = material_table[n].use_cache; ++new_material_slot_count; } material_table.Transfer(new_material_slot); // Remap polygon references. for (uint n = 0; n < pol.GetCount(); ++n) pol[n].material = (ushort)material_remap[pol[n].material]; uint merge_count = old_slot_count - material_table.GetCount(); __LOG__ << "Done, merged " << merge_count << " material slot(s).\n"; return merge_count; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Geometry::Free() { bone_name.Free(); skin.Free(); bone_bind_matrix.Free(); vtx.Free(); vtx_normal.Free(); vtx_tangent.Free(); pol_normal.Free(); pol_tangent.Free(); pol.Free(); binding.Free(); for (uint n = 0; n < __UV_PER_GEOMETRY__; n++) uv[n].Free(); rgb.Free(); material_table.Free(); lod_proxy = NULL; lod_distance = Units::Mtr(100.f); shadow_proxy = NULL; flag.Raise(FlagNullShadowProxy | FlagNullLodProxy, false); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Geometry::Geometry() { lod_distance = Units::Mtr(100.f); } Geometry::~Geometry() { Free(); } //------------------------------------------------------------------------------