/* Crystal Space Entity Layer Copyright (C) 2005 by Jorrit Tyberghein This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "cssysdef.h" #include "csgeom/plane3.h" #include "celtool/camera.h" #include "propclass/zone.h" #include "propclass/region.h" #include "physicallayer/pl.h" #include "physicallayer/entity.h" #include "physicallayer/persist.h" #include "physicallayer/datatype.h" #include "behaviourlayer/behave.h" #include "iutil/objreg.h" #include "iutil/object.h" #include "iutil/eventq.h" #include "iutil/event.h" #include "iutil/evdefs.h" #include "iutil/virtclk.h" #include "iengine/engine.h" #include "iengine/camera.h" #include "iengine/region.h" #include "iengine/campos.h" #include "iengine/sector.h" #include "cstool/csview.h" #include "ivaria/view.h" #include "ivideo/graph3d.h" celPcCameraCommon::celPcCameraCommon (iObjectRegistry* object_reg) : celPcCommon (object_reg) { engine = CS_QUERY_REGISTRY (object_reg, iEngine); g3d = CS_QUERY_REGISTRY (object_reg, iGraphics3D); view = csPtr (new csView (engine, g3d)); rect_set = false; vc = CS_QUERY_REGISTRY (object_reg, iVirtualClock); CS_ASSERT (vc != 0); clear_zbuf = false; clear_screen = false; DisableDistanceClipping (); pl->CallbackEveryFrame ((iCelTimerListener*)this, CEL_EVENT_VIEW); } celPcCameraCommon::~celPcCameraCommon () { } void celPcCameraCommon::Report(iObjectRegistry* reg, const char* msg, ...) { va_list arg; va_start (arg, msg); csRef rep (CS_QUERY_REGISTRY (reg, iReporter)); if (rep) rep->ReportV (CS_REPORTER_SEVERITY_ERROR, "cel.persistence", msg, arg); else { csPrintfV (msg, arg); csPrintf ("\n"); fflush (stdout); } va_end (arg); } bool celPcCameraCommon::SetRegion (iPcRegion* newregion, bool point, const char *name) { region = newregion; zonemgr = 0; if (point) { csRef camera = SCF_QUERY_INTERFACE (this, iPcCamera); if (region) region->PointCamera (camera, name); else { // camera->GetCamera ()->SetSector (0); camera->GetCamera ()->GetTransform ().SetOrigin (csVector3(0,0,0)); } } return true; } bool celPcCameraCommon::SetZoneManager (iPcZoneManager* newzonemgr, bool point, const char* regionname, const char *name) { region = 0; zonemgr = newzonemgr; if (point) { csRef camera = SCF_QUERY_INTERFACE (this, iPcCamera); if (zonemgr) zonemgr->PointCamera (entity->GetName (), regionname, name); else { // camera->GetCamera ()->SetSector (0); camera->GetCamera ()->GetTransform ().SetOrigin (csVector3(0,0,0)); } } return true; } bool celPcCameraCommon::SetZoneManager (const char* entityname, bool point, const char* regionname, const char* name) { region = 0; csRef zoneent = pl->FindEntity (entityname); zonemgr = CEL_QUERY_PROPCLASS_ENT (zoneent, iPcZoneManager); if (point) { if (zonemgr) zonemgr->PointCamera (entity->GetName (), regionname, name); else GetCamera ()->GetTransform ().SetOrigin (csVector3(0,0,0)); } return true; } void celPcCameraCommon::SetRectangle (int x, int y, int w, int h) { rect_x = x; rect_y = y; rect_w = w; rect_h = h; view->SetRectangle (x, y, w, h); rect_set = true; } iCamera* celPcCameraCommon::GetCamera () const { return view->GetCamera (); } void celPcCameraCommon::DisableDistanceClipping () { fp.use_farplane = false; if (view && view->GetCamera ()) view->GetCamera ()->SetFarPlane (0); } void celPcCameraCommon::EnableFixedDistanceClipping (float dist) { fp.use_farplane = true; fp.fixed_distance = dist; SetDistanceClipping (dist); } void celPcCameraCommon::EnableAdaptiveDistanceClipping (float min_fps, float max_fps, float min_dist) { fp.use_farplane = true; fp.fixed_distance = -1.0f; fp.min_fps = min_fps; fp.max_fps = max_fps; fp.min_dist = min_dist; fp.fps_valid = false; fp.smooth_fps = 30.0f; fp.accumulated_elapsed = 0; fp.current_distance = -1.0f; } void celPcCameraCommon::SetDistanceClipping (float dist) { csVector3 v1 (0, 0, dist), v2 (0, 1, dist), v3 (1, 0, dist); csPlane3 p (v1, v2, v3); view->GetCamera ()->SetFarPlane (&p); fp.current_distance = dist; } // 1. qsqrt vs sqrt // 2. don't use static fields, may conflict with other camera instances // 3. use virtual clock and not csGetTicks(), movie recorder, speed, elapsed // 4. GetDistanceClipping() is expensive, better remember current distance. // 5. Adapts very slow to lower clipping plane. void celPcCameraCommon::AdaptDistanceClipping (csTicks elapsed_time) { if (!fp.use_farplane) return; if (fp.fixed_distance > 0.0f) return; float curr_fps; // FPS calculated from the last frame. // When we are called for the first time, we just initialize some variables // and exit. if (!fp.fps_valid) { fp.fps_valid = true; return; } if (!elapsed_time) curr_fps = 1000.0; else curr_fps = 1000.0 / float (elapsed_time); fp.smooth_fps = 0.5*curr_fps + 0.5*fp.smooth_fps; fp.accumulated_elapsed += elapsed_time; if (fp.accumulated_elapsed > 500) { fp.accumulated_elapsed = 0; float change; float curr_dist = fp.current_distance; if (curr_dist < 0.0f) curr_dist = FP_INIT_DIST; if (fp.smooth_fps < fp.min_fps) { change = (fp.min_fps - fp.smooth_fps); curr_dist -= change; if (curr_dist < fp.min_dist) curr_dist = fp.min_dist; SetDistanceClipping (curr_dist); } else if (fp.smooth_fps > fp.max_fps) { change = (fp.smooth_fps - fp.max_fps); curr_dist += change; if (curr_dist > FP_MAX_DIST) curr_dist = FP_MAX_DIST; SetDistanceClipping (curr_dist); } } } void celPcCameraCommon::SetAutoDraw(bool auto_draw) { if (auto_draw) pl->CallbackEveryFrame ((iCelTimerListener*)this, CEL_EVENT_VIEW); else pl->RemoveCallbackEveryFrame ((iCelTimerListener*)this, CEL_EVENT_VIEW); } void celPcCameraCommon::Draw () { // First get elapsed time from the virtual clock. csTicks elapsed_time = vc->GetElapsedTicks (); AdaptDistanceClipping (elapsed_time); // Tell 3D driver we're going to display 3D things. if (g3d->BeginDraw (engine->GetBeginDrawFlags () | CSDRAW_3DGRAPHICS | (clear_zbuf ? CSDRAW_CLEARZBUFFER : 0) | (clear_screen ? CSDRAW_CLEARSCREEN : 0))) view->Draw (); } void celPcCameraCommon::TickEveryFrame () { Draw(); } void celPcCameraCommon::SaveCommon (iCelDataBuffer* databuf) { csRef pc; if (region) pc = SCF_QUERY_INTERFACE (region, iCelPropertyClass); databuf->Add (pc); if (zonemgr) pc = SCF_QUERY_INTERFACE (zonemgr, iCelPropertyClass); databuf->Add (pc); databuf->Add (view->GetCamera()->GetSector()->QueryObject()->GetName()); const csTransform& tr = view->GetCamera ()->GetTransform (); databuf->Add (tr.GetO2TTranslation ()); databuf->Add (tr.GetO2T ().m11); databuf->Add (tr.GetO2T ().m12); databuf->Add (tr.GetO2T ().m13); databuf->Add (tr.GetO2T ().m21); databuf->Add (tr.GetO2T ().m22); databuf->Add (tr.GetO2T ().m23); databuf->Add (tr.GetO2T ().m31); databuf->Add (tr.GetO2T ().m32); databuf->Add (tr.GetO2T ().m33); databuf->Add (rect_set); databuf->Add ((uint16)rect_x); databuf->Add ((uint16)rect_y); databuf->Add ((uint16)rect_w); databuf->Add ((uint16)rect_h); databuf->Add (clear_zbuf); databuf->Add (clear_screen); } bool celPcCameraCommon::LoadCommon (iCelDataBuffer* databuf) { csMatrix3 m_o2t; csVector3 v_o2t; iCelPropertyClass* pc = databuf->GetPC (); if (pc) { region = SCF_QUERY_INTERFACE (pc, iPcRegion); if (region) SetRegion (region, false, 0); } pc = databuf->GetPC (); if (pc) { zonemgr = SCF_QUERY_INTERFACE (pc, iPcZoneManager); if (zonemgr) SetZoneManager (zonemgr, false, 0, 0); } const char* sectname = databuf->GetString ()->GetData (); iSector* sector; if (region) sector = region->FindSector (sectname); else sector = engine->FindSector (sectname); if (!sector) { Report (object_reg,"Illegal sector '%s' specified. Cannot load.", sectname); return false; } databuf->GetVector3 (v_o2t); m_o2t.m11 = databuf->GetFloat (); m_o2t.m12 = databuf->GetFloat (); m_o2t.m13 = databuf->GetFloat (); m_o2t.m21 = databuf->GetFloat (); m_o2t.m22 = databuf->GetFloat (); m_o2t.m23 = databuf->GetFloat (); m_o2t.m31 = databuf->GetFloat (); m_o2t.m32 = databuf->GetFloat (); m_o2t.m33 = databuf->GetFloat (); view->GetCamera ()->SetSector (sector); csOrthoTransform tr (m_o2t, v_o2t); view->GetCamera ()->SetTransform (tr); rect_set = databuf->GetBool (); rect_x = databuf->GetUInt16 (); rect_y = databuf->GetUInt16 (); rect_w = databuf->GetUInt16 (); rect_h = databuf->GetUInt16 (); clear_zbuf = databuf->GetBool (); clear_screen = databuf->GetBool (); if (rect_set) view->SetRectangle (rect_x, rect_y, rect_w, rect_h); return true; } #define CAMERA_SERIAL 1 csPtr celPcCameraCommon::Save () { csRef databuf = pl->CreateDataBuffer (CAMERA_SERIAL); SaveCommon (databuf); return csPtr (databuf); } bool celPcCameraCommon::Load (iCelDataBuffer* databuf) { int serialnr = databuf->GetSerialNumber (); if (serialnr != CAMERA_SERIAL) { Report (object_reg, "serialnr != CAMERA_SERIAL. Cannot load."); return false; } return LoadCommon (databuf); } //---------------------------------------------------------------------------