450 lines
12 KiB
C++
450 lines
12 KiB
C++
/* -----------------------------------------------------------------------------
|
|
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 <Vector4> &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 <MinMax> &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 <bool> 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 <uint> &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 <VertexToPolygon> &vtx_to_pol) const
|
|
{
|
|
if (!pol || !vtx)
|
|
return;
|
|
|
|
Array <uint> 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 <VertexToVertex> &vtx_to_vtx, const Array <VertexToPolygon> *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 <VertexToPolygon> _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 <bool> &flag, const Array <uint> &pol_index, const Array <VertexToPolygon> &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 <bool> drop(material_table.GetCount());
|
|
for (uint n = 0; n < material_table.GetCount(); ++n)
|
|
drop[n] = false;
|
|
|
|
Array <uint> 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 <Geometry::MaterialSlot> 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();
|
|
}
|
|
//------------------------------------------------------------------------------
|