214 lines
5.9 KiB
C++
214 lines
5.9 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "core/object.h"
|
|
#include "core/camera.h"
|
|
#include "core/geometry.h"
|
|
#include "core/resource_factories.h"
|
|
#include "core/render_resource_factory.h"
|
|
#include "timing/benchmark.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::Core;
|
|
|
|
float Object::lod_bias = 0.f;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Object::RenderSetup(ResourceFactories *f)
|
|
{
|
|
render_data = new RenderData;
|
|
|
|
if (f && f->render)
|
|
{
|
|
render_data->geometry = f->render->LoadGeometry(geometry, !cache_geometry);
|
|
|
|
if (render_data->geometry.IsValid())
|
|
{
|
|
// Assert skin correctness.
|
|
uint bone_count = render_data->geometry->bone_bind_matrix.GetCount();
|
|
|
|
if (bone_count > 0)
|
|
{
|
|
if (skin.IsNull() || (skin->bones_mtx.GetCount() != bone_count))
|
|
{
|
|
AllocateSkin(render_data->geometry->bone_bind_matrix.GetCount());
|
|
|
|
// Initialize skin to the binding position.
|
|
for (uint n = 0; n < render_data->geometry->bone_bind_matrix.GetCount(); ++n)
|
|
skin->previous_bones_mtx[n] = skin->bones_mtx[n] = render_data->geometry->bone_bind_matrix[n];
|
|
}
|
|
}
|
|
else
|
|
FreeSkin();
|
|
}
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
static float GetGeometrySortValue(const Matrix4 &vm, Render::Geometry *g, const Matrix4 &gm)
|
|
{ return vm.GetRow(2).Dot((g->hotspot * gm) - vm.GetRow(3)); }
|
|
|
|
void Object::ComputeRenderableMinMax(MinMax &mm)
|
|
{
|
|
ComputeLocalMinMax(mm);
|
|
OBB obb = OBB::FromMinMax(mm);
|
|
obb.Transform(GetMatrix());
|
|
obb.ComputeMinMax(mm);
|
|
}
|
|
uint Object::GetRenderablePrimitiveList(const Camera &view, const Camera &default_view, Stack <Render::Primitive *> &list, Renderable::Context context, bool cull)
|
|
{
|
|
Render::Geometry *g = render_data.IsValid() ? render_data->geometry.c_ptr() : NULL;
|
|
if (!g)
|
|
return 1;
|
|
|
|
// Recursive LOD selection.
|
|
{
|
|
float d2 = Vector4::Dist2(default_view.GetMatrix().GetRow(3), GetMatrix().GetRow(3)) + lod_bias * lod_bias;
|
|
|
|
d2 /= GetScale().Len2(); // [EJ] scale affects lod selection
|
|
|
|
for ( ; g; g = g->lod_proxy)
|
|
{
|
|
// If closer than lod, stop there.
|
|
if (d2 < (g->lod_distance * g->lod_distance))
|
|
break;
|
|
|
|
// Null lod.
|
|
if (g->flag.IsSet(Geometry::FlagNullLodProxy))
|
|
return 1;
|
|
|
|
// No lod to follow, stop there.
|
|
if (g->lod_proxy.IsNull())
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Check context proxy on the selected lod.
|
|
if (context == Renderable::Context_Shadow)
|
|
{
|
|
// Null proxy.
|
|
if (g->flag.IsSet(Geometry::FlagNullShadowProxy))
|
|
return 1;
|
|
|
|
if (g->shadow_proxy.IsValid())
|
|
g = g->shadow_proxy;
|
|
}
|
|
|
|
// Test geometry hidden flag.
|
|
if (g->flag.IsSet(Geometry::FlagHidden))
|
|
return 1;
|
|
|
|
// Culling.
|
|
if (cull)
|
|
{
|
|
MinMax minmax;
|
|
ComputeLocalMinMax(minmax);
|
|
|
|
if (view.frustum.ClassifyMinMax(minmax, &GetMatrix()) == Frustum::Outside)
|
|
return 1;
|
|
}
|
|
|
|
list.Push(new Render::Primitive(g, this, GetGeometrySortValue(view.GetMatrix(), g, GetMatrix())));
|
|
|
|
return 1;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Object::GetBindMatrix(uint n, Matrix4 &m) const
|
|
{
|
|
if (render_data.IsNull() || render_data->geometry.IsNull())
|
|
return false;
|
|
if (n >= render_data->geometry->bone_bind_matrix.GetCount())
|
|
return false;
|
|
|
|
m = render_data->geometry->bone_bind_matrix[n];
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Object::ComputeLocalMinMax(MinMax &minmax) const
|
|
{
|
|
if (render_data.IsNull() || render_data->geometry.IsNull())
|
|
Item::ComputeLocalMinMax(minmax);
|
|
|
|
else
|
|
{
|
|
if (HasSkin())
|
|
{
|
|
// Compute skin minmax.
|
|
OBB obb;
|
|
for (uint n = 0; n < GetBoneCount(); ++n)
|
|
if (skin->ComputeBoneBoundingVolume(n, obb))
|
|
{
|
|
MinMax bmm;
|
|
obb.ComputeMinMax(bmm);
|
|
if (n)
|
|
minmax.Grow(bmm);
|
|
else minmax = bmm;
|
|
}
|
|
}
|
|
else
|
|
minmax = render_data->geometry->minmax;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Object::UpdateSkin()
|
|
{
|
|
if (render_data.IsValid() && render_data->geometry.IsValid())
|
|
for (uint n = 0; n < GetBoneCount(); ++n)
|
|
if (Item *bone = GetBone(n))
|
|
{
|
|
skin->previous_bones_mtx[n] = GetInverseMatrix() * (bone->GetPreviousMatrix() * render_data->geometry->bone_bind_matrix[n]);
|
|
skin->bones_mtx[n] = GetInverseMatrix() * (bone->GetMatrix() * render_data->geometry->bone_bind_matrix[n]);
|
|
}
|
|
else
|
|
{
|
|
skin->previous_bones_mtx[n] = Matrix4::IdentityMatrix();
|
|
skin->bones_mtx[n] = Matrix4::IdentityMatrix();
|
|
}
|
|
}
|
|
bool Object::BindBone(uint n, Item *bone)
|
|
{
|
|
if (skin.IsNull() || (n >= skin->bones.GetCount()))
|
|
return false;
|
|
|
|
skin->bones[n] = bone;
|
|
if (bone)
|
|
bone->item_flags.Set(ItemFlagBoneHint);
|
|
return true;
|
|
}
|
|
bool Object::AllocateSkin(uint bone_count)
|
|
{
|
|
skin = new Skin;
|
|
|
|
if (!skin->bones.Allocate(bone_count) || !skin->bones_mtx.Allocate(bone_count) || !skin->previous_bones_mtx.Allocate(bone_count) || !skin->bones_minmax.Allocate(bone_count))
|
|
{
|
|
FreeSkin();
|
|
__ERR__(__LOG_E__ << "Failed to allocate skinning structure.\n", false);
|
|
}
|
|
for (uint n = 0; n < bone_count; ++n)
|
|
{
|
|
skin->bones[n] = NULL;
|
|
skin->bones_mtx[n] = Matrix4::IdentityMatrix();
|
|
skin->previous_bones_mtx[n] = Matrix4::IdentityMatrix();
|
|
}
|
|
return true;
|
|
}
|
|
void Object::FreeSkin()
|
|
{
|
|
skin = NULL;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
Object::Object() : cache_geometry(true) {}
|