/* ----------------------------------------------------------------------------- GSFramework Copyright 2001-2013 Emmanuel Julien. All Rights Reserved. ----------------------------------------------------------------------------- */ #include "raytracer/raytracer_scene.h" #include "scene3d/mobject.h" #include "scene3d/mlight.h" #include "scene3d/scene.h" #include "scene3d/instance.h" #include "scene3d/group.h" #include "core/geometry_bih.h" #include "metafile/nml_object.h" using namespace GS; using namespace GS::Core; using namespace GS::Raytrace; //------------------------------------------------------------------------------ void SceneBIH::ResetStats() { ray_count = 0; } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void SceneBIH::TraceLeaf(BIH::Node *leaf, float tmin, float tmax, BIH::Trace &trace, void *parm) { Vector4 &s = trace.s, &d = trace.d; Trace *s_trace = (Trace *)parm; uint *leaf_indice = (uint *)leaf->p; for (uint n = 0; n < leaf->count; ++n) { Core::Object *o = obj[leaf_indice[n]].o; IGeometryTree *tree = obj[leaf_indice[n]].tree; // Raytrace object in local space. Vector4 local_s = s * o->GetInverseMatrix(), local_d; o->GetInverseMatrix().ApplyRotation(&local_d, &d); GeometryTrace geo_trace; tree->RaytraceGeometry(geo_trace, local_s, local_d, tmax); if (!geo_trace.has_i) continue; // Integrate result. if (!s_trace->has_i || (geo_trace.i_t < s_trace->i_t)) { /* Note: Do not copy the complete geo_trace, we do not want to duplicate trace stacks. */ *((GeometryTraceBase *)s_trace) = ((GeometryTraceBase &)geo_trace); s_trace->has_i = true; s_trace->o = o; s_trace->tri_test += geo_trace.tri_test; } } } void SceneBIH::RaytraceScene(Trace &trace, const Vector4 &s, const Vector4 &d, float l) { ray_count++; trace.s = s; trace.d = d; BIH::Trace bih_trace; Tree::Raytrace(bih_trace, s, d, l, (void *)&trace); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ Geometry *SceneBIH::TranslateGeometry(Geometry *g) const { if (obj && g) for (uint n = 0; n < obj.GetCount(); ++n) if (obj[n].g == g) return obj[n].og; return g; } void SceneBIH::AddObject(ResourceFactory &gf, S3D::MObject *o, uint &obj_count, MinMax *varray, bool shadow) { if (!o->isActive()) return; if (o->geometry.IsEmpty() || !o->GetBaseItem()->opacity) return; if (o->mitem_flags.IsSet(S3D::MItem::Flag_IsHelper | S3D::MItem::Flag_EditorHidden | S3D::MItem::Flag_EditorLocked)) return; // Grab object geometry. Geometry *g = gf.LoadGeometry(o->geometry); if (!g) return; obj[obj_count].og = g; // Store original geometry to map back from skinned geometry. if (!g->material_table.GetCount() || !g->pol.GetCount()) return; if (shadow) { if (g->flag.IsSet(Geometry::FlagNullShadowProxy)) return; if (!g->shadow_proxy.IsEmpty()) g = gf.LoadGeometry(g->shadow_proxy); } // Perform skinning. if (o->HasSkin() && g->skin) { Skin *skin = o->GetSkin(); // Serialize geometry. using namespace NML; File file; file.AddRoot(g->AsMetaTag()); Geometry *sg = new Geometry; LoadFromFile(*sg, file); // Build required structures upfront. sg->ComputeVertexNormal(); sg->ComputeVertexTangent(); // Vertex skinning. for (uint n = 0; n < sg->vtx.GetCount(); ++n) { Vector4 v(0, 0, 0); for (int b = 0; b < 4; ++b) { if (!sg->skin[n].w[b]) break; v += (sg->vtx[n] * skin->bones_mtx[sg->skin[n].bone_index[b]]) * sg->skin[n].w[b]; } sg->vtx[n] = v; } // Normal skinning. Vector4 s, w; int tt = 0; for (uint p = 0; p < sg->pol.GetCount(); ++p) for (uint n = 0; n < sg->pol[p].vtx_count; ++n) { s.Set(0, 0, 0); for (int b = 0; b < 4; ++b) { int i = sg->pol[p].binding[n]; if (!sg->skin[i].w[b]) break; skin->bones_mtx[sg->skin[i].bone_index[b]].ApplyRotation(&w, &sg->vtx_normal[tt]); s += w * sg->skin[i].w[b]; } sg->vtx_normal[tt++] = s; } // Tangent base skinning. Vector4 _b, _t; tt = 0; for (uint p = 0; p < sg->pol.GetCount(); ++p) for (uint n = 0; n < sg->pol[p].vtx_count; ++n) { _b.Set(0, 0, 0); _t.Set(0, 0, 0); for (int b = 0; b < 4; ++b) { int i = sg->pol[p].binding[n]; if (!sg->skin[i].w[b]) break; skin->bones_mtx[sg->skin[i].bone_index[b]].ApplyRotation(&w, &sg->vtx_tangent[tt].B); _b += w * sg->skin[i].w[b]; skin->bones_mtx[sg->skin[i].bone_index[b]].ApplyRotation(&w, &sg->vtx_tangent[tt].T); _t += w * sg->skin[i].w[b]; } sg->vtx_tangent[tt].B = _b; sg->vtx_tangent[tt].T = _t; tt++; } // Use as the base geometry. // but first copy the material from the base material sg->material_table.Allocate(g->material_table.GetCount()); for (uint k = 0; k < g->material_table.GetCount(); ++k) sg->material_table[k] = g->material_table[k]; g = sg; } // Prepare geometry. IGeometryTree *tree = new GeometryBIHTree; tree->BuildFromGeometry(gf, g); // Build minmax for the transformed geometry. varray[obj_count] = g->ComputeMinMax(&o->GetMatrix()); obj[obj_count].g = g; obj[obj_count].tree = tree; obj[obj_count++].o = o; } bool SceneBIH::SetScene(ResourceFactory &gf, const S3D::Scene *s, bool shadow) { Free(); using namespace S3D; SharedList objects; s->GetItemListByType(objects); SharedList instances; s->GetItemListByType(instances); // Grab the scene content. uint obj_count = 0; ListForeachPtr(MObject *, o, objects) if (!o->geometry.IsEmpty()) obj_count++; // Count the instance objects. ListForeachPtr(Instance *, i, instances) { if (!i->instance_scene) { if (!(i->instance_scene = new Scene(s->GetVM()))) continue; i->instance_scene->FromMetaFileStoreGroup(i->template_path, &i->instance_group, SceneIOObject | SceneIOLight); if (i->instance_group != NULL) i->instance_group->SetRootItem(i); } if (i->instance_group) ListForeachPtr(MItem *, ig, i->instance_group->GetItemList()) if (ig->GetItemType() == Type_Object && !((MObject *)ig)->geometry.IsEmpty()) obj_count++; } if (!obj_count) return true; if (!obj.Allocate(obj_count)) __ERR__(__LOG_E__ << "Failed to grab scene to raytracer.\n", false) // Build scene tree and object trees. Array varray(obj_count); if (!varray) __ERR__(__LOG_E__ << "Failed to allocate volume array to build scene tree.\n", false) obj_count = 0; ListForeachPtr(MObject *, o, objects) AddObject(gf, o, obj_count, varray, shadow); // Add the instance objects. ListForeachPtr(Instance *, i, instances) if (i->instance_group) ListForeachPtr(MItem *, ig, i->instance_group->GetItemList()) if (ig->GetItemType() == Type_Object && !((MObject *)ig)->geometry.IsEmpty()) AddObject(gf, ((MObject *)ig), obj_count, varray, shadow); // Build tree. if (!Build(obj_count, varray)) return false; return true; } void SceneBIH::Free() { obj.Free(); Tree::Free(); } //------------------------------------------------------------------------------