/* ----------------------------------------------------------------------------- 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 &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) {}