495 lines
13 KiB
C++
495 lines
13 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "scene3d/scene.h"
|
|
#include "scene3d/mlight.h"
|
|
#include "scene3d/mobject.h"
|
|
#include "scene3d/mcamera.h"
|
|
#include "scene3d/mtrigger.h"
|
|
#include "scene3d/memitter.h"
|
|
#include "scene3d/mterrain.h"
|
|
#include "scene3d/instance.h"
|
|
#include "scene3d/mconstraint.h"
|
|
#include "scene3d/mitem_scripted_object.h"
|
|
#include "scene3d/scene_event_script_interface.h"
|
|
#include "scene3d/scene_renderer_environment_interface.h"
|
|
#include "scene3d/scene_physic_world_interface.h"
|
|
#include "scene3d/scene_scripted_object.h"
|
|
#include "scene3d/scene_script_unit.h"
|
|
#include "scene3d/mitem_event_script_interface.h"
|
|
#include "physic/physic_constraint.h"
|
|
#include "physic/physic_world.h"
|
|
#include "ui/ui.h"
|
|
|
|
using namespace GS;
|
|
using namespace S3D;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::SetClock(Core::Clock *c, bool set_ui)
|
|
{
|
|
clock = c ? c : new Core::Clock;
|
|
if (set_ui)
|
|
ui->SetClock(c);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::GetMinMax(MinMax &minmax) const
|
|
{
|
|
bool initial = true;
|
|
|
|
ArrayListForeachPtr(MItem *, i, active_list)
|
|
{
|
|
if (i->GetItemType() != Type_Object)
|
|
continue;
|
|
|
|
MObject *object = (MObject *)i;
|
|
if (object->render_data.IsNull() || object->render_data->geometry.IsNull())
|
|
continue;
|
|
|
|
MinMax local_minmax = object->render_data->geometry->minmax;
|
|
|
|
if (initial)
|
|
minmax = local_minmax;
|
|
else minmax.Grow(local_minmax);
|
|
initial = false;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Group *Scene::AddGroup(Group *g)
|
|
{
|
|
group_list.Add(g);
|
|
return g;
|
|
}
|
|
bool Scene::RemoveGroup(Group *g)
|
|
{
|
|
return group_list.Remove(g);
|
|
}
|
|
Group *Scene::FindGroup(const char *name) const
|
|
{
|
|
ListForeachPtr(Group *, g, group_list)
|
|
if (g->name == name)
|
|
return g;
|
|
return NULL;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
uint Scene::GetItemGroupList(const MItem *item, SharedList <Group *> &list) const
|
|
{
|
|
uint count = 0;
|
|
ListForeachPtr(Group *, g, group_list)
|
|
if (g->IsMember(item))
|
|
{
|
|
count++;
|
|
list.Add(g);
|
|
}
|
|
|
|
return count;
|
|
}
|
|
void Scene::UnregisterItemFromGroups(MItem *item) const
|
|
{
|
|
ListForeachPtr(Group *, g, group_list)
|
|
g->Remove(item);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Scene::ActivateItem(MItem *i, bool v)
|
|
{
|
|
if (v)
|
|
{
|
|
if (active_list.IndexOf(i) == -1)
|
|
active_list.Add(i);
|
|
}
|
|
else
|
|
active_list.Remove(i);
|
|
|
|
if (i->physic_item)
|
|
i->physic_item->SetActive(v);
|
|
|
|
i->mitem_flags.Raise(MItem::Flag_IsActive, v);
|
|
return true;
|
|
}
|
|
void Scene::ProcessItemActivationQueue()
|
|
{
|
|
ListForeachPtr(MItem *, i, activation_queue)
|
|
ActivateItem(i, true);
|
|
ListForeachPtr(MItem *, i, deactivation_queue)
|
|
ActivateItem(i, false);
|
|
|
|
activation_queue.Clear();
|
|
deactivation_queue.Clear();
|
|
}
|
|
void Scene::QueueItemActivation(MItem *item, bool activate, bool propagate)
|
|
{
|
|
if (item->mitem_flags.IsSet(MItem::Flag_IsActive) != activate)
|
|
{
|
|
// Queue item.
|
|
if (activate)
|
|
{
|
|
if (!activation_queue.Find(item))
|
|
activation_queue.Add(item);
|
|
deactivation_queue.Remove(item);
|
|
}
|
|
else
|
|
{
|
|
if (!deactivation_queue.Find(item))
|
|
deactivation_queue.Add(item);
|
|
activation_queue.Remove(item);
|
|
}
|
|
|
|
// Script callback.
|
|
item->item_event->OnActivate(item, activate);
|
|
item->mitem_flags.Raise(MItem::Flag_IsActive, activate);
|
|
}
|
|
|
|
if (propagate)
|
|
ListForeachPtr(Core::Item *, child, item->GetBaseItem()->GetChildren())
|
|
QueueItemActivation(LocateManagedItem(child), activate, true);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool Scene::Create(PhysicWorld *w)
|
|
{
|
|
Clear();
|
|
|
|
if ((physic_world = w) != NULL)
|
|
{
|
|
if (!physic_world->Create())
|
|
return false;
|
|
|
|
// bullet_world->CreateDebugger(GetEngine().GetRenderer());
|
|
physic_world->SetWorldInterface(iphysic_world);
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
MTrigger *Scene::RaytraceTriggerList(Vector4 &s, Vector4 &d, float l, Vector4 *i)
|
|
{
|
|
ListForeachPtr(MItem *, item, item_list)
|
|
if (item->GetItemType() == Type_Trigger)
|
|
{
|
|
MTrigger *t = (MTrigger *)item;
|
|
|
|
// Move ray to trigger space.
|
|
Vector4 local_s, local_d;
|
|
t->GetInverseMatrix().Apply(&local_s, &s);
|
|
t->GetInverseMatrix().ApplyRotation(&local_d, &d);
|
|
|
|
// Test ray against bbox normalized space.
|
|
MinMax mm;
|
|
mm.SetFromPositionSize(Vector4(0, 0, 0), Vector4(1, 1, 1));
|
|
|
|
// Raytrace against minmax.
|
|
Vector4 local_i;
|
|
if (l == -1 ? mm.ClassifyLine(local_s, local_d, local_i) : mm.ClassifySegment(local_s, local_s + local_d * l, local_i))
|
|
{
|
|
if (i)
|
|
*i = local_i * t->GetMatrix();
|
|
return t;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::RecordAllItemLocation()
|
|
{
|
|
ListForeachPtr(MItem *, i, item_list)
|
|
i->SetInitialTransformation();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
MItem *Scene::ItemFromUid(uint uid) const
|
|
{
|
|
ListForeachPtr(MItem *, i, item_list)
|
|
if (i->GetUid() == uid)
|
|
return i;
|
|
|
|
return NULL;
|
|
}
|
|
MItemType Scene::LocateItem(const Core::Item *item, MItem **entity) const
|
|
{
|
|
ListForeachPtr(MItem *, i, item_list)
|
|
if (i->GetBaseItem() == item)
|
|
{
|
|
if (entity)
|
|
*entity = i;
|
|
return i->GetItemType();
|
|
}
|
|
|
|
return Type_None;
|
|
}
|
|
MItem *Scene::LocateManagedItem(Core::Item *i)
|
|
{
|
|
return i ? (MItem *)i->mitem : NULL;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
static bool FindItemByName(const MItem *i, const char *name) { return i->name == name; }
|
|
|
|
//------------------------------------------------------------------------------
|
|
MItem *Scene::Item(const char *item_name, MItem *parent) const
|
|
{
|
|
StringList node;
|
|
String(item_name).Split("/", node);
|
|
|
|
MItem *item = NULL;
|
|
|
|
if (parent)
|
|
ListForeachPtr(Core::Item *, child, parent->GetBaseItem()->GetChildren())
|
|
{
|
|
MItem *child_item = LocateManagedItem(child);
|
|
if (child_item && (child_item->name == node[0]))
|
|
{
|
|
item = child_item;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
item = ListFindEx(item_list, FindItemByName, node[0]);
|
|
|
|
if (!item)
|
|
__ERR__(__LOG_W__ << "No item named '" << item_name << "' in scene '" << name << "'.\n", NULL)
|
|
|
|
// Solve additional path.
|
|
if (node.GetCount() > 1)
|
|
for (uint n = 1; n < node.GetCount(); ++n)
|
|
{
|
|
if (item->GetItemType() != Type_Instance)
|
|
__ERR__(__LOG_W__ << "Cannot solve sub-path '" << node[n] << "' on item '" << item->name << "'.\n", NULL)
|
|
|
|
Instance *instance = (Instance *)item;
|
|
if (!instance->instance_group)
|
|
return NULL;
|
|
|
|
item = instance->instance_group->Item(node[n]);
|
|
}
|
|
|
|
return item;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::SetItemStatic(MItem *item, bool v)
|
|
{
|
|
item->mitem_flags.Raise(MItem::Flag_IsStatic, v);
|
|
|
|
if (Core::Renderable *r = item->GetRenderable())
|
|
{
|
|
RemoveRenderable(r);
|
|
AddRenderable(r, v);
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::AddRenderable(Core::Renderable *r, bool is_static)
|
|
{
|
|
if (is_static)
|
|
octree_culling_system.AddRenderable(r);
|
|
else simple_culling_system.AddRenderable(r);
|
|
}
|
|
void Scene::RemoveRenderable(Core::Renderable *r)
|
|
{
|
|
simple_culling_system.DeleteRenderable(r);
|
|
octree_culling_system.DeleteRenderable(r);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::SetupItemComponents(MItem *i)
|
|
{
|
|
// Create scripted object and event handler.
|
|
i->scripted_object = new MItemScriptedObject(i, vm);
|
|
if (vm)
|
|
i->item_event = new MItemScriptEvent;
|
|
|
|
// Create physic item.
|
|
if (physic_world && i->GetBaseItem())
|
|
{
|
|
i->physic_item = physic_world->NewItem();
|
|
i->physic_item->SetUserPointer((void *)i);
|
|
}
|
|
}
|
|
void Scene::AddItem(MItem *i, bool setup_components)
|
|
{
|
|
i->uid = current_item_uid++;
|
|
|
|
if (setup_components)
|
|
SetupItemComponents(i);
|
|
|
|
g_scene_messenger.BroadcastMessage(SceneMsg_AddingItem, this, i);
|
|
|
|
// Add to all lists.
|
|
item_list.Add(i);
|
|
active_list.Add(i);
|
|
|
|
switch (i->GetItemType())
|
|
{
|
|
case Type_Light: light_list.Add((MLight *)i); break;
|
|
case Type_Trigger: trigger_list.Add((MTrigger *)i); break;
|
|
}
|
|
|
|
// Add to culling systems.
|
|
if (Core::Renderable *r = i->GetRenderable())
|
|
AddRenderable(r, i->mitem_flags.IsSet(MItem::Flag_IsStatic));
|
|
|
|
g_scene_messenger.BroadcastMessage(SceneMsg_ItemAdded, this, i);
|
|
}
|
|
bool Scene::RemoveItem(MItem *i)
|
|
{
|
|
g_scene_messenger.BroadcastMessage(SceneMsg_DeletingItem, this, (void *)i);
|
|
|
|
i->item_event->OnDelete(i);
|
|
|
|
if (vm.IsValid())
|
|
vm->InvalidateNativeReference(i);
|
|
|
|
// Remove from groups.
|
|
UnregisterItemFromGroups(i);
|
|
|
|
if (Core::Renderable *r = i->GetRenderable())
|
|
{
|
|
// Remove from culling systems.
|
|
simple_culling_system.DeleteRenderable(r);
|
|
octree_culling_system.DeleteRenderable(r);
|
|
}
|
|
|
|
if (Core::Item *b = i->GetBaseItem())
|
|
{
|
|
// Unset if current camera.
|
|
if (current_camera == b)
|
|
current_camera = NULL;
|
|
|
|
// Remove from triggers & skins.
|
|
ListForeachPtr(MItem *, item, item_list)
|
|
switch (item->GetItemType())
|
|
{
|
|
case Type_Trigger:
|
|
((MTrigger *)item)->DropItem(b);
|
|
break;
|
|
|
|
case Type_Object:
|
|
{
|
|
MObject *o = (MObject *)item;
|
|
for (uint n = 0; n < o->GetBoneCount(); ++n)
|
|
if (o->GetBone(n) == b)
|
|
o->BindBone(n, NULL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Remove from physic world.
|
|
i->physic_item = NULL;
|
|
|
|
switch (i->GetItemType())
|
|
{
|
|
case Type_Light: light_list.Remove((MLight *)i); break;
|
|
case Type_Trigger: trigger_list.Remove((MTrigger *)i); break;
|
|
}
|
|
|
|
active_list.Remove(i);
|
|
removal_queue.Remove(i);
|
|
activation_queue.Remove(i);
|
|
deactivation_queue.Remove(i);
|
|
|
|
item_list.Remove(i); // keep last
|
|
|
|
g_scene_messenger.BroadcastMessage(SceneMsg_ItemDeleted, this, i);
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::QueueItemRemoval(MItem *i)
|
|
{ removal_queue.Add(i); }
|
|
void Scene::ProcessItemRemovalQueue()
|
|
{
|
|
while (List <MItem *> ::Item *r = removal_queue.GetRoot())
|
|
RemoveItem(r->Object());
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void Scene::Clear(bool keep_script)
|
|
{
|
|
g_scene_messenger.BroadcastMessage(SceneMsg_Clearing, this, 0);
|
|
|
|
scene_event->OnDelete(this);
|
|
|
|
// Remove all items (immediate/non-queued).
|
|
while (List <MItem *> ::Item *i = item_list.GetRoot())
|
|
RemoveItem(i->Object());
|
|
|
|
// Drop UI.
|
|
ui->Clear();
|
|
|
|
// Close scripts.
|
|
if (!keep_script)
|
|
scripted_object->RemoveAllUnit();
|
|
|
|
fog_color.Set();
|
|
fog_near = 0;
|
|
fog_far = 0;
|
|
|
|
current_item_uid = 0;
|
|
|
|
g_scene_messenger.BroadcastMessage(SceneMsg_Cleared, this, 0);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Scene::Scene(Script::IVM *script_vm) : profiler(*this)
|
|
{
|
|
vm = script_vm;
|
|
current_camera = NULL;
|
|
|
|
current_item_uid = 0;
|
|
|
|
ambient_intensity = 1;
|
|
target_exposure = 0.25;
|
|
|
|
time_of_day = 0.5;
|
|
|
|
fog_near = 0;
|
|
fog_far = 0;
|
|
fog_color.Set(0, 0, 0);
|
|
|
|
background_color.Set(0, 0, 0);
|
|
ambient_color.Set(0, 0, 0);
|
|
|
|
clock = new Core::Clock;
|
|
|
|
scene_event = vm ? new SceneScriptEvent : new ISceneEvent;
|
|
scripted_object = new SceneScriptedObject(vm, this);
|
|
|
|
irenderer_environment = new IEnvironment(this);
|
|
iphysic_world = new IScenePhysicWorld(this);
|
|
|
|
ui = new S2D::Scene(vm);
|
|
}
|
|
Scene::~Scene()
|
|
{
|
|
SetAsScriptGlobalScene();
|
|
Clear();
|
|
|
|
if (vm)
|
|
vm->InvalidateNativeReference((void *)this);
|
|
}
|
|
//------------------------------------------------------------------------------
|