/* ----------------------------------------------------------------------------- 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; } //------------------------------------------------------------------------------