forked from Mirrors/openclonk
Shader landscape rendering, first simple version (scaler, shading)
parent
c84458f451
commit
a02ccafc4d
|
@ -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
|
||||
|
|
|
@ -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 |
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Reference in New Issue