/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ /* * Copyright (C) 2006-2010 - Frictional Games * * This file is part of HPL1 Engine. */ #include "hpl1/engine/scene/Light3D.h" #include "hpl1/engine/graphics/BillBoard.h" #include "hpl1/engine/graphics/LowLevelGraphics.h" #include "hpl1/engine/graphics/Mesh.h" #include "hpl1/engine/graphics/ParticleSystem3D.h" #include "hpl1/engine/graphics/RenderList.h" #include "hpl1/engine/graphics/Renderer3D.h" #include "hpl1/engine/graphics/SubMesh.h" #include "hpl1/engine/impl/tinyXML/tinyxml.h" #include "hpl1/engine/math/Math.h" #include "hpl1/engine/resources/FileSearcher.h" #include "hpl1/engine/resources/Resources.h" #include "hpl1/engine/resources/TextureManager.h" #include "hpl1/engine/scene/Camera3D.h" #include "hpl1/engine/scene/MeshEntity.h" #include "hpl1/engine/scene/PortalContainer.h" #include "hpl1/engine/scene/SectorVisibility.h" #include "hpl1/engine/scene/World3D.h" #include "hpl1/engine/system/low_level_system.h" namespace hpl { ////////////////////////////////////////////////////////////////////////// // CONSTRUCTORS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- iLight3D::iLight3D(tString asName, cResources *apResources) : iLight(), iRenderable(asName) { mbStaticCasterAdded = false; mbOnlyAffectInInSector = false; mbApplyTransformToBV = false; mpTextureManager = apResources->GetTextureManager(); mpFileSearcher = apResources->GetFileSearcher(); mpFalloffMap = mpTextureManager->Create1D("core_falloff_linear", false); if (mpFalloffMap) { mpFalloffMap->SetWrapS(eTextureWrap_ClampToEdge); mpFalloffMap->SetWrapT(eTextureWrap_ClampToEdge); } mpVisSectorCont = NULL; mlSectorVisibilityCount = -1; for (int i = 0; i < 3; ++i) mvTempTextures[i] = NULL; } //----------------------------------------------------------------------- iLight3D::~iLight3D() { if (mpFalloffMap) mpTextureManager->Destroy(mpFalloffMap); if (mpVisSectorCont) hplDelete(mpVisSectorCont); } //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void iLight3D::SetVisible(bool abVisible) { SetRendered(abVisible); for (size_t i = 0; i < mvBillboards.size(); ++i) { mvBillboards[i]->SetVisible(abVisible); } } //----------------------------------------------------------------------- void iLight3D::AddShadowCaster(iRenderable *apObject, cFrustum *apFrustum, bool abStatic, cRenderList *apRenderList) { // Log("Testing: %s\n",apObject->GetName().c_str()); ///////////////////////////////////////////////////// // Check if the object should be added eRenderableType renderType = apObject->GetRenderType(); // Is it affected by the light. if (GetOnlyAffectInSector() && apObject->IsInSector(GetCurrentSector()) == false) return; // Is it visible if (apObject->GetForceShadow() == false) { if (apObject->IsVisible() == false) return; // Does the object cast shadows? if (apObject->IsShadowCaster() == false) return; // Can be material cast shadows? iMaterial *pMaterial = apObject->GetMaterial(); if (pMaterial) { if (pMaterial->IsTransperant() || pMaterial->HasAlpha()) return; } // Check so that the object is the right type if (renderType != eRenderableType_Mesh && renderType != eRenderableType_Normal) { return; } } // Log("Right type!\n"); // Check if the object has all ready been added if (abStatic) { if (m_setStaticCasters.find(apObject) != m_setStaticCasters.end()) return; } else { if (m_setDynamicCasters.find(apObject) != m_setDynamicCasters.end()) return; } // Log("Not in list!\n"); // Check if the object touches the light. // if(CollidesWithBV(apObject->GetBoundingVolume())==false){ // return; // } if (CheckObjectIntersection(apObject) == false) return; // Log("Collides!\n"); // Log("Shadow is in frustum!\n"); //////////////////////////////////////////////////// // All checks passed, add the object! if (renderType == eRenderableType_Mesh) { cMeshEntity *pMesh = static_cast(apObject); /// Need to add shadow casted objects else shadows might get choppy. if (abStatic == false) { pMesh->SetGlobalRenderCount(cRenderList::GetGlobalRenderCount()); } for (int i = 0; i < pMesh->GetSubMeshEntityNum(); i++) { cSubMeshEntity *pSub = pMesh->GetSubMeshEntity(i); if (pSub->IsVisible() == false) continue; if (apObject->GetForceShadow() == false) { iMaterial *pMaterial = pSub->GetMaterial(); if (pMaterial) { if (pMaterial->IsTransperant() || pMaterial->HasAlpha()) continue; } } if (abStatic) { m_setStaticCasters.insert(pSub); } else { m_setDynamicCasters.insert(pSub); /// Need to add shadow casted objects else shadows might get choppy. pSub->SetGlobalRenderCount(cRenderList::GetGlobalRenderCount()); } } } else { if (abStatic) { m_setStaticCasters.insert(apObject); } else { m_setDynamicCasters.insert(apObject); apObject->SetGlobalRenderCount(cRenderList::GetGlobalRenderCount()); } } } bool iLight3D::HasStaticCasters() { return m_setStaticCasters.empty() ? false : true; } void iLight3D::ClearCasters(bool abClearStatic) { if (abClearStatic) m_setStaticCasters.clear(); m_setDynamicCasters.clear(); } //----------------------------------------------------------------------- bool iLight3D::CheckObjectIntersection(iRenderable *apObject) { // Log("------ Checking %s with light %s -----\n",apObject->GetName().c_str(), GetName().c_str()); // Log(" BV: min: %s max: %s\n", apObject->GetBoundingVolume()->GetMin().ToString().c_str(), // apObject->GetBoundingVolume()->GetMax().ToString().c_str()); ////////////////////////////////////////////////////////////// // If the lights cast shadows, cull objects that are in shadow if (mbCastShadows) { ///////////////////////////////////// // Check if the visibility needs update if (mlSectorVisibilityCount != GetMatrixUpdateCount()) { mlSectorVisibilityCount = GetMatrixUpdateCount(); if (mpVisSectorCont) hplDelete(mpVisSectorCont); mpVisSectorCont = CreateSectorVisibility(); // Log("Creating Visibility container!\n"); } // Get the data list containing the sectors the object is connected to tRenderContainerDataList *pDataList = apObject->GetRenderContainerDataList(); // It is not attached to any room. Just use BV test. if (pDataList->empty()) { // Log("Empty data list using BV\n"); return CollidesWithBV(apObject->GetBoundingVolume()); } // The object is in one or more sectors else { // Iterate the sectors and remove the object from them. tRenderContainerDataListIt it = pDataList->begin(); for (; it != pDataList->end(); ++it) { cSector *pSector = static_cast(*it); // Log("Checking intersection in sector %s\n",pSector->GetId().c_str()); cSectorVisibility *pVisSector = mpVisSectorCont->GetSectorVisibilty(pSector); if (pVisSector) { if (pVisSector->IntersectionBV(apObject->GetBoundingVolume())) { // Log("Intersected!\n"); // Log("-----------------------"); return true; } } } // Log("-----------------------"); return false; } } ///////////////////////////////////////////////// // Light is not in shadow, do not do any culling else { // Log("No shadow, using BV\n"); return CollidesWithBV(apObject->GetBoundingVolume()); } } //----------------------------------------------------------------------- bool iLight3D::BeginDraw(cRenderSettings *apRenderSettings, iLowLevelGraphics *apLowLevelGraphics) { // Clear Stencil Buffer /* apLowLevelGraphics->SetClearStencilActive(true); apLowLevelGraphics->SetClearDepthActive(false); apLowLevelGraphics->SetClearColorActive(false); apLowLevelGraphics->SetClearStencil(0); apLowLevelGraphics->ClearScreen(); apLowLevelGraphics->SetClearStencilActive(false); apLowLevelGraphics->SetClearDepthActive(true); apLowLevelGraphics->SetClearColorActive(true);*/ cRect2l ClipRect; bool bVisible = CreateClipRect(ClipRect, apRenderSettings, apLowLevelGraphics); if (bVisible) { apLowLevelGraphics->SetScissorActive(true); apLowLevelGraphics->SetScissorRect(ClipRect); if (apRenderSettings->mbLog) Log("Cliprect pos: (%d, %d) size: (%d, %d)\n", ClipRect.x, ClipRect.y, ClipRect.w, ClipRect.h); } else { if (apRenderSettings->mbLog) Log("Cliprect entire screen\n"); } ////////////////////////////////////////////////////////// // Cast shadows if (mbCastShadows && apRenderSettings->mShowShadows != eRendererShowShadows_None && apRenderSettings->extrudeProgram) { // Get temp index array. (Remove this when the index pool // is implemented.). mpIndexArray = apRenderSettings->mpTempIndexArray; // Setup for shadow drawing apLowLevelGraphics->SetStencilActive(true); // Do no set this when debugging. apLowLevelGraphics->SetColorWriteActive(false, false, false, false); /////////////////////////////////////////////////////////////////////////// // Clear stencil, since scissor is set this should be fast. apLowLevelGraphics->SetClearStencilActive(true); apLowLevelGraphics->SetClearDepthActive(false); apLowLevelGraphics->SetClearColorActive(false); apLowLevelGraphics->SetClearStencil(0); apLowLevelGraphics->ClearScreen(); apLowLevelGraphics->SetClearStencilActive(false); apLowLevelGraphics->SetClearDepthActive(true); apLowLevelGraphics->SetClearColorActive(true); if (apLowLevelGraphics->GetCaps(eGraphicCaps_TwoSideStencil)) { apLowLevelGraphics->SetCullActive(false); } apLowLevelGraphics->SetDepthWriteActive(false); // Setup the depth test so that shadow volume is not rendered in front // off the normal graphics. apLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_Less); // Resert the algo (zfail or zpass) used. apRenderSettings->mlLastShadowAlgo = 0; // Reset this variable so it can be used when rendering shadows. apRenderSettings->mbMatrixWasNULL = false; // Set the fragment program. if (apRenderSettings->extrudeProgram) { if (apRenderSettings->mbLog) Log("Setting fragment program: '%s'\n", apRenderSettings->extrudeProgram->GetName().c_str()); apRenderSettings->extrudeProgram->Bind(); apRenderSettings->gpuProgram = apRenderSettings->extrudeProgram; } // Render shadows tCasterCacheSetIt it = m_setDynamicCasters.begin(); if (apRenderSettings->mShowShadows == eRendererShowShadows_All) { it = m_setDynamicCasters.begin(); for (; it != m_setDynamicCasters.end(); ++it) { RenderShadow(*it, apRenderSettings, apLowLevelGraphics); } } it = m_setStaticCasters.begin(); for (; it != m_setStaticCasters.end(); ++it) { RenderShadow(*it, apRenderSettings, apLowLevelGraphics); } // Make rendering ready for the objects. // apLowLevelGraphics->SetStencilTwoSideActive(false); apLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_Equal); apLowLevelGraphics->SetColorWriteActive(true, true, true, true); apLowLevelGraphics->SetCullActive(true); apLowLevelGraphics->SetStencil(eStencilFunc_Equal, 0, 0xFF, eStencilOp_Keep, eStencilOp_Keep, eStencilOp_Keep); } // Reset this var so that the new light properties are set. apRenderSettings->mbMatrixWasNULL = false; return true; } //----------------------------------------------------------------------- void iLight3D::EndDraw(cRenderSettings *apRenderSettings, iLowLevelGraphics *apLowLevelGraphics) { apLowLevelGraphics->SetScissorActive(false); apLowLevelGraphics->SetStencilActive(false); } //----------------------------------------------------------------------- void iLight3D::SetFarAttenuation(float afX) { mfFarAttenuation = afX; mbUpdateBoundingVolume = true; // This is so that the render container is updated. SetTransformUpdated(); } //----------------------------------------------------------------------- void iLight3D::SetNearAttenuation(float afX) { mfNearAttenuation = afX; if (mfNearAttenuation > mfFarAttenuation) SetFarAttenuation(mfNearAttenuation); } //----------------------------------------------------------------------- cVector3f iLight3D::GetLightPosition() { return GetWorldPosition(); } //----------------------------------------------------------------------- void iLight3D::UpdateLogic(float afTimeStep) { UpdateLight(afTimeStep); if (mfFadeTime > 0 || mbFlickering) { mbUpdateBoundingVolume = true; // This is so that the render container is updated. SetTransformUpdated(); } } //----------------------------------------------------------------------- cBoundingVolume *iLight3D::GetBoundingVolume() { if (mbUpdateBoundingVolume) { UpdateBoundingVolume(); mbUpdateBoundingVolume = false; } return &mBoundingVolume; } //----------------------------------------------------------------------- cMatrixf *iLight3D::GetModelMatrix(cCamera3D *apCamera) { mtxTemp = GetWorldMatrix(); return &mtxTemp; } //----------------------------------------------------------------------- bool iLight3D::IsVisible() { if (mDiffuseColor.r <= 0 && mDiffuseColor.g <= 0 && mDiffuseColor.b <= 0 && mDiffuseColor.a <= 0) return false; if (mfFarAttenuation <= 0) return false; return IsRendered(); } //----------------------------------------------------------------------- iTexture *iLight3D::GetFalloffMap() { return mpFalloffMap; } void iLight3D::SetFalloffMap(iTexture *apTexture) { if (mpFalloffMap) mpTextureManager->Destroy(mpFalloffMap); if (apTexture) { mpFalloffMap = apTexture; mpFalloffMap->SetWrapS(eTextureWrap_ClampToEdge); mpFalloffMap->SetWrapT(eTextureWrap_ClampToEdge); } else { mpFalloffMap = nullptr; } Common::fill(mvTempTextures, mvTempTextures + 3, nullptr); } //----------------------------------------------------------------------- void iLight3D::LoadXMLProperties(const tString asFile) { tString sPath = mpFileSearcher->GetFilePath(asFile); if (sPath != "") { TiXmlDocument *pDoc = hplNew(TiXmlDocument, (sPath.c_str())); if (pDoc->LoadFile()) { TiXmlElement *pRootElem = pDoc->RootElement(); TiXmlElement *pMainElem = pRootElem->FirstChildElement("MAIN"); if (pMainElem != NULL) { mbCastShadows = cString::ToBool(pMainElem->Attribute("CastsShadows"), mbCastShadows); mDiffuseColor.a = cString::ToFloat(pMainElem->Attribute("Specular"), mDiffuseColor.a); tString sFalloffImage = cString::ToString(pMainElem->Attribute("FalloffImage"), ""); iTexture *pTexture = mpTextureManager->Create1D(sFalloffImage, false); if (pTexture) SetFalloffMap(pTexture); ExtraXMLProperties(pMainElem); } else { Error("Cannot find main element in %s\n", asFile.c_str()); } } else { Error("Couldn't load file '%s'\n", asFile.c_str()); } hplDelete(pDoc); } else { Error("Couldn't find file '%s'\n", asFile.c_str()); } } //----------------------------------------------------------------------- void iLight3D::AttachBillboard(cBillboard *apBillboard) { mvBillboards.push_back(apBillboard); apBillboard->SetColor(cColor(mDiffuseColor.r, mDiffuseColor.g, mDiffuseColor.b, 1)); apBillboard->SetVisible(IsVisible()); } void iLight3D::RemoveBillboard(cBillboard *apBillboard) { Common::Array::iterator it = mvBillboards.begin(); for (; it != mvBillboards.end(); ++it) { if (*it == apBillboard) { mvBillboards.erase(it); } } } //----------------------------------------------------------------------- //----------------------------------------------------------------------- ////////////////////////////////////////////////////////////////////////// // PROTECTED METHODS ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- void iLight3D::RenderShadow(iRenderable *apObject, cRenderSettings *apRenderSettings, iLowLevelGraphics *apLowLevelGraphics) { int lIndexCount = 0; //////////////////////////////////////////////////////////////////////// // Check if the shadow volume collides with the frustum cShadowVolumeBV *pVolume = apObject->GetBoundingVolume()->GetShadowVolume( GetWorldPosition(), mfFarAttenuation, true); cFrustum *pFrustum = apRenderSettings->mpFrustum; if (pVolume && pFrustum) { if (pFrustum->CheckVolumeIntersection(pVolume) == false) { // This can be skipped if the AABB shadow volume is better. /// that is includes the AABB. if (pFrustum->CollideBoundingVolume(apObject->GetBoundingVolume()) == eFrustumCollision_Outside) { return; } } } if (apRenderSettings->mbLog) Log("Rendering shadow for '%s'\n", apObject->GetName().c_str()); cSubMeshEntity *pSubEntity = static_cast(apObject); cSubMesh *pSubMesh = pSubEntity->GetSubMesh(); ////////////////////////////////////////// // Check what method to use. bool bZFail = false; if (pVolume && pFrustum) { cBoundingVolume *pFrustumBV = pFrustum->GetOriginBV(); if (cMath::CheckSphereInPlanes(pFrustumBV->GetWorldCenter(), pFrustumBV->GetRadius(), pVolume->mvPlanes, pVolume->mlPlaneCount)) { bZFail = true; } // This is because the AABB-shadow volume is not perfect else if (cMath::CheckCollisionBV(*pFrustumBV, *apObject->GetBoundingVolume())) { bZFail = true; } } else { bZFail = true; } if (apRenderSettings->mbLog) Log("Rendering shadow with '%s'\n", bZFail ? "ZFail" : "ZPass"); ////////////////////////////////////////// // Setup the stencil buffer. // If two sided stencil is not supported, do the set up later on. if (apLowLevelGraphics->GetCaps(eGraphicCaps_TwoSideStencil)) { if (bZFail) { if (apRenderSettings->mlLastShadowAlgo != 1) { apLowLevelGraphics->SetStencilTwoSide(eStencilFunc_Always, eStencilFunc_Always, 0, 0x00, eStencilOp_Keep, eStencilOp_DecrementWrap, eStencilOp_Keep, eStencilOp_Keep, eStencilOp_IncrementWrap, eStencilOp_Keep); apRenderSettings->mlLastShadowAlgo = 1; } } else { if (apRenderSettings->mlLastShadowAlgo != 2) { apLowLevelGraphics->SetStencilTwoSide(eStencilFunc_Always, eStencilFunc_Always, 0, 0x00, eStencilOp_Keep, eStencilOp_Keep, eStencilOp_IncrementWrap, eStencilOp_Keep, eStencilOp_Keep, eStencilOp_DecrementWrap); apRenderSettings->mlLastShadowAlgo = 2; } } } ////////////////////////////////////////// // Check if the cache as data. /* TO BE IMPLEMENTED*/ /////////////////////////////////////////// // Get local light position cVector3f vLocalLight = GetWorldPosition(); cMatrixf *pInvModelMtx = apObject->GetInvModelMatrix(); if (pInvModelMtx) { vLocalLight = cMath::MatrixMul(*pInvModelMtx, vLocalLight); } ///////////////////////////////////////////// // Set the model matrix cMatrixf *pModelMtx = apObject->GetModelMatrix(NULL); if (pModelMtx) { apLowLevelGraphics->SetMatrix(eMatrix_ModelView, cMath::MatrixMul( apRenderSettings->mpCamera->GetViewMatrix(), *pModelMtx)); } else if (apRenderSettings->mbMatrixWasNULL == false) { apLowLevelGraphics->SetMatrix(eMatrix_ModelView, apRenderSettings->mpCamera->GetViewMatrix()); } ///////////////////////////////////////////////////////// // Get the data arrays const float *pPosArray = pSubEntity->GetVertexBuffer()->GetArray(eVertexFlag_Position); unsigned int *pIdxArray = pSubEntity->GetVertexBuffer()->GetIndices(); int lVtxStride = kvVertexElements[cMath::Log2ToInt(eVertexFlag_Position)]; const bool bDoubleSided = pSubMesh->GetDoubleSided(); ///////////////////////////////////////////////////////// // Iterate faces and check which are facing the light. cTriangleData *pTriangles = pSubEntity->GetTriangleVecPtr()->data(); const int lTriNum = pSubEntity->GetTriangleNum(); for (int tri = 0, idx = 0; tri < lTriNum; tri++, idx += 3) { cTriangleData &Tri = pTriangles[tri]; const float *pPoint = &pPosArray[pIdxArray[idx] * lVtxStride]; const cVector3f &vNormal = Tri.normal; // Use Dot product to check Tri.facingLight = ((pPoint[0] - vLocalLight.x) * vNormal.x + (pPoint[1] - vLocalLight.y) * vNormal.y + (pPoint[2] - vLocalLight.z) * vNormal.z) < 0; } ///////////////////////////////////////////////////////// // Iterate edges and find possible silhouette // Get edge pointer, index pointer and offset unsigned int *pCurrentIndexPos = &mpIndexArray[0]; const cTriEdge *pEdges = pSubMesh->GetEdgeVecPtr()->data(); int lOffset = pSubEntity->GetVertexBuffer()->GetVertexNum(); // Iterate const int lEdgeNum = pSubMesh->GetEdgeNum(); for (int edge = 0; edge < lEdgeNum; edge++) { const cTriEdge &Edge = pEdges[edge]; const cTriangleData *pTri1 = &pTriangles[Edge.tri1]; const cTriangleData *pTri2 = nullptr; if (Edge.invert_tri2 == false) pTri2 = &pTriangles[Edge.tri2]; // Check if this edge has one triangle facing and one not facing the light. // If the triangel is onesided (invert_tri2) then it is always a silhouette if ((Edge.invert_tri2) || (pTri1->facingLight && !pTri2->facingLight) || (pTri2->facingLight && !pTri1->facingLight)) { if (pTri1->facingLight) { *(pCurrentIndexPos++) = Edge.point1; *(pCurrentIndexPos++) = Edge.point2; *(pCurrentIndexPos++) = Edge.point2 + lOffset; *(pCurrentIndexPos++) = Edge.point1; *(pCurrentIndexPos++) = Edge.point2 + lOffset; *(pCurrentIndexPos++) = Edge.point1 + lOffset; lIndexCount += 6; } // Do not draw if the edge only has one face and it is not facing the light. else if (!Edge.invert_tri2) { *(pCurrentIndexPos++) = Edge.point2; *(pCurrentIndexPos++) = Edge.point1; *(pCurrentIndexPos++) = Edge.point1 + lOffset; *(pCurrentIndexPos++) = Edge.point2; *(pCurrentIndexPos++) = Edge.point1 + lOffset; *(pCurrentIndexPos++) = Edge.point2 + lOffset; lIndexCount += 6; } // DEBUG: /*if(!(bDoubleSided && Edge.invert_tri2==false)) { //apRenderSettings->mpVtxExtrudeProgram->UnBind(); apLowLevelGraphics->SetDepthTestActive(false); apLowLevelGraphics->SetStencilActive(false); apLowLevelGraphics->DrawLine( pSubMesh->GetVertexBuffer()->GetVector3(eVertexFlag_Position,Edge.point1), pSubMesh->GetVertexBuffer()->GetVector3(eVertexFlag_Position,Edge.point2), cColor(1,0.9f,0,1)); apLowLevelGraphics->SetStencilActive(true); apLowLevelGraphics->SetDepthTestActive(true); }*/ } } /////////////////////////////////////////////////////// // If Z fail is used, generate front and back cap if (bZFail) { for (int tri = 0, idx = 0; tri < lTriNum; tri++, idx += 3) { cTriangleData &Data = pSubEntity->GetTriangle(tri); // Front cap if (Data.facingLight) { memcpy(pCurrentIndexPos, &pIdxArray[idx], 3 * sizeof(unsigned int)); pCurrentIndexPos += 3; lIndexCount += 3; /*mpIndexArray[lIndexCount+0] = pIdxArray[idx+0]; mpIndexArray[lIndexCount+1] = pIdxArray[idx+1]; mpIndexArray[lIndexCount+2] = pIdxArray[idx+2];*/ if (bDoubleSided) { mpIndexArray[lIndexCount + 0] = pIdxArray[idx + 2] + lOffset; mpIndexArray[lIndexCount + 1] = pIdxArray[idx + 1] + lOffset; mpIndexArray[lIndexCount + 2] = pIdxArray[idx + 0] + lOffset; pCurrentIndexPos += 3; lIndexCount += 3; } } // Back Cap // If double sided, the sides facing the light supply their own else if (!bDoubleSided) { mpIndexArray[lIndexCount + 0] = pIdxArray[idx + 0] + lOffset; mpIndexArray[lIndexCount + 1] = pIdxArray[idx + 1] + lOffset; mpIndexArray[lIndexCount + 2] = pIdxArray[idx + 2] + lOffset; pCurrentIndexPos += 3; lIndexCount += 3; } } } /////////////////////////////////////////////////////// // Draw the volume: // Set light position and model view matrix, this does not have to be set if last // object was static. if (pModelMtx || apRenderSettings->mbMatrixWasNULL == false) { apRenderSettings->extrudeProgram->SetVec3f("lightPosition", vLocalLight); apRenderSettings->extrudeProgram->SetMatrixf("worldViewProj", eGpuProgramMatrix_ViewProjection, eGpuProgramMatrixOp_Identity); // If a null matrix has been set, let other passes know. if (pModelMtx) apRenderSettings->mbMatrixWasNULL = false; else apRenderSettings->mbMatrixWasNULL = true; } // Set vertex buffer if (apRenderSettings->mpVtxBuffer != pSubEntity->GetVertexBuffer()) { if (apRenderSettings->mbLog) Log(" Setting vertex buffer %d\n", (size_t)pSubEntity->GetVertexBuffer()); pSubEntity->GetVertexBuffer()->Bind(); apRenderSettings->mpVtxBuffer = pSubEntity->GetVertexBuffer(); } // Draw vertex buffer if (apLowLevelGraphics->GetCaps(eGraphicCaps_TwoSideStencil)) { pSubEntity->GetVertexBuffer()->DrawIndices(mpIndexArray, lIndexCount); if (apRenderSettings->mbLog) Log(" Drawing front and back simultaneously.\n"); } else { if (apRenderSettings->mbLog) Log(" Drawing front and back separately.\n"); if (bZFail) { // Front apLowLevelGraphics->SetStencil(eStencilFunc_Always, 0, 0x0, eStencilOp_Keep, eStencilOp_DecrementWrap, eStencilOp_Keep); pSubEntity->GetVertexBuffer()->DrawIndices(mpIndexArray, lIndexCount); // Back apLowLevelGraphics->SetCullMode(eCullMode_Clockwise); apLowLevelGraphics->SetStencil(eStencilFunc_Always, 0, 0x0, eStencilOp_Keep, eStencilOp_IncrementWrap, eStencilOp_Keep); pSubEntity->GetVertexBuffer()->DrawIndices(mpIndexArray, lIndexCount); } else { // Front apLowLevelGraphics->SetStencil(eStencilFunc_Always, 0, 0x0, eStencilOp_Keep, eStencilOp_Keep, eStencilOp_IncrementWrap); pSubEntity->GetVertexBuffer()->DrawIndices(mpIndexArray, lIndexCount); // Back apLowLevelGraphics->SetCullMode(eCullMode_Clockwise); apLowLevelGraphics->SetStencil(eStencilFunc_Always, 0, 0x0, eStencilOp_Keep, eStencilOp_Keep, eStencilOp_DecrementWrap); pSubEntity->GetVertexBuffer()->DrawIndices(mpIndexArray, lIndexCount); } apLowLevelGraphics->SetCullMode(eCullMode_CounterClockwise); } if (apLowLevelGraphics->GetCaps(eGraphicCaps_TwoSideStencil)) { apLowLevelGraphics->SetStencilTwoSide(false); apRenderSettings->mlLastShadowAlgo = 0; } } //----------------------------------------------------------------------- void iLight3D::OnFlickerOff() { // Particle system if (msFlickerOffPS != "") { /*cParticleSystem3D *pPS = */ mpWorld3D->CreateParticleSystem(GetName() + "_PS", msFlickerOffPS, cVector3f(1, 1, 1), GetWorldMatrix()); } } //----------------------------------------------------------------------- void iLight3D::OnFlickerOn() { // Particle system if (msFlickerOnPS != "") { /*cParticleSystem3D *pPS = */ mpWorld3D->CreateParticleSystem(GetName() + "_PS", msFlickerOnPS, cVector3f(1, 1, 1), GetWorldMatrix()); } } //----------------------------------------------------------------------- void iLight3D::OnSetDiffuse() { for (size_t i = 0; i < mvBillboards.size(); ++i) { cBillboard *pBill = mvBillboards[i]; pBill->SetColor(cColor(mDiffuseColor.r, mDiffuseColor.g, mDiffuseColor.b, 1)); } } ////////////////////////////////////////////////////////////////////////// // SAVE OBJECT STUFF ////////////////////////////////////////////////////////////////////////// //----------------------------------------------------------------------- kBeginSerializeVirtual(cSaveData_iLight3D, cSaveData_iRenderable) kSerializeVar(msFalloffMap, eSerializeType_String) kSerializeVarContainer(mlstBillboardIds, eSerializeType_Int32) kSerializeVar(mDiffuseColor, eSerializeType_Color) kSerializeVar(mSpecularColor, eSerializeType_Color) kSerializeVar(mfIntensity, eSerializeType_Float32) kSerializeVar(mfFarAttenuation, eSerializeType_Float32) kSerializeVar(mfNearAttenuation, eSerializeType_Float32) kSerializeVar(mfSourceRadius, eSerializeType_Float32) kSerializeVar(mbCastShadows, eSerializeType_Bool) kSerializeVar(mbAffectMaterial, eSerializeType_Bool) kSerializeVar(mColAdd, eSerializeType_Color) kSerializeVar(mfRadiusAdd, eSerializeType_Float32) kSerializeVar(mDestCol, eSerializeType_Color) kSerializeVar(mfDestRadius, eSerializeType_Float32) kSerializeVar(mfFadeTime, eSerializeType_Float32) kSerializeVar(mbFlickering, eSerializeType_Bool) kSerializeVar(msFlickerOffSound, eSerializeType_String) kSerializeVar(msFlickerOnSound, eSerializeType_String) kSerializeVar(msFlickerOffPS, eSerializeType_String) kSerializeVar(msFlickerOnPS, eSerializeType_String) kSerializeVar(mfFlickerOnMinLength, eSerializeType_Float32) kSerializeVar(mfFlickerOffMinLength, eSerializeType_Float32) kSerializeVar(mfFlickerOnMaxLength, eSerializeType_Float32) kSerializeVar(mfFlickerOffMaxLength, eSerializeType_Float32) kSerializeVar(mFlickerOffColor, eSerializeType_Color) kSerializeVar(mfFlickerOffRadius, eSerializeType_Float32) kSerializeVar(mbFlickerFade, eSerializeType_Bool) kSerializeVar(mfFlickerOnFadeLength, eSerializeType_Float32) kSerializeVar(mfFlickerOffFadeLength, eSerializeType_Float32) kSerializeVar(mFlickerOnColor, eSerializeType_Color) kSerializeVar(mfFlickerOnRadius, eSerializeType_Float32) kSerializeVar(mbFlickerOn, eSerializeType_Bool) kSerializeVar(mfFlickerTime, eSerializeType_Float32) kSerializeVar(mfFlickerStateLength, eSerializeType_Float32) kEndSerialize() //----------------------------------------------------------------------- iSaveData *iLight3D::CreateSaveData() { return NULL; } //----------------------------------------------------------------------- void iLight3D::SaveToSaveData(iSaveData *apSaveData) { kSaveData_SaveToBegin(iLight3D); ////////////////////////// // Data pData->msFalloffMap = mpFalloffMap == NULL ? "" : mpFalloffMap->GetName(); pData->mlstBillboardIds.Clear(); for (size_t i = 0; i < mvBillboards.size(); ++i) { pData->mlstBillboardIds.Add(mvBillboards[i]->GetSaveObjectId()); } ////////////////////////// // Variables kSaveData_SaveTo(mDiffuseColor); kSaveData_SaveTo(mSpecularColor); kSaveData_SaveTo(mfIntensity); kSaveData_SaveTo(mfFarAttenuation); kSaveData_SaveTo(mfNearAttenuation); kSaveData_SaveTo(mfSourceRadius); kSaveData_SaveTo(mbCastShadows); kSaveData_SaveTo(mbAffectMaterial); kSaveData_SaveTo(mColAdd); kSaveData_SaveTo(mfRadiusAdd); kSaveData_SaveTo(mDestCol); kSaveData_SaveTo(mfDestRadius); kSaveData_SaveTo(mfFadeTime); kSaveData_SaveTo(mbFlickering); kSaveData_SaveTo(msFlickerOffSound); kSaveData_SaveTo(msFlickerOnSound); kSaveData_SaveTo(msFlickerOffPS); kSaveData_SaveTo(msFlickerOnPS); kSaveData_SaveTo(mfFlickerOnMinLength); kSaveData_SaveTo(mfFlickerOffMinLength); kSaveData_SaveTo(mfFlickerOnMaxLength); kSaveData_SaveTo(mfFlickerOffMaxLength); kSaveData_SaveTo(mFlickerOffColor); kSaveData_SaveTo(mfFlickerOffRadius); kSaveData_SaveTo(mbFlickerFade); kSaveData_SaveTo(mfFlickerOnFadeLength); kSaveData_SaveTo(mfFlickerOffFadeLength); kSaveData_SaveTo(mFlickerOnColor); kSaveData_SaveTo(mfFlickerOnRadius); kSaveData_SaveTo(mbFlickerOn); kSaveData_SaveTo(mfFlickerTime); kSaveData_SaveTo(mfFlickerStateLength); } //----------------------------------------------------------------------- void iLight3D::LoadFromSaveData(iSaveData *apSaveData) { kSaveData_LoadFromBegin(iLight3D); ////////////////////////// // Data if (pData->msFalloffMap != "") { iTexture *pTex = mpTextureManager->Create1D(pData->msFalloffMap, false); if (pTex) SetFalloffMap(pTex); } ////////////////////////// // Variables kSaveData_LoadFrom(mDiffuseColor); kSaveData_LoadFrom(mSpecularColor); kSaveData_LoadFrom(mfIntensity); kSaveData_LoadFrom(mfFarAttenuation); kSaveData_LoadFrom(mfNearAttenuation); kSaveData_LoadFrom(mfSourceRadius); kSaveData_LoadFrom(mbCastShadows); kSaveData_LoadFrom(mbAffectMaterial); kSaveData_LoadFrom(mColAdd); kSaveData_LoadFrom(mfRadiusAdd); kSaveData_LoadFrom(mDestCol); kSaveData_LoadFrom(mfDestRadius); kSaveData_LoadFrom(mfFadeTime); kSaveData_LoadFrom(mbFlickering); kSaveData_LoadFrom(msFlickerOffSound); kSaveData_LoadFrom(msFlickerOnSound); kSaveData_LoadFrom(msFlickerOffPS); kSaveData_LoadFrom(msFlickerOnPS); kSaveData_LoadFrom(mfFlickerOnMinLength); kSaveData_LoadFrom(mfFlickerOffMinLength); kSaveData_LoadFrom(mfFlickerOnMaxLength); kSaveData_LoadFrom(mfFlickerOffMaxLength); kSaveData_LoadFrom(mFlickerOffColor); kSaveData_LoadFrom(mfFlickerOffRadius); kSaveData_LoadFrom(mbFlickerFade); kSaveData_LoadFrom(mfFlickerOnFadeLength); kSaveData_LoadFrom(mfFlickerOffFadeLength); kSaveData_LoadFrom(mFlickerOnColor); kSaveData_LoadFrom(mfFlickerOnRadius); kSaveData_LoadFrom(mbFlickerOn); kSaveData_LoadFrom(mfFlickerTime); kSaveData_LoadFrom(mfFlickerStateLength); } //----------------------------------------------------------------------- void iLight3D::SaveDataSetup(cSaveObjectHandler *apSaveObjectHandler, cGame *apGame) { kSaveData_SetupBegin(iLight3D); // Get attached billboards. cContainerListIterator it = pData->mlstBillboardIds.GetIterator(); while (it.HasNext()) { int lId = it.Next(); iSaveObject *pObject = apSaveObjectHandler->Get(lId); cBillboard *pBill = static_cast(pObject); if (pBill == NULL) { Error("Couldn't find billboard id %s\n", lId); continue; } AttachBillboard(pBill); } } //----------------------------------------------------------------------- } // namespace hpl