/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include #include "scene3d/scene_debug.h" #include "scene3d/scene.h" #include "scene3d/mobject.h" #include "scene3d/mlight.h" #include "scene3d/mcamera.h" #include "scene3d/mtrigger.h" #include "scene3d/mconstraint.h" #include "scene3d/instance.h" #include "scene3d/group.h" #include "physic/physic_constraint.h" #include "core/renderer_toolbox.h" #include "metafile/nml_object.h" #include "core/path_kdtree.h" using namespace GS; using namespace GS::Core; using namespace GS::S3D; using namespace GS::Units; //----------------------------------------------------------------------------- void SceneDebugger::DrawItemBoundingVolume(Renderer &render, MItem *i, uint display_flags) { if (i && (i->GetItemType() == Type_Object)) { if (display_flags & SceneDebugDisplayBoundingVolumes) { MObject *mobj = (MObject *)i; MinMax pinmax; mobj->ComputeLocalMinMax(pinmax); OBB obb = OBB::FromMinMax(pinmax); obb.Transform(i->GetBaseItem()->GetMatrix()); Color color(1, 1, 0, 0.35f); RendererToolbox::DrawOBB(render, obb, &color, Material::Blend_Alpha); } if (display_flags & SceneDebugDisplayOctreeVolumes) { /// TODO } } } void SceneDebugger::DrawItem(Renderer &render, MItem *pitem, uint display_flags, Color *draw_color) { if (!pitem) return; Color item_color = draw_color ? *draw_color : Color(0.75f, 0.75f, 0.75f); Color bone_color = item_color; bone_color.w = 0.5f; render.SetWorldMatrix(Matrix4::IdentityMatrix(), &Matrix4::IdentityMatrix()); if (pitem->GetBaseItem()->item_flags.IsSet(ItemFlagBoneHint) && pitem->GetBaseItem()->GetParent()) { if (display_flags & SceneDebugDisplayBones) { Item *link = pitem->GetBaseItem()->GetParent(); Vector4 w = pitem->GetBaseItem()->GetMatrix().GetRow(3) - link->GetMatrix().GetRow(3); float k = w.Len(); Vector4 row = link->GetMatrix().GetRow(1); Matrix4 m = Matrix4::FromMatrix3(Matrix3::FromOrthonormalBasis(w, &row)); m.SetRow(3, link->GetMatrix().GetRow(3)); Vector4 p[6] = { Vector4(0, 0, 0), Vector4(-k * 0.1f, k * 0.1f, k * 0.2f), Vector4( k * 0.1f, k * 0.1f, k * 0.2f), Vector4( k * 0.1f, -k * 0.1f, k * 0.2f), Vector4(-k * 0.1f, -k * 0.1f, k * 0.2f), Vector4( 0, 0, k) }, wp[6]; m.Apply(wp, p, 6); RendererToolbox::Line3D(render, wp[0], wp[1], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[0], wp[2], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[0], wp[3], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[0], wp[4], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[1], wp[5], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[2], wp[5], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[3], wp[5], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[4], wp[5], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[1], wp[2], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[2], wp[3], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[3], wp[4], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); RendererToolbox::Line3D(render, wp[4], wp[1], &bone_color, Material::Blend_Alpha, Material::RenderWord(Material::Render_NoZTest | Material::Render_NoZWrite)); } } // Display item motions. if (display_flags & SceneDebugDisplayMotions) if (pitem->automation_player && pitem->automation_player->GetMotionList().GetCount() && pitem->GetBaseItem()) { bool found = true; Matrix4 m = pitem->GetBaseItem()->GetMatrix(); for (int index=0; index< pitem->automation_player->GetMotionList().GetCount(); ++index) { Motion* motion = pitem->automation_player->GetMotionFromIndex(index); // if(found) { found = false; if(render.GetCamera()) { Vector4 view_position = render.GetCamera()->GetPosition(); Vector4 view_position_closest_point(0, 0, 0); float closest_t; motion->GetClosestPoint(view_position * pitem->GetBaseItem()->GetInverseMatrix(), view_position_closest_point, &closest_t); Vector4 sample(0, 0, 0); motion->EvaluatePosition(Time::fromSec(closest_t+1), sample, Curve::Constant); Color color(1,1,0); if(sample == view_position_closest_point) RendererToolbox::DrawCross(render, view_position_closest_point * m, Units::Mtr(1), &color); else { Vector4 OrthoVec = ((view_position_closest_point - sample).Normalized()).Cross(Vector4(0.0,1.0,0.0)); RendererToolbox::Line3D(render, sample * m, (view_position_closest_point-OrthoVec) * m, &color); RendererToolbox::Line3D(render, sample * m, (view_position_closest_point+OrthoVec) * m, &color); } // if(motion->quadtree.IsValid()) // motion->quadtree->draw_scene_debug(render, m); } } MotionChannel *c[3]; motion->GetTransformationChannels(c, NULL, NULL); // check all 3 channel has the same number of point, because it's a vector // if not just continue to the next if( c[0]->GetPoints().GetCount() != c[1]->GetPoints().GetCount()|| c[0]->GetPoints().GetCount() != c[2]->GetPoints().GetCount()|| c[1]->GetPoints().GetCount() != c[2]->GetPoints().GetCount()|| c[1]->GetPoints().GetCount() <= 1) continue; ArrayList ::Iterator iteratorX(c[0]->GetPoints()); ArrayList ::Iterator iteratorY(c[1]->GetPoints()); ArrayList ::Iterator iteratorZ(c[2]->GetPoints()); Vector4 vec; Array v; v.Allocate(c[0]->GetPointCount()*2); Vector4 prev_vec; int count = 0; for ( ; iteratorX.ObjectPtr(); ) // need to test iterator validity prior to access in case the motion has no point { vec.x = iteratorX.ObjectPtr()->v; vec.y = iteratorY.ObjectPtr()->v; vec.z = iteratorZ.ObjectPtr()->v; if(count != 0) { v[(count++)-1] = prev_vec; v[(count++)-1] = vec*m; } else ++count; prev_vec = vec*m; ++iteratorX; ++iteratorY; ++iteratorZ; } render.DrawLine(c[0]->GetPointCount()-1, v); // draw cross for the first and the last point if ((c[0]->GetPoints().GetCount() > 1) && (c[1]->GetPoints().GetCount() > 1) && (c[2]->GetPoints().GetCount() > 1)) { RendererToolbox::DrawCross(render, Vector4(c[0]->GetPoints()[0]->v, c[1]->GetPoints()[0]->v, c[2]->GetPoints()[0]->v)*m, Mtr(1), &Color::Green); RendererToolbox::DrawCross(render, Vector4(c[0]->GetPoints()[c[0]->GetPointCount()-1]->v, c[1]->GetPoints()[c[1]->GetPointCount()-1]->v, c[2]->GetPoints()[c[2]->GetPointCount()-1]->v)*m, Mtr(1.1), &Color::Red); } } } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void Triangle2d(Renderer &renderer, Vector4 sv[3], Color *c) { Vector4 wv[3]; for (int n = 0; n < 3; ++n) wv[n] = renderer.GetCamera()->ScreenToWorld(renderer.GetViewport(), sv[n].x, sv[n].y); Color cl[3]; for (int n = 0; n < 3; ++n) cl[n] = *c; RendererToolbox::Triangle3D(renderer, wv, cl, 0, 0, Material::Blend_Alpha, (Material::RenderWord)(Material::Render_NoZTest | Material::Render_NoZWrite)); } void Cartouche(Renderer &render, Vector4 sp, float width, float height, Color *color) { float offset_y = 0.6f; Vector4 v[3]; v[0].Set(sp.x - width * 0.5f, sp.y + (offset_y - 1) * height * 0.5f); v[1].Set(sp.x + width * 0.5f, sp.y + (offset_y - 1) * height * 0.5f); v[2].Set(sp.x + width * 0.5f, sp.y + (offset_y + 1) * height * 0.5f); Triangle2d(render, v, color); v[0].Set(sp.x + width * 0.5f, sp.y + (offset_y + 1) * height * 0.5f); v[1].Set(sp.x - width * 0.5f, sp.y + (offset_y + 1) * height * 0.5f); v[2].Set(sp.x - width * 0.5f, sp.y + (offset_y - 1) * height * 0.5f); Triangle2d(render, v, color); // Draw border. for (int n = 0; n < 16; ++n) { v[2].Set(sp.x - width * 0.5f, sp.y); float a = n * Deg(180.f / 16.f); v[1].Set(sp.x - width * 0.5f - sin(a) * height * 0.25f, sp.y + (offset_y - cos(a)) * height * 0.5f); a = (n + 1) * Deg(180.f / 16.f); v[0].Set(sp.x - width * 0.5f - sin(a) * height * 0.25f, sp.y + (offset_y - cos(a)) * height * 0.5f); Triangle2d(render, v, color); } for (int n = 0; n < 16; ++n) { v[0].Set(sp.x + width * 0.5f, sp.y); float a = n * Deg(180.f / 16.f); v[1].Set(sp.x + width * 0.5f + sin(a) * height * 0.25f, sp.y + (offset_y - cos(a)) * height * 0.5f); a = (n + 1) * Deg(180.f / 16.f); v[2].Set(sp.x + width * 0.5f + sin(a) * height * 0.25f, sp.y + (offset_y - cos(a)) * height * 0.5f); Triangle2d(render, v, color); } } //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void SceneDebugger::DrawItemName(Renderer &renderer, MItem *pitem) { String label; switch (pitem->GetItemType()) { case Type_Object: label = "Object: "; break; case Type_Camera: label = "Camera: "; break; case Type_Light: label = "Light: "; break; case Type_Trigger: label = "Trigger: "; break; default: break; } label += pitem->name; Vector4 sp; if (!renderer.GetCamera()->WorldToScreen(renderer.GetViewport(), pitem->GetBaseItem()->GetMatrix().GetRow(3), sp)) return; sp.y += 0.05f; // Draw a background. // fRect viewport = renderer.GetViewport(); // float width = profiler_font.ComputeLineWidth(label.c_str()); Color bg_color(0.5f, 0.5f, 0.5f, 0.7f); // Cartouche(render, sp, k * width * 0.5f, k * 0.075f, &bg_color); // render.Write(profiler_font, label, sp.x, sp.y, k, true, Renderer::AlignMiddle); } void SceneDebugger::DrawFigure(Renderer &render, Geometry *figure, const Matrix4 &matrix, Color &color) { if (!figure) return; uint line_count = 0; for (uint n = 0; n < figure->pol.GetCount(); ++n) line_count += figure->pol[n].vtx_count; Array p_line(line_count * 2); Array c_line(line_count * 2); Vector4 *p_p = p_line; Color *p_c = c_line; if (p_p && p_c) { for (uint n = 0; n < figure->pol.GetCount(); ++n) for (int v = 0; v < figure->pol[n].vtx_count; ++v) { int w = v + 1; if (w == figure->pol[n].vtx_count) w = 0; *p_p++ = figure->vtx[figure->pol[n].binding[v]]; *p_p++ = figure->vtx[figure->pol[n].binding[w]]; *p_c++ = color; *p_c++ = color; } render.SetWorldMatrix(matrix.AsOrthonormalBase() * Matrix4::ScaleMatrix(Vector4(scale, scale, scale))); render.DrawLine(line_count, p_line, c_line, Material::Blend_Alpha, (Material::RenderWord)(Material::Render_NoZTest | Material::Render_NoZWrite)); } } //----------------------------------------------------------------------------- //------------------------------------------------------------------------------ static void DrawLineHatched(const Vector4 &a, const Vector4 &b, Vector4 *p_p, Color *p_c) { (*p_c++).Set(0.8f, 0.8f, 0.8f, 0.75f); (*p_c++).Set(0.8f, 0.8f, 0.8f, 0.75f); (*p_c++).Set(0.8f, 0.8f, 0.8f, 0.75f); (*p_c++).Set(0.8f, 0.8f, 0.8f, 0.75f); Vector4 dt = b - a; *p_p++ = a; *p_p++ = a + dt * 0.2f; *p_p++ = b; *p_p++ = b - dt * 0.2f; } static void DrawOBBCorners(Renderer &render, const OBB &obb, const Matrix4 &m) { Vector4 vtx[8]; vtx[0].Set(-0.5, 0.5, 0.5); vtx[1].Set( 0.5, 0.5, 0.5); vtx[2].Set( 0.5, -0.5, 0.5); vtx[3].Set(-0.5, -0.5, 0.5); vtx[4].Set(-0.5, 0.5, -0.5); vtx[5].Set( 0.5, 0.5, -0.5); vtx[6].Set( 0.5, -0.5, -0.5); vtx[7].Set(-0.5, -0.5, -0.5); render.SetWorldMatrix(m * Matrix4::TransformationMatrix(obb.bb_position, obb.bb_rotation, obb.bb_scale)); Array p_line(12 * 2 * 2); Array c_line(12 * 2 * 2); Vector4 *p_p = p_line; Color *p_c = c_line; if (p_p && p_c) { DrawLineHatched(vtx[0], vtx[1], p_p + 0, p_c + 0); DrawLineHatched(vtx[1], vtx[2], p_p + 4, p_c + 4); DrawLineHatched(vtx[2], vtx[3], p_p + 8, p_c + 8); DrawLineHatched(vtx[3], vtx[0], p_p + 12, p_c + 12); DrawLineHatched(vtx[4], vtx[5], p_p + 16, p_c + 16); DrawLineHatched(vtx[5], vtx[6], p_p + 20, p_c + 20); DrawLineHatched(vtx[6], vtx[7], p_p + 24, p_c + 24); DrawLineHatched(vtx[7], vtx[4], p_p + 28, p_c + 28); DrawLineHatched(vtx[0], vtx[4], p_p + 32, p_c + 32); DrawLineHatched(vtx[1], vtx[5], p_p + 36, p_c + 36); DrawLineHatched(vtx[2], vtx[6], p_p + 40, p_c + 40); DrawLineHatched(vtx[3], vtx[7], p_p + 44, p_c + 44); render.DrawLine(12 * 2, p_line, c_line, Material::Blend_Alpha, Material::Render_NoZTest); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void SceneDebugger::DrawScene(Renderer &render, const List *selection, const List *blacklist, uint display_flags) { Color middle_grey(0.72f, 0.72f, 0.72f), selected_orange(1, 0.65f, 0.16f), draw_color; // Display light schematics. ListForeachPtr(MItem *, i, scene->GetItemList()) { if (blacklist && blacklist->Find(i)) continue; switch (i->GetItemType()) { case Type_Light: if (MLight *l = (MLight *)i) { DrawItem(render, l, display_flags); if (selection && selection->Find(l)) draw_color = selected_orange; else draw_color = middle_grey; switch (l->model) { case Light::Model_Point: DrawFigure(render, pointlight.c_ptr(), l->GetMatrix(), draw_color); break; case Light::Model_Linear: DrawFigure(render, linearlight.c_ptr(), l->GetMatrix(), draw_color); break; case Light::Model_Spot: DrawFigure(render, spotlight.c_ptr(), l->GetMatrix(), draw_color); break; default: break; } } break; case Type_Object: if (MObject *o = (MObject *)i) { DrawItemBoundingVolume(render, o, display_flags); bool display_item = false; if (!selection || selection->Find(o)) { display_item = true; draw_color = selected_orange; } else draw_color = middle_grey; if (display_item || o->geometry.IsEmpty()) DrawItem(render, o, display_flags, &draw_color); // DrawFigure(render, dbg_object, obj->GetMatrix(), draw_color); } break; case Type_Camera: if (MCamera *c = (MCamera *)i) { DrawItem(render, c, display_flags); if (!selection || selection->Find(c)) draw_color = selected_orange; else draw_color = middle_grey; DrawFigure(render, camera.c_ptr(), c->GetMatrix(), draw_color); } break; case Type_Trigger: if (MTrigger *t = (MTrigger *)i) { OBB obb(t->GetMatrix().GetRow(3), t->GetScale(), &t->GetRotationMatrix()); Color color(1, 0, 0); if (t->isActive()) color.Set(0, 1, 0); RendererToolbox::DrawOBB(render, obb, &color); if (!selection || selection->Find(t)) draw_color = selected_orange; else draw_color = middle_grey; DrawFigure(render, trigger.c_ptr(), t->GetMatrix(), draw_color); } break; case Type_Constraint: if (MConstraint *c = (MConstraint *)i) { if (c->desc.item_a.IsNull() || c->desc.item_b.IsNull()) continue; Vector4 wp_a = c->desc.pivot_a.GetRow(3) * c->desc.item_a->GetBaseItem()->GetMatrix(), wp_b = c->desc.pivot_b.GetRow(3) * c->desc.item_b->GetBaseItem()->GetMatrix(); RendererToolbox::DrawCross(render, wp_a, Cm(20.f)); RendererToolbox::DrawCross(render, wp_b, Cm(20.f)); RendererToolbox::Line3D(render, wp_a, wp_b); } break; case Type_Instance: { // the items draw debug inside this instance, only if this instance is selected if (selection && selection->Find(i)) { if (Instance *instance = (Instance *)i) { if (instance->instance_scene) { // call the draw debug for the object inside this scene instance ListForeachPtr(MItem *, i_instance, instance->instance_scene->GetItemList()) { if (i_instance->GetItemType() == Type_Object) { if (MObject *o = (MObject *)i_instance) { DrawItemBoundingVolume(render, o, display_flags); bool display_item = false; if (selection->Find(o)) { display_item = true; draw_color = selected_orange; } else draw_color = middle_grey; if (display_item || o->geometry.IsEmpty()) DrawItem(render, o, display_flags, &draw_color); } } } } } } } break; default: break; } } // Display bounding. MinMax minmax; ListForeachPtr(MItem *, i, *selection) switch (i->GetItemType()) { case Type_Object: { ((MObject *)i)->ComputeLocalMinMax(minmax); OBB obb(minmax); DrawOBBCorners(render, obb, i->GetBaseItem()->GetMatrix()); } break; case Type_Instance: { Instance *instance = (Instance *)i; if (instance->instance_scene) { instance->instance_scene->GetMinMax(minmax); OBB obb(minmax); DrawOBBCorners(render, obb, i->GetBaseItem()->GetMatrix()); } } break; default: break; } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ bool SceneDebugger::LoadSchematicFigures(const char *dbg_pointlight, const char *dbg_spotlight, const char *dbg_linearlight, const char *dbg_camera, const char *dbg_object, const char *dbg_trigger) { pointlight = new Geometry; pointlight->name = dbg_pointlight; NML::LoadFromFile(*pointlight, dbg_pointlight); spotlight = new Geometry; spotlight->name = dbg_spotlight; NML::LoadFromFile(*spotlight, dbg_spotlight); linearlight = new Geometry; linearlight->name = dbg_linearlight; NML::LoadFromFile(*linearlight, dbg_linearlight); camera = new Geometry; camera->name = dbg_camera; NML::LoadFromFile(*camera, dbg_camera); object = new Geometry; object->name = dbg_object; NML::LoadFromFile(*object, dbg_object); trigger = new Geometry; trigger->name = dbg_trigger; NML::LoadFromFile(*trigger, dbg_trigger); return true; } void SceneDebugger::SetScene(Scene *s) { scene = s; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ SceneDebugger::SceneDebugger() { scene = NULL; scale = 1.f; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void Scene::DumpContentToLog() const { __LOG_V__ << "Dumping scene content\n\n"; __LOG_V__ << "Item list (" << GetActiveList().GetCount() << " entries):\n"; ArrayListForeachPtr(MItem *, i, GetActiveList()) { MItem *pi = NULL; if (i->GetBaseItem()) LocateItem(i->GetBaseItem()->GetParent(), &pi); __LOG_V__ << " - '" << i->name << "' (" << i->GetUid() << ")"; if (pi) __LOG_V__ << "(Linked to '" << pi->name << "' (" << pi->GetUid() << ")), (Active: " << (i->isActive() ? "yes" : "no") << ", Invisible: " << (i->GetBaseItem()->item_flags.IsSet(ItemFlagInvisible) ? "yes" : "no") << ")\n"; else __LOG_V__ << "\n"; } __LOG_V__ << "\n"; } //------------------------------------------------------------------------------