609 lines
15 KiB
C++
609 lines
15 KiB
C++
/* -----------------------------------------------------------------------------
|
|
GSFramework
|
|
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
|
----------------------------------------------------------------------------- */
|
|
|
|
|
|
#include "viewer_base/viewer_base.h"
|
|
#include "viewer_base/viewer_base_debugger.h"
|
|
#include "physic_bullet/bullet_world.h"
|
|
#include "io_archive/io_archive.h"
|
|
#include "raytracer/raytracer_core.h"
|
|
#include "font_freetype/ft2_font_factory.h"
|
|
#include "script_squirrel/engine_vm_debugger.h"
|
|
#include "script_squirrel/engine_vm.h"
|
|
#include "script/script_variant.h"
|
|
#include "ui/ui.h"
|
|
#include "gpu/gpu_triangle_batch.h"
|
|
#include "gpu/gpu_renderer.h"
|
|
#include "core/embedded_resource_extractor.h"
|
|
#include "core/renderer_toolbox.h"
|
|
#include "metafile/nml_object.h"
|
|
#include "picture/pict_io.h"
|
|
#include "filesystem/filesystem.h"
|
|
#include "filesystem/io_cfile.h"
|
|
#include "input/input_system.h"
|
|
#include "log/log.h"
|
|
|
|
using namespace GS;
|
|
using namespace GS::Core;
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool ViewerBase::OpenViewer()
|
|
{
|
|
// Check core file system.
|
|
if (!Platform::Get().io->Exists("@core/noise.tga"))
|
|
__ERR__(__LOG_E__ << "@core is not properly mounted.\n", false)
|
|
|
|
// Embedded resources will be extracted to a ram disk.
|
|
IEmbeddedResourceHandler::Set(new EmbeddedResourceExtractor(true));
|
|
|
|
// Create outputs.
|
|
factories = new ResourceFactories;
|
|
if (!CreateRenderer() || !CreateMixer())
|
|
return false;
|
|
|
|
// Load renderer configuration.
|
|
NML::Parser::Load(config_path, renderer->registry);
|
|
|
|
// Open output subsystems.
|
|
if (!OpenVideo() || !OpenAudio())
|
|
return false;
|
|
|
|
profiler_font[0] = new Render::RasterFont;
|
|
profiler_font[0]->Load(*factories->render, "@core/fonts/profiler_base.nml", "@core/fonts/profiler_base");
|
|
profiler_font[1] = new Render::RasterFont;
|
|
profiler_font[1]->Load(*factories->render, "@core/fonts/profiler_bold.nml", "@core/fonts/profiler_bold");
|
|
|
|
fps_font = new Render::RasterFont;
|
|
fps_font->Load(*factories->render, "@core/fonts/fps.nml", "@core/fonts/fps");
|
|
|
|
gpu_batch = new GPU::TriangleBatch((GPU::Renderer &)*renderer);
|
|
|
|
// Initialize input interface.
|
|
Platform::Get().input_system->SetHandle(renderer->GetCurrentSystemWindowHandle());
|
|
|
|
state = SessionSetup;
|
|
return true;
|
|
}
|
|
void ViewerBase::CloseViewer()
|
|
{
|
|
CloseSession();
|
|
|
|
for (uint n = 0; n < 2; ++n)
|
|
_safe_delete(profiler_font[n]);
|
|
_safe_delete(fps_font);
|
|
|
|
factories = NULL;
|
|
|
|
if (renderer.IsValid())
|
|
{
|
|
renderer->Close();
|
|
renderer = NULL;
|
|
}
|
|
if (mixer.IsValid())
|
|
{
|
|
mixer->Close();
|
|
mixer = NULL;
|
|
}
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool ViewerBase::SetupSessionSource() // session
|
|
{
|
|
switch (session_source)
|
|
{
|
|
case SessionSourceFilesystem:
|
|
Platform::Get().io->Mount(new IO::CFile(session_source_path));
|
|
break;
|
|
case SessionSourceArchive:
|
|
Platform::Get().io->Mount(new IO::Archive(session_source_path));
|
|
break;
|
|
case SessionSourceArchiveBootstrap:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool ViewerBase::OpenScriptVM() // session
|
|
{
|
|
// Compile core library.
|
|
if (!script_vm->CompileFile("@core/script/nad.nut"))
|
|
return false;
|
|
|
|
// Set defines.
|
|
ListForeachPtr(Variant *, prop, define_list)
|
|
script_vm->Set(prop->id, *prop);
|
|
|
|
// Load optional includes.
|
|
for (uint n = 0; n < include_list.GetCount(); ++n)
|
|
if (!script_vm->CompileFile(include_list.ObjectAt(n)))
|
|
return false;
|
|
|
|
// Compile bootstrap.
|
|
if (!bootstrap_script.IsEmpty() && !script_vm->Compile(bootstrap_script, bootstrap_script.Len(), NULL, "Bootstrap"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
void ViewerBase::SetVMGlobals()
|
|
{
|
|
script_vm->Set("g_project", Script::Variant(project, Script::typetag_Project));
|
|
|
|
script_vm->Set("g_factory", Script::Variant(factories, Script::typetag_ResourceFactories));
|
|
script_vm->Set("g_render", Script::Variant(renderer, Script::typetag_Renderer));
|
|
script_vm->Set("g_mixer", Script::Variant(mixer, Script::typetag_Mixer));
|
|
|
|
script_vm->Set("g_dt_frame", 1.f / 60.f);
|
|
script_vm->Set("g_raw_dt_frame", 1.f / 60.f);
|
|
script_vm->Set("g_clock", 0);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool ViewerBase::LoadSessionData()
|
|
{
|
|
if (remote)
|
|
session_type = remote_scene.isEmpty() ? SessionProject : SessionScene;
|
|
|
|
__LOG__ << "Session type: " << session_type << "\n";
|
|
|
|
using namespace NML;
|
|
|
|
if (session_type == SessionProject)
|
|
{
|
|
if (!remote)
|
|
{
|
|
if (!LoadFromFile(*project, input_path))
|
|
return false;
|
|
}
|
|
else
|
|
if (!LoadFromFile(*project, remote_project))
|
|
return false;
|
|
}
|
|
|
|
if (!project->Open(session_type == SessionProject))
|
|
return false;
|
|
|
|
switch (session_type)
|
|
{
|
|
case SessionScene:
|
|
{
|
|
// Determine the scene type.
|
|
if (!remote)
|
|
if (!Parser::Load(input_path, remote_scene)) // load local scene over the remote_scene meta file
|
|
return false;
|
|
|
|
Tag *t_scene2d = remote_scene.GetTag("Scene2D"),
|
|
*t_scene3d = remote_scene.GetTag("Scene");
|
|
|
|
//
|
|
if (t_scene3d)
|
|
{
|
|
scene_3d = new S3D::Scene(script_vm);
|
|
scene_3d->SetClock(project->clock);
|
|
|
|
if (!scene_3d->Create(project->iproject_factory->NewPhysicWorld()))
|
|
return false;
|
|
if (!scene_3d->FromMetaTag(*t_scene3d, tool_mode ? ToolPreview : NoTool))
|
|
return false;
|
|
|
|
scene_3d->name = input_path;
|
|
scene_3d->SetAsScriptGlobalScene();
|
|
scene_3d->InstanceSetup();
|
|
scene_3d->RenderSetup(factories);
|
|
scene_3d->Setup(tool_mode ? ToolPreview : NoTool);
|
|
scene_3d->Reset();
|
|
}
|
|
else if (t_scene2d)
|
|
{
|
|
scene_2d = new S2D::Scene(script_vm);
|
|
scene_2d->SetClock(project->clock);
|
|
|
|
if (!scene_2d->FromMetaTag(*t_scene2d, tool_mode ? ToolPreview : NoTool))
|
|
return false;
|
|
|
|
scene_2d->name = input_path;
|
|
scene_2d->SetAsScriptGlobalScene();
|
|
scene_2d->RenderSetup(factories);
|
|
scene_2d->Setup();
|
|
scene_2d->Reset();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SessionProject:
|
|
project->Setup();
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
bool ViewerBase::OpenSession()
|
|
{
|
|
if (!OpenScriptVM())
|
|
return false;
|
|
|
|
missing_resource = false;
|
|
|
|
// Create project.
|
|
struct ProjectFactory : public IProjectFactory
|
|
{ S3D::PhysicWorld *NewPhysicWorld() const { return new S3D::BulletWorld; } };
|
|
|
|
project = new Project(factories, new Freetype2FontFactory, script_vm);
|
|
project->iproject_factory = new ProjectFactory;
|
|
|
|
SetVMGlobals();
|
|
|
|
// Setup data source.
|
|
if (!SetupSessionSource())
|
|
return false;
|
|
|
|
// Load viewer data.
|
|
if (!LoadSessionData())
|
|
return false;
|
|
|
|
time_start = Platform::Get().GetClock();
|
|
return true;
|
|
}
|
|
void ViewerBase::CloseSession()
|
|
{
|
|
paused = false;
|
|
|
|
scene_2d = NULL;
|
|
scene_3d = NULL;
|
|
project = NULL;
|
|
|
|
// [EJ] explicitly close the VM now as is may hold references to render resources
|
|
if (script_vm.IsValid())
|
|
script_vm->Close();
|
|
|
|
// script_vm = NULL;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void ViewerBase::ExecuteSession()
|
|
{
|
|
if (scene_3d)
|
|
scene_3d->profiler.ResetProfiles();
|
|
|
|
project->ResetStatistics();
|
|
renderer->ResetStatistics();
|
|
|
|
// Start frame.
|
|
if (!paused)
|
|
{
|
|
project->clock->Update();
|
|
project->UpdateScriptClock();
|
|
}
|
|
|
|
float x = 32, y = 32;
|
|
bool end_session = false;
|
|
|
|
switch (session_type)
|
|
{
|
|
case SessionScene:
|
|
if (scene_2d.IsValid())
|
|
{
|
|
renderer->Clear(0, 0, 0);
|
|
|
|
if (!paused)
|
|
scene_2d->Update();
|
|
|
|
// nGPUTriangleBatch batch(*renderer);
|
|
scene_2d->Render(*renderer/*, &batch*/);
|
|
}
|
|
if (scene_3d.IsValid())
|
|
{
|
|
if (!paused)
|
|
scene_3d->Update();
|
|
scene_3d->Render(*renderer);
|
|
scene_3d->RenderUI(*renderer, gpu_batch);
|
|
|
|
// Viewer-specific
|
|
{
|
|
if (enable_profiler)
|
|
scene_3d->DrawProfilerText(*renderer, profiler_font, x, y);
|
|
|
|
if (scene_3d->flags.IsSet(S3D::Scene::FlagEnd))
|
|
end_session = true;
|
|
|
|
if (!raytrace_path.IsEmpty())
|
|
{
|
|
int w = raytrace_width == -1 ? width : raytrace_width,
|
|
h = raytrace_height == -1 ? height : raytrace_height;
|
|
|
|
__LOG_H__ << "Raytracing frame " << frame_count << " (" << w << "x" << h << ")...\n";
|
|
Raytrace::Raytracer ray(factories->graphic);
|
|
|
|
if (ray.SetScene(scene_3d))
|
|
{
|
|
Picture out;
|
|
ray.GetConfiguration().aa_sample = raytrace_aa;
|
|
if (ray.Render(out, w, h))
|
|
PictureIO::Get().TgaSave(out, String::Format("%s/out_%05d.tga", raytrace_path.c_str(), frame_count));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case SessionProject:
|
|
if (!paused)
|
|
project->Update(SceneUpdateAll);
|
|
|
|
project->Render(*renderer);
|
|
|
|
// Viewer-specific
|
|
{
|
|
if (enable_profiler)
|
|
project->DrawProfilerText(*renderer, profiler_font, x, y);
|
|
|
|
if (project->flags.IsSet(Project::ProjectFlagEnd))
|
|
end_session = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
// End frame.
|
|
fRect viewport = renderer->GetViewport();
|
|
|
|
if (enable_profiler)
|
|
{
|
|
float x = viewport.GetWidth() - 400.f, y = 32.f;
|
|
renderer->DrawProfilerText(profiler_font, x, y);
|
|
}
|
|
else if (memory_profiler)
|
|
{
|
|
float x = 0, y = 0;
|
|
DrawAllocProfilerText(*renderer, profiler_font, x, y);
|
|
}
|
|
|
|
if (enable_profiler || display_fps)
|
|
{
|
|
Color shadow(0, 0, 0, 0.5);
|
|
Render::Renderer::WriterConfig config(false);
|
|
|
|
float x = 32, y = viewport.GetHeight() - 102;
|
|
renderer->Write(*fps_font, String::Format("%02.01f\n", fps.GetFps()), x, y, config, 1, &shadow);
|
|
y = viewport.GetHeight() - 112;
|
|
renderer->Write(*fps_font, String::Format("%02.01f\n", fps.GetFps()), x, y, config);
|
|
}
|
|
|
|
renderer->ShowFrame();
|
|
frame_count++;
|
|
|
|
// Pause support.
|
|
/* if (Input::Device *keyboard = Platform::Get().input_system->GetDevice("keyboard"))
|
|
if (keyboard->WasPressed(Input::Device::Key_P))
|
|
{
|
|
paused = !paused;
|
|
if (!paused)
|
|
project->clock->EatDeltaClock();
|
|
}
|
|
*/
|
|
// Check session runtime error.
|
|
if (CheckRuntimeError())
|
|
end_session = true;
|
|
if (time_live && ((Platform::Get().GetClock() - time_start) > (time_live * Platform::Get().GetClockFrequency())))
|
|
end_session = true;
|
|
|
|
if (end_session)
|
|
state = SessionClose;
|
|
}
|
|
bool ViewerBase::CheckRuntimeError()
|
|
{
|
|
// Check VM state.
|
|
switch (script_vm->GetState())
|
|
{
|
|
case Script::IVM::StateDead:
|
|
case Script::IVM::StateExceptionThrown:
|
|
return true;
|
|
|
|
case Script::IVM::StateOk:
|
|
break;
|
|
}
|
|
|
|
// Check for a missing resource error.
|
|
if (missing_resource && !ignore_missing_resource)
|
|
__ERR__(__LOG_E__ << "Closing session due to missing resources.\n", true)
|
|
|
|
return false;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
Script::NetworkDebugger *ViewerBase::CreateVMDebugInterface()
|
|
{
|
|
return new Script::ViewerBaseDebugger(*this, new Script::EngineDebugger((Script::EngineVM &)*script_vm), NULL, remote_port);
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
ViewerBase::State ViewerBase::Execute()
|
|
{
|
|
fps.MarkLoop();
|
|
|
|
Platform::Get().input_system->Update();
|
|
bool platform_update = PlatformUpdate();
|
|
|
|
switch (state)
|
|
{
|
|
//----------------------------------------------------------------------
|
|
case ViewerSetup:
|
|
script_vm = new Script::EngineVM;
|
|
script_vm->Open();
|
|
|
|
if (active_debug)
|
|
script_vm->SetDebugInterface(script_debugger, true);
|
|
|
|
if (remote)
|
|
{
|
|
script_debugger = CreateVMDebugInterface();
|
|
script_vm->SetDebugInterface(script_debugger, true);
|
|
|
|
// @FIXME make sure the network thread is running ok.
|
|
|
|
__LOG_H__ << "Waiting for controller connection...\n";
|
|
state = WaitRemoteController;
|
|
}
|
|
else
|
|
state = SessionSetup; // local setup done
|
|
break;
|
|
//----------------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------------
|
|
case WaitRemoteController:
|
|
if (script_debugger && script_debugger->IsConnected())
|
|
{
|
|
__LOG_H__ << "Controller connected.\n";
|
|
state = WaitRemoteSetup;
|
|
}
|
|
if (!platform_update)
|
|
state = ViewerClose;
|
|
break;
|
|
|
|
case WaitRemoteSetup:
|
|
if (script_debugger && !script_debugger->IsConnected())
|
|
{
|
|
__LOG_H__ << "Controller lost, waiting for controller connection...\n";
|
|
state = WaitRemoteController;
|
|
}
|
|
break; // state will be altered by the monitor
|
|
//----------------------------------------------------------------------
|
|
|
|
case SessionSetup:
|
|
if (OpenViewer() && OpenSession())
|
|
{
|
|
__LOG_H__ << "Session running.\n";
|
|
state = SessionRunning;
|
|
}
|
|
else
|
|
state = remote ? WaitRemoteSetup : SessionClose;
|
|
break;
|
|
|
|
case SessionRunning:
|
|
ExecuteSession();
|
|
|
|
if (state == SessionRunning)
|
|
{
|
|
if (script_debugger && !script_debugger->IsConnected())
|
|
state = SessionClose;
|
|
|
|
if (!platform_update)
|
|
state = SessionClose;
|
|
}
|
|
|
|
if (state != SessionRunning)
|
|
__LOG_H__ << "Closing session.\n";
|
|
break;
|
|
|
|
case SessionClose:
|
|
CloseSession();
|
|
CloseViewer();
|
|
|
|
if (remote)
|
|
{
|
|
script_debugger->Stop();
|
|
|
|
if (remote_fs.IsValid())
|
|
{
|
|
Platform::Get().io->Unmount(remote_fs);
|
|
remote_fs = NULL;
|
|
}
|
|
|
|
state = ViewerSetup;
|
|
}
|
|
else
|
|
state = ViewerClose;
|
|
break;
|
|
|
|
case ViewerClose:
|
|
break;
|
|
}
|
|
|
|
if (script_debugger)
|
|
while (script_debugger->async.Execute());
|
|
|
|
Platform::Get().Sleep(1);
|
|
return state;
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
void ViewerBase::Suspend() // app sent to background (iOS/Android)
|
|
{
|
|
if (script_vm && script_vm->SetupFunctionCall("OnSuspend"))
|
|
script_vm->DoFunctionCall();
|
|
if (mixer)
|
|
mixer->SuspendWorkerThread();
|
|
}
|
|
void ViewerBase::Resume() // app sent back to foreground (iOS/Android)
|
|
{
|
|
if (script_vm && script_vm->SetupFunctionCall("OnResume"))
|
|
script_vm->DoFunctionCall();
|
|
if (mixer)
|
|
mixer->ResumeWorkerThread();
|
|
|
|
if (project.IsValid())
|
|
project->clock->EatDeltaClock();
|
|
}
|
|
//------------------------------------------------------------------------------
|
|
|
|
//------------------------------------------------------------------------------
|
|
ViewerBase::ViewerBase()
|
|
{
|
|
state = ViewerSetup;
|
|
|
|
paused = false;
|
|
missing_resource = false;
|
|
|
|
time_start = 0;
|
|
time_live = 0;
|
|
frame_count = 0;
|
|
|
|
raytrace_width = -1;
|
|
raytrace_height = -1;
|
|
|
|
session_source = SessionSourceFilesystem;
|
|
session_type = SessionScene;
|
|
session_source_path = "./";
|
|
input_path = "scene.nms";
|
|
|
|
active_debug = false;
|
|
|
|
remote = false;
|
|
remote_port = 999;
|
|
|
|
tool_mode = false;
|
|
|
|
script_debugger = NULL;
|
|
|
|
safe_mode = false;
|
|
ignore_esc = false;
|
|
ignore_missing_resource = false;
|
|
fullscreen = false;
|
|
|
|
enable_profiler = false;
|
|
memory_profiler = false;
|
|
display_fps = false;
|
|
|
|
enable_pause = false;
|
|
|
|
for (int n = 0; n < 2; ++n)
|
|
profiler_font[n] = NULL;
|
|
fps_font = NULL;
|
|
|
|
width = 800;
|
|
height = 600;
|
|
aspect_ratio = 1;
|
|
}
|
|
//------------------------------------------------------------------------------
|