Shader landscape rendering, first simple version (scaler, shading)

Peter Wortmann 2011-05-24 00:12:19 +01:00
parent c84458f451
commit a02ccafc4d
12 changed files with 1008 additions and 206 deletions

View File

@ -196,6 +196,9 @@ set(OC_CLONK_SOURCES
src/game/C4Physics.h
src/game/landscape/C4Landscape.cpp
src/game/landscape/C4Landscape.h
src/game/landscape/C4LandscapeRender.cpp
src/game/landscape/C4LandscapeRenderClassic.cpp
src/game/landscape/C4LandscapeRender.h
src/game/landscape/C4Map.cpp
src/game/landscape/C4MapCreatorS2.cpp
src/game/landscape/C4MapCreatorS2.h

View File

@ -0,0 +1,81 @@
#version 130
// Input textures
uniform sampler2D landscapeTex[1];
uniform sampler2D scalerTex;
uniform sampler3D materialTex;
// Resolution of the landscape texture
uniform vec2 resolution;
// Texture map
uniform float matTexMap[256];
// Expected parameters for the scaler
const vec2 scalerStepX = vec2(1.0 / 8.0, 0.0);
const vec2 scalerStepY = vec2(0.0, 1.0 / 32.0);
const vec2 scalerOffset = vec2(0.0, 0.0) + scalerStepX / 3.0 + scalerStepY / 3.0;
const vec2 scalerPixel = vec2(scalerStepX.x, scalerStepY.y) / 3.0;
out vec4 gl_FragColor;
void main()
{
// full pixel steps in the landscape texture (depends on landscape resolution)
vec2 fullStep = vec2(1.0, 1.0) / resolution;
vec2 fullStepX = vec2(fullStep.x, 0.0);
vec2 fullStepY = vec2(0.0, fullStep.y);
// calculate pixel position in landscape, find center of current pixel
vec2 pixelCoo = gl_TexCoord[0].st * resolution;
vec2 centerCoo = (floor(pixelCoo) + vec2(0.5, 0.5)) / resolution;
// our pixel color (with and without interpolation)
vec4 lpx = texture2D(landscapeTex[0], centerCoo);
vec4 rlpx = texture2D(landscapeTex[0], gl_TexCoord[0].st);
// gen2 other coordinate calculation (TODO: scaler map)
vec2 otherCoo = centerCoo + fullStep * round(normalize(mod(pixelCoo, vec2(1.0, 1.0)) - vec2(0.5, 0.5)));
vec4 lopx = texture2D(landscapeTex[0], otherCoo);
// find scaler coordinate
vec2 scalerCoo = scalerOffset + mod(pixelCoo, vec2(1.0, 1.0)) * scalerPixel;
if(texture2D(landscapeTex[0], centerCoo - fullStepX - fullStepY).r == lpx.r)
scalerCoo += scalerStepX;
if(texture2D(landscapeTex[0], centerCoo - fullStepY).r == lpx.r)
scalerCoo += 2.0 * scalerStepX;
if(texture2D(landscapeTex[0], centerCoo + fullStepX - fullStepY).r == lpx.r)
scalerCoo += 4.0 * scalerStepX;
if(texture2D(landscapeTex[0], centerCoo - fullStepX ).r == lpx.r)
scalerCoo += scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepX ).r == lpx.r)
scalerCoo += 2.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo - fullStepX + fullStepY).r == lpx.r)
scalerCoo += 4.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepY).r == lpx.r)
scalerCoo += 8.0 * scalerStepY;
if(texture2D(landscapeTex[0], centerCoo + fullStepX + fullStepY).r == lpx.r)
scalerCoo += 16.0 * scalerStepY;
vec4 spx = texture2DLod(scalerTex, scalerCoo, 0.0);
// Material pixel
float mix = matTexMap[int(lpx.r * 255.0)];
vec4 mpx = texture3D(materialTex, vec3(gl_TexCoord[0].st * resolution / vec2(512.0, 512.0) * vec2(3.0, 3.0), mix));
float omix = matTexMap[int(lopx.r * 255.0)];
vec4 ompx = texture3D(materialTex, vec3(gl_TexCoord[0].st * resolution / vec2(512.0, 512.0) * vec2(3.0, 3.0), omix));
// Brightness
vec2 normal = (1.5 * rlpx.yz - vec2(1.0, 1.0));
float ambientBright = 1.0;
float bright = ambientBright * (1.0 + dot(normal, vec2(0.0, -1.0)));
float bright2 = ambientBright; // * length(onormal + lightDir); // - bla.z;
gl_FragColor = vec4(
bright * spx.r * mpx.rgb + bright2 * (1.0-spx.r) * ompx.rgb,
spx.r * mpx.a + (1.0-spx.r) * ompx.a);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -117,6 +117,8 @@
#define C4CFN_Parameters "Parameters.txt"
#define C4CFN_RoundResults "RoundResults.txt"
#define C4CFN_PlayerControls "PlayerControls.txt"
#define C4CFN_LandscapeShader "LandscapeShader.c"
#define C4CFN_LandscapeScaler "Scaler.png"
#define C4CFN_MapFolderData "FolderMap.txt"
#define C4CFN_MapFolderBG "FolderMap"

View File

@ -56,9 +56,6 @@
#include <StdSurface8.h>
#include <StdPNG.h>
const int C4LS_MaxLightDistY = 8;
const int C4LS_MaxLightDistX = 1;
C4Landscape::C4Landscape()
{
Default();
@ -285,7 +282,7 @@ void C4Landscape::Clear(bool fClearMapCreator, bool fClearSky)
// clear sky
if (fClearSky) Sky.Clear();
// clear surfaces, if assigned
delete Surface32; Surface32=NULL;
delete pLandscapeRender; pLandscapeRender=NULL;
delete Surface8; Surface8=NULL;
delete Map; Map=NULL;
// clear initial landscape
@ -307,20 +304,13 @@ void C4Landscape::Clear(bool fClearMapCreator, bool fClearSky)
void C4Landscape::Draw(C4TargetFacet &cgo, int32_t iPlayer)
{
if (Modulation) lpDDraw->ActivateBlitModulation(Modulation);
// do relights
DoRelights();
// blit landscape
if (::GraphicsSystem.ShowSolidMask)
lpDDraw->Blit8Fast(Surface8, cgo.TargetX, cgo.TargetY, cgo.Surface, cgo.X,cgo.Y,cgo.Wdt,cgo.Hgt);
else
else if(pLandscapeRender)
{
const CSurface * Surfaces[C4M_MaxTexIndex];
if (Config.Graphics.HighResLandscape)
for (int i = 0; i < C4M_MaxTexIndex; ++i)
Surfaces[i] = ::TextureMap.GetEntry(i)->GetPattern().getSurface();
lpDDraw->BlitLandscape(Surface32, cgo.TargetX, cgo.TargetY, cgo.Surface,
cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt,
Config.Graphics.HighResLandscape ? Surfaces : 0);
DoRelights();
pLandscapeRender->Draw(cgo);
}
if (Modulation) lpDDraw->DeactivateBlitModulation();
}
@ -401,7 +391,6 @@ void C4Landscape::ChunkOZoom(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY, i
iMapWdt=BoundBy<int32_t>(iMapWdt,0,iMapWidth-iMapX); iMapHgt=BoundBy<int32_t>(iMapHgt,0,iMapHeight-iMapY);
// get chunk size
iChunkWidth=MapZoom; iChunkHeight=MapZoom;
Surface32->Lock();
// Scan map lines
for (iY=iMapY; iY<iMapY+iMapHgt; iY++)
{
@ -448,7 +437,6 @@ void C4Landscape::ChunkOZoom(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY, i
}
}
}
Surface32->Unlock();
}
bool C4Landscape::GetTexUsage(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, DWORD *dwpTextureUsage)
@ -486,27 +474,14 @@ bool C4Landscape::TexOZoom(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY, int
return true;
}
bool C4Landscape::SkyToLandscape(int32_t iToX, int32_t iToY, int32_t iToWdt, int32_t iToHgt, int32_t iOffX, int32_t iOffY)
{
if (!Surface32->Lock()) return false;
// newgfx: simply blit the sky in realtime...
Surface32->ClearBoxDw(iToX, iToY, iToWdt, iToHgt);
Surface8->ClearBox8Only(iToX, iToY, iToWdt, iToHgt);
// unlock
Surface32->Unlock();
// Done
return true;
}
bool C4Landscape::MapToSurface(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iToX, int32_t iToY, int32_t iToWdt, int32_t iToHgt, int32_t iOffX, int32_t iOffY)
{
// Sky background segment
SkyToLandscape(iToX, iToY, iToWdt, iToHgt, iOffX, iOffY);
// Clear surface
Surface8->ClearBox8Only(iToX, iToY, iToWdt, iToHgt);
// assign clipper
Surface8->Clip(iToX,iToY,iToX+iToWdt-1,iToY+iToHgt-1);
Surface32->Clip(iToX,iToY,iToX+iToWdt-1,iToY+iToHgt-1);
lpDDraw->NoPrimaryClipper();
// Enlarge map segment for chunky rim
@ -520,7 +495,6 @@ bool C4Landscape::MapToSurface(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY,
// remove clipper
Surface8->NoClip();
Surface32->NoClip();
// success
return true;
@ -528,7 +502,7 @@ bool C4Landscape::MapToSurface(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY,
bool C4Landscape::MapToLandscape(CSurface8 * sfcMap, int32_t iMapX, int32_t iMapY, int32_t iMapWdt, int32_t iMapHgt, int32_t iOffsX, int32_t iOffsY)
{
assert(Surface8); assert(Surface32);
assert(Surface8);
// Clip to map/landscape segment
int iMapWidth,iMapHeight,iLandscapeWidth,iLandscapeHeight;
// Get map & landscape size
@ -547,11 +521,9 @@ bool C4Landscape::MapToLandscape(CSurface8 * sfcMap, int32_t iMapX, int32_t iMap
To.Wdt = iMapWdt*MapZoom;
To.Hgt = iMapHgt*MapZoom;
Surface32->Lock();
PrepareChange(To);
MapToSurface(sfcMap, iMapX, iMapY, iMapWdt, iMapHgt, To.x, To.y, To.Wdt, To.Hgt, iOffsX, iOffsY);
FinishChange(To);
Surface32->Unlock();
return true;
}
@ -746,27 +718,41 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
// Scan settings
ScanSpeed=BoundBy(Width/500,2,15);
// create it
if (!Game.C4S.Landscape.ExactLandscape)
// map to big surface and sectionize it
// (not for shaders though - they require continous textures)
// Create landscape surface
Surface8 = new CSurface8();
if (!Surface8->Create(Width, Height) || !Mat2Pal())
{
// map to big surface and sectionize it
// Create landscape surface
Surface32 = new CSurface();
Surface8 = new CSurface8();
if (!Surface32->Create(Width, Height, true, false, lpDDraw->IsShaderific() ? 0 : 64)
|| !Surface8->Create(Width, Height)
|| !Mat2Pal())
{
delete Surface8; delete Surface32;
Surface8 = 0; Surface32 = 0;
return false;
}
// Map to landscape
if (!MapToLandscape()) return false;
delete Surface8; Surface8 = 0;
return false;
}
Game.SetInitProgress(87);
// Map to landscape
if (!MapToLandscape()) return false;
Game.SetInitProgress(84);
// Create renderer
pLandscapeRender = NULL;
#ifdef USE_GL
if (!pLandscapeRender && ::Config.Graphics.HighResLandscape)
pLandscapeRender = new C4LandscapeRenderGL();
#endif
#ifndef USE_CONSOLE
if (!pLandscapeRender)
pLandscapeRender = new C4LandscapeRenderClassic();
#endif
if(pLandscapeRender)
{
// Initialize renderer
if(!pLandscapeRender->Init(Width, Height, &::TextureMap, &::GraphicsResource.Files))
return false;
// Write landscape data
pLandscapeRender->Update(C4Rect(0, 0, Width, Height), this);
Game.SetInitProgress(87);
}
#ifdef DEBUGREC
AddDbgRec(RCT_Block, "|---LS---|", 11);
AddDbgRec(RCT_Ls, Surface8->Bits, Surface8->Pitch*Surface8->Hgt);
@ -793,6 +779,7 @@ bool C4Landscape::Init(C4Group &hGroup, bool fOverloadCurrent, bool fLoadSky, bo
// and not creating the map
Game.FixRandom(Game.RandomSeed);
// Success
rfLoaded=true;
return true;
@ -812,23 +799,20 @@ bool C4Landscape::SetPix(int32_t x, int32_t y, BYTE npix)
if (npix == _GetPix(x, y))
return true;
// note for relight
C4Rect CheckRect(x - 2 * C4LS_MaxLightDistX, y - 2 * C4LS_MaxLightDistY, 4 * C4LS_MaxLightDistX + 1, 4 * C4LS_MaxLightDistY + 1);
for (int32_t i = 0; i < C4LS_MaxRelights; i++)
if (!Relights[i].Wdt || Relights[i].Overlap(CheckRect) || i + 1 >= C4LS_MaxRelights)
{
Relights[i].Add(C4Rect(x,y,1,1));
break;
}
if(pLandscapeRender)
{
C4Rect CheckRect = pLandscapeRender->GetAffectedRect(C4Rect(x, y, 1, 1));
for (int32_t i = 0; i < C4LS_MaxRelights; i++)
if (!Relights[i].Wdt || Relights[i].Overlap(CheckRect) || i + 1 >= C4LS_MaxRelights)
{
Relights[i].Add(CheckRect);
break;
}
}
// set pixel
return _SetPix(x, y, npix);
}
bool C4Landscape::SetPixDw(int32_t x, int32_t y, DWORD dwPix)
{
// set in surface
return Surface32->SetPixDw(x, y, dwPix);
}
bool C4Landscape::_SetPix(int32_t x, int32_t y, BYTE npix)
{
#ifdef DEBUGREC
@ -1514,13 +1498,7 @@ bool C4Landscape::SaveInternal(C4Group &hGroup)
if (!hGroup.Move( szTempLandscape, C4CFN_Landscape ))
return false;
SCopy(Config.AtTempPath(C4CFN_TempLandscapePNG), szTempLandscape);
MakeTempFilename(szTempLandscape);
if (!Surface32->SavePNG(szTempLandscape, true, false, false))
return false;
if (!hGroup.Move( szTempLandscape, C4CFN_LandscapePNG ))
return false;
// Save map
if (fMapChanged && Map)
if (!SaveMap(hGroup))
return false;
@ -1608,29 +1586,10 @@ bool C4Landscape::Load(C4Group &hGroup, bool fLoadSky, bool fSavegame)
int iWidth, iHeight;
Surface8->GetSurfaceSize(iWidth,iHeight);
Width = iWidth; Height = iHeight;
Surface32 = new CSurface(Width, Height);
// adjust pal
if (!Mat2Pal()) return false;
// load the 32bit-surface, too
size_t iSize;
if (hGroup.AccessEntry(C4CFN_LandscapePNG, &iSize))
{
CPNGFile png;
BYTE *pPNG = new BYTE [iSize];
hGroup.Read(pPNG, iSize);
bool fSuccess = png.Load(pPNG, iSize);
delete [] pPNG;
if (fSuccess)
fSuccess = !!Surface32->Lock();
if (fSuccess)
{
for (int32_t y=0; y<Height; ++y) for (int32_t x=0; x<Width; ++x)
Surface32->SetPixDw(x, y, png.GetPix(x, y));
Surface32->Unlock();
}
}
// no PNG: convert old-style landscapes
else if (!Game.C4S.Landscape.NewStyleLandscape)
if (!Game.C4S.Landscape.NewStyleLandscape)
{
// convert all pixels
for (int32_t y=0; y<Height; ++y) for (int32_t x=0; x<Width; ++x)
@ -1705,7 +1664,7 @@ void C4Landscape::Default()
{
Mode=C4LSC_Undefined;
Surface8=NULL;
Surface32=NULL;
pLandscapeRender=NULL;
Map=NULL;
Width=Height=0;
MapWidth=MapHeight=MapZoom=0;
@ -2576,118 +2535,28 @@ inline DWORD DarkenClr1_4(DWORD &dwDst) // make it 3/4 as bright, slightly viole
bool C4Landscape::DoRelights()
{
if (!pLandscapeRender) return true;
for (int32_t i = 0; i < C4LS_MaxRelights; i++)
{
if (!Relights[i].Wdt)
break;
C4Rect SolidMaskRect = Relights[i];
SolidMaskRect.x -= 2 * C4LS_MaxLightDistX; SolidMaskRect.y -= 2 * C4LS_MaxLightDistY;
SolidMaskRect.Wdt += 4 * C4LS_MaxLightDistX; SolidMaskRect.Hgt += 4 * C4LS_MaxLightDistY;
// Remove all solid masks in the (twice!) extended region around the change
C4Rect SolidMaskRect = pLandscapeRender->GetAffectedRect(Relights[i]);
C4SolidMask * pSolid;
for (pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev)
{
pSolid->RemoveTemporary(SolidMaskRect);
}
Relight(Relights[i]);
// Perform the update
pLandscapeRender->Update(Relights[i], this);
// Restore Solidmasks
for (pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next)
{
pSolid->PutTemporary(SolidMaskRect);
}
Relights[i].Default();
C4SolidMask::CheckConsistency();
// Clear slot
Relights[i].Default();
}
return true;
}
bool C4Landscape::Relight(C4Rect To)
{
// Enlarge to relight pixels surrounding a changed one
To.x -= C4LS_MaxLightDistX; To.y -= C4LS_MaxLightDistY;
To.Wdt += 2 * C4LS_MaxLightDistX; To.Hgt += 2 * C4LS_MaxLightDistY;
// Apply lighting
return ApplyLighting(To);
}
bool C4Landscape::ApplyLighting(C4Rect To)
{
// clip to landscape size
To.Intersect(C4Rect(0,0,GBackWdt,GBackHgt));
// everything clipped?
if (To.Wdt<=0 || To.Hgt<=0) return true;
if (!Surface32->Lock()) return false;
// We clear the affected region here because ClearBoxDw allocates the
// main memory buffer for the box, so that only that box needs to be
// sent to the gpu, and not the whole texture, or every pixel
// separately. It's an important optimization.
Surface32->ClearBoxDw(To.x, To.y, To.Wdt, To.Hgt);
if (lpDDraw->IsShaderific() && Config.Graphics.HighResLandscape)
{
for (int32_t iX=To.x; iX<To.x+To.Wdt; ++iX)
for (int32_t iY=To.y; iY<To.y+To.Hgt; ++iY)
Surface32->SetPixDw(iX, iY, _GetPix(iX, iY));
}
else
// do lightning
for (int32_t iX=To.x; iX<To.x+To.Wdt; ++iX)
{
int AboveDensity = 0, BelowDensity = 0;
for (int i = 1; i <= 8; ++i)
{
AboveDensity += GetPlacement(iX, To.y - i - 1);
BelowDensity += GetPlacement(iX, To.y + i - 1);
}
for (int32_t iY=To.y; iY<To.y+To.Hgt; ++iY)
{
AboveDensity -= GetPlacement(iX, iY - 9);
AboveDensity += GetPlacement(iX, iY - 1);
BelowDensity -= GetPlacement(iX, iY);
BelowDensity += GetPlacement(iX, iY + 8);
BYTE pix = _GetPix(iX, iY);
// Sky
if (!pix)
{
Surface32->SetPixDw(iX, iY, 0x00ffffff);
continue;
}
// get density
int iOwnDens = Pix2Place[pix];
if (!iOwnDens) continue;
iOwnDens *= 2;
iOwnDens += GetPlacement(iX + 1, iY) + GetPlacement(iX - 1, iY);
iOwnDens /= 4;
// get texture map entry for pixel
const C4TexMapEntry *pTex = ::TextureMap.GetEntry(PixCol2Tex(pix));
assert(pTex);
// get texture contents
DWORD dwBackClr;
if (pTex) dwBackClr = pTex->GetPattern().PatternClr(iX, iY);
// get density of surrounding materials
int iCompareDens = AboveDensity / 8;
if (iOwnDens > iCompareDens)
{
// apply light
LightenClrBy(dwBackClr, Min(30, 2 * (iOwnDens - iCompareDens)));
}
else if (iOwnDens < iCompareDens && iOwnDens < 30)
{
DarkenClrBy(dwBackClr, Min(30, 2 * (iCompareDens - iOwnDens)));
}
iCompareDens = BelowDensity / 8;
if (iOwnDens > iCompareDens)
{
DarkenClrBy(dwBackClr, Min(30, 2 * (iOwnDens - iCompareDens)));
}
Surface32->SetPixDw(iX, iY, dwBackClr);
}
}
Surface32->Unlock();
// done
return true;
}
bool C4Landscape::DrawMap(int32_t iX, int32_t iY, int32_t iWdt, int32_t iHgt, const char *szMapDef)
{
// safety
@ -2911,8 +2780,8 @@ void C4Landscape::PrepareChange(C4Rect BoundingBox)
{
// move solidmasks out of the way
C4Rect SolidMaskRect = BoundingBox;
SolidMaskRect.x -= 2 * C4LS_MaxLightDistX; SolidMaskRect.y -= 2 * C4LS_MaxLightDistY;
SolidMaskRect.Wdt += 4 * C4LS_MaxLightDistX; SolidMaskRect.Hgt += 4 * C4LS_MaxLightDistY;
if (pLandscapeRender)
SolidMaskRect = pLandscapeRender->GetAffectedRect(pLandscapeRender->GetAffectedRect(SolidMaskRect));
for (C4SolidMask * pSolid = C4SolidMask::Last; pSolid; pSolid = pSolid->Prev)
{
pSolid->RemoveTemporary(SolidMaskRect);
@ -2922,19 +2791,20 @@ void C4Landscape::PrepareChange(C4Rect BoundingBox)
void C4Landscape::FinishChange(C4Rect BoundingBox)
{
// relight
Relight(BoundingBox);
// update render
if(pLandscapeRender)
pLandscapeRender->Update(BoundingBox, this);
UpdateMatCnt(BoundingBox, true);
// Restore Solidmasks
C4Rect SolidMaskRect = BoundingBox;
SolidMaskRect.x -= 2 * C4LS_MaxLightDistX; SolidMaskRect.y -= 2 * C4LS_MaxLightDistY;
SolidMaskRect.Wdt += 4 * C4LS_MaxLightDistX; SolidMaskRect.Hgt += 4 * C4LS_MaxLightDistY;
if (pLandscapeRender)
SolidMaskRect = pLandscapeRender->GetAffectedRect(pLandscapeRender->GetAffectedRect(SolidMaskRect));
for (C4SolidMask * pSolid = C4SolidMask::First; pSolid; pSolid = pSolid->Next)
{
pSolid->Repair(SolidMaskRect);
}
UpdatePixCnt(BoundingBox);
C4SolidMask::CheckConsistency();
UpdatePixCnt(BoundingBox);
}
void C4Landscape::UpdatePixCnt(const C4Rect &Rect, bool fCheck)

View File

@ -27,6 +27,7 @@
#include "C4Sky.h"
#include "C4Shape.h"
#include "C4LandscapeRender.h"
#include <StdSurface8.h>
#include <C4Material.h>
@ -76,8 +77,8 @@ public:
bool fMapChanged;
BYTE *pInitial; // Initial landscape after creation - used for diff
protected:
CSurface * Surface32;
CSurface8 * Surface8;
C4LandscapeRender *pLandscapeRender;
int32_t Pix2Mat[256], Pix2Dens[256], Pix2Place[256];
int32_t PixCntPitch;
uint8_t *PixCnt;
@ -115,7 +116,6 @@ public:
bool ApplyDiff(C4Group &hGroup);
bool SetMode(int32_t iMode);
bool SetPix(int32_t x, int32_t y, BYTE npix); // set landscape pixel (bounds checked)
bool SetPixDw(int32_t x, int32_t y, DWORD dwPix); // set pixel how it is visible only
bool _SetPix(int32_t x, int32_t y, BYTE npix); // set landsape pixel (bounds not checked)
bool _SetPixIfMask(int32_t x, int32_t y, BYTE npix, BYTE nMask) ; // set landscape pixel, if it matches nMask color (no bound-checks)
bool CheckInstability(int32_t tx, int32_t ty);
@ -136,10 +136,6 @@ public:
{
return Surface8->_GetPix(x,y);
}
inline DWORD _GetPixDw(int32_t x, int32_t y, bool fApplyModulation) // get landscape pixel (bounds not checked)
{
return Surface32->GetPixDw(x, y, fApplyModulation);
}
inline BYTE GetPix(int32_t x, int32_t y) // get landscape pixel (bounds checked)
{
extern BYTE MCVehic;

View File

@ -0,0 +1,589 @@
#include "C4Include.h"
#include "C4LandscapeRender.h"
#include "C4Landscape.h"
#include "C4Texture.h"
#include "C4GroupSet.h"
#include "C4Components.h"
#include "StdGL.h"
#include "StdColors.h"
#ifdef USE_GL
// Automatically reload shaders when changed at runtime?
#define AUTO_RELOAD_SHADERS
// How much to look into each direction for bias
const int C4LR_BiasDistanceX = 8;
const int C4LR_BiasDistanceY = 8;
C4LandscapeRenderGL::C4LandscapeRenderGL()
: iLandscapeShaderTime(0),
hVert(0), hFrag(0), hProg(0),
hLandscapeUnit(0), hScalerUnit(0), hMaterialUnit(0),
hResolutionUniform(0), hMatTexMapUniform(0),
iTexCount(0),
hMaterialTexture(0)
{
ZeroMem(MatTexMap, sizeof(MatTexMap));
ZeroMem(Surfaces, sizeof(Surfaces));
}
C4LandscapeRenderGL::~C4LandscapeRenderGL()
{
Clear();
}
bool C4LandscapeRenderGL::Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pTexs, C4GroupSet *pGraphics)
{
Clear();
// Create our surfaces
for(int i = 0; i < C4LR_SurfaceCount; i++)
{
Surfaces[i] = new CSurface();
if(!Surfaces[i]->Create(iWidth, iHeight))
{
Clear();
return false;
}
}
// Safe info
this->iWidth = iWidth;
this->iHeight = iHeight;
// Count the textures
iTexCount = 0;
while(pTexs->GetTexture(iTexCount))
iTexCount++;
// Build material-texture map (depth parameter where to find appropriate texture)
for(int pix = 0; pix < 256; pix++)
{
// Look up indexed entry
const C4TexMapEntry *pEntry = pTexs->GetEntry(PixCol2Tex(BYTE(pix)));
if(!pEntry->GetTextureName())
{
MatTexMap[pix] = 0.5 / (iTexCount - 1);
continue;
}
// Assign texture
int32_t iTexIndex = pTexs->GetTextureIndex(pEntry->GetTextureName());
if(iTexIndex < 0) iTexIndex = 0;
MatTexMap[pix] = (float(iTexIndex) + 0.5) / (iTexCount - 1);
}
// Build texture, er, texture
if (!InitMaterialTexture(pTexs))
{
LogFatal("[!] Could not initialize landscape textures for rendering!");
return false;
}
// Load sclaer
if (!LoadScaler(pGraphics))
{
LogFatal("[!] Could not load scaler!");
return false;
}
// Load shader
if (!LoadShaders(pGraphics))
{
LogFatal("[!] Could not initialize landscape shader!");
return false;
}
return true;
}
void C4LandscapeRenderGL::Clear()
{
ClearShaders();
// free textures
for (int i = 0; i < C4LR_SurfaceCount; i++)
{
delete Surfaces[i];
Surfaces[i] = NULL;
}
glDeleteObjectARB(hMaterialTexture);
hMaterialTexture = 0;
LandscapeShader.Clear();
LandscapeShaderPath.Clear();
iLandscapeShaderTime = 0;
}
bool C4LandscapeRenderGL::InitMaterialTexture(C4TextureMap *pTexs)
{
// Find first (actual) texture
int iRefTexIx = 0; C4Texture *pRefTex; CSurface *pRefSfc = NULL;
for(; pRefTex = pTexs->GetTexture(pTexs->GetTexture(iRefTexIx)); iRefTexIx++)
if(pRefSfc = pRefTex->Surface32)
break;
if(!pRefSfc)
return false;
// Compose together data of all textures
const int iTexWdt = pRefSfc->Wdt, iTexHgt = pRefSfc->Hgt;
const int iBytesPP = pRefSfc->byBytesPP;
const int iTexSize = iTexWdt * iTexHgt * iBytesPP;
const int iSize = iTexSize * iTexCount;
char *pData = new char [iSize];
for(int i = 0; i < iTexCount; i++)
{
char *p = pData + i * iTexSize;
C4Texture *pTex; CSurface *pSurface;
if(!(pTex = pTexs->GetTexture(pTexs->GetTexture(i))))
{}
if(!(pSurface = pTex->Surface32))
{}
else if(pSurface->Wdt != iTexWdt || pSurface->Hgt != iTexHgt)
LogF(" gl: texture %s size mismatch (%dx%d vs %dx%d)!", pTexs->GetTexture(i), pSurface->Wdt, pSurface->Hgt, iTexWdt, iTexHgt);
else if(pSurface->iTexX != 1 || pSurface->iTexY != 1)
Log(" gl: Halp! Material texture is fragmented!");
else
{
memcpy(p, pSurface->ppTex[0]->texLock.pBits, iTexSize);
continue;
}
memset(p, 0, iTexSize);
}
// Clear error error(s?)
while(glGetError()) {}
// Alloc a 3D texture
glEnable(GL_TEXTURE_3D);
glGenTextures(1, &hMaterialTexture);
glBindTexture(GL_TEXTURE_3D, hMaterialTexture);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
// We fully expect to tile these
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// Make it happen!
glTexImage3D(GL_TEXTURE_3D, 0, 4, iTexWdt, iTexHgt, iTexCount, 0, GL_BGRA,
iBytesPP == 2 ? GL_UNSIGNED_SHORT_4_4_4_4_REV : GL_UNSIGNED_INT_8_8_8_8_REV,
pData);
glDisable(GL_TEXTURE_3D);
// Dispose of data
delete [] pData;
// Check whether we were successful
if(int err = glGetError())
{
LogF(" gl: Could not load textures (error %d)", err);
return false;
}
return true;
}
C4Rect C4LandscapeRenderGL::GetAffectedRect(C4Rect Rect)
{
Rect.Enlarge(C4LR_BiasDistanceX, C4LR_BiasDistanceY);
return Rect;
}
void C4LandscapeRenderGL::Update(C4Rect To, C4Landscape *pSource)
{
// clip to landscape size
To.Intersect(C4Rect(0,0,iWidth,iHeight));
// everything clipped?
if (To.Wdt<=0 || To.Hgt<=0) return;
// Lock surfaces
// We clear the affected region here because ClearBoxDw allocates the
// main memory buffer for the box, so that only that box needs to be
// sent to the gpu, and not the whole texture, or every pixel
// separately. It's an important optimization.
for (int i = 0; i < C4LR_SurfaceCount; i++) {
if (!Surfaces[i]->Lock()) return;
Surfaces[i]->ClearBoxDw(To.x, To.y, To.Wdt, To.Hgt);
}
// Initialize up & down arrays
int x, y;
int *pUp = new int [To.Wdt * 2];
int *pDown = pUp + To.Wdt;
for(x = 0; x < To.Wdt; x++) {
int iSum = 0;
for(y = 1; y < Min(C4LR_BiasDistanceY, To.y+1); y++)
iSum += pSource->_GetPlacement(To.x+x, To.y-y);
pUp[x] = iSum;
iSum = 0;
for(y = 1; y < Min(C4LR_BiasDistanceY, iHeight - To.y); y++)
iSum += pSource->_GetPlacement(To.x+x, To.y+y);
pDown[x] = iSum;
}
// Get tex refs (shortcut, we will use them quite heavily)
CTexRef *TexRefs[C4LR_SurfaceCount];
x = y = 0;
for(int i = 0; i < C4LR_SurfaceCount; i++)
Surfaces[i]->GetTexAt(&TexRefs[i], x, y);
// Go through it from top to bottom
for(y = 0; y < To.Hgt; y++) {
// Initialize left & right
int iLeft = 0;
int iRight = 0;
for(x = 1; x < Min(C4LR_BiasDistanceX, To.x+1); x++)
iLeft += pSource->_GetPlacement(To.x-x,To.y+y);
for(x = 1; x < Min(C4LR_BiasDistanceX, iWidth - To.x); x++)
iRight += pSource->_GetPlacement(To.x+x,To.y+y);
for(x = 0; x < To.Wdt; x++) {
// Biases
int iPlac = pSource->_GetPlacement(To.x+x, To.y+y);
int iHBias = Max(0, iPlac * (C4LR_BiasDistanceY-1) - iRight) -
Max(0, iPlac * (C4LR_BiasDistanceY-1) - iLeft);
int iVBias = Max(0, iPlac * (C4LR_BiasDistanceY-1) - pDown[x]) -
Max(0, iPlac * (C4LR_BiasDistanceY-1) - pUp[x]);
// Maximum placement differences that make a difference in the result,
// after which we are at the limits of what can be packed into a byte
const int iMaxPlacDiff = 40;
int iHBiasScaled = BoundBy(iHBias * 127 / iMaxPlacDiff / C4LR_BiasDistanceX + 128, 0, 255);
int iVBiasScaled = BoundBy(iVBias * 127 / iMaxPlacDiff / C4LR_BiasDistanceY + 128, 0, 255);
// Collect data to save per pixel
unsigned char data[C4LR_SurfaceCount * 4];
memset(data, 0, sizeof(data));
data[C4LR_Material] = pSource->_GetPix(To.x+x, To.y+y);
data[C4LR_BiasX] = iHBiasScaled;
data[C4LR_BiasY] = iVBiasScaled;
for(int i = 0; i < C4LR_SurfaceCount; i++)
TexRefs[i]->SetPix4(To.x+x, To.y+y,
RGBA(data[i*4+0], data[i*4+1], data[i*4+2], data[i*4+3]));
// Update left & right
if(To.x+x + 1 < iWidth)
iRight -= pSource->_GetPlacement(To.x+x + 1, To.y+y);
if(To.x+x + C4LR_BiasDistanceX < iWidth)
iRight += pSource->_GetPlacement(To.x+x + C4LR_BiasDistanceX, To.y+y);
iLeft += pSource->_GetPlacement(To.x+x, To.y+y);
if(To.x+x - C4LR_BiasDistanceX - 1 >= 0)
iLeft -= pSource->_GetPlacement(To.x+x - C4LR_BiasDistanceX - 1, To.y+y);
// Update up & down arrays
if(To.y+y + 1 < iHeight)
pDown[x] -= pSource->_GetPlacement(To.x+x, To.y+y + 1);
if(To.y+y + C4LR_BiasDistanceY < iHeight)
pDown[x] += pSource->_GetPlacement(To.x+x, To.y+y + C4LR_BiasDistanceY);
pUp[x] += pSource->_GetPlacement(To.x+x, To.y+y);
if(To.y+y - C4LR_BiasDistanceY + 1 >= 0) {
pUp[x] -= pSource->_GetPlacement(To.x+x, To.y+y - C4LR_BiasDistanceY + 1);
}
}
}
// done
delete[] pUp;
for (int i = 0; i < C4LR_SurfaceCount; i++)
Surfaces[i]->Unlock();
}
void C4LandscapeRenderGL::DumpInfoLog(const char *szWhat, GLhandleARB hShader)
{
// Get length of info line
int iLength = 0;
glGetObjectParameterivARB(hShader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &iLength);
if(iLength <= 1) return;
// Allocate buffer, get data
char *pBuf = new char [iLength + 1];
int iActualLength = 0;
glGetInfoLogARB(hShader, iLength, &iActualLength, pBuf);
if(iActualLength > iLength || iActualLength <= 0) return;
// Terminate, log
pBuf[iActualLength + 1] = '\0';
Log(pBuf);
delete[] pBuf;
}
int C4LandscapeRenderGL::GetObjectStatus(GLhandleARB hObj, GLenum type)
{
int iStatus = 0;
glGetObjectParameterivARB(hObj, type, &iStatus);
return iStatus;
}
GLhandleARB C4LandscapeRenderGL::CreateShader(GLenum iShaderType, const char *szWhat, const char *szCode)
{
const char *szCodes[1] = { szCode };
GLhandleARB hShader = glCreateShaderObjectARB(iShaderType);
glShaderSourceARB(hShader, 1, szCodes, 0);
glCompileShaderARB(hShader);
// Dump any information to log
DumpInfoLog(szWhat, hShader);
// Success?
if(GetObjectStatus(hShader, GL_OBJECT_COMPILE_STATUS_ARB) != 1)
return 0;
else
return hShader;
}
bool C4LandscapeRenderGL::InitShaders()
{
// Already initialized or no shader load?
if(hProg || LandscapeShader.getLength() <= 0)
return false;
// No support?
if(!GLEW_ARB_fragment_program)
{
Log(" gl: no shader support!");
return false;
}
// Create trivial fragment shader
const char *szVert = "void main() { gl_TexCoord[0] = gl_MultiTexCoord0; gl_Position = ftransform(); } ";
hVert = CreateShader(GL_VERTEX_SHADER_ARB, "Vertex shader", szVert);
hFrag = CreateShader(GL_FRAGMENT_SHADER_ARB, "Fragment shader", LandscapeShader.getData());
if(!hFrag || !hVert) return false;
// Link program
hProg = glCreateProgramObjectARB();
glAttachObjectARB(hProg, hVert);
glAttachObjectARB(hProg, hFrag);
glLinkProgramARB(hProg);
// Link successful?
DumpInfoLog("Shader program", hProg);
if(GetObjectStatus(hProg, GL_OBJECT_LINK_STATUS_ARB) != 1)
{
ClearShaders();
return false;
}
// Get variable locations
hLandscapeUnit = glGetUniformLocationARB(hProg, "landscapeTex");
hScalerUnit = glGetUniformLocationARB(hProg, "scalerTex");
hMaterialUnit = glGetUniformLocationARB(hProg, "materialTex");
hResolutionUniform = glGetUniformLocationARB(hProg, "resolution");
hMatTexMapUniform = glGetUniformLocationARB(hProg, "matTexMap");
// Success?
if(int err = glGetError())
{
LogF(" gl: error code %d", err);
return false;
}
return true;
}
void C4LandscapeRenderGL::ClearShaders()
{
if (!hProg) return;
// Need to be detached, then deleted
glDetachShader(hProg, hFrag);
glDetachShader(hProg, hVert);
glDeleteObjectARB(hFrag);
glDeleteObjectARB(hVert);
glDeleteObjectARB(hProg);
hFrag = hVert = hProg = 0;
hLandscapeUnit = hScalerUnit = hMaterialUnit = 0;
hResolutionUniform = hMatTexMapUniform = 0;
}
bool C4LandscapeRenderGL::LoadShaders(C4GroupSet *pGroups)
{
// First, clear out all existing shaders
ClearShaders();
// Search for our shaders
C4Group *pGroup = pGroups->FindEntry(C4CFN_LandscapeShader);
if(!pGroup) return false;
// Load it, save the path for later reloading
if(!pGroup->LoadEntryString(C4CFN_LandscapeShader, &LandscapeShader))
return false;
// If it physically exists, save back file name
if(FileExists(pGroup->GetFullName().getData()))
{
LandscapeShaderPath.Format("%s" DirSep C4CFN_LandscapeShader, pGroup->GetFullName().getData());
iLandscapeShaderTime = FileTime(LandscapeShaderPath.getData());
}
// Initialize
return InitShaders();
}
bool C4LandscapeRenderGL::LoadScaler(C4GroupSet *pGroups)
{
// Search for scaler
C4Group *pGroup = pGroups->FindEntry(C4CFN_LandscapeScaler);
if(!pGroup) return false;
// Load scaler from group
return fctScaler.Load(*pGroup, C4CFN_LandscapeScaler);
}
void C4LandscapeRenderGL::Draw(const C4TargetFacet &cgo)
{
// Must have GL and be initialized
if(!pGL && !hProg) return;
// prepare rendering to surface
CSurface *sfcTarget = cgo.Surface;
if (!pGL->PrepareRendering(sfcTarget)) return;
#ifdef AUTO_RELOAD_SHADERS
// File changed?
if(!LandscapeShaderPath.isNull() &&
FileTime(LandscapeShaderPath.getData()) != iLandscapeShaderTime)
{
ClearShaders();
// Load new shader
char szParentPath[_MAX_PATH+1]; C4Group Group;
GetParentPath(LandscapeShaderPath.getData(),szParentPath);
if(!Group.Open(szParentPath) ||
!Group.LoadEntryString(GetFilename(LandscapeShaderPath.getData()),&LandscapeShader) ||
!Group.Close())
return;
// Reinitialize
InitShaders();
iLandscapeShaderTime = FileTime(LandscapeShaderPath.getData());
}
#endif // AUTO_RELOAD_SHADERS
// Clear error(s?)
while(glGetError()) {}
// Activate shader
glUseProgramObjectARB(hProg);
// Bind data
glUniform1fvARB(hMatTexMapUniform, 256, MatTexMap);
glUniform2fARB(hResolutionUniform, iWidth, iHeight);
// Bind textures
int iUnit = 0; int iMaterialUnit = -1;
if(hScalerUnit >= 0)
{
glUniform1iARB(hScalerUnit, iUnit);
glActiveTexture(GL_TEXTURE0 + iUnit);
iUnit++;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, fctScaler.Surface->ppTex[0]->texName);
}
if(hMaterialUnit >= 0)
{
iMaterialUnit = iUnit;
glUniform1iARB(hMaterialUnit, iUnit);
glActiveTexture(GL_TEXTURE0 + iUnit);
iUnit++;
glEnable(GL_TEXTURE_3D);
glBindTexture(GL_TEXTURE_3D, hMaterialTexture);
}
if(hLandscapeUnit >= 0)
{
GLint iLandscapeUnits[C4LR_SurfaceCount];
for(int i = 0; i < C4LR_SurfaceCount; i++)
{
iLandscapeUnits[i] = iUnit;
glActiveTexture(GL_TEXTURE0 + iUnit);
iUnit++;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, Surfaces[i]->ppTex[0]->texName);
if (pGL->Zoom != 1.0)
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
else
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
}
}
glUniform1ivARB(hLandscapeUnit, C4LR_SurfaceCount, iLandscapeUnits);
}
// get current blitting offset in texture
int iBlitX=0;
int iBlitY=0;
// set up blit data as rect
FLOAT_RECT fTexBlt, tTexBlt;
float fx = float(cgo.TargetX), fy = float(cgo.TargetY);
fTexBlt.left = fx;
fTexBlt.top = fy;
fTexBlt.right = fx + float(cgo.Wdt);
fTexBlt.bottom= fy + float(cgo.Hgt);
// apply Zoom
float tx = float(cgo.X), ty = float(cgo.Y);
pGL->ApplyZoom(tx, ty);
tTexBlt.left = tx;
tTexBlt.top = ty;
tTexBlt.right = tx + float(cgo.Wdt) * pGL->Zoom;
tTexBlt.bottom= ty + float(cgo.Hgt) * pGL->Zoom;
// blit positions
CBltVertex Vtx[4];
Vtx[0].ftx = tTexBlt.left; Vtx[0].fty = tTexBlt.top;
Vtx[1].ftx = tTexBlt.right; Vtx[1].fty = tTexBlt.top;
Vtx[2].ftx = tTexBlt.right; Vtx[2].fty = tTexBlt.bottom;
Vtx[3].ftx = tTexBlt.left; Vtx[3].fty = tTexBlt.bottom;
Vtx[0].tx = fTexBlt.left; Vtx[0].ty = fTexBlt.top;
Vtx[1].tx = fTexBlt.right; Vtx[1].ty = fTexBlt.top;
Vtx[2].tx = fTexBlt.right; Vtx[2].ty = fTexBlt.bottom;
Vtx[3].tx = fTexBlt.left; Vtx[3].ty = fTexBlt.bottom;
for (int i=0; i<4; ++i)
{
Vtx[i].tx /= float(iWidth);
Vtx[i].ty /= float(iHeight);
Vtx[i].ftz = 0;
Vtx[i].color[0] = 255;
Vtx[i].color[1] = 255;
Vtx[i].color[2] = 255;
Vtx[i].color[3] = 255;
//DwTo4UB(RGBA(255, 255, 255, 255), Vtx[i].color);
}
// color modulation?
//DWORD dwModClr = BlitModulated ? BlitModulateClr : 0xffffffff;
//for (int i=0; i<4; ++i)
// DwTo4UB(dwModClr | dwModMask, Vtx[i].color);
// Blit
glInterleavedArrays(GL_T2F_C4UB_V3F, sizeof(CBltVertex), Vtx);
glDrawArrays(GL_QUADS, 0, 4);
// Remove shader
glUseProgramObjectARB(0);
// Unbind textures
while(iUnit > 0)
{
iUnit--;
glActiveTexture(GL_TEXTURE0 + iUnit);
glDisable(iUnit == iMaterialUnit ? GL_TEXTURE_3D : GL_TEXTURE_2D);
}
// Got an error?
if(int err = glGetError())
{
LogF("GL error: %d", err);
}
}
#endif // USE_GL

View File

@ -0,0 +1,127 @@
#ifndef C4LANDSCAPE_RENDER_H
#define C4LANDSCAPE_RENDER_H
#include "StdSurface2.h"
#include "C4FacetEx.h"
// Data we want to store per landscape pixel
enum C4LR_Byte {
C4LR_Material,
C4LR_BiasX,
C4LR_BiasY,
C4LR_ByteCount
};
// How much data we want to store per landscape pixel
const int C4LR_BytesPerPx = 3;
// How much data we can hold per surface, how much surfaces we therefore need.
const int C4LR_BytesPerSurface = 4;
const int C4LR_SurfaceCount = (C4LR_ByteCount + C4LR_BytesPerSurface - 1) / C4LR_BytesPerSurface;
class C4Landscape; class C4TextureMap;
class C4LandscapeRender
{
public:
C4LandscapeRender()
: iWidth(0), iHeight(0), pTexs(NULL) { }
virtual ~C4LandscapeRender()
{}
protected:
int32_t iWidth, iHeight;
C4TextureMap *pTexs;
public:
virtual bool Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pTexs, C4GroupSet *pGraphics) = 0;
virtual void Clear() = 0;
// Returns the rectangle of pixels that must be updated on changes in the given rect
virtual C4Rect GetAffectedRect(C4Rect Rect) = 0;
// Updates the landscape rendering to reflect the landscape contents in
// the given rectangle
virtual void Update(C4Rect Rect, C4Landscape *pSource) = 0;
virtual void Draw(const C4TargetFacet &cgo) = 0;
};
#ifdef USE_GL
class C4LandscapeRenderGL : public C4LandscapeRender
{
public:
C4LandscapeRenderGL();
~C4LandscapeRenderGL();
private:
// surfaces
CSurface *Surfaces[C4LR_SurfaceCount];
// shader sources
StdStrBuf LandscapeShader;
StdStrBuf LandscapeShaderPath;
int iLandscapeShaderTime;
// shaders
GLhandleARB hVert, hFrag, hProg;
// shader variables
GLhandleARB hLandscapeUnit, hScalerUnit, hMaterialUnit;
GLhandleARB hResolutionUniform, hMatTexMapUniform;
// Texture count
int32_t iTexCount;
// 3D material textures
GLuint hMaterialTexture;
// material map
GLfloat MatTexMap[256];
// scaler image
C4FacetSurface fctScaler;
public:
virtual bool Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pMap, C4GroupSet *pGraphics);
virtual void Clear();
virtual C4Rect GetAffectedRect(C4Rect Rect);
virtual void Update(C4Rect Rect, C4Landscape *pSource);
virtual void Draw(const C4TargetFacet &cgo);
private:
bool InitMaterialTexture(C4TextureMap *pMap);
bool LoadShaders(C4GroupSet *pGraphics);
bool LoadScaler(C4GroupSet *pGraphics);
void DumpInfoLog(const char *szWhat, GLhandleARB hShader);
int GetObjectStatus(GLhandleARB hObj, GLenum type);
GLhandleARB CreateShader(GLenum iShaderType, const char *szWhat, const char *szCode);
bool InitShaders();
void ClearShaders();
};
#endif
class C4LandscapeRenderClassic : public C4LandscapeRender
{
public:
C4LandscapeRenderClassic();
~C4LandscapeRenderClassic();
private:
CSurface *Surface32;
public:
virtual bool Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pMap, C4GroupSet *pGraphics);
virtual void Clear();
virtual C4Rect GetAffectedRect(C4Rect Rect);
virtual void Update(C4Rect Rect, C4Landscape *pSource);
virtual void Draw(const C4TargetFacet &cgo);
};
#endif // C4LANDSCAPE_RENDER_H

View File

@ -0,0 +1,120 @@
#include "C4Include.h"
#include "C4LandscapeRender.h"
#include "C4Landscape.h"
#include "C4Texture.h"
const int C4LS_MaxLightDistY = 8;
const int C4LS_MaxLightDistX = 1;
C4LandscapeRenderClassic::C4LandscapeRenderClassic()
: Surface32(NULL)
{
}
C4LandscapeRenderClassic::~C4LandscapeRenderClassic()
{
Clear();
}
bool C4LandscapeRenderClassic::Init(int32_t iWidth, int32_t iHeight, C4TextureMap *pTexs, C4GroupSet *pGraphics)
{
// Create surface
Surface32 = new CSurface();
if(!Surface32->Create(iWidth, iHeight, false, false, 256))
return false;
// Safe back info
this->iWidth = iWidth;
this->iHeight = iHeight;
this->pTexs = pTexs;
return true;
}
void C4LandscapeRenderClassic::Clear()
{
delete Surface32; Surface32 = 0;
iWidth = iHeight = 0;
pTexs = NULL;
}
C4Rect C4LandscapeRenderClassic::GetAffectedRect(C4Rect Rect)
{
Rect.Enlarge(C4LS_MaxLightDistX, C4LS_MaxLightDistY);
return Rect;
}
void C4LandscapeRenderClassic::Update(C4Rect To, C4Landscape *pSource)
{
// clip to landscape size
To.Intersect(C4Rect(0,0,iWidth,iHeight));
// everything clipped?
if (To.Wdt<=0 || To.Hgt<=0) return;
if (!Surface32->Lock()) return;
// We clear the affected region here because ClearBoxDw allocates the
// main memory buffer for the box, so that only that box needs to be
// sent to the gpu, and not the whole texture, or every pixel
// separately. It's an important optimization.
Surface32->ClearBoxDw(To.x, To.y, To.Wdt, To.Hgt);
// do lightning
for (int32_t iX=To.x; iX<To.x+To.Wdt; ++iX)
{
int AboveDensity = 0, BelowDensity = 0;
for (int i = 1; i <= 8; ++i)
{
AboveDensity += pSource->GetPlacement(iX, To.y - i - 1);
BelowDensity += pSource->GetPlacement(iX, To.y + i - 1);
}
for (int32_t iY=To.y; iY<To.y+To.Hgt; ++iY)
{
AboveDensity -= pSource->GetPlacement(iX, iY - 9);
AboveDensity += pSource->GetPlacement(iX, iY - 1);
BelowDensity -= pSource->GetPlacement(iX, iY);
BelowDensity += pSource->GetPlacement(iX, iY + 8);
BYTE pix = pSource->_GetPix(iX, iY);
// Sky
if (!pix)
{
Surface32->SetPixDw(iX, iY, 0x00ffffff);
continue;
}
// get density
int iOwnDens = pSource->_GetPlacement(iX, iY);
if (!iOwnDens) continue;
iOwnDens *= 2;
iOwnDens += pSource->GetPlacement(iX + 1, iY) + pSource->GetPlacement(iX - 1, iY);
iOwnDens /= 4;
// get texture map entry for pixel
const C4TexMapEntry *pTex = pTexs->GetEntry(PixCol2Tex(pix));
assert(pTex);
// get texture contents
DWORD dwBackClr;
if (pTex) dwBackClr = pTex->GetPattern().PatternClr(iX, iY);
// get density of surrounding materials
int iCompareDens = AboveDensity / 8;
if (iOwnDens > iCompareDens)
{
// apply light
LightenClrBy(dwBackClr, Min(30, 2 * (iOwnDens - iCompareDens)));
}
else if (iOwnDens < iCompareDens && iOwnDens < 30)
{
DarkenClrBy(dwBackClr, Min(30, 2 * (iCompareDens - iOwnDens)));
}
iCompareDens = BelowDensity / 8;
if (iOwnDens > iCompareDens)
{
DarkenClrBy(dwBackClr, Min(30, 2 * (iOwnDens - iCompareDens)));
}
Surface32->SetPixDw(iX, iY, dwBackClr);
}
}
Surface32->Unlock();
}
void C4LandscapeRenderClassic::Draw(const C4TargetFacet &cgo)
{
lpDDraw->BlitLandscape(Surface32, cgo.TargetX, cgo.TargetY, cgo.Surface,
cgo.X, cgo.Y, cgo.Wdt, cgo.Hgt, 0);
}

View File

@ -287,7 +287,7 @@ int32_t C4TextureMap::LoadTextures(C4Group &hGroup, C4Group* OverloadFile)
char texname[256+1];
C4Surface *ctex;
size_t binlen;
// newgfx: load PNG-textures first
hGroup.ResetSearch();
while (hGroup.AccessNextEntry("*",&binlen,texname))
{
@ -306,6 +306,7 @@ int32_t C4TextureMap::LoadTextures(C4Group &hGroup, C4Group* OverloadFile)
delete ctex;
}
}
return texnum;
}
@ -381,6 +382,16 @@ C4Texture * C4TextureMap::GetTexture(const char *szTexture)
return NULL;
}
int32_t C4TextureMap::GetTextureIndex(const char *szName)
{
C4Texture *pTexture;
int32_t i=0;
for (pTexture=FirstTexture; pTexture; pTexture=pTexture->Next, i++)
if (SEqualNoCase(pTexture->Name,szName))
return i;
return -1;
}
bool C4TextureMap::CheckTexture(const char *szTexture)
{
C4Texture *pTexture;

View File

@ -97,6 +97,7 @@ public:
C4Texture * GetTexture(const char *szTexture);
bool CheckTexture(const char *szTexture); // return whether texture exists
bool AddEntry(BYTE byIndex, const char *szMaterial, const char *szTexture);
int32_t GetTextureIndex(const char *pTexName);
protected:
bool AddTexture(const char *szTexture, CSurface * sfcSurface);
};

View File

@ -57,8 +57,10 @@ public:
void Normalize()
{ if (Wdt < 0) { x+=Wdt+1; Wdt=-Wdt; } if (Hgt < 0) { y+=Hgt+1; Hgt=-Hgt; } }
void Enlarge(int32_t iByX, int32_t iByY)
{ x -= iByX; y -= iByY; Wdt += 2*iByX; Hgt += 2*iByY; }
void Enlarge(int32_t iBy)
{ x -= iBy; y -= iBy; Wdt += 2*iBy; Hgt += 2*iBy; }
{ Enlarge(iBy, iBy); }
int32_t GetMiddleX() { return x+Wdt/2; }
int32_t GetMiddleY() { return y+Hgt/2; }