Index: ps/trunk/source/graphics/Camera.h =================================================================== --- ps/trunk/source/graphics/Camera.h (revision 3918) +++ ps/trunk/source/graphics/Camera.h (revision 3919) @@ -1,120 +1,119 @@ //*********************************************************** // // Name: Camera.H // Last Update: 24/2/02 // Author: Poya Manouchehri // // Description: CCamera holds a view and a projection matrix. // It also has a frustum which can be used to // cull objects for rendering. // //*********************************************************** #ifndef CAMERA_H #define CAMERA_H #include "Frustum.h" #include "Matrix3D.h" extern int g_mouse_x, g_mouse_y; // view port struct SViewPort { unsigned int m_X; unsigned int m_Y; unsigned int m_Width; unsigned int m_Height; }; class CCamera { public: CCamera (); ~CCamera (); // Methods for projection void SetProjection (CMatrix3D *proj) { m_ProjMat = *proj; } void SetProjection (float nearp, float farp, float fov); void SetProjectionTile (int tiles, int tile_x, int tile_y); CMatrix3D GetProjection () { return m_ProjMat; } // Updates the frustum planes. Should be called // everytime the view or projection matrices are // altered. void UpdateFrustum (); CFrustum GetFrustum () { return m_ViewFrustum; } void SetViewPort (SViewPort *viewport); SViewPort GetViewPort () { return m_ViewPort; } // getters float GetNearPlane() const { return m_NearPlane; } float GetFarPlane() const { return m_FarPlane; } float GetFOV() const { return m_FOV; } // calculate and return the position of the 8 points of the frustum in world space void GetFrustumPoints(CVector3D pts[8]) const; // return four points in camera space at given distance from camera void GetCameraPlanePoints(float dist,CVector3D pts[4]) const; // Build a ray passing through the screen coordinate (px, py) and the camera ///////////////////////////////////////////////////////////////////////////////////////// // BuildCameraRay: calculate origin and ray direction of a ray through // the pixel (px,py) on the screen void BuildCameraRay(int px, int py, CVector3D& origin, CVector3D& dir); // BuildCameraRay: as previous, using global mouse position void BuildCameraRay(CVector3D& origin, CVector3D& dir) { BuildCameraRay(g_mouse_x, g_mouse_y, origin, dir); } // General helpers that seem to fit here // Get the screen-space coordinates corresponding to a given world-space position void GetScreenCoordinates(const CVector3D& world, float& x, float& y) const; // Get the point on the terrain corresponding to pixel (px,py) (or the mouse coordinates) CVector3D GetWorldCoordinates(int px, int py); CVector3D GetWorldCoordinates() { return GetWorldCoordinates(g_mouse_x, g_mouse_y); } // Get the point on the plane at height h corresponding to pixel (px,py) CVector3D GetWorldCoordinates(int px, int py, float h); // Get the point on the terrain the camera is pointing towards CVector3D GetFocus(); // Build an orientation matrix from camera position, camera focus point, and up-vector void LookAt(const CVector3D& camera, const CVector3D& orientation, const CVector3D& up); // Build an orientation matrix from camera position, camera orientation, and up-vector void LookAlong(CVector3D camera, CVector3D focus, CVector3D up); /** * Render: Renders the camera's frustum in world space. * The caller should set the color using glColorXy before calling Render. * * @param intermediates determines how many intermediate distance planes should * be hinted at between the near and far planes */ void Render(uint intermediates = 0) const; public: // This is the orientation matrix. The inverse of this // is the view matrix CMatrix3D m_Orientation; - - private: - // keep the projection matrix private - // so others can't fiddle with it. + + // Should not be tweaked externally if possible CMatrix3D m_ProjMat; + private: float m_NearPlane; float m_FarPlane; float m_FOV; SViewPort m_ViewPort; CFrustum m_ViewFrustum; }; #endif Index: ps/trunk/source/renderer/Renderer.cpp =================================================================== --- ps/trunk/source/renderer/Renderer.cpp (revision 3918) +++ ps/trunk/source/renderer/Renderer.cpp (revision 3919) @@ -1,1756 +1,1769 @@ /////////////////////////////////////////////////////////////////////////////// // // Name: Renderer.cpp // Author: Rich Cross // Contact: rich@wildfiregames.com // // Description: OpenGL renderer class; a higher level interface // on top of OpenGL to handle rendering the basic visual games // types - terrain, models, sprites, particles etc // /////////////////////////////////////////////////////////////////////////////// #include "precompiled.h" #include #include #include #include "Renderer.h" #include "Terrain.h" #include "Matrix3D.h" #include "MathUtil.h" #include "Camera.h" #include "Texture.h" #include "LightEnv.h" #include "Terrain.h" #include "CLogger.h" #include "ps/Game.h" #include "Profile.h" #include "Game.h" #include "World.h" #include "Player.h" #include "LOSManager.h" #include "Model.h" #include "ModelDef.h" #include "ogl.h" #include "lib/path_util.h" #include "lib/res/res.h" #include "lib/res/file/file.h" #include "lib/res/graphics/tex.h" #include "lib/res/graphics/ogl_tex.h" #include "ps/Loader.h" #include "ps/ProfileViewer.h" #include "graphics/ParticleEngine.h" #include "graphics/DefaultEmitter.h" #include "renderer/FixedFunctionModelRenderer.h" #include "renderer/HWLightingModelRenderer.h" #include "renderer/InstancingModelRenderer.h" #include "renderer/ModelRenderer.h" #include "renderer/PlayerRenderer.h" #include "renderer/RenderModifiers.h" #include "renderer/RenderPathVertexShader.h" #include "renderer/ShadowMap.h" #include "renderer/TerrainOverlay.h" #include "renderer/TerrainRenderer.h" #include "renderer/TransparencyRenderer.h" #include "renderer/WaterManager.h" #include "renderer/SkyManager.h" #define LOG_CATEGORY "graphics" /////////////////////////////////////////////////////////////////////////////////// // CRendererStatsTable - Profile display of rendering stats /** * Class CRendererStatsTable: Implementation of AbstractProfileTable to * display the renderer stats in-game. * * Accesses CRenderer::m_Stats by keeping the reference passed to the * constructor. */ class CRendererStatsTable : public AbstractProfileTable { public: CRendererStatsTable(const CRenderer::Stats& st); // Implementation of AbstractProfileTable interface CStr GetName(); CStr GetTitle(); uint GetNumberRows(); const std::vector& GetColumns(); CStr GetCellText(uint row, uint col); AbstractProfileTable* GetChild(uint row); private: /// Reference to the renderer singleton's stats const CRenderer::Stats& Stats; /// Column descriptions std::vector columnDescriptions; enum { Row_Counter = 0, Row_DrawCalls, Row_TerrainTris, Row_ModelTris, Row_BlendSplats, // Must be last to count number of rows NumberRows }; NO_COPY_CTOR(CRendererStatsTable); }; // Construction CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st) : Stats(st) { columnDescriptions.push_back(ProfileColumn("Name", 230)); columnDescriptions.push_back(ProfileColumn("Value", 100)); } // Implementation of AbstractProfileTable interface CStr CRendererStatsTable::GetName() { return "renderer"; } CStr CRendererStatsTable::GetTitle() { return "Renderer statistics"; } uint CRendererStatsTable::GetNumberRows() { return NumberRows; } const std::vector& CRendererStatsTable::GetColumns() { return columnDescriptions; } CStr CRendererStatsTable::GetCellText(uint row, uint col) { char buf[256]; switch(row) { case Row_Counter: if (col == 0) return "counter"; snprintf(buf, sizeof(buf), "%d", Stats.m_Counter); return buf; case Row_DrawCalls: if (col == 0) return "# draw calls"; snprintf(buf, sizeof(buf), "%d", Stats.m_DrawCalls); return buf; case Row_TerrainTris: if (col == 0) return "# terrain tris"; snprintf(buf, sizeof(buf), "%d", Stats.m_TerrainTris); return buf; case Row_ModelTris: if (col == 0) return "# model tris"; snprintf(buf, sizeof(buf), "%d", Stats.m_ModelTris); return buf; case Row_BlendSplats: if (col == 0) return "# blend splats"; snprintf(buf, sizeof(buf), "%d", Stats.m_BlendSplats); return buf; default: return "???"; } } AbstractProfileTable* CRendererStatsTable::GetChild(uint UNUSED(row)) { return 0; } /////////////////////////////////////////////////////////////////////////////////// // CRenderer implementation enum { AmbientDiffuse = 0, OnlyDiffuse, NumVertexTypes }; /** * Struct CRendererInternals: Truly hide data that is supposed to be hidden * in this structure so it won't even appear in header files. */ struct CRendererInternals { /// true if CRenderer::Open has been called bool IsOpen; /// Table to display renderer stats in-game via profile system CRendererStatsTable profileTable; /// Water manager WaterManager waterManager; /// Sky manager SkyManager skyManager; /// Terrain renderer TerrainRenderer* terrainRenderer; /// Shadow map ShadowMap* shadow; /// Various model renderers struct Models { // The following model renderers are aliases for the appropriate real_* // model renderers (depending on hardware availability and current settings) // and must be used for actual model submission and rendering ModelRenderer* Normal; ModelRenderer* NormalInstancing; ModelRenderer* Player; ModelRenderer* PlayerInstancing; ModelRenderer* Transp; // "Palette" of available ModelRenderers. Do not use these directly for // rendering and submission; use the aliases above instead. ModelRenderer* pal_NormalFF[NumVertexTypes]; ModelRenderer* pal_PlayerFF[NumVertexTypes]; ModelRenderer* pal_NormalHWLit[NumVertexTypes]; ModelRenderer* pal_PlayerHWLit[NumVertexTypes]; ModelRenderer* pal_NormalInstancing[NumVertexTypes]; ModelRenderer* pal_PlayerInstancing[NumVertexTypes]; ModelRenderer* pal_TranspFF[NumVertexTypes]; ModelRenderer* pal_TranspHWLit[NumVertexTypes]; ModelRenderer* pal_TranspSortAll; ModelVertexRendererPtr VertexFF[NumVertexTypes]; ModelVertexRendererPtr VertexHWLit[NumVertexTypes]; ModelVertexRendererPtr VertexInstancing[NumVertexTypes]; ModelVertexRendererPtr VertexPolygonSort; // generic RenderModifiers that are supposed to be used directly RenderModifierPtr ModWireframe; RenderModifierPtr ModSolidColor; RenderModifierPtr ModTransparentShadow; RenderModifierPtr ModTransparentDepthShadow; // RenderModifiers that are selected from the palette below RenderModifierPtr ModNormal; RenderModifierPtr ModPlayer; RenderModifierPtr ModTransparent; // Palette of available RenderModifiers RenderModifierPtr ModPlain; LitRenderModifierPtr ModPlainLit; RenderModifierPtr ModPlayerUnlit; LitRenderModifierPtr ModPlayerLit; RenderModifierPtr ModTransparentUnlit; LitRenderModifierPtr ModTransparentLit; } Model; CRendererInternals() : IsOpen(false), profileTable(g_Renderer.m_Stats) { terrainRenderer = new TerrainRenderer(); shadow = new ShadowMap(); for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType) { Model.pal_NormalFF[vertexType] = 0; Model.pal_PlayerFF[vertexType] = 0; Model.pal_TranspFF[vertexType] = 0; Model.pal_NormalHWLit[vertexType] = 0; Model.pal_PlayerHWLit[vertexType] = 0; Model.pal_TranspHWLit[vertexType] = 0; Model.pal_NormalInstancing[vertexType] = 0; Model.pal_PlayerInstancing[vertexType] = 0; } Model.pal_TranspSortAll = 0; Model.Normal = 0; Model.NormalInstancing = 0; Model.Player = 0; Model.PlayerInstancing = 0; Model.Transp = 0; } ~CRendererInternals() { delete shadow; delete terrainRenderer; } bool CanUseRenderPathVertexShader() { for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType) { if (!Model.pal_NormalHWLit[vertexType] || !Model.pal_PlayerHWLit[vertexType]) return false; } return true; } }; /////////////////////////////////////////////////////////////////////////////////// // CRenderer destructor CRenderer::CRenderer() { m = new CRendererInternals; m_WaterManager = &m->waterManager; m_SkyManager = &m->skyManager; g_ProfileViewer.AddRootTable(&m->profileTable); m_Width=0; m_Height=0; m_Depth=0; m_FrameCounter=0; m_TerrainRenderMode=SOLID; m_ModelRenderMode=SOLID; m_ClearColor[0]=m_ClearColor[1]=m_ClearColor[2]=m_ClearColor[3]=0; m_SortAllTransparent = false; m_FastNormals = true; m_DisplayFrustum = false; m_DisableCopyShadow = false; m_FastPlayerColor = true; m_VertexShader = 0; m_Options.m_NoVBO=false; m_Options.m_NoFramebufferObject = false; m_Options.m_Shadows=true; m_Options.m_RenderPath = RP_DEFAULT; m_ShadowZBias = 0.01f; for (uint i=0;iModel.pal_NormalFF[vertexType]; delete m->Model.pal_PlayerFF[vertexType]; delete m->Model.pal_TranspFF[vertexType]; delete m->Model.pal_NormalHWLit[vertexType]; delete m->Model.pal_PlayerHWLit[vertexType]; delete m->Model.pal_TranspHWLit[vertexType]; delete m->Model.pal_NormalInstancing[vertexType]; delete m->Model.pal_PlayerInstancing[vertexType]; } delete m->Model.pal_TranspSortAll; // general delete m_VertexShader; m_VertexShader = 0; CParticleEngine::GetInstance()->cleanup(); // we no longer UnloadAlphaMaps / UnloadWaterTextures here - // that is the responsibility of the module that asked for // them to be loaded (i.e. CGameView). delete m; } /////////////////////////////////////////////////////////////////////////////////// // EnumCaps: build card cap bits void CRenderer::EnumCaps() { // assume support for nothing m_Caps.m_VBO=false; m_Caps.m_TextureBorderClamp=false; m_Caps.m_GenerateMipmaps=false; m_Caps.m_VertexShader=false; m_Caps.m_DepthTextureShadows = false; m_Caps.m_FramebufferObject = false; // now start querying extensions if (!m_Options.m_NoVBO) { if (oglHaveExtension("GL_ARB_vertex_buffer_object")) { m_Caps.m_VBO=true; } } if (oglHaveExtension("GL_ARB_texture_border_clamp")) { m_Caps.m_TextureBorderClamp=true; } if (oglHaveExtension("GL_SGIS_generate_mipmap")) { m_Caps.m_GenerateMipmaps=true; } if (0 == oglHaveExtensions(0, "GL_ARB_shader_objects", "GL_ARB_shading_language_100", 0)) { if (oglHaveExtension("GL_ARB_vertex_shader")) m_Caps.m_VertexShader=true; } if (0 == oglHaveExtensions(0, "GL_ARB_shadow", "GL_ARB_depth_texture", 0)) { // According to Delphi3d.net, all relevant graphics chips that support depth textures // (i.e. Geforce3+, Radeon9500+, even i915) also have >= 4 TMUs, so this restriction // isn't actually a restriction, and it helps with integrating depth texture // shadows into rendering paths. if (ogl_max_tex_units >= 4) m_Caps.m_DepthTextureShadows = true; } if (!m_Options.m_NoFramebufferObject) { if (oglHaveExtension("GL_EXT_framebuffer_object")) m_Caps.m_FramebufferObject = true; } } bool CRenderer::Open(int width, int height, int depth) { m->IsOpen = true; // Must query card capabilities before creating renderers that depend // on card capabilities. EnumCaps(); m->shadow->SetUseDepthTexture(true); m_VertexShader = new RenderPathVertexShader; if (!m_VertexShader->Init()) { delete m_VertexShader; m_VertexShader = 0; } // model rendering m->Model.VertexFF[AmbientDiffuse] = ModelVertexRendererPtr(new FixedFunctionModelRenderer(false)); m->Model.VertexFF[OnlyDiffuse] = ModelVertexRendererPtr(new FixedFunctionModelRenderer(true)); if (HWLightingModelRenderer::IsAvailable()) { m->Model.VertexHWLit[AmbientDiffuse] = ModelVertexRendererPtr(new HWLightingModelRenderer(false)); m->Model.VertexHWLit[OnlyDiffuse] = ModelVertexRendererPtr(new HWLightingModelRenderer(true)); } if (InstancingModelRenderer::IsAvailable()) { m->Model.VertexInstancing[AmbientDiffuse] = ModelVertexRendererPtr(new InstancingModelRenderer(false)); m->Model.VertexInstancing[OnlyDiffuse] = ModelVertexRendererPtr(new InstancingModelRenderer(true)); } m->Model.VertexPolygonSort = ModelVertexRendererPtr(new PolygonSortModelRenderer); for(int vertexType = 0; vertexType < NumVertexTypes; ++vertexType) { m->Model.pal_NormalFF[vertexType] = new BatchModelRenderer(m->Model.VertexFF[vertexType]); m->Model.pal_PlayerFF[vertexType] = new BatchModelRenderer(m->Model.VertexFF[vertexType]); m->Model.pal_TranspFF[vertexType] = new SortModelRenderer(m->Model.VertexFF[vertexType]); if (m->Model.VertexHWLit[vertexType]) { m->Model.pal_NormalHWLit[vertexType] = new BatchModelRenderer(m->Model.VertexHWLit[vertexType]); m->Model.pal_PlayerHWLit[vertexType] = new BatchModelRenderer(m->Model.VertexHWLit[vertexType]); m->Model.pal_TranspHWLit[vertexType] = new SortModelRenderer(m->Model.VertexHWLit[vertexType]); } if (m->Model.VertexInstancing[vertexType]) { m->Model.pal_NormalInstancing[vertexType] = new BatchModelRenderer(m->Model.VertexInstancing[vertexType]); m->Model.pal_PlayerInstancing[vertexType] = new BatchModelRenderer(m->Model.VertexInstancing[vertexType]); } } m->Model.pal_TranspSortAll = new SortModelRenderer(m->Model.VertexPolygonSort); m->Model.ModWireframe = RenderModifierPtr(new WireframeRenderModifier); m->Model.ModPlain = RenderModifierPtr(new PlainRenderModifier); m->Model.ModPlainLit = LitRenderModifierPtr(new PlainLitRenderModifier); SetFastPlayerColor(true); m->Model.ModPlayerLit = LitRenderModifierPtr(new LitPlayerColorRender); m->Model.ModSolidColor = RenderModifierPtr(new SolidColorRenderModifier); m->Model.ModTransparentUnlit = RenderModifierPtr(new TransparentRenderModifier); m->Model.ModTransparentLit = LitRenderModifierPtr(new LitTransparentRenderModifier); m->Model.ModTransparentShadow = RenderModifierPtr(new TransparentShadowRenderModifier); m->Model.ModTransparentDepthShadow = RenderModifierPtr(new TransparentDepthShadowModifier); // Particle engine CParticleEngine::GetInstance()->initParticleSystem(); CEmitter *pEmitter = new CDefaultEmitter(1000, -1); CParticleEngine::GetInstance()->addEmitter(pEmitter); // Dimensions m_Width = width; m_Height = height; m_Depth = depth; // set packing parameters glPixelStorei(GL_PACK_ALIGNMENT,1); glPixelStorei(GL_UNPACK_ALIGNMENT,1); // setup default state glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); glCullFace(GL_BACK); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); GLint bits; glGetIntegerv(GL_DEPTH_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: depth bits %d",bits); glGetIntegerv(GL_STENCIL_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: stencil bits %d",bits); glGetIntegerv(GL_ALPHA_BITS,&bits); LOG(NORMAL, LOG_CATEGORY, "CRenderer::Open: alpha bits %d",bits); // Validate the currently selected render path SetRenderPath(m_Options.m_RenderPath); return true; } // resize renderer view void CRenderer::Resize(int width,int height) { // need to recreate the shadow map object to resize the shadow texture m->shadow->RecreateTexture(); m_Width = width; m_Height = height; } ////////////////////////////////////////////////////////////////////////////////////////// // SetOptionBool: set boolean renderer option void CRenderer::SetOptionBool(enum Option opt,bool value) { switch (opt) { case OPT_NOVBO: m_Options.m_NoVBO=value; break; case OPT_NOFRAMEBUFFEROBJECT: m_Options.m_NoFramebufferObject=value; break; case OPT_SHADOWS: m_Options.m_Shadows=value; break; case OPT_FANCYWATER: m_Options.m_FancyWater=value; break; default: debug_warn("CRenderer::SetOptionBool: unknown option"); break; } } ////////////////////////////////////////////////////////////////////////////////////////// // GetOptionBool: get boolean renderer option bool CRenderer::GetOptionBool(enum Option opt) const { switch (opt) { case OPT_NOVBO: return m_Options.m_NoVBO; case OPT_NOFRAMEBUFFEROBJECT: return m_Options.m_NoFramebufferObject; case OPT_SHADOWS: return m_Options.m_Shadows; case OPT_FANCYWATER: return m_Options.m_FancyWater; default: debug_warn("CRenderer::GetOptionBool: unknown option"); break; } return false; } ////////////////////////////////////////////////////////////////////////////////////////// // SetOptionColor: set color renderer option void CRenderer::SetOptionColor(Option UNUSED(opt),const RGBAColor& UNUSED(value)) { // switch (opt) { // default: debug_warn("CRenderer::SetOptionColor: unknown option"); // break; // } } void CRenderer::SetOptionFloat(enum Option opt, float val) { switch(opt) { case OPT_LODBIAS: m_Options.m_LodBias = val; break; default: debug_warn("CRenderer::SetOptionFloat: unknown option"); break; } } ////////////////////////////////////////////////////////////////////////////////////////// // GetOptionColor: get color renderer option const RGBAColor& CRenderer::GetOptionColor(Option UNUSED(opt)) const { static const RGBAColor defaultColor(1.0f,1.0f,1.0f,1.0f); // switch (opt) { // default: debug_warn("CRenderer::GetOptionColor: unknown option"); // break; // } return defaultColor; } ////////////////////////////////////////////////////////////////////////////////////////// // SetRenderPath: Select the preferred render path. // This may only be called before Open(), because the layout of vertex arrays and other // data may depend on the chosen render path. void CRenderer::SetRenderPath(RenderPath rp) { if (!m->IsOpen) { // Delay until Open() is called. m_Options.m_RenderPath = rp; return; } // Renderer has been opened, so validate the selected renderpath if (rp == RP_DEFAULT) { if (m->CanUseRenderPathVertexShader()) rp = RP_VERTEXSHADER; else rp = RP_FIXED; } if (rp == RP_VERTEXSHADER) { if (!m->CanUseRenderPathVertexShader()) { LOG(WARNING, LOG_CATEGORY, "Falling back to fixed function\n"); rp = RP_FIXED; } } m_Options.m_RenderPath = rp; } CStr CRenderer::GetRenderPathName(RenderPath rp) { switch(rp) { case RP_DEFAULT: return "default"; case RP_FIXED: return "fixed"; case RP_VERTEXSHADER: return "vertexshader"; default: return "(invalid)"; } } CRenderer::RenderPath CRenderer::GetRenderPathByName(CStr name) { if (name == "fixed") return RP_FIXED; if (name == "vertexshader") return RP_VERTEXSHADER; if (name == "default") return RP_DEFAULT; LOG(WARNING, LOG_CATEGORY, "Unknown render path name '%hs', assuming 'default'", name.c_str()); return RP_DEFAULT; } ////////////////////////////////////////////////////////////////////////////////////////// // SetFastPlayerColor void CRenderer::SetFastPlayerColor(bool fast) { m_FastPlayerColor = fast; if (m_FastPlayerColor) { if (!FastPlayerColorRender::IsAvailable()) { LOG(WARNING, LOG_CATEGORY, "Falling back to slower player color rendering."); m_FastPlayerColor = false; } } if (m_FastPlayerColor) m->Model.ModPlayerUnlit = RenderModifierPtr(new FastPlayerColorRender); else m->Model.ModPlayerUnlit = RenderModifierPtr(new SlowPlayerColorRender); } ////////////////////////////////////////////////////////////////////////////////////////// // BeginFrame: signal frame start void CRenderer::BeginFrame() { if(!g_Game || !g_Game->IsGameStarted()) return; // bump frame counter m_FrameCounter++; if (m_VertexShader) m_VertexShader->BeginFrame(); // zero out all the per-frame stats m_Stats.Reset(); // init per frame stuff m->shadow->SetupFrame(m_CullCamera, m_LightEnv->GetSunDir()); // choose model renderers for this frame int vertexType; if (m_Options.m_Shadows && m->shadow->GetUseDepthTexture()) { vertexType = OnlyDiffuse; m->Model.ModNormal = m->Model.ModPlainLit; m->Model.ModPlainLit->SetShadowMap(m->shadow); m->Model.ModPlainLit->SetLightEnv(m_LightEnv); m->Model.ModPlayer = m->Model.ModPlayerLit; m->Model.ModPlayerLit->SetShadowMap(m->shadow); m->Model.ModPlayerLit->SetLightEnv(m_LightEnv); m->Model.ModTransparent = m->Model.ModTransparentLit; m->Model.ModTransparentLit->SetShadowMap(m->shadow); m->Model.ModTransparentLit->SetLightEnv(m_LightEnv); } else { vertexType = AmbientDiffuse; m->Model.ModNormal = m->Model.ModPlain; m->Model.ModPlayer = m->Model.ModPlayerUnlit; m->Model.ModTransparent = m->Model.ModTransparentUnlit; } if (m_Options.m_RenderPath == RP_VERTEXSHADER) { debug_assert(m->Model.pal_NormalHWLit[vertexType] != 0); if (m->Model.pal_NormalInstancing) m->Model.NormalInstancing = m->Model.pal_NormalInstancing[vertexType]; else m->Model.NormalInstancing = m->Model.pal_NormalHWLit[vertexType]; m->Model.Normal = m->Model.pal_NormalHWLit[vertexType]; if (m->Model.pal_PlayerInstancing) m->Model.PlayerInstancing = m->Model.pal_PlayerInstancing[vertexType]; else m->Model.PlayerInstancing = m->Model.pal_PlayerHWLit[vertexType]; m->Model.Player = m->Model.pal_PlayerHWLit[vertexType]; } else { m->Model.NormalInstancing = m->Model.pal_NormalFF[vertexType]; m->Model.Normal = m->Model.pal_NormalFF[vertexType]; m->Model.PlayerInstancing = m->Model.pal_PlayerFF[vertexType]; m->Model.Player = m->Model.pal_PlayerFF[vertexType]; } if (m_SortAllTransparent) m->Model.Transp = m->Model.pal_TranspSortAll; else if (m_Options.m_RenderPath == RP_VERTEXSHADER) m->Model.Transp = m->Model.pal_TranspHWLit[vertexType]; else m->Model.Transp = m->Model.pal_TranspFF[vertexType]; } ////////////////////////////////////////////////////////////////////////////////////////// // SetClearColor: set color used to clear screen in BeginFrame() void CRenderer::SetClearColor(u32 color) { m_ClearColor[0]=float(color & 0xff)/255.0f; m_ClearColor[1]=float((color>>8) & 0xff)/255.0f; m_ClearColor[2]=float((color>>16) & 0xff)/255.0f; m_ClearColor[3]=float((color>>24) & 0xff)/255.0f; } void CRenderer::RenderShadowMap() { PROFILE( "render shadow map" ); m->shadow->BeginRender(); float shadowTransp = m_LightEnv->GetTerrainShadowTransparency(); glColor3f(shadowTransp, shadowTransp, shadowTransp); // Figure out transparent rendering strategy RenderModifierPtr transparentShadows = m->Model.ModTransparentShadow; if (m->shadow->GetUseDepthTexture()) transparentShadows = m->Model.ModTransparentDepthShadow; // Render all closed models (i.e. models where rendering back faces will produce // the correct result) glCullFace(GL_FRONT); if (m->shadow->GetUseDepthTexture()) m->terrainRenderer->RenderPatches(); glCullFace(GL_BACK); // Render models that aren't closed glDisable(GL_CULL_FACE); m->Model.Normal->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS); if (m->Model.Normal != m->Model.NormalInstancing) m->Model.NormalInstancing->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS); m->Model.Player->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS); if (m->Model.Player != m->Model.PlayerInstancing) m->Model.PlayerInstancing->Render(m->Model.ModSolidColor, MODELFLAG_CASTSHADOWS); m->Model.Transp->Render(transparentShadows, MODELFLAG_CASTSHADOWS); glEnable(GL_CULL_FACE); glColor3f(1.0, 1.0, 1.0); m->shadow->EndRender(); } void CRenderer::RenderPatches() { PROFILE( "render patches" ); // switch on wireframe if we need it if (m_TerrainRenderMode==WIREFRAME) { MICROLOG(L"wireframe on"); glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } // render all the patches, including blend pass MICROLOG(L"render patch submissions"); m->terrainRenderer->RenderTerrain(m_Options.m_Shadows ? m->shadow : 0); if (m_TerrainRenderMode==WIREFRAME) { // switch wireframe off again MICROLOG(L"wireframe off"); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } else if (m_TerrainRenderMode==EDGED_FACES) { // edged faces: need to make a second pass over the data: // first switch on wireframe glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); // setup some renderstate .. glDepthMask(0); SetTexture(0,0); glColor4f(1,1,1,0.35f); glLineWidth(2.0f); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); // render tiles edges m->terrainRenderer->RenderPatches(); // set color for outline glColor3f(0,0,1); glLineWidth(4.0f); // render outline of each patch m->terrainRenderer->RenderOutlines(); // .. and restore the renderstates glDisable(GL_BLEND); glDepthMask(1); // restore fill mode, and we're done glLineWidth(1.0f); glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } } void CRenderer::RenderModels() { PROFILE( "render models "); // switch on wireframe if we need it if (m_ModelRenderMode==WIREFRAME) { glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } m->Model.Normal->Render(m->Model.ModNormal, 0); m->Model.Player->Render(m->Model.ModPlayer, 0); if (m->Model.Normal != m->Model.NormalInstancing) m->Model.NormalInstancing->Render(m->Model.ModNormal, 0); if (m->Model.Player != m->Model.PlayerInstancing) m->Model.PlayerInstancing->Render(m->Model.ModPlayer, 0); if (m_ModelRenderMode==WIREFRAME) { // switch wireframe off again glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } else if (m_ModelRenderMode==EDGED_FACES) { m->Model.Normal->Render(m->Model.ModWireframe, 0); m->Model.Player->Render(m->Model.ModWireframe, 0); if (m->Model.Normal != m->Model.NormalInstancing) m->Model.NormalInstancing->Render(m->Model.ModWireframe, 0); if (m->Model.Player != m->Model.PlayerInstancing) m->Model.PlayerInstancing->Render(m->Model.ModWireframe, 0); } } void CRenderer::RenderTransparentModels() { PROFILE( "render transparent models "); // switch on wireframe if we need it if (m_ModelRenderMode==WIREFRAME) { glPolygonMode(GL_FRONT_AND_BACK,GL_LINE); } m->Model.Transp->Render(m->Model.ModTransparent, 0); if (m_ModelRenderMode==WIREFRAME) { // switch wireframe off again glPolygonMode(GL_FRONT_AND_BACK,GL_FILL); } else if (m_ModelRenderMode==EDGED_FACES) { m->Model.Transp->Render(m->Model.ModWireframe, 0); } } /////////////////////////////////////////////////////////////////////////////////////////////////// // GetModelViewProjectionMatrix: save the current OpenGL model-view-projection matrix CMatrix3D CRenderer::GetModelViewProjectionMatrix() { CMatrix3D proj; CMatrix3D view; glGetFloatv( GL_PROJECTION_MATRIX, &proj._11 ); glGetFloatv( GL_MODELVIEW_MATRIX, &view._11 ); return( proj * view ); } /////////////////////////////////////////////////////////////////////////////////////////////////// // SetObliqueFrustumClipping: change the near plane to the given clip plane (in world space) // Based on code from Game Programming Gems 5, from http://www.terathon.com/code/oblique.html // - cp is a clip plane in camera space (cp.dot(v) = 0 for any vector v on the plane) // - sign is 1 or -1, to specify the side to clip on void CRenderer::SetObliqueFrustumClipping(const CVector4D& cp, int sign) { float matrix[16]; CVector4D q; // First, we'll convert the given clip plane to camera space, then we'll // Get the view matrix and normal matrix (top 3x3 part of view matrix) CMatrix3D viewMatrix; m_ViewCamera.m_Orientation.GetInverse(viewMatrix); CMatrix3D normalMatrix = viewMatrix; normalMatrix._14 = 0; normalMatrix._24 = 0; normalMatrix._34 = 0; normalMatrix._44 = 1; normalMatrix._41 = 0; normalMatrix._42 = 0; normalMatrix._43 = 0; // Convert the normal to camera space CVector4D planeNormal(cp.m_X, cp.m_Y, cp.m_Z, 0); planeNormal = normalMatrix.Transform(planeNormal); planeNormal.normalize(); // Find a point on the plane: we'll take the normal times -D float oldD = cp.m_W; CVector4D pointOnPlane(-oldD * cp.m_X, -oldD * cp.m_Y, -oldD * cp.m_Z, 1); pointOnPlane = viewMatrix.Transform(pointOnPlane); float newD = -pointOnPlane.dot(planeNormal); // Now create a clip plane from the new normal and new D CVector4D camPlane = planeNormal; camPlane.m_W = newD; // Grab the current projection matrix from OpenGL glGetFloatv(GL_PROJECTION_MATRIX, matrix); // Calculate the clip-space corner point opposite the clipping plane // as (sgn(camPlane.x), sgn(camPlane.y), 1, 1) and // transform it into camera space by multiplying it // by the inverse of the projection matrix q.m_X = (sgn(camPlane.m_X) + matrix[8]) / matrix[0]; q.m_Y = (sgn(camPlane.m_Y) + matrix[9]) / matrix[5]; q.m_Z = -1.0f; q.m_W = (1.0f + matrix[10]) / matrix[14]; // Calculate the scaled plane vector CVector4D c = camPlane * (sign * 2.0f / camPlane.dot(q)); // Replace the third row of the projection matrix matrix[2] = c.m_X; matrix[6] = c.m_Y; matrix[10] = c.m_Z + 1.0f; matrix[14] = c.m_W; // Load it back into OpenGL glMatrixMode(GL_PROJECTION); glLoadMatrixf(matrix); glMatrixMode(GL_MODELVIEW); } /////////////////////////////////////////////////////////////////////////////////////////////////// // RenderReflections: render the water reflections to the reflection texture void CRenderer::RenderReflections() { MICROLOG(L"render reflections"); WaterManager& wm = m->waterManager; // Remember old camera CCamera normalCamera = m_ViewCamera; - // Temporarily change the camera to one that is reflected + // Temporarily change the camera to one that is reflected. + // Also, for texturing purposes, make it render to a view port the size of the + // water texture, stretch the image according to our aspect ratio so it covers + // the whole screen despite being rendered into a square, and cover slightly more + // of the view so we can see wavy reflections of slightly off-screen objects. m_ViewCamera.m_Orientation.Translate(0, -wm.m_WaterHeight, 0); m_ViewCamera.m_Orientation.Scale(1, -1, 1); m_ViewCamera.m_Orientation.Translate(0, wm.m_WaterHeight, 0); SViewPort vp; vp.m_Height = wm.m_ReflectionTextureSize; vp.m_Width = wm.m_ReflectionTextureSize; vp.m_X = 0; vp.m_Y = 0; m_ViewCamera.SetViewPort(&vp); - m_ViewCamera.SetProjection(1, 5000, DEGTORAD(25)); + m_ViewCamera.SetProjection(1, 5000, DEGTORAD(21)); // Slightly higher than view FOV of 20 + CMatrix3D scaleMat; + scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f); + m_ViewCamera.m_ProjMat = scaleMat * m_ViewCamera.m_ProjMat; SetCamera(m_ViewCamera, m_CullCamera); CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight); SetObliqueFrustumClipping(camPlane, -1); // Save the model-view-projection matrix so the shaders can use it for projective texturing wm.m_ReflectionMatrix = GetModelViewProjectionMatrix(); // Disable backface culling so trees render properly (it might also be possible to flip // the culling direction here, but this seems to lead to problems) glDisable(GL_CULL_FACE); // Make the depth buffer work backwards; there seems to be some oddness with // oblique frustum clipping and the "sign" parameter here glClearDepth(0); glClear(GL_DEPTH_BUFFER_BIT); glDepthFunc(GL_GEQUAL); // Render sky, terrain and models m->skyManager.RenderSky(); oglCheck(); RenderPatches(); oglCheck(); RenderModels(); oglCheck(); RenderTransparentModels(); oglCheck(); // Copy the image to a texture pglActiveTextureARB(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, wm.m_ReflectionTexture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, wm.m_ReflectionTextureSize, wm.m_ReflectionTextureSize); //Reset old camera and re-enable backface culling SetCamera(normalCamera, m_CullCamera); glEnable(GL_CULL_FACE); //glClearDepth(1); //glClear(GL_DEPTH_BUFFER_BIT); //glDepthFunc(GL_LEQUAL); } /////////////////////////////////////////////////////////////////////////////////////////////////// // RenderRefractions: render the water refractions to the refraction texture void CRenderer::RenderRefractions() { MICROLOG(L"render refractions"); WaterManager& wm = m->waterManager; // Remember old camera CCamera normalCamera = m_ViewCamera; - // Temporarily change the camera to have a higher FOV (so we get bits on the edge of the view) + // Temporarily change the camera to make it render to a view port the size of the + // water texture, stretch the image according to our aspect ratio so it covers + // the whole screen despite being rendered into a square, and cover slightly more + // of the view so we can see wavy refractions of slightly off-screen objects. SViewPort vp; vp.m_Height = wm.m_RefractionTextureSize; vp.m_Width = wm.m_RefractionTextureSize; vp.m_X = 0; vp.m_Y = 0; m_ViewCamera.SetViewPort(&vp); - m_ViewCamera.SetProjection(1, 5000, DEGTORAD(25)); + m_ViewCamera.SetProjection(1, 5000, DEGTORAD(21)); // Slightly higher than view FOV of 20 + CMatrix3D scaleMat; + scaleMat.SetScaling(m_Height/float(std::max(1, m_Width)), 1.0f, 1.0f); + m_ViewCamera.m_ProjMat = scaleMat * m_ViewCamera.m_ProjMat; SetCamera(m_ViewCamera, m_CullCamera); CVector4D camPlane(0, 1, 0, -wm.m_WaterHeight); SetObliqueFrustumClipping(camPlane, -1); // Save the model-view-projection matrix so the shaders can use it for projective texturing wm.m_RefractionMatrix = GetModelViewProjectionMatrix(); // Make the depth buffer work backwards; there seems to be some oddness with // oblique frustum clipping and the "sign" parameter here glClearDepth(0); glClearColor(0.5f, 0.5f, 0.5f, 1.0f); // a neutral gray to blend in with shores glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glDepthFunc(GL_GEQUAL); // Render terrain and models RenderPatches(); oglCheck(); RenderModels(); oglCheck(); RenderTransparentModels(); oglCheck(); // Copy the image to a texture pglActiveTextureARB(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, wm.m_RefractionTexture); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, wm.m_RefractionTextureSize, wm.m_RefractionTextureSize); //Reset old camera and re-enable backface culling SetCamera(normalCamera, m_CullCamera); glEnable(GL_CULL_FACE); glClearDepth(1); glDepthFunc(GL_LEQUAL); } /////////////////////////////////////////////////////////////////////////////////////////////////// // FlushFrame: force rendering of any batched objects void CRenderer::FlushFrame() { if(!g_Game || !g_Game->IsGameStarted()) return; oglCheck(); // Prepare model renderers PROFILE_START("prepare models"); m->Model.Normal->PrepareModels(); m->Model.Player->PrepareModels(); if (m->Model.Normal != m->Model.NormalInstancing) m->Model.NormalInstancing->PrepareModels(); if (m->Model.Player != m->Model.PlayerInstancing) m->Model.PlayerInstancing->PrepareModels(); m->Model.Transp->PrepareModels(); PROFILE_END("prepare models"); PROFILE_START("prepare terrain"); m->terrainRenderer->PrepareForRendering(); PROFILE_END("prepare terrain"); if (m_Options.m_Shadows) { MICROLOG(L"render shadows"); RenderShadowMap(); } // clear buffers glClearColor(m_ClearColor[0],m_ClearColor[1],m_ClearColor[2],m_ClearColor[3]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); oglCheck(); if(m_WaterManager->m_RenderWater && m_Options.m_FancyWater) { // render reflected and refracted scenes, then re-clear the screen RenderReflections(); RenderRefractions(); glClearColor(m_ClearColor[0],m_ClearColor[1],m_ClearColor[2],m_ClearColor[3]); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); } // render sky; this is done before everything so that // (a) we can use a box around the camera instead of placing it "infinitely far away" // (we just disable depth write so that this doesn't affect future rendering) // (b) transparent objects properly overlap the sky if (m_SkyManager->m_RenderSky) { MICROLOG(L"render sky"); m->skyManager.RenderSky(); oglCheck(); } // render submitted patches and models MICROLOG(L"render patches"); RenderPatches(); oglCheck(); // render debug-related terrain overlays TerrainOverlay::RenderOverlays(); MICROLOG(L"render models"); RenderModels(); oglCheck(); // render transparent stuff MICROLOG(L"render transparent"); RenderTransparentModels(); oglCheck(); // render water if (m_WaterManager->m_RenderWater) { MICROLOG(L"render water"); m->terrainRenderer->RenderWater(); oglCheck(); // render transparent stuff again, so it can overlap the water MICROLOG(L"render transparent 2"); RenderTransparentModels(); oglCheck(); } // Clean up texture blend mode so particles and other things render OK // (really this should be cleaned up by whoever set it) glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //// Particle Engine Rendering. MICROLOG(L"render particles"); CParticleEngine::GetInstance()->renderParticles(); oglCheck(); // render debug lines if (m_DisplayFrustum) { MICROLOG(L"display frustum"); DisplayFrustum(); m->shadow->RenderDebugDisplay(); oglCheck(); } // empty lists MICROLOG(L"empty lists"); m->terrainRenderer->EndFrame(); // Finish model renderers m->Model.Normal->EndFrame(); m->Model.Player->EndFrame(); if (m->Model.Normal != m->Model.NormalInstancing) m->Model.NormalInstancing->EndFrame(); if (m->Model.Player != m->Model.PlayerInstancing) m->Model.PlayerInstancing->EndFrame(); m->Model.Transp->EndFrame(); } /////////////////////////////////////////////////////////////////////////////////////////////////// // EndFrame: signal frame end void CRenderer::EndFrame() { if(!g_Game || !g_Game->IsGameStarted()) return; g_Renderer.SetTexture(0,0); static bool once=false; if (!once && glGetError()) { LOG(ERROR, LOG_CATEGORY, "CRenderer::EndFrame: GL errors occurred"); once=true; } } /////////////////////////////////////////////////////////////////////////////////////////////////// // DisplayFrustum: debug displays // - white: cull camera frustum // - red: bounds of shadow casting objects void CRenderer::DisplayFrustum() { glDepthMask(0); glDisable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glColor4ub(255,255,255,64); m_CullCamera.Render(2); glDisable(GL_BLEND); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glColor3ub(255,255,255); m_CullCamera.Render(2); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glEnable(GL_CULL_FACE); glDepthMask(1); } /////////////////////////////////////////////////////////////////////////////////////////////////// // SetCamera: setup projection and transform of camera and adjust viewport to current view void CRenderer::SetCamera(const CCamera& viewCamera, const CCamera& cullCamera) { m_ViewCamera = viewCamera; m_CullCamera = cullCamera; CMatrix3D view; m_ViewCamera.m_Orientation.GetInverse(view); const CMatrix3D& proj = m_ViewCamera.GetProjection(); glMatrixMode(GL_PROJECTION); glLoadMatrixf(&proj._11); glMatrixMode(GL_MODELVIEW); glLoadMatrixf(&view._11); SetViewport(m_ViewCamera.GetViewPort()); } void CRenderer::SetViewport(const SViewPort &vp) { glViewport(vp.m_X,vp.m_Y,vp.m_Width,vp.m_Height); } void CRenderer::Submit(CPatch* patch) { m->terrainRenderer->Submit(patch); } void CRenderer::Submit(CModel* model) { if (model->GetFlags() & MODELFLAG_CASTSHADOWS) { PROFILE( "updating shadow bounds" ); m->shadow->AddShadowedBound(model->GetBounds()); } // Tricky: The call to GetBounds() above can invalidate the position model->ValidatePosition(); bool canUseInstancing = false; if (model->GetModelDef()->GetNumBones() == 0) canUseInstancing = true; if (model->GetMaterial().IsPlayer()) { if (canUseInstancing) m->Model.PlayerInstancing->Submit(model); else m->Model.Player->Submit(model); } else if (model->GetMaterial().UsesAlpha()) { m->Model.Transp->Submit(model); } else { if (canUseInstancing) m->Model.NormalInstancing->Submit(model); else m->Model.Normal->Submit(model); } } void CRenderer::Submit(CSprite* UNUSED(sprite)) { } void CRenderer::Submit(CParticleSys* UNUSED(psys)) { } void CRenderer::Submit(COverlay* UNUSED(overlay)) { } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // LoadTexture: try and load the given texture; set clamp/repeat flags on texture object if necessary bool CRenderer::LoadTexture(CTexture* texture,u32 wrapflags) { const Handle errorhandle = -1; Handle h=texture->GetHandle(); // already tried to load this texture if (h) { // nothing to do here - just return success according to // whether this is a valid handle or not return h==errorhandle ? true : false; } h=ogl_tex_load(texture->GetName()); if (h <= 0) { LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\"",(const char*) texture->GetName()); texture->SetHandle(errorhandle); return false; } if(!wrapflags) wrapflags = GL_CLAMP_TO_EDGE; (void)ogl_tex_set_wrap(h, wrapflags); (void)ogl_tex_set_filter(h, GL_LINEAR_MIPMAP_LINEAR); // (this also verifies that the texture is a power-of-two) if(ogl_tex_upload(h) < 0) { LOG(ERROR, LOG_CATEGORY, "LoadTexture failed on \"%s\" : upload failed",(const char*) texture->GetName()); ogl_tex_free(h); texture->SetHandle(errorhandle); return false; } texture->SetHandle(h); return true; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // BindTexture: bind a GL texture object to current active unit void CRenderer::BindTexture(int unit,GLuint tex) { pglActiveTextureARB(GL_TEXTURE0+unit); glBindTexture(GL_TEXTURE_2D,tex); if (tex) { glEnable(GL_TEXTURE_2D); } else { glDisable(GL_TEXTURE_2D); } m_ActiveTextures[unit]=tex; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // SetTexture: set the given unit to reference the given texture; pass a null texture to disable texturing on any unit void CRenderer::SetTexture(int unit,CTexture* texture) { Handle h = texture? texture->GetHandle() : 0; // Errored textures will give a handle of -1 if (h == -1) h = 0; ogl_tex_bind(h, unit); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // IsTextureTransparent: return true if given texture is transparent, else false - note texture must be loaded // beforehand bool CRenderer::IsTextureTransparent(CTexture* texture) { if (!texture) return false; Handle h=texture->GetHandle(); uint flags = 0; // assume no alpha on failure (void)ogl_tex_get_format(h, &flags, 0); return (flags & TEX_ALPHA) != 0; } static inline void CopyTriple(unsigned char* dst,const unsigned char* src) { dst[0]=src[0]; dst[1]=src[1]; dst[2]=src[2]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // LoadAlphaMaps: load the 14 default alpha maps, pack them into one composite texture and // calculate the coordinate of each alphamap within this packed texture int CRenderer::LoadAlphaMaps() { const char* const key = "(alpha map composite)"; Handle ht = ogl_tex_find(key); // alpha map texture had already been created and is still in memory: // reuse it, do not load again. if(ht > 0) { m_hCompositeAlphaMap = ht; return 0; } // // load all textures and store Handle in array // Handle textures[NumAlphaMaps] = {0}; PathPackage pp; (void)path_package_set_dir(&pp, "art/textures/terrain/alphamaps/special"); const char* fnames[NumAlphaMaps] = { "blendcircle.dds", "blendlshape.dds", "blendedge.dds", "blendedgecorner.dds", "blendedgetwocorners.dds", "blendfourcorners.dds", "blendtwooppositecorners.dds", "blendlshapecorner.dds", "blendtwocorners.dds", "blendcorner.dds", "blendtwoedges.dds", "blendthreecorners.dds", "blendushape.dds", "blendbad.dds" }; uint base = 0; // texture width/height (see below) // for convenience, we require all alpha maps to be of the same BPP // (avoids another ogl_tex_get_size call, and doesn't hurt) uint bpp = 0; for(uint i=0;ishadow->GetUseDepthTexture()); } void CRenderer::JSI_SetUseDepthTexture(JSContext* ctx, jsval newval) { bool depthTexture; if (!ToPrimitive(ctx, newval, depthTexture)) return; m->shadow->SetUseDepthTexture(depthTexture); } jsval CRenderer::JSI_GetDepthTextureBits(JSContext*) { return ToJSVal(m->shadow->GetDepthTextureBits()); } void CRenderer::JSI_SetDepthTextureBits(JSContext* ctx, jsval newval) { int depthTextureBits; if (!ToPrimitive(ctx, newval, depthTextureBits)) return; m->shadow->SetDepthTextureBits(depthTextureBits); } jsval CRenderer::JSI_GetSky(JSContext*) { return ToJSVal(m->skyManager.GetSkySet()); } void CRenderer::JSI_SetSky(JSContext* ctx, jsval newval) { CStrW skySet; if (!ToPrimitive(ctx, newval, skySet)) return; m->skyManager.SetSkySet(skySet); } jsval CRenderer::JSI_GetHorizonHeight(JSContext*) { return ToJSVal(m->skyManager.m_HorizonHeight); } void CRenderer::JSI_SetHorizonHeight(JSContext* ctx, jsval newval) { float value; if (!ToPrimitive(ctx, newval, value)) return; m->skyManager.m_HorizonHeight = value; } jsval CRenderer::JSI_GetWaterShininess(JSContext*) { return ToJSVal(m->waterManager.m_Shininess); } void CRenderer::JSI_SetWaterShininess(JSContext* ctx, jsval newval) { float value; if (!ToPrimitive(ctx, newval, value)) return; m->waterManager.m_Shininess = value; } jsval CRenderer::JSI_GetWaterWaviness(JSContext*) { return ToJSVal(m->waterManager.m_Waviness); } void CRenderer::JSI_SetWaterWaviness(JSContext* ctx, jsval newval) { float value; if (!ToPrimitive(ctx, newval, value)) return; m->waterManager.m_Waviness = value; } jsval CRenderer::JSI_GetWaterRepeatPeriod(JSContext*) { return ToJSVal(m->waterManager.m_RepeatPeriod); } void CRenderer::JSI_SetWaterRepeatPeriod(JSContext* ctx, jsval newval) { float value; if (!ToPrimitive(ctx, newval, value)) return; m->waterManager.m_RepeatPeriod = value; } void CRenderer::ScriptingInit() { AddProperty(L"fastPlayerColor", &CRenderer::JSI_GetFastPlayerColor, &CRenderer::JSI_SetFastPlayerColor); AddProperty(L"renderpath", &CRenderer::JSI_GetRenderPath, &CRenderer::JSI_SetRenderPath); AddProperty(L"useDepthTexture", &CRenderer::JSI_GetUseDepthTexture, &CRenderer::JSI_SetUseDepthTexture); AddProperty(L"sortAllTransparent", &CRenderer::m_SortAllTransparent); AddProperty(L"fastNormals", &CRenderer::m_FastNormals); AddProperty(L"displayFrustum", &CRenderer::m_DisplayFrustum); AddProperty(L"shadowZBias", &CRenderer::m_ShadowZBias); AddProperty(L"disableCopyShadow", &CRenderer::m_DisableCopyShadow); AddProperty(L"depthTextureBits", &CRenderer::JSI_GetDepthTextureBits, &CRenderer::JSI_SetDepthTextureBits); AddProperty(L"skySet", &CRenderer::JSI_GetSky, &CRenderer::JSI_SetSky); AddProperty(L"horizonHeight", &CRenderer::JSI_GetHorizonHeight, &CRenderer::JSI_SetHorizonHeight); AddProperty(L"waterShininess", &CRenderer::JSI_GetWaterShininess, &CRenderer::JSI_SetWaterShininess); AddProperty(L"waterWaviness", &CRenderer::JSI_GetWaterWaviness, &CRenderer::JSI_SetWaterWaviness); AddProperty(L"waterRepeatPeriod", &CRenderer::JSI_GetWaterRepeatPeriod, &CRenderer::JSI_SetWaterRepeatPeriod); CJSObject::ScriptingInit("Renderer"); } Index: ps/trunk/source/renderer/WaterManager.cpp =================================================================== --- ps/trunk/source/renderer/WaterManager.cpp (revision 3918) +++ ps/trunk/source/renderer/WaterManager.cpp (revision 3919) @@ -1,180 +1,180 @@ /** * ========================================================================= * File : WaterManager.cpp * Project : Pyrogenesis * Description : Water settings (speed, height) and texture management * * @author Nicolai Hähnle * ========================================================================= */ #include "precompiled.h" #include "lib/timer.h" #include "lib/res/file/vfs.h" #include "lib/res/graphics/tex.h" #include "lib/res/graphics/ogl_tex.h" #include "maths/MathUtil.h" #include "ps/CLogger.h" #include "ps/Loader.h" #include "renderer/WaterManager.h" #include "renderer/Renderer.h" #define LOG_CATEGORY "graphics" /////////////////////////////////////////////////////////////////////////////////////////////// // WaterManager implementation /////////////////////////////////////////////////////////////////// // Construction/Destruction WaterManager::WaterManager() { // water m_RenderWater = true; m_WaterHeight = 5.0f; m_WaterColor = CColor(0.3f, 0.35f, 0.7f, 1.0f); m_WaterFullDepth = 4.0f; m_WaterMaxAlpha = 0.85f; m_WaterAlphaOffset = -0.05f; m_SWaterTrans = 0; m_TWaterTrans = 0; m_SWaterSpeed = 0.0015f; m_TWaterSpeed = 0.0015f; m_SWaterScrollCounter = 0; m_TWaterScrollCounter = 0; m_WaterCurrentTex = 0; m_ReflectionTexture = 0; m_RefractionTexture = 0; m_WaterTexTimer = 0.0; m_Shininess = 200.0f; m_Waviness = 2.9f; - m_RepeatPeriod = 16.0f; + m_RepeatPeriod = 17.0f; for (uint i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) m_WaterTexture[i] = 0; for (uint i = 0; i < ARRAY_SIZE(m_NormalMap); i++) m_NormalMap[i] = 0; cur_loading_water_tex = 0; cur_loading_normal_map = 0; } WaterManager::~WaterManager() { // Cleanup if the caller messed up UnloadWaterTextures(); } /////////////////////////////////////////////////////////////////// // Progressive load of water textures int WaterManager::LoadWaterTextures() { const uint num_textures = ARRAY_SIZE(m_WaterTexture); const uint num_normal_maps = ARRAY_SIZE(m_NormalMap); // TODO: add a member variable and setter for this. (can't make this // a parameter because this function is called via delay-load code) static const char* const water_type = "default"; // yield after this time is reached. balances increased progress bar // smoothness vs. slowing down loading. const double end_time = get_time() + 100e-3; char filename[PATH_MAX]; // Load diffuse grayscale images (for non-fancy water) while (cur_loading_water_tex < num_textures) { snprintf(filename, ARRAY_SIZE(filename), "art/textures/animated/water/%s/diffuse%02d.dds", water_type, cur_loading_water_tex+1); Handle ht = ogl_tex_load(filename); if (ht <= 0) { LOG(ERROR, LOG_CATEGORY, "LoadWaterTextures failed on \"%s\"", filename); return ht; } m_WaterTexture[cur_loading_water_tex] = ht; RETURN_ERR(ogl_tex_upload(ht)); cur_loading_water_tex++; LDR_CHECK_TIMEOUT(cur_loading_water_tex, num_textures + num_normal_maps); } // Load normalmaps (for fancy water) while (cur_loading_normal_map < num_normal_maps) { snprintf(filename, ARRAY_SIZE(filename), "art/textures/animated/water/%s/normal%02d.tga", water_type, cur_loading_normal_map+1); Handle ht = ogl_tex_load(filename); if (ht <= 0) { LOG(ERROR, LOG_CATEGORY, "LoadWaterTextures failed on \"%s\"", filename); return ht; } m_NormalMap[cur_loading_normal_map] = ht; RETURN_ERR(ogl_tex_upload(ht)); cur_loading_normal_map++; LDR_CHECK_TIMEOUT(num_textures + cur_loading_normal_map, num_textures + num_normal_maps); } // Set the size to the largest power of 2 that is <= to the window height, so // the reflection/reflaction images will fit within the window // (alternative: use FBO's, which can have arbitrary size - but do we need // the reflection/refraction textures to be that large?) int size = RoundUpToPowerOf2(g_Renderer.GetHeight()); if(size > g_Renderer.GetHeight()) size /= 2; m_ReflectionTextureSize = size; m_RefractionTextureSize = size; // Create reflection texture glGenTextures(1, &m_ReflectionTexture); glBindTexture(GL_TEXTURE_2D, m_ReflectionTexture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, m_ReflectionTextureSize, m_ReflectionTextureSize, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); // Create refraction texture glGenTextures(1, &m_RefractionTexture); glBindTexture(GL_TEXTURE_2D, m_RefractionTexture); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, m_RefractionTextureSize, m_RefractionTextureSize, 0, GL_RGB, GL_UNSIGNED_BYTE, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); return 0; } /////////////////////////////////////////////////////////////////// // Unload water textures void WaterManager::UnloadWaterTextures() { for(uint i = 0; i < ARRAY_SIZE(m_WaterTexture); i++) { ogl_tex_free(m_WaterTexture[i]); m_WaterTexture[i] = 0; } for(uint i = 0; i < ARRAY_SIZE(m_NormalMap); i++) { ogl_tex_free(m_NormalMap[i]); m_NormalMap[i] = 0; } cur_loading_water_tex = 0; // so they will be reloaded if LoadWaterTextures is called again cur_loading_normal_map = 0; } Index: ps/trunk/source/maths/Matrix3D.cpp =================================================================== --- ps/trunk/source/maths/Matrix3D.cpp (revision 3918) +++ ps/trunk/source/maths/Matrix3D.cpp (revision 3919) @@ -1,526 +1,525 @@ //*********************************************************** // // Name: Matrix3D.Cpp // Last Update: 31/1/02 // Author: Poya Manouchehri // // Description: A Matrix class used for holding and // manipulating transformation info. // //*********************************************************** #include "precompiled.h" #include "Matrix3D.h" #include "Quaternion.h" #include "self_test.h" - CMatrix3D::CMatrix3D () { } CMatrix3D::CMatrix3D(float a11,float a12,float a13,float a14,float a21,float a22,float a23,float a24, float a31,float a32,float a33,float a34,float a41,float a42,float a43,float a44) { _11=a11; _12=a12; _13=a13; _14=a14; _21=a21; _22=a22; _23=a23; _24=a24; _31=a31; _32=a32; _33=a33; _34=a34; _41=a41; _42=a42; _43=a43; _44=a44; } CMatrix3D::CMatrix3D(float data[]) { for(int i=0; i<16; i++) { _data[i] = data[i]; } } //Matrix multiplication CMatrix3D CMatrix3D::operator*(const CMatrix3D& matrix) const { return CMatrix3D( _11*matrix._11 + _12*matrix._21 + _13*matrix._31 + _14*matrix._41, _11*matrix._12 + _12*matrix._22 + _13*matrix._32 + _14*matrix._42, _11*matrix._13 + _12*matrix._23 + _13*matrix._33 + _14*matrix._43, _11*matrix._14 + _12*matrix._24 + _13*matrix._34 + _14*matrix._44, _21*matrix._11 + _22*matrix._21 + _23*matrix._31 + _24*matrix._41, _21*matrix._12 + _22*matrix._22 + _23*matrix._32 + _24*matrix._42, _21*matrix._13 + _22*matrix._23 + _23*matrix._33 + _24*matrix._43, _21*matrix._14 + _22*matrix._24 + _23*matrix._34 + _24*matrix._44, _31*matrix._11 + _32*matrix._21 + _33*matrix._31 + _34*matrix._41, _31*matrix._12 + _32*matrix._22 + _33*matrix._32 + _34*matrix._42, _31*matrix._13 + _32*matrix._23 + _33*matrix._33 + _34*matrix._43, _31*matrix._14 + _32*matrix._24 + _33*matrix._34 + _34*matrix._44, _41*matrix._11 + _42*matrix._21 + _43*matrix._31 + _44*matrix._41, _41*matrix._12 + _42*matrix._22 + _43*matrix._32 + _44*matrix._42, _41*matrix._13 + _42*matrix._23 + _43*matrix._33 + _44*matrix._43, _41*matrix._14 + _42*matrix._24 + _43*matrix._34 + _44*matrix._44 ); } //Matrix multiplication/assignment CMatrix3D& CMatrix3D::operator*=(const CMatrix3D& matrix) { Concatenate(matrix); return *this; } //Matrix scaling CMatrix3D CMatrix3D::operator*(float f) const { CMatrix3D tmp; for (int i=0;i<16;i++) { tmp._data[i]=_data[i]*f; } return tmp; } //Matrix scaling/assignment CMatrix3D& CMatrix3D::operator*=(float f) { for (int i=0;i<16;i++) { _data[i]*=f; } return *this; } //Matrix addition CMatrix3D CMatrix3D::operator+(const CMatrix3D& m) const { CMatrix3D tmp; for (int i=0;i<16;i++) { tmp._data[i]=_data[i]+m._data[i]; } return tmp; } //Matrix addition/assignment CMatrix3D& CMatrix3D::operator+=(const CMatrix3D& m) { for (int i=0;i<16;i++) { _data[i]+=m._data[i]; } return *this; } //Sets the identity matrix void CMatrix3D::SetIdentity () { _11=1.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=1.0f; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=1.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //Sets the zero matrix void CMatrix3D::SetZero () { _11=0.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=0.0f; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=0.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=0.0f; } //The following clear the matrix and set the //rotation of each of the 3 axes void CMatrix3D::SetXRotation (float angle) { float Cos = cosf (angle); float Sin = sinf (angle); _11=1.0f; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=Cos; _23=-Sin; _24=0.0f; _31=0.0f; _32=Sin; _33=Cos; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetYRotation (float angle) { float Cos = cosf (angle); float Sin = sinf (angle); _11=Cos; _12=0.0f; _13=Sin; _14=0.0f; _21=0.0f; _22=1.0f; _23=0.0f; _24=0.0f; _31=-Sin; _32=0.0f; _33=Cos; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetZRotation (float angle) { float Cos = cosf (angle); float Sin = sinf (angle); _11=Cos; _12=-Sin; _13=0.0f; _14=0.0f; _21=Sin; _22=Cos; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=1.0f; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //The following apply a rotation to the matrix //about each of the axes; void CMatrix3D::RotateX (float angle) { CMatrix3D Temp; Temp.SetXRotation (angle); Concatenate(Temp); } void CMatrix3D::RotateY (float angle) { CMatrix3D Temp; Temp.SetYRotation (angle); Concatenate(Temp); } void CMatrix3D::RotateZ (float angle) { CMatrix3D Temp; Temp.SetZRotation(angle); Concatenate(Temp); } //Sets the translation of the matrix void CMatrix3D::SetTranslation (float x, float y, float z) { _11=1.0f; _12=0.0f; _13=0.0f; _14=x; _21=0.0f; _22=1.0f; _23=0.0f; _24=y; _31=0.0f; _32=0.0f; _33=1.0f; _34=z; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } void CMatrix3D::SetTranslation(const CVector3D& vector) { SetTranslation(vector.X, vector.Y, vector.Z); } //Applies a translation to the matrix void CMatrix3D::Translate(float x, float y, float z) { CMatrix3D Temp; Temp.SetTranslation(x,y,z); Concatenate(Temp); } void CMatrix3D::Translate(const CVector3D &vector) { Translate(vector.X,vector.Y,vector.Z); } void CMatrix3D::Concatenate(const CMatrix3D& m) { (*this)=m*(*this); } CVector3D CMatrix3D::GetTranslation() const { CVector3D Temp; Temp.X = _14; Temp.Y = _24; Temp.Z = _34; return Temp; } //Clears and sets the scaling of the matrix void CMatrix3D::SetScaling (float x_scale, float y_scale, float z_scale) { _11=x_scale; _12=0.0f; _13=0.0f; _14=0.0f; _21=0.0f; _22=y_scale; _23=0.0f; _24=0.0f; _31=0.0f; _32=0.0f; _33=z_scale; _34=0.0f; _41=0.0f; _42=0.0f; _43=0.0f; _44=1.0f; } //Scales the matrix void CMatrix3D::Scale (float x_scale, float y_scale, float z_scale) { CMatrix3D Temp; Temp.SetScaling(x_scale,y_scale,z_scale); Concatenate(Temp); } //Returns the transpose of the matrix. For orthonormal //matrices, this is the same is the inverse matrix void CMatrix3D::GetTranspose(CMatrix3D& result) const { result._11 = _11; result._21 = _12; result._31 = _13; result._41 = _14; result._12 = _21; result._22 = _22; result._32 = _23; result._42 = _24; result._13 = _31; result._23 = _32; result._33 = _33; result._43 = _34; result._14 = _41; result._24 = _42; result._34 = _43; result._44 = _44; } //Get a vector which points to the left of the matrix CVector3D CMatrix3D::GetLeft () const { CVector3D Temp; Temp.X = -_11; Temp.Y = -_21; Temp.Z = -_31; return Temp; } //Get a vector which points up from the matrix CVector3D CMatrix3D::GetUp () const { CVector3D Temp; Temp.X = _12; Temp.Y = _22; Temp.Z = _32; return Temp; } //Get a vector which points to front of the matrix CVector3D CMatrix3D::GetIn () const { CVector3D Temp; Temp.X = _13; Temp.Y = _23; Temp.Z = _33; return Temp; } //Transform a vector by this matrix CVector3D CMatrix3D::Transform (const CVector3D &vector) const { CVector3D result; Transform(vector,result); return result; } void CMatrix3D::Transform(const CVector3D& vector,CVector3D& result) const { result.X = _11*vector.X + _12*vector.Y + _13*vector.Z + _14; result.Y = _21*vector.X + _22*vector.Y + _23*vector.Z + _24; result.Z = _31*vector.X + _32*vector.Y + _33*vector.Z + _34; } //Transform a vector by this matrix CVector4D CMatrix3D::Transform(const CVector4D &vector) const { CVector4D result; Transform(vector,result); return result; } void CMatrix3D::Transform(const CVector4D& vector,CVector4D& result) const { result[0] = _11*vector[0] + _12*vector[1] + _13*vector[2] + _14*vector[3]; result[1] = _21*vector[0] + _22*vector[1] + _23*vector[2] + _24*vector[3]; result[2] = _31*vector[0] + _32*vector[1] + _33*vector[2] + _34*vector[3]; result[3] = _41*vector[0] + _42*vector[1] + _43*vector[2] + _44*vector[3]; } //Only rotate (not translate) a vector by this matrix CVector3D CMatrix3D::Rotate(const CVector3D& vector) const { CVector3D result; Rotate(vector,result); return result; } void CMatrix3D::Rotate(const CVector3D& vector,CVector3D& result) const { result.X = _11*vector.X + _12*vector.Y + _13*vector.Z; result.Y = _21*vector.X + _22*vector.Y + _23*vector.Z; result.Z = _31*vector.X + _32*vector.Y + _33*vector.Z; } /////////////////////////////////////////////////////////////////////////////// // RotateTransposed: rotate a vector by the transpose of this matrix CVector3D CMatrix3D::RotateTransposed(const CVector3D& vector) const { CVector3D result; RotateTransposed(vector,result); return result; } /////////////////////////////////////////////////////////////////////////////// // RotateTransposed: rotate a vector by the transpose of this matrix void CMatrix3D::RotateTransposed(const CVector3D& vector,CVector3D& result) const { result.X = _11*vector.X + _21*vector.Y + _31*vector.Z; result.Y = _12*vector.X + _22*vector.Y + _32*vector.Z; result.Z = _13*vector.X + _23*vector.Y + _33*vector.Z; } void CMatrix3D::GetInverse(CMatrix3D& dst) const { float tmp[12]; // temp array for pairs float src[16]; // array of transpose source matrix float det; // determinant // transpose matrix for (int i = 0; i < 4; ++i) { src[i] = _data[i*4]; src[i + 4] = _data[i*4 + 1]; src[i + 8] = _data[i*4 + 2]; src[i + 12] = _data[i*4 + 3]; } // calculate pairs for first 8 elements (cofactors) tmp[0] = src[10] * src[15]; tmp[1] = src[11] * src[14]; tmp[2] = src[9] * src[15]; tmp[3] = src[11] * src[13]; tmp[4] = src[9] * src[14]; tmp[5] = src[10] * src[13]; tmp[6] = src[8] * src[15]; tmp[7] = src[11] * src[12]; tmp[8] = src[8] * src[14]; tmp[9] = src[10] * src[12]; tmp[10] = src[8] * src[13]; tmp[11] = src[9] * src[12]; // calculate first 8 elements (cofactors) dst._data[0] = tmp[0]*src[5] + tmp[3]*src[6] + tmp[4]*src[7]; dst._data[0] -= tmp[1]*src[5] + tmp[2]*src[6] + tmp[5]*src[7]; dst._data[1] = tmp[1]*src[4] + tmp[6]*src[6] + tmp[9]*src[7]; dst._data[1] -= tmp[0]*src[4] + tmp[7]*src[6] + tmp[8]*src[7]; dst._data[2] = tmp[2]*src[4] + tmp[7]*src[5] + tmp[10]*src[7]; dst._data[2] -= tmp[3]*src[4] + tmp[6]*src[5] + tmp[11]*src[7]; dst._data[3] = tmp[5]*src[4] + tmp[8]*src[5] + tmp[11]*src[6]; dst._data[3] -= tmp[4]*src[4] + tmp[9]*src[5] + tmp[10]*src[6]; dst._data[4] = tmp[1]*src[1] + tmp[2]*src[2] + tmp[5]*src[3]; dst._data[4] -= tmp[0]*src[1] + tmp[3]*src[2] + tmp[4]*src[3]; dst._data[5] = tmp[0]*src[0] + tmp[7]*src[2] + tmp[8]*src[3]; dst._data[5] -= tmp[1]*src[0] + tmp[6]*src[2] + tmp[9]*src[3]; dst._data[6] = tmp[3]*src[0] + tmp[6]*src[1] + tmp[11]*src[3]; dst._data[6] -= tmp[2]*src[0] + tmp[7]*src[1] + tmp[10]*src[3]; dst._data[7] = tmp[4]*src[0] + tmp[9]*src[1] + tmp[10]*src[2]; dst._data[7] -= tmp[5]*src[0] + tmp[8]*src[1] + tmp[11]*src[2]; // calculate pairs for second 8 elements (cofactors) tmp[0] = src[2]*src[7]; tmp[1] = src[3]*src[6]; tmp[2] = src[1]*src[7]; tmp[3] = src[3]*src[5]; tmp[4] = src[1]*src[6]; tmp[5] = src[2]*src[5]; tmp[6] = src[0]*src[7]; tmp[7] = src[3]*src[4]; tmp[8] = src[0]*src[6]; tmp[9] = src[2]*src[4]; tmp[10] = src[0]*src[5]; tmp[11] = src[1]*src[4]; // calculate second 8 elements (cofactors) dst._data[8] = tmp[0]*src[13] + tmp[3]*src[14] + tmp[4]*src[15]; dst._data[8] -= tmp[1]*src[13] + tmp[2]*src[14] + tmp[5]*src[15]; dst._data[9] = tmp[1]*src[12] + tmp[6]*src[14] + tmp[9]*src[15]; dst._data[9] -= tmp[0]*src[12] + tmp[7]*src[14] + tmp[8]*src[15]; dst._data[10] = tmp[2]*src[12] + tmp[7]*src[13] + tmp[10]*src[15]; dst._data[10]-= tmp[3]*src[12] + tmp[6]*src[13] + tmp[11]*src[15]; dst._data[11] = tmp[5]*src[12] + tmp[8]*src[13] + tmp[11]*src[14]; dst._data[11]-= tmp[4]*src[12] + tmp[9]*src[13] + tmp[10]*src[14]; dst._data[12] = tmp[2]*src[10] + tmp[5]*src[11] + tmp[1]*src[9]; dst._data[12]-= tmp[4]*src[11] + tmp[0]*src[9] + tmp[3]*src[10]; dst._data[13] = tmp[8]*src[11] + tmp[0]*src[8] + tmp[7]*src[10]; dst._data[13]-= tmp[6]*src[10] + tmp[9]*src[11] + tmp[1]*src[8]; dst._data[14] = tmp[6]*src[9] + tmp[11]*src[11] + tmp[3]*src[8]; dst._data[14]-= tmp[10]*src[11] + tmp[2]*src[8] + tmp[7]*src[9]; dst._data[15] = tmp[10]*src[10] + tmp[4]*src[8] + tmp[9]*src[9]; dst._data[15]-= tmp[8]*src[9] + tmp[11]*src[10] + tmp[5]*src[8]; // calculate matrix inverse det=src[0]*dst._data[0]+src[1]*dst._data[1]+src[2]*dst._data[2]+src[3]*dst._data[3]; det = 1/det; for ( int j = 0; j < 16; j++) { dst._data[j] *= det; } } void CMatrix3D::Rotate(const CQuaternion& quat) { CMatrix3D rotationMatrix=quat.ToMatrix(); Concatenate(rotationMatrix); } CQuaternion CMatrix3D::GetRotation() const { float tr = _data2d[0][0] + _data2d[1][1] + _data2d[2][2]; int next[] = { 1, 2, 0 }; float quat[4]; if (tr > 0.f) { float s = sqrtf(tr + 1.f); quat[3] = s * 0.5f; s = 0.5f / s; quat[0] = (_data2d[1][2] - _data2d[2][1]) * s; quat[1] = (_data2d[2][0] - _data2d[0][2]) * s; quat[2] = (_data2d[0][1] - _data2d[1][0]) * s; } else { int i = 0; if (_data2d[1][1] > _data2d[0][0]) i = 1; if (_data2d[2][2] > _data2d[i][i]) i = 2; int j = next[i]; int k = next[j]; float s = sqrtf((_data2d[i][i] - (_data2d[j][j] + _data2d[k][k])) + 1.f); quat[i] = s * 0.5f; if (s != 0.f) s = 0.5f / s; quat[3] = (_data2d[j][k] - _data2d[k][j]) * s; quat[j] = (_data2d[i][j] + _data2d[j][i]) * s; quat[k] = (_data2d[i][k] + _data2d[k][i]) * s; } return CQuaternion(quat[0], quat[1], quat[2], quat[3]); } void CMatrix3D::SetRotation(const CQuaternion& quat) { quat.ToMatrix(*this); }