Files
scummvm-cursorfix/engines/hpl1/engine/graphics/Renderer2D.cpp
2026-02-02 04:50:13 +01:00

895 lines
28 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/graphics/Renderer2D.h"
#include "hpl1/engine/graphics/GraphicsDrawer.h"
#include "hpl1/engine/graphics/LowLevelGraphics.h"
#include "hpl1/engine/graphics/Mesh2d.h"
#include "hpl1/engine/graphics/RenderObject2D.h"
#include "hpl1/engine/graphics/Texture.h"
#include "hpl1/engine/math/Math.h"
#include "hpl1/engine/resources/Resources.h"
#include "hpl1/engine/resources/TextureManager.h"
#include "hpl1/engine/resources/low_level_resources.h"
#include "hpl1/engine/scene/Camera2D.h"
#include "hpl1/engine/scene/GridMap2D.h"
#include "hpl1/engine/scene/Light2D.h"
#include "hpl1/engine/scene/Scene.h"
#include "hpl1/engine/scene/TileData.h"
#include "hpl1/engine/scene/TileMap.h"
#include "hpl1/engine/scene/World2D.h"
#include "hpl1/engine/system/low_level_system.h"
namespace hpl {
//////////////////////////////////////////////////////////////////////////
// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
cRenderer2D::cRenderer2D(iLowLevelGraphics *apLowLevelGraphics, cResources *apResources, cGraphicsDrawer *apGraphicsDrawer) {
Log(" Creating Renderer2D\n");
mpLowLevelGraphics = apLowLevelGraphics;
mpLowLevelResources = apResources->GetLowLevel();
mpResources = apResources;
mpGraphicsDrawer = apGraphicsDrawer;
/// LIGHTING SETUP
mpLightMap[0] = mpResources->GetTextureManager()->Create2D("PointLight2D.bmp", false);
if (mpLightMap[0] == NULL)
error("Couldn't load PointLight2D");
mpLightMap[0]->SetWrapS(eTextureWrap_ClampToEdge);
mpLightMap[0]->SetWrapT(eTextureWrap_ClampToEdge);
mpLightMap[1] = NULL;
mPrevLightRect = cRect2f(0, 0, -1, -1);
mAmbientLight = cColor(0.0f, 0.0f, 0.0f, 0);
mfShadowZ = 9.5f;
Log(" Renderer2D created\n");
}
//-----------------------------------------------------------------------
cRenderer2D::~cRenderer2D() {
for (int i = 0; i < 2; i++) {
if (mpLightMap[i]) {
mpResources->GetTextureManager()->Destroy(mpLightMap[i]);
}
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PUBLIC METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
////////////////////// RENDER OBJECT COMPARE ////////////////////////////
bool cRenderObject2DCompare::operator()(const cRenderObject2D &aObjectA, const cRenderObject2D &aObjectB) const {
if (aObjectA.GetMaterial()->GetTexture(eMaterialTexture_Diffuse) !=
aObjectB.GetMaterial()->GetTexture(eMaterialTexture_Diffuse)) {
return aObjectA.GetMaterial()->GetTexture(eMaterialTexture_Diffuse) >
aObjectB.GetMaterial()->GetTexture(eMaterialTexture_Diffuse);
} else if (aObjectA.GetMaterial()->GetType(eMaterialRenderType_Diffuse) !=
aObjectB.GetMaterial()->GetType(eMaterialRenderType_Diffuse)) {
return aObjectA.GetMaterial()->GetType(eMaterialRenderType_Diffuse) >
aObjectB.GetMaterial()->GetType(eMaterialRenderType_Diffuse);
}
/*else if(aObjectA.GetMaterial()->GetType(eMaterialRenderType_Light) !=
aObjectB.GetMaterial()->GetType(eMaterialRenderType_Light))
{
return aObjectA.GetMaterial()->GetType(eMaterialRenderType_Light) >
aObjectB.GetMaterial()->GetType(eMaterialRenderType_Light);
}*/
/*else if(Some other thing to sort by)*/
/*Sort by Z type as well!*/
return false;
}
////////////////////// TRANS RENDER OBJECT COMPARE ////////////////////////////
bool cRenderTransObjectCompare::operator()(const cRenderObject2D &aObjectA, const cRenderObject2D &aObjectB) const {
if (aObjectA.GetZ() != aObjectB.GetZ()) {
return aObjectA.GetZ() < aObjectB.GetZ();
} else if (aObjectA.GetMaterial()->GetTexture(eMaterialTexture_Diffuse) !=
aObjectB.GetMaterial()->GetTexture(eMaterialTexture_Diffuse)) {
return aObjectA.GetMaterial()->GetTexture(eMaterialTexture_Diffuse) >
aObjectB.GetMaterial()->GetTexture(eMaterialTexture_Diffuse);
} else if (aObjectA.GetMaterial() != aObjectB.GetMaterial()) {
return aObjectA.GetMaterial() > aObjectB.GetMaterial();
} else {
}
return false;
}
//-----------------------------------------------------------------------
void cRenderer2D::RenderObject(const cRenderObject2D &aObject, unsigned int &aIdxAdd, iMaterial *pMat,
iLight2D *pLight, eMaterialRenderType aRenderType, cCamera2D *apCam) {
if (aObject.GetCustomRenderer()) {
aObject.GetCustomRenderer()->RenderToBatch(aRenderType, aIdxAdd);
return;
}
int i;
tVertexVec *pVtxVec = aObject.GetVertexVec();
tUIntVec *pIdxVec = aObject.GetIndexVec();
cVector3f *pTransform = aObject.GetTransform();
// bool bUsesLights = aObject.GetMaterial()->UsesLights();
// pMat->EditVertexes(aRenderType,apCam,pLight,pVtxVec,pTransform,aIdxAdd);
if (pTransform == NULL) {
for (i = 0; i < (int)pVtxVec->size(); i++)
mpLowLevelGraphics->AddVertexToBatch((*pVtxVec)[i]);
} else {
for (i = 0; i < (int)pVtxVec->size(); i++)
mpLowLevelGraphics->AddVertexToBatch(&(*pVtxVec)[i], pTransform);
}
for (i = 0; i < (int)pIdxVec->size(); i++) {
mpLowLevelGraphics->AddIndexToBatch((*pIdxVec)[i] + aIdxAdd);
}
aIdxAdd += (unsigned int)pVtxVec->size();
}
//-----------------------------------------------------------------------
void cRenderer2D::RenderObjects(cCamera2D *apCamera, cGridMap2D *apMapLights, cWorld2D *apWorld) {
iMaterial *pMat = NULL;
iMaterial *pPrevMat = NULL;
cRect2f ClipRect;
unsigned int lIdxAdd = 0;
apCamera->GetClipRect(ClipRect);
// Setup the screen
mpLowLevelGraphics->SetClearColor(mAmbientLight);
mpLowLevelGraphics->ClearScreen();
apCamera->SetModelViewMatrix(mpLowLevelGraphics);
apCamera->SetProjectionMatrix(mpLowLevelGraphics);
////// BEGIN SET UP LIGHTS /////////////
iGridMap2DIt *pLightIt = apMapLights->GetRectIterator(ClipRect);
while (pLightIt->HasNext()) {
iLight2D *pLight = static_cast<iLight2D *>(pLightIt->Next());
if (pLight->GetDiffuseColor().r == 0 && pLight->GetDiffuseColor().g == 0 &&
pLight->GetDiffuseColor().b == 0) {
continue;
}
if (pLight->GetAffectMaterial()) {
mlstLights.push_back(pLight);
} else {
mlstFastLights.push_back(pLight);
}
}
hplDelete(pLightIt);
////// END SET UP LIGHTS /////////////
/*int lCount=0;
tRenderObjectSetIt TestIt = m_mapObject.begin();
while(TestIt != m_mapObject.end())
{
iMaterial *pMat = TestIt->GetMaterial();
Log("%d Tex: %d Light: %d Diff: %d\n", lCount, (int)pMat->GetTexture(eMaterialTexture_Diffuse),
(int)pMat->GetType(eMaterialRenderType_Light),
(int)pMat->GetType(eMaterialRenderType_Diffuse)
);
TestIt++;
lCount++;
}*/
////// BEGIN RENDER ZBUFFER ////////////
mpLowLevelGraphics->SetDepthWriteActive(true);
mpLowLevelGraphics->SetColorWriteActive(false, false, false, false);
mpLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_LessOrEqual);
tRenderObjectSetIt ObjectIt = m_mapObject.begin();
if (ObjectIt != m_mapObject.end())
pMat = ObjectIt->GetMaterial();
while (ObjectIt != m_mapObject.end()) {
if (pMat->StartRendering(eMaterialRenderType_Z, apCamera, NULL) == false) {
ObjectIt++; // ObjectIt = m_mapObject.erase(ObjectIt);?
if (ObjectIt != m_mapObject.end())
pMat = ObjectIt->GetMaterial();
continue;
}
do {
RenderObject(*ObjectIt, lIdxAdd, pMat, NULL, eMaterialRenderType_Z, apCamera);
pPrevMat = pMat;
ObjectIt++;
if (ObjectIt == m_mapObject.end()) {
pMat = NULL;
break;
} else {
pMat = ObjectIt->GetMaterial();
}
} while (pMat->GetType(eMaterialRenderType_Z) ==
pPrevMat->GetType(eMaterialRenderType_Z) &&
pMat->GetTexture(eMaterialTexture_Diffuse) ==
pPrevMat->GetTexture(eMaterialTexture_Diffuse));
lIdxAdd = 0;
do {
mpLowLevelGraphics->FlushTriBatch(pPrevMat->GetBatchFlags(eMaterialRenderType_Z), false);
} while (pPrevMat->NextPass(eMaterialRenderType_Z));
mpLowLevelGraphics->ClearBatch();
pPrevMat->EndRendering(eMaterialRenderType_Z);
}
////// BEGIN RENDER ZBUFFER ////////////
////// BEGIN RENDER LIGHTS////////////
mpLowLevelGraphics->SetDepthWriteActive(false);
mpLowLevelGraphics->SetColorWriteActive(true, true, true, true);
// Iterate the lights:
tLightListIt LightIt = mlstLights.begin();
while (LightIt != mlstLights.end()) {
if ((*LightIt)->IsActive() == false) {
LightIt++;
continue;
}
if ((*LightIt)->GetCastShadows())
if (RenderShadows(apCamera, *LightIt, apWorld) == false) {
LightIt++;
continue;
}
mpLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_Equal);
// Set up stencil so shaodws are not drawn
mpLowLevelGraphics->SetStencilActive(true);
mpLowLevelGraphics->SetStencil(eStencilFunc_NotEqual, 0x1, 0x1,
eStencilOp_Keep, eStencilOp_Keep, eStencilOp_Keep);
///////////////////////////
// Set up scissortest:
///////////////////////////
/*mpLowLevelGraphics->SetScissorActive(true);
cRect2f LightBB = (*LightIt)->GetBoundingBox();
cRect2l LightRect( (int)floor( ( (LightBB.x - ClipRect.x)/ClipRect.w) * mpLowLevelGraphics->GetScreenSize().x),
(int)floor( ( (LightBB.y - ClipRect.y)/ClipRect.h) * mpLowLevelGraphics->GetScreenSize().y),
(int)((LightBB.w/ClipRect.w)*mpLowLevelGraphics->GetScreenSize().x),
(int)((LightBB.h/ClipRect.h)*mpLowLevelGraphics->GetScreenSize().y)
);
if(LightRect.x<0){
LightRect.w += LightRect.x;
LightRect.x =0;
}
if(LightRect.y<0){
LightRect.h += LightRect.y;
LightRect.y =0;
}
if(LightRect.x + LightRect.w >= mpLowLevelGraphics->GetScreenSize().x)
LightRect.w = (int)mpLowLevelGraphics->GetScreenSize().x - LightRect.x;
if(LightRect.y + LightRect.h >= mpLowLevelGraphics->GetScreenSize().y)
LightRect.h = (int)mpLowLevelGraphics->GetScreenSize().y - LightRect.y;
mpLowLevelGraphics->SetScissorRect(LightRect);*/
/////////////////////////////////
// Draw objects the light touches
/////////////////////////////////
ObjectIt = m_mapObject.begin();
if (ObjectIt != m_mapObject.end())
pMat = ObjectIt->GetMaterial();
while (ObjectIt != m_mapObject.end()) {
// If light does not touch this object, don't draw it.
if (cMath::BoxCollision((*LightIt)->GetBoundingBox(), ObjectIt->GetRect()) == false) {
ObjectIt++;
if (ObjectIt != m_mapObject.end())
pMat = ObjectIt->GetMaterial();
continue;
}
if (pMat->StartRendering(eMaterialRenderType_Light, apCamera, *LightIt) == false) {
ObjectIt++;
if (ObjectIt != m_mapObject.end())
pMat = ObjectIt->GetMaterial();
continue;
}
do {
RenderObject(*ObjectIt, lIdxAdd, pMat, *LightIt, eMaterialRenderType_Light, apCamera);
pPrevMat = pMat;
do {
ObjectIt++;
} while (cMath::BoxCollision((*LightIt)->GetBoundingBox(), ObjectIt->GetRect()) == false && ObjectIt != m_mapObject.end());
if (ObjectIt == m_mapObject.end()) {
pMat = NULL;
break;
} else {
pMat = ObjectIt->GetMaterial();
}
} while (pMat->GetType(eMaterialRenderType_Light) ==
pPrevMat->GetType(eMaterialRenderType_Light) &&
pMat->GetTexture(eMaterialTexture_Diffuse) ==
pPrevMat->GetTexture(eMaterialTexture_Diffuse));
lIdxAdd = 0;
do {
mpLowLevelGraphics->FlushTriBatch(pPrevMat->GetBatchFlags(eMaterialRenderType_Light), false);
} while (pPrevMat->NextPass(eMaterialRenderType_Light));
mpLowLevelGraphics->ClearBatch();
pPrevMat->EndRendering(eMaterialRenderType_Light);
}
// mpLowLevelGraphics->SetScissorActive(false);
ClearShadows();
mpLowLevelGraphics->SetStencilActive(false);
LightIt++;
}
////// END RENDER LIGHTS ////////////
////// BEGIN RENDER FAST LIGHTS ////////////
/*LightIt = mlstFastLights.begin();
while(LightIt != mlstFastLights.end())
{
lIdxAdd = (*LightIt)->Render(mpLowLevelGraphics,lIdxAdd);
LightIt++;
}
mpLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_LessOrEqual);
mpLowLevelGraphics->SetBlendActive(true);
mpLowLevelGraphics->SetBlendFunc(eBlendFunc_One,eBlendFunc_One);
mpLowLevelGraphics->FlushTriBatch(eVtxBatchFlag_Position | eVtxBatchFlag_Color0,true);*/
mpLowLevelGraphics->SetBlendActive(false);
lIdxAdd = 0;
////// END RENDER FAST LIGHTS ////////////
////// BEGIN RENDER DIFFUSE ////////////
mpLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_Equal);
ObjectIt = m_mapObject.begin();
if (ObjectIt != m_mapObject.end())
pMat = ObjectIt->GetMaterial();
while (ObjectIt != m_mapObject.end()) {
if (pMat->StartRendering(eMaterialRenderType_Diffuse, apCamera, NULL) == false) {
ObjectIt++; // ObjectIt = m_mapObject.erase(ObjectIt);?
if (ObjectIt != m_mapObject.end())
pMat = ObjectIt->GetMaterial();
continue;
}
do {
RenderObject(*ObjectIt, lIdxAdd, pMat, NULL, eMaterialRenderType_Diffuse, apCamera);
pPrevMat = pMat;
ObjectIt++; // ObjectIt = m_mapObject.erase(ObjectIt);?
// if(ObjectIt == m_mapObject.end()) pMat=NULL;
// else pMat = ObjectIt->GetMaterial();
if (ObjectIt == m_mapObject.end()) {
pMat = NULL;
break;
} else {
pMat = ObjectIt->GetMaterial();
}
}
// while(pMat == pPrevMat);
while (pMat->GetType(eMaterialRenderType_Diffuse) ==
pPrevMat->GetType(eMaterialRenderType_Diffuse) &&
pMat->GetTexture(eMaterialTexture_Diffuse) ==
pPrevMat->GetTexture(eMaterialTexture_Diffuse));
lIdxAdd = 0;
do {
mpLowLevelGraphics->FlushTriBatch(pPrevMat->GetBatchFlags(eMaterialRenderType_Diffuse), false);
} while (pPrevMat->NextPass(eMaterialRenderType_Diffuse));
mpLowLevelGraphics->ClearBatch();
pPrevMat->EndRendering(eMaterialRenderType_Diffuse);
}
////// END RENDER DIFFUSE ////////////
////// BEGIN RENDER BACKGROUND ////////////
cRect2f TempRect;
apCamera->GetClipRect(TempRect);
mpGraphicsDrawer->DrawBackgrounds(TempRect);
////// EMD RENDER BACKGROUND ////////////
////// BEGIN RENDER TRANS ////////////
apCamera->SetModelViewMatrix(mpLowLevelGraphics);
mpLowLevelGraphics->SetDepthWriteActive(false);
mpLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_LessOrEqual);
tRenderTransObjectSetIt TransIt = m_mapTransObject.begin();
if (TransIt != m_mapTransObject.end())
pMat = TransIt->GetMaterial();
lIdxAdd = 0;
while (TransIt != m_mapTransObject.end()) {
if (pMat->StartRendering(eMaterialRenderType_Diffuse, apCamera, NULL) == false) {
TransIt++; // TransIt = mapTransObject.erase(TransIt);?
if (TransIt != m_mapTransObject.end())
pMat = TransIt->GetMaterial();
continue;
}
do {
RenderObject(*TransIt, lIdxAdd, pMat, NULL, eMaterialRenderType_Diffuse, apCamera);
pPrevMat = pMat;
TransIt++; // TransIt = mapTransObject.erase(TransIt);?
if (TransIt == m_mapTransObject.end())
pMat = NULL;
else
pMat = TransIt->GetMaterial();
} while (pMat == pPrevMat);
// while(pMat->GetType() == pPrevMat->GetType()); //better right?
lIdxAdd = 0;
do {
mpLowLevelGraphics->FlushTriBatch(pPrevMat->GetBatchFlags(eMaterialRenderType_Diffuse), false);
} while (pPrevMat->NextPass(eMaterialRenderType_Diffuse));
mpLowLevelGraphics->ClearBatch();
pPrevMat->EndRendering(eMaterialRenderType_Diffuse);
}
////// END RENDER TRANS ////////////
////// BEGIN CLEAN UP ////////////
mpLowLevelGraphics->SetStencilActive(false);
mpLowLevelGraphics->SetDepthWriteActive(true);
mpLowLevelGraphics->SetColorWriteActive(true, true, true, true);
mpLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_LessOrEqual);
mlstLights.clear();
mlstFastLights.clear();
m_mapObject.clear();
m_mapTransObject.clear();
////// END CLEAN UP ////////////
}
//-----------------------------------------------------------------------
void cRenderer2D::AddObject(cRenderObject2D &aObject) {
if (aObject.GetMaterial()->IsTransperant()) {
// Todo non glowing trans thing need specieal treatment.
if (aObject.GetMaterial()->IsGlowing()) {
m_mapTransObject.insert(aObject);
}
} else {
m_mapObject.insert(aObject);
}
}
//-----------------------------------------------------------------------
//////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////
//-----------------------------------------------------------------------
void cRenderer2D::ClearShadows() {
// Todo: if the light almost covers the whole screen, it is faster to clear!
mpLowLevelGraphics->SetStencilActive(true);
mpLowLevelGraphics->SetDepthWriteActive(false);
mpLowLevelGraphics->SetDepthTestActive(false);
mpLowLevelGraphics->SetColorWriteActive(false, false, false, false); // just draw to stencil
mpLowLevelGraphics->SetTexture(0, NULL);
mpLowLevelGraphics->SetStencil(eStencilFunc_Always, 0x0, 0x0,
eStencilOp_Replace, eStencilOp_Replace, eStencilOp_Replace);
mpLowLevelGraphics->DrawFilledRect2D(mPrevLightRect, 10, cColor(0, 0, 0, 0));
mpLowLevelGraphics->SetDepthWriteActive(true);
mpLowLevelGraphics->SetDepthTestActive(true);
mpLowLevelGraphics->SetColorWriteActive(true, true, true, true); // just draw to stencil
mpLowLevelGraphics->SetStencilActive(false);
}
//-----------------------------------------------------------------------
/**
* This function renders the lightmap that is drawn on the normal gfx in a later pass
*/
bool cRenderer2D::RenderShadows(cCamera2D *apCamera, iLight2D *pLight, cWorld2D *apWorld) {
cRect2f ClipRect;
int lFirstIndex = 0;
apCamera->GetClipRect(ClipRect);
mpLowLevelGraphics->SetDepthTestFunc(eDepthTestFunc_LessOrEqual);
mpLowLevelGraphics->SetDepthWriteActive(false);
mpLowLevelGraphics->SetColorWriteActive(false, false, false, false); // just draw to stencil
mpLowLevelGraphics->SetTexture(0, NULL);
// Set the stencil buffer so above shadows don't get affected by em.
mpLowLevelGraphics->SetStencilActive(true);
// Not needed to clear since that is made in the GL setup, right?
/*if(mPrevLightRect.h<0)
{
mpLowLevelGraphics->SetClearColorActive(false); mpLowLevelGraphics->SetClearDepthActive(false);
mpLowLevelGraphics->SetClearStencilActive(true);
mpLowLevelGraphics->SetClearStencil(0);
mpLowLevelGraphics->ClearScreen();
mpLowLevelGraphics->SetClearStencilActive(true);
mpLowLevelGraphics->SetClearColorActive(true);mpLowLevelGraphics->SetClearDepthActive(true);
mpLowLevelGraphics->SetClearStencilActive(false);
}*/
mpLowLevelGraphics->SetStencil(eStencilFunc_Always, 0x1, 0x1,
eStencilOp_Keep, eStencilOp_Keep, eStencilOp_Replace);
apCamera->SetModelViewMatrix(mpLowLevelGraphics);
//// RENDER SHADOW VARS
cColor ShadowColor = cColor(0, 0, 0, 0);
float fTileSize = apWorld->GetTileMap()->GetTileSize();
float fHalfTileSize = fTileSize / 2;
/// BEGIN CHECK LIGHT VISIBLE //////////////
float fSourceSize = pLight->GetSourceRadius();
cVector3f vPos = pLight->GetWorldPosition();
cTile *pTile = apWorld->GetTileMap()->GetWorldTile(cVector2f(vPos.x, vPos.y),
apWorld->GetTileMap()->GetShadowLayer());
if (pTile) {
cTileDataNormal *pData = static_cast<cTileDataNormal *>(pTile->GetTileData());
if (pData->GetCollideMesh()) {
if (pData->GetCollideMesh()->PointIsInside(cVector2f(vPos.x, vPos.y),
cVector2f(pTile->GetPosition().x, pTile->GetPosition().y),
pTile->GetAngle())) {
mpLowLevelGraphics->SetColorWriteActive(true, true, true, true);
mpLowLevelGraphics->SetStencilActive(false);
mpLowLevelGraphics->SetDepthWriteActive(true);
return false;
}
}
}
cRect2f LightRect = pLight->GetBoundingBox();
/// END CHECK LIGHT VISIBLE //////////////
/// BEGIN CAST SHADOWS //////////////
if (pLight->GetCastShadows()) {
lFirstIndex = 0; // Begin a new batch!
// Get tile iterator
iTileMapIt *pTileIt = apWorld->GetTileMap()->GetRectIterator(pLight->GetBoundingBox(),
apWorld->GetTileMap()->GetShadowLayer());
// Iterate all tiles
while (pTileIt->HasNext()) {
cTile *pTile2 = pTileIt->Next();
cTileDataNormal *pData = static_cast<cTileDataNormal *>(pTile2->GetTileData());
if (pData->GetCollideMesh() == NULL)
continue;
bool vbTileInDir[4];
if (pData->IsSolid()) {
int lTileDirNum = apWorld->GetTileMap()->GetTileNeighbours4Dir(
pTileIt->GetNum(),
apWorld->GetTileMap()->GetShadowLayer(), &vbTileInDir[0]);
if (lTileDirNum >= 4) {
continue;
}
}
tMesh2DEdgeVec *pEdgeVec = pData->GetCollideMesh()->GetEdgeVec(pTile2->GetAngle());
tVertexVec *pVtxVec = pData->GetCollideVertexVec(pTile2->GetAngle());
cVector2f vTilePos = cVector2f(pTile2->GetPosition().x, pTile2->GetPosition().y);
cVector2f vLightPos = cVector2f(pLight->GetWorldPosition().x, pLight->GetWorldPosition().y);
float fRadius = pLight->GetFarAttenuation();
bool bNonFit = false;
// See if the tile fits in the light rect. If not some shadows need clipping
//(Clipping is made in the CreateVertexes() )
if (cMath::BoxFit(cRect2f(vTilePos.x - fHalfTileSize, vTilePos.y - fHalfTileSize,
fTileSize, fTileSize),
LightRect) == false) {
bNonFit = true;
}
// Find points (edges) that cast shadows
FindShadowPoints(pEdgeVec, vLightPos, vTilePos);
// Add the shadows to the vertex batch
lFirstIndex = CreateVertexes(vLightPos, LightRect, fRadius, bNonFit, vTilePos, pVtxVec,
ShadowColor, lFirstIndex, fSourceSize);
}
hplDelete(pTileIt);
// Draw the shadows to the stencil buffer
mpLowLevelGraphics->FlushTriBatch(eVtxBatchFlag_Color0 |
eVtxBatchFlag_Position);
lFirstIndex = 0;
}
/// END CAST SHADOWS //////////////
mPrevLightRect = pLight->GetBoundingBox();
mpLowLevelGraphics->SetColorWriteActive(true, true, true, true);
mpLowLevelGraphics->SetStencilActive(false);
mpLowLevelGraphics->SetDepthWriteActive(true);
return true;
}
//-----------------------------------------------------------------------
cVector2f cRenderer2D::CalcLineEnd(cVector3f avLight, cVector3f avPoint, float afRadius,
cVector2f &avSide, cVector2f avClipPos) {
cVector2f vEndPos;
cVector2f vSize(afRadius);
float dX = avPoint.x - avLight.x;
float dY = avPoint.y - avLight.y;
float kY, kX;
if (dX == 0)
kY = 100000;
else
kY = dY / dX;
if (dY == 0)
kX = 100000;
else
kX = dX / dY;
if (ABS(dX) <= ABS(dY) && dY > 0) {
vEndPos.y = avClipPos.y + vSize.y;
float A = avPoint.y - kY * avPoint.x;
vEndPos.x = (vEndPos.y - A) / kY;
avSide.y = 1;
} else if (ABS(dX) <= ABS(dY) && dY <= 0) {
vEndPos.y = avClipPos.y - vSize.y;
float A = avPoint.y - kY * avPoint.x;
vEndPos.x = (vEndPos.y - A) / kY;
avSide.y = -1;
} else if (ABS(dX) > ABS(dY) && dX > 0) {
vEndPos.x = avClipPos.x + vSize.y;
float A = avPoint.x - kX * avPoint.y;
vEndPos.y = (vEndPos.x - A) / kX;
avSide.x = 1;
} else if (ABS(dX) > ABS(dY) && dX <= 0) {
vEndPos.x = avClipPos.x - vSize.y;
float A = avPoint.x - kX * avPoint.y;
vEndPos.y = (vEndPos.x - A) / kX;
avSide.x = -1;
}
return vEndPos;
}
//-----------------------------------------------------------------------
void cRenderer2D::FindShadowPoints(tMesh2DEdgeVec *apEdgeVec, cVector2f avLightPos, cVector2f avTilePos) {
cVector2f vEdgeNormal;
cVector2f vLightNormal;
// int lPrevWasShadow = -1;
// int lFirstWasShadow = -1;
mlShadowPointSize = 0;
// int lMaxEdge = (int)apEdgeVec->size() - 1;
for (int i = 0; i < (int)apEdgeVec->size(); i++) {
int point = (*apEdgeVec)[i].mlStartIndex;
int next = (*apEdgeVec)[i].mlEndIndex;
vEdgeNormal = (*apEdgeVec)[i].mvNormal;
vLightNormal = avLightPos - avTilePos - (*apEdgeVec)[i].mvMidPos;
float fDot = vEdgeNormal.x * vLightNormal.x + vEdgeNormal.y * vLightNormal.y;
if (fDot < 0) {
mvShadowPoints[mlShadowPointSize][0] = point;
mvShadowPoints[mlShadowPointSize][1] = next;
mlShadowPointSize++;
}
}
}
//-----------------------------------------------------------------------
int cRenderer2D::CreateVertexes(cVector2f vLightPos, cRect2f LightRect, float fRadius, bool bNonFit,
cVector2f vTilePos, tVertexVec *apVtxVec, cColor ShadowColor, int lFirstIndex, float fSourceSize) {
// int lNum = 0;
// Walk through the edges
for (int idx = 0; idx < mlShadowPointSize; idx++) {
cVector3f vPointPos[2];
cVector3f vEndPos[2];
cVector3f vExtraPos;
bool bExtraPos = false;
cVector2f vSide[2];
int point = mvShadowPoints[idx][0];
int next = mvShadowPoints[idx][1];
vPointPos[0] = vTilePos + cVector2f((*apVtxVec)[point].pos.x, (*apVtxVec)[point].pos.y);
vPointPos[0].z = mfShadowZ;
vEndPos[0] = CalcLineEnd(vLightPos, vPointPos[0], fRadius, vSide[0], vLightPos);
vEndPos[0].z = mfShadowZ;
vPointPos[1] = vTilePos + cVector2f((*apVtxVec)[next].pos.x, (*apVtxVec)[next].pos.y);
vPointPos[1].z = mfShadowZ;
vEndPos[1] = CalcLineEnd(vLightPos, vPointPos[1], fRadius, vSide[1], vLightPos);
vEndPos[1].z = mfShadowZ;
// Check what sides of the light rect the points are clipped at.
// If the edges are not the same the then there must be added an extra
// point in a corner of the light rect.
if (vSide[0] != vSide[1]) {
if ((vSide[0].x == 0 && vSide[1].y == 0) || (vSide[1].x == 0 && vSide[0].y == 0)) {
bExtraPos = true;
vExtraPos = vLightPos + (vSide[0] + vSide[1]) * fRadius;
vExtraPos.z = mfShadowZ;
}
}
// If the entire object doesn't fit in the light rect we might wanna discard some points
if (bNonFit) {
if (ClipPoints(&vPointPos[0], LightRect, vLightPos, fRadius))
continue;
}
// MAYBE TODO: Fix so that the shadows from different edges share vertices
// Add vertexes and indexes to the vertex batcher
mpLowLevelGraphics->AddVertexToBatch(cVertex(vPointPos[0], ShadowColor));
mpLowLevelGraphics->AddVertexToBatch(cVertex(vPointPos[1], ShadowColor));
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex);
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 1);
mpLowLevelGraphics->AddVertexToBatch(cVertex(vEndPos[0], ShadowColor));
mpLowLevelGraphics->AddVertexToBatch(cVertex(vEndPos[1], ShadowColor));
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 2);
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 1);
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 2);
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 3);
// Debug:
/*mpLowLevelGraphics->SetDepthWriteActive(true);
mpLowLevelGraphics->DrawLine(cVector2f(vPointPos[0].x,vPointPos[0].y),
cVector2f(vEndPos[0].x,vEndPos[0].y),100,cColor(0.4));
mpLowLevelGraphics->DrawLine(cVector2f(vPointPos[1].x,vPointPos[1].y),
cVector2f(vEndPos[1].x,vEndPos[1].y),100,cColor(0.4));
mpLowLevelGraphics->SetDepthWriteActive(false);*/
// If we had an extra point one for triangle is needed.
if (bExtraPos) {
mpLowLevelGraphics->AddVertexToBatch(cVertex(vExtraPos, ShadowColor));
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 3);
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 2);
mpLowLevelGraphics->AddIndexToBatch(lFirstIndex + 4);
lFirstIndex += 5;
} else {
lFirstIndex += 4;
}
}
return lFirstIndex;
}
//-----------------------------------------------------------------------
/**
*
* \param *avPoint
* \param aRect
* \return True if points should be discarded, else false.
*/
bool cRenderer2D::ClipPoints(cVector3f *avPoint, cRect2f aRect, cVector2f avPos, float afSize) {
// Both points lie outside of the rect, discard them
if (cMath::PointBoxCollision(cVector2f(avPoint[0].x, avPoint[0].y), aRect) == false &&
cMath::PointBoxCollision(cVector2f(avPoint[1].x, avPoint[1].y), aRect) == false) {
return true;
}
// At least one is inside, so clip the points
for (int i = 0; i < 2; i++) {
if (avPoint[i].x < avPos.x - afSize)
avPoint[i].x = avPos.x - afSize;
if (avPoint[i].x > avPos.x + afSize)
avPoint[i].x = avPos.x + afSize;
if (avPoint[i].y < avPos.y - afSize)
avPoint[i].y = avPos.y - afSize;
if (avPoint[i].y > avPos.y + afSize)
avPoint[i].y = avPos.y + afSize;
}
return false;
}
//-----------------------------------------------------------------------
} // namespace hpl