openclonk/src/landscape/C4Texture.cpp

470 lines
13 KiB
C++
Raw Normal View History

2009-05-08 13:28:41 +00:00
/*
* OpenClonk, http://www.openclonk.org
*
* Copyright (c) 1998-2000, 2007 Matthes Bender
* Copyright (c) 2001-2003, 2005, 2007 Sven Eberhardt
2011-09-01 14:58:52 +00:00
* Copyright (c) 2002, 2004, 2007-2008, 2011 Peter Wortmann
* Copyright (c) 2006-2007, 2009 Günther Brammer
* Copyright (c) 2008, 2010 Armin Burgmeier
2009-05-08 13:28:41 +00:00
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
* to OpenClonk.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
* See isc_license.txt for full license and disclaimer.
*
* "Clonk" is a registered trademark of Matthes Bender.
* See clonk_trademark_license.txt for full license.
*/
/* Textures used by the landscape */
#include <C4Include.h>
#include <C4Texture.h>
#include <C4Group.h>
#include <C4Game.h>
#include <C4Config.h>
#include <C4Components.h>
#include <C4Application.h>
#include <C4Material.h>
#include <C4Landscape.h>
#include <C4Log.h>
2009-05-08 13:28:41 +00:00
C4Texture::C4Texture()
2010-03-28 18:58:01 +00:00
{
Name[0]=0;
Surface32=NULL;
AvgColor = 0x00000000;
Next=NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Texture::~C4Texture()
2010-03-28 18:58:01 +00:00
{
delete Surface32;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4TexMapEntry::C4TexMapEntry()
2010-03-28 18:58:01 +00:00
: iMaterialIndex(MNone), pMaterial(NULL)
{
}
2009-05-08 13:28:41 +00:00
void C4TexMapEntry::Clear()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Material.Clear(); Texture.Clear();
iMaterialIndex = MNone;
pMaterial = NULL;
MatPattern.Clear();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4TexMapEntry::Create(const char *szMaterial, const char *szTexture)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Clear previous data
Clear();
// Save names
Material = szMaterial; Texture = szTexture;
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4TexMapEntry::Init()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Find material
2009-06-05 18:46:03 +00:00
iMaterialIndex = ::MaterialMap.Get(Material.getData());
2010-03-28 18:58:01 +00:00
if (!MatValid(iMaterialIndex))
{
2009-05-08 13:28:41 +00:00
DebugLogF("Error initializing material %s-%s: Invalid material!", Material.getData(), Texture.getData());
return false;
2010-03-28 18:58:01 +00:00
}
2009-06-05 18:46:03 +00:00
pMaterial = &::MaterialMap.Map[iMaterialIndex];
2009-05-08 13:28:41 +00:00
// Find texture
StdStrBuf FirstTexture;
FirstTexture.CopyUntil(Texture.getData(), '-');
C4Texture * sfcTexture = ::TextureMap.GetTexture(FirstTexture.getData());
2010-03-28 18:58:01 +00:00
if (!sfcTexture)
{
DebugLogF("Error initializing material %s-%s: Invalid texture!", Material.getData(), FirstTexture.getData());
2009-05-08 13:28:41 +00:00
Clear();
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Get overlay properties
int32_t iOverlayType=pMaterial->OverlayType;
int32_t iZoom=0;
if (iOverlayType & C4MatOv_Exact) iZoom=1;
if (iOverlayType & C4MatOv_HugeZoom) iZoom=4;
// Create pattern
MatPattern.Set(sfcTexture->Surface32, iZoom);
2009-05-08 13:28:41 +00:00
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4TextureMap::C4TextureMap()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Default();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4TextureMap::~C4TextureMap()
2010-03-28 18:58:01 +00:00
{
Clear();
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4TextureMap::AddEntry(BYTE byIndex, const char *szMaterial, const char *szTexture)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Security
2010-03-28 18:58:01 +00:00
if (byIndex <= 0 || byIndex >= C4M_MaxTexIndex)
return false;
2009-05-08 13:28:41 +00:00
// Set entry and initialize
Entry[byIndex].Create(szMaterial, szTexture);
2010-03-28 18:58:01 +00:00
if (fInitialized)
{
if (!Entry[byIndex].Init())
2009-05-08 13:28:41 +00:00
{
// Clear entry if it could not be initialized
Entry[byIndex].Clear();
return false;
2009-05-08 13:28:41 +00:00
}
// Landscape must be notified (new valid pixel clr)
2009-06-05 15:20:41 +00:00
::Landscape.HandleTexMapUpdate();
}
2010-03-28 18:58:01 +00:00
return true;
}
2009-05-08 13:28:41 +00:00
bool C4TextureMap::AddTexture(const char *szTexture, C4Surface * sfcSurface)
2010-03-28 18:58:01 +00:00
{
C4Texture *pTexture;
if (!(pTexture=new C4Texture)) return false;
SCopy(szTexture,pTexture->Name,C4M_MaxName);
pTexture->Surface32=sfcSurface;
pTexture->Next=FirstTexture;
FirstTexture=pTexture;
// Compute average texture color
sfcSurface->Lock();
uint32_t avg_c[4] = { 0, 0, 0, 0 };
for(int32_t y = 0; y < sfcSurface->Hgt; ++y)
{
for(int32_t x = 0; x < sfcSurface->Wdt; ++x)
{
DWORD c = sfcSurface->GetPixDw(x, y, false);
avg_c[0] += c & 0xff;
avg_c[1] += (c >> 8) & 0xff;
avg_c[2] += (c >> 16) & 0xff;
avg_c[3] += (c >> 24) & 0xff;
}
}
sfcSurface->Unlock();
double Size = sfcSurface->Wdt * sfcSurface->Hgt;
avg_c[0] = static_cast<uint32_t>(avg_c[0] / Size + 0.5);
avg_c[1] = static_cast<uint32_t>(avg_c[1] / Size + 0.5);
avg_c[2] = static_cast<uint32_t>(avg_c[2] / Size + 0.5);
avg_c[3] = static_cast<uint32_t>(avg_c[3] / Size + 0.5);
pTexture->AvgColor = avg_c[0] | (avg_c[1] << 8) | (avg_c[2] << 16) | (avg_c[3] << 24);
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4TextureMap::Clear()
2010-03-28 18:58:01 +00:00
{
for (int32_t i = 1; i < C4M_MaxTexIndex; i++)
2009-05-08 13:28:41 +00:00
Entry[i].Clear();
C4Texture *ctex,*next2;
for (ctex=FirstTexture; ctex; ctex=next2)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
next2=ctex->Next;
delete ctex;
2010-03-28 18:58:01 +00:00
}
FirstTexture=NULL;
2009-05-08 13:28:41 +00:00
fInitialized = false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4TextureMap::LoadFlags(C4Group &hGroup, const char *szEntryName, bool *pOverloadMaterials, bool *pOverloadTextures)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Load the file
StdStrBuf TexMap;
if (!hGroup.LoadEntryString(szEntryName, &TexMap))
return false;
2009-05-08 13:28:41 +00:00
// Reset flags
2010-03-28 18:58:01 +00:00
if (pOverloadMaterials) *pOverloadMaterials = false;
if (pOverloadTextures) *pOverloadTextures = false;
2009-05-08 13:28:41 +00:00
// Check if there are flags in there
2010-03-28 18:58:01 +00:00
for (const char *pPos = TexMap.getData(); pPos && *pPos; pPos = SSearch(pPos + 1, "\n"))
{
2009-05-08 13:28:41 +00:00
// Go over newlines
2010-03-28 18:58:01 +00:00
while (*pPos == '\r' || *pPos == '\n') pPos++;
2009-05-08 13:28:41 +00:00
// Flag?
if (pOverloadMaterials && SEqual2(pPos, "OverloadMaterials"))
*pOverloadMaterials = true;
2009-05-08 13:28:41 +00:00
if (pOverloadTextures && SEqual2(pPos, "OverloadTextures"))
*pOverloadTextures = true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// Done
return true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4TextureMap::LoadMap(C4Group &hGroup, const char *szEntryName, bool *pOverloadMaterials, bool *pOverloadTextures)
2010-03-28 18:58:01 +00:00
{
char *bpMap;
char szLine[100+1];
int32_t cnt, iIndex, iTextures = 0;
// Load text file into memory
if (!hGroup.LoadEntry(szEntryName,&bpMap,NULL,1)) return 0;
// Scan text buffer lines
for (cnt=0; SCopySegment(bpMap,cnt,szLine,0x0A,100); cnt++)
if ( (szLine[0]!='#') && (SCharCount('=',szLine)==1) )
2010-03-28 18:58:01 +00:00
{
SReplaceChar(szLine,0x0D,0x00);
2009-05-08 13:28:41 +00:00
if (Inside<int32_t>( iIndex = strtol(szLine,NULL,10), 0, C4M_MaxTexIndex-1 ))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
const char *szMapping = szLine+SCharPos('=',szLine)+1;
StdStrBuf Material, Texture;
Material.CopyUntil(szMapping, '-'); Texture.Copy(SSearch(szMapping, "-"));
if (AddEntry(iIndex, Material.getData(), Texture.getData()))
iTextures++;
}
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
else
2010-03-28 18:58:01 +00:00
{
if (SEqual2(szLine, "OverloadMaterials")) { fOverloadMaterials = true; if (pOverloadMaterials) *pOverloadMaterials = true; }
if (SEqual2(szLine, "OverloadTextures")) { fOverloadTextures = true; if (pOverloadTextures) *pOverloadTextures = true; }
}
// Delete buffer, return entry count
delete [] bpMap;
2009-05-08 13:28:41 +00:00
fEntriesAdded=false;
return iTextures;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4TextureMap::Init()
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
int32_t iRemoved = 0;
// Initialize texture mappings
int32_t i;
for (i = 0; i < C4M_MaxTexIndex; i++)
if (!Entry[i].isNull())
if (!Entry[i].Init())
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
LogF("Error in TextureMap initialization at entry %d", (int) i);
Entry[i].Clear();
iRemoved++;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
fInitialized = true;
return iRemoved;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
bool C4TextureMap::SaveMap(C4Group &hGroup, const char *szEntryName)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// build file in memory
StdStrBuf sTexMapFile;
// add desc
sTexMapFile.Append("# Automatically generated texture map" LineFeed);
sTexMapFile.Append("# Contains material-texture-combinations added at runtime" LineFeed);
// add overload-entries
if (fOverloadMaterials) sTexMapFile.Append("# Import materials from global file as well" LineFeed "OverloadMaterials" LineFeed);
if (fOverloadTextures) sTexMapFile.Append("# Import textures from global file as well" LineFeed "OverloadTextures" LineFeed);
sTexMapFile.Append(LineFeed);
// add entries
for (int32_t i = 0; i < C4M_MaxTexIndex; i++)
if (!Entry[i].isNull())
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// compose line
sTexMapFile.AppendFormat("%d=%s-%s" LineFeed, i, Entry[i].GetMaterialName(), Entry[i].GetTextureName());
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// create new buffer allocated with new [], because C4Group cannot handle StdStrBuf-buffers
size_t iBufSize = sTexMapFile.getLength();
BYTE *pBuf = new BYTE[iBufSize];
memcpy(pBuf, sTexMapFile.getData(), iBufSize);
// add to group
bool fSuccess = !!hGroup.Add(szEntryName, pBuf, iBufSize, false, true);
if (!fSuccess) delete [] pBuf;
// done
return fSuccess;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4TextureMap::LoadTextures(C4Group &hGroup, C4Group* OverloadFile)
2010-03-28 18:58:01 +00:00
{
int32_t texnum=0;
2009-05-08 13:28:41 +00:00
// overload: load from other file
if (OverloadFile) texnum+=LoadTextures(*OverloadFile);
char texname[256+1];
2009-05-08 13:28:41 +00:00
C4Surface *ctex;
size_t binlen;
2009-05-08 13:28:41 +00:00
hGroup.ResetSearch();
while (hGroup.AccessNextEntry("*",&binlen,texname))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// check if it already exists in the map
if (GetTexture(GetFilenameOnly(texname))) continue;
// create surface
ctex = new C4Surface();
if (ctex->Read(hGroup, GetExtension(texname)))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
SReplaceChar(texname,'.',0);
if (AddTexture(texname,ctex)) texnum++;
2009-05-08 13:28:41 +00:00
else delete ctex;
2010-03-28 18:58:01 +00:00
}
else
2010-03-28 18:58:01 +00:00
{
delete ctex;
2009-05-08 13:28:41 +00:00
}
}
2010-03-28 18:58:01 +00:00
return texnum;
}
2009-05-08 13:28:41 +00:00
bool C4TextureMap::HasTextures(C4Group &hGroup)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
return hGroup.EntryCount(C4CFN_PNGFiles) || hGroup.EntryCount(C4CFN_BitmapFiles);
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4TextureMap::MoveIndex(BYTE byOldIndex, BYTE byNewIndex)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
Entry[byNewIndex] = Entry[byOldIndex];
fEntriesAdded = true;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4TextureMap::GetIndex(const char *szMaterial, const char *szTexture, bool fAddIfNotExist, const char *szErrorIfFailed)
2010-03-28 18:58:01 +00:00
{
BYTE byIndex;
// Find existing
2009-05-08 13:28:41 +00:00
for (byIndex = 1; byIndex < C4M_MaxTexIndex; byIndex++)
if (!Entry[byIndex].isNull())
if (SEqualNoCase(Entry[byIndex].GetMaterialName(), szMaterial))
if (!szTexture || SEqualNoCase(Entry[byIndex].GetTextureName(), szTexture))
return byIndex;
// Add new entry
if (fAddIfNotExist)
for (byIndex=1; byIndex<C4M_MaxTexIndex; byIndex++)
if (Entry[byIndex].isNull())
2010-03-28 18:58:01 +00:00
{
if (AddEntry(byIndex, szMaterial, szTexture))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
fEntriesAdded=true;
return byIndex;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
if (szErrorIfFailed) DebugLogF("Error getting MatTex %s-%s for %s from TextureMap: Init failed.", szMaterial, szTexture, szErrorIfFailed);
return 0;
2010-03-28 18:58:01 +00:00
}
// Else, fail
2009-05-08 13:28:41 +00:00
if (szErrorIfFailed) DebugLogF("Error getting MatTex %s-%s for %s from TextureMap: %s.", szMaterial, szTexture, szErrorIfFailed, fAddIfNotExist ? "Map is full!" : "Entry not found.");
return 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
int32_t C4TextureMap::GetIndexMatTex(const char *szMaterialTexture, const char *szDefaultTexture, bool fAddIfNotExist, const char *szErrorIfFailed)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// split material/texture pair
StdStrBuf Material, Texture;
Material.CopyUntil(szMaterialTexture, '-');
Texture.Copy(SSearch(szMaterialTexture, "-"));
// texture not given or invalid?
int32_t iMatTex = 0;
2010-03-28 18:58:01 +00:00
if (Texture.getData())
if ((iMatTex = GetIndex(Material.getData(), Texture.getData(), fAddIfNotExist)))
2009-05-08 13:28:41 +00:00
return iMatTex;
2010-03-28 18:58:01 +00:00
if (szDefaultTexture)
if ((iMatTex = GetIndex(Material.getData(), szDefaultTexture, fAddIfNotExist)))
2009-05-08 13:28:41 +00:00
return iMatTex;
// search material
2009-06-05 18:46:03 +00:00
long iMaterial = ::MaterialMap.Get(szMaterialTexture);
2009-05-08 13:28:41 +00:00
if (!MatValid(iMaterial))
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
if (szErrorIfFailed) DebugLogF("Error getting MatTex for %s: Invalid material", szErrorIfFailed);
return 0;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
// return default map entry
2009-06-05 18:46:03 +00:00
return ::MaterialMap.Map[iMaterial].DefaultMatTex;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
C4Texture * C4TextureMap::GetTexture(const char *szTexture)
2010-03-28 18:58:01 +00:00
{
C4Texture *pTexture;
for (pTexture=FirstTexture; pTexture; pTexture=pTexture->Next)
if (SEqualNoCase(pTexture->Name,szTexture))
return pTexture;
return NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
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;
}
2009-05-08 13:28:41 +00:00
bool C4TextureMap::CheckTexture(const char *szTexture)
2010-03-28 18:58:01 +00:00
{
C4Texture *pTexture;
for (pTexture=FirstTexture; pTexture; pTexture=pTexture->Next)
if (SEqualNoCase(pTexture->Name,szTexture))
return true;
return false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
const char* C4TextureMap::GetTexture(int32_t iIndex)
2010-03-28 18:58:01 +00:00
{
C4Texture *pTexture;
2009-05-08 13:28:41 +00:00
int32_t cindex;
for (pTexture=FirstTexture,cindex=0; pTexture; pTexture=pTexture->Next,cindex++)
2009-05-08 13:28:41 +00:00
if (cindex==iIndex)
return pTexture->Name;
return NULL;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4TextureMap::Default()
2010-03-28 18:58:01 +00:00
{
FirstTexture=NULL;
2009-05-08 13:28:41 +00:00
fEntriesAdded=false;
fOverloadMaterials=false;
fOverloadTextures=false;
fInitialized = false;
2010-03-28 18:58:01 +00:00
}
2009-05-08 13:28:41 +00:00
void C4TextureMap::StoreMapPalette(BYTE *bypPalette, C4MaterialMap &rMaterial)
2010-03-28 18:58:01 +00:00
{
2009-05-08 13:28:41 +00:00
// Zero palette
ZeroMem(bypPalette,256*3);
// Sky color
bypPalette[0]=192;
bypPalette[1]=196;
bypPalette[2]=252;
// Material colors by texture map entries
bool fSet[256];
ZeroMem(&fSet, sizeof (fSet));
2009-05-08 13:28:41 +00:00
int32_t i;
2010-03-28 18:58:01 +00:00
for (i = 0; i < C4M_MaxTexIndex; i++)
{
2009-05-08 13:28:41 +00:00
// Find material
DWORD dwPix = Entry[i].GetPattern().PatternClr(0, 0);
bypPalette[3*i+0]=dwPix >> 16;
bypPalette[3*i+1]=dwPix >> 8;
bypPalette[3*i+2]=dwPix;
bypPalette[3*(i+IFT)+0]=dwPix >> 16;
bypPalette[3*(i+IFT)+1]=dwPix >> 8;
bypPalette[3*(i+IFT)+2]=dwPix | 0x0F; // IFT arbitrarily gets more blue
fSet[i] = fSet[i + IFT] = true;
2010-03-28 18:58:01 +00:00
}
// Crosscheck colors, change equal palette entries
2010-03-28 18:58:01 +00:00
for (i = 0; i < 256; i++) if (fSet[i])
for (;;)
{
2010-03-28 18:58:01 +00:00
// search equal entry
int32_t j = 0;
for (; j < i; j++) if (fSet[j])
if (bypPalette[3*i+0] == bypPalette[3*j+0] &&
bypPalette[3*i+1] == bypPalette[3*j+1] &&
bypPalette[3*i+2] == bypPalette[3*j+2])
break;
// not found? ok then
if (j >= i) break;
// change randomly
if (rand() < RAND_MAX / 2) bypPalette[3*i+0] += 3; else bypPalette[3*i+0] -= 3;
if (rand() < RAND_MAX / 2) bypPalette[3*i+1] += 3; else bypPalette[3*i+1] -= 3;
if (rand() < RAND_MAX / 2) bypPalette[3*i+2] += 3; else bypPalette[3*i+2] -= 3;
}
2010-03-28 18:58:01 +00:00
}
2009-06-05 15:09:54 +00:00
C4TextureMap TextureMap;