Files
2026-02-02 04:50:13 +01:00

1037 lines
34 KiB
C++

/* 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 <http://www.gnu.org/licenses/>.
*
*/
/*
* 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<cMeshEntity *>(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<cSector *>(*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<cBillboard *>::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<cSubMeshEntity *>(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<int> it = pData->mlstBillboardIds.GetIterator();
while (it.HasNext()) {
int lId = it.Next();
iSaveObject *pObject = apSaveObjectHandler->Get(lId);
cBillboard *pBill = static_cast<cBillboard *>(pObject);
if (pBill == NULL) {
Error("Couldn't find billboard id %s\n", lId);
continue;
}
AttachBillboard(pBill);
}
}
//-----------------------------------------------------------------------
} // namespace hpl