openclonk/src/landscape/fow/C4FoW.cpp

258 lines
6.0 KiB
C++

/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 2014-2016, The OpenClonk Team and contributors
*
* Distributed under the terms of the ISC license; see accompanying file
* "COPYING" for details.
*
* "Clonk" is a registered trademark of Matthes Bender, used with permission.
* See accompanying file "TRADEMARK" for details.
*
* To redistribute this file separately, substitute the full license texts
* for the above references.
*/
#include "C4Include.h"
#include "C4ForbidLibraryCompilation.h"
#include "landscape/fow/C4FoW.h"
#include "graphics/C4Draw.h"
#include <float.h>
C4FoW::C4FoW()
: pLights(nullptr), deleted_lights(nullptr)
{
}
C4FoW::~C4FoW()
{
if (deleted_lights)
{
if (pDraw) pDraw->EnsureMainContextSelected();
ClearDeletedLights();
}
}
void C4FoW::ClearDeletedLights()
{
#ifndef USE_CONSOLE
// Kill any dead lights
while (deleted_lights)
{
C4FoWLight *light = deleted_lights;
deleted_lights = deleted_lights->getNext();
delete light;
}
#endif
}
C4Shader *C4FoW::GetFramebufShader()
{
#ifndef USE_CONSOLE
// Not created yet?
if (!FramebufShader.Initialised())
{
// Create the frame buffer shader. The details are in C4FoWRegion, but
// this is about how to utilise old frame buffer data in the lights texture.
// Or put in other words: This shader is responsible for fading lights out.
const char* FramebufVertexShader =
"in vec2 oc_Position;\n"
"in vec2 oc_TexCoord;\n"
"out vec2 texcoord;\n"
"uniform mat4 projectionMatrix;\n"
"\n"
"slice(position)\n"
"{\n"
" gl_Position = projectionMatrix * vec4(oc_Position, 0.0, 1.0);\n"
"}\n"
"\n"
"slice(texcoord)\n"
"{\n"
" texcoord = oc_TexCoord;\n"
"}";
const char* FramebufFragmentShader =
"in vec2 texcoord;\n"
"uniform sampler2D tex;\n"
"out vec4 fragColor;\n"
"\n"
"slice(color)\n"
"{\n"
" fragColor = texture(tex, texcoord);\n"
"}";
FramebufShader.AddVertexSlices("built-in FoW framebuf shader", FramebufVertexShader);
FramebufShader.AddFragmentSlices("built-in FoW framebuf shader", FramebufFragmentShader);
const char *szUniforms[C4FoWFSU_Count + 1];
szUniforms[C4FoWFSU_ProjectionMatrix] = "projectionMatrix";
szUniforms[C4FoWFSU_Texture] = "tex";
szUniforms[C4FoWFSU_Count] = nullptr;
const char *szAttributes[C4FoWFSA_Count + 1];
szAttributes[C4FoWFSA_Position] = "oc_Position";
szAttributes[C4FoWFSA_TexCoord] = "oc_TexCoord";
szAttributes[C4FoWFSA_Count] = nullptr;
if (!FramebufShader.Init("framebuf", szUniforms, szAttributes)) {
FramebufShader.ClearSlices();
return nullptr;
}
}
return &FramebufShader;
#else
return nullptr;
#endif
}
C4Shader *C4FoW::GetRenderShader()
{
#ifndef USE_CONSOLE
// Not created yet?
if (!RenderShader.Initialised())
{
// Create the render shader. Fairly simple pass-through.
const char* RenderVertexShader =
"in vec2 oc_Position;\n"
"in vec4 oc_Color;\n"
"out vec4 vtxColor;\n"
"uniform mat4 projectionMatrix;\n"
"uniform vec2 vertexOffset;\n"
"\n"
"slice(position)\n"
"{\n"
" gl_Position = projectionMatrix * vec4(oc_Position + vertexOffset, 0.0, 1.0);\n"
"}\n"
"\n"
"slice(color)\n"
"{\n"
" vtxColor = oc_Color;\n"
"}";
const char* RenderFragmentShader =
"in vec4 vtxColor;\n"
"out vec4 fragColor;\n"
"\n"
"slice(color)\n"
"{\n"
" fragColor = vtxColor;\n"
"}";
RenderShader.AddVertexSlices("built-in FoW render shader", RenderVertexShader);
RenderShader.AddFragmentSlices("built-in FoW render shader", RenderFragmentShader);
const char* szUniforms[C4FoWRSU_Count + 1];
szUniforms[C4FoWRSU_ProjectionMatrix] = "projectionMatrix";
szUniforms[C4FoWRSU_VertexOffset] = "vertexOffset";
szUniforms[C4FoWRSU_Count] = nullptr;
const char* szAttributes[C4FoWRSA_Count + 1];
szAttributes[C4FoWRSA_Position] = "oc_Position";
szAttributes[C4FoWRSA_Color] = "oc_Color";
szAttributes[C4FoWRSA_Count] = nullptr;
if (!RenderShader.Init("fowRender", szUniforms, szAttributes)) {
RenderShader.ClearSlices();
return nullptr;
}
}
return &RenderShader;
#else
return nullptr;
#endif
}
void C4FoW::Add(C4Object *pObj)
{
#ifndef USE_CONSOLE
// No view range? Probably want to remove instead
if(!pObj->lightRange && !pObj->lightFadeoutRange)
{
Remove(pObj);
return;
}
// Safety
if (!pObj->Status) return;
// Look for matching light
C4FoWLight *pLight;
for (pLight = pLights; pLight; pLight = pLight->getNext())
if (pLight->getObj() == pObj)
break;
if (pLight)
{
// Update reach and light color
pLight->SetReach(pObj->lightRange, pObj->lightFadeoutRange);
pLight->SetColor(pObj->lightColor);
}
else
{
// Create new light otherwise
pLight = new C4FoWLight(pObj);
pLight->pNext = pLights;
pLights = pLight;
}
#endif
}
void C4FoW::Remove(C4Object *pObj)
{
#ifndef USE_CONSOLE
// Look for matching light
C4FoWLight *pPrev = nullptr, *pLight;
for (pLight = pLights; pLight; pPrev = pLight, pLight = pLight->getNext())
if (pLight->getObj() == pObj)
break;
if (!pLight)
return;
// Remove from list
(pPrev ? pPrev->pNext : pLights) = pLight->getNext();
// Delete on next render pass
pLight->pNext = deleted_lights;
deleted_lights = pLight;
#endif
}
void C4FoW::Invalidate(C4Rect r)
{
#ifndef USE_CONSOLE
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
pLight->Invalidate(r);
#endif
}
void C4FoW::Update(C4Rect r, C4Player *pPlr)
{
#ifndef USE_CONSOLE
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
if (pLight->IsVisibleForPlayer(pPlr))
pLight->Update(r);
#endif
}
void C4FoW::Render(C4FoWRegion *pRegion, const C4TargetFacet *pOnScreen, C4Player *pPlr, const StdProjectionMatrix& projectionMatrix)
{
#ifndef USE_CONSOLE
// Delete any dead lights
ClearDeletedLights();
// Set up shader. If this one doesn't work, we're really in trouble.
C4Shader *pShader = GetRenderShader();
assert(pShader);
if (!pShader) return;
for (C4FoWLight *pLight = pLights; pLight; pLight = pLight->getNext())
if (pLight->IsVisibleForPlayer(pPlr))
pLight->Render(pRegion, pOnScreen, projectionMatrix, *pShader);
#endif
}