forked from Mirrors/openclonk
436 lines
12 KiB
C++
436 lines
12 KiB
C++
/*
|
|
* OpenClonk, http://www.openclonk.org
|
|
*
|
|
* Copyright (c) 1998-2000, 2007 Matthes Bender
|
|
* Copyright (c) 2001-2003, 2005, 2007 Sven Eberhardt
|
|
* Copyright (c) 2002, 2004, 2007-2008 Peter Wortmann
|
|
* Copyright (c) 2006-2007, 2009 Günther Brammer
|
|
* Copyright (c) 2008 Armin Burgmeier
|
|
* 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>
|
|
|
|
#ifndef BIG_C4INCLUDE
|
|
#include <C4SurfaceFile.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>
|
|
#endif
|
|
|
|
C4Texture::C4Texture()
|
|
{
|
|
Name[0]=0;
|
|
Surface32=NULL;
|
|
Next=NULL;
|
|
}
|
|
|
|
C4Texture::~C4Texture()
|
|
{
|
|
delete Surface32;
|
|
}
|
|
|
|
C4TexMapEntry::C4TexMapEntry()
|
|
: pMaterial(NULL), iMaterialIndex(MNone)
|
|
{
|
|
}
|
|
|
|
void C4TexMapEntry::Clear()
|
|
{
|
|
Material.Clear(); Texture.Clear();
|
|
iMaterialIndex = MNone;
|
|
pMaterial = NULL;
|
|
MatPattern.Clear();
|
|
}
|
|
|
|
bool C4TexMapEntry::Create(const char *szMaterial, const char *szTexture)
|
|
{
|
|
// Clear previous data
|
|
Clear();
|
|
// Save names
|
|
Material = szMaterial; Texture = szTexture;
|
|
return true;
|
|
}
|
|
|
|
bool C4TexMapEntry::Init()
|
|
{
|
|
// Find material
|
|
iMaterialIndex = ::MaterialMap.Get(Material.getData());
|
|
if(!MatValid(iMaterialIndex))
|
|
{
|
|
DebugLogF("Error initializing material %s-%s: Invalid material!", Material.getData(), Texture.getData());
|
|
return false;
|
|
}
|
|
pMaterial = &::MaterialMap.Map[iMaterialIndex];
|
|
// Find texture
|
|
C4Texture * sfcTexture = ::TextureMap.GetTexture(Texture.getData());
|
|
if(!sfcTexture)
|
|
{
|
|
DebugLogF("Error initializing material %s-%s: Invalid texture!", Material.getData(), Texture.getData());
|
|
Clear();
|
|
return false;
|
|
}
|
|
// 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);
|
|
return true;
|
|
}
|
|
|
|
C4TextureMap::C4TextureMap()
|
|
{
|
|
Default();
|
|
}
|
|
|
|
C4TextureMap::~C4TextureMap()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
BOOL C4TextureMap::AddEntry(BYTE byIndex, const char *szMaterial, const char *szTexture)
|
|
{
|
|
// Security
|
|
if(byIndex <= 0 || byIndex >= C4M_MaxTexIndex)
|
|
return FALSE;
|
|
// Set entry and initialize
|
|
Entry[byIndex].Create(szMaterial, szTexture);
|
|
if(fInitialized)
|
|
{
|
|
if(!Entry[byIndex].Init())
|
|
{
|
|
// Clear entry if it could not be initialized
|
|
Entry[byIndex].Clear();
|
|
return FALSE;
|
|
}
|
|
// Landscape must be notified (new valid pixel clr)
|
|
::Landscape.HandleTexMapUpdate();
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL C4TextureMap::AddTexture(const char *szTexture, CSurface * sfcSurface)
|
|
{
|
|
C4Texture *pTexture;
|
|
if (!(pTexture=new C4Texture)) return FALSE;
|
|
SCopy(szTexture,pTexture->Name,C4M_MaxName);
|
|
pTexture->Surface32=sfcSurface;
|
|
pTexture->Next=FirstTexture;
|
|
FirstTexture=pTexture;
|
|
return TRUE;
|
|
}
|
|
|
|
void C4TextureMap::Clear()
|
|
{
|
|
for(int32_t i = 1; i < C4M_MaxTexIndex; i++)
|
|
Entry[i].Clear();
|
|
C4Texture *ctex,*next2;
|
|
for (ctex=FirstTexture; ctex; ctex=next2)
|
|
{
|
|
next2=ctex->Next;
|
|
delete ctex;
|
|
}
|
|
FirstTexture=NULL;
|
|
fInitialized = false;
|
|
}
|
|
|
|
BOOL C4TextureMap::LoadFlags(C4Group &hGroup, const char *szEntryName, BOOL *pOverloadMaterials, BOOL *pOverloadTextures)
|
|
{
|
|
// Load the file
|
|
StdStrBuf TexMap;
|
|
if(!hGroup.LoadEntryString(szEntryName, TexMap))
|
|
return FALSE;
|
|
// Reset flags
|
|
if(pOverloadMaterials) *pOverloadMaterials = FALSE;
|
|
if(pOverloadTextures) *pOverloadTextures = FALSE;
|
|
// Check if there are flags in there
|
|
for(const char *pPos = TexMap.getData(); pPos && *pPos; pPos = SSearch(pPos + 1, "\n"))
|
|
{
|
|
// Go over newlines
|
|
while(*pPos == '\r' || *pPos == '\n') pPos++;
|
|
// Flag?
|
|
if (pOverloadMaterials && SEqual2(pPos, "OverloadMaterials"))
|
|
*pOverloadMaterials = TRUE;
|
|
if (pOverloadTextures && SEqual2(pPos, "OverloadTextures"))
|
|
*pOverloadTextures = TRUE;
|
|
}
|
|
// Done
|
|
return TRUE;
|
|
}
|
|
|
|
int32_t C4TextureMap::LoadMap(C4Group &hGroup, const char *szEntryName, BOOL *pOverloadMaterials, BOOL *pOverloadTextures)
|
|
{
|
|
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) )
|
|
{
|
|
SReplaceChar(szLine,0x0D,0x00);
|
|
if (Inside<int32_t>( iIndex = strtol(szLine,NULL,10), 0, C4M_MaxTexIndex-1 ))
|
|
{
|
|
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++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
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;
|
|
fEntriesAdded=false;
|
|
return iTextures;
|
|
}
|
|
|
|
int32_t C4TextureMap::Init()
|
|
{
|
|
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())
|
|
{
|
|
LogF("Error in TextureMap initialization at entry %d", (int) i);
|
|
Entry[i].Clear();
|
|
iRemoved++;
|
|
}
|
|
fInitialized = true;
|
|
return iRemoved;
|
|
}
|
|
|
|
bool C4TextureMap::SaveMap(C4Group &hGroup, const char *szEntryName)
|
|
{
|
|
// 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())
|
|
{
|
|
// compose line
|
|
sTexMapFile.AppendFormat("%d=%s-%s" LineFeed, i, Entry[i].GetMaterialName(), Entry[i].GetTextureName());
|
|
}
|
|
// 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;
|
|
}
|
|
|
|
int32_t C4TextureMap::LoadTextures(C4Group &hGroup, C4Group* OverloadFile)
|
|
{
|
|
int32_t texnum=0;
|
|
// overload: load from other file
|
|
if (OverloadFile) texnum+=LoadTextures(*OverloadFile);
|
|
|
|
char texname[256+1];
|
|
C4Surface *ctex;
|
|
size_t binlen;
|
|
// newgfx: load PNG-textures first
|
|
hGroup.ResetSearch();
|
|
while (hGroup.AccessNextEntry("*",&binlen,texname))
|
|
{
|
|
// check if it already exists in the map
|
|
if (GetTexture(GetFilenameOnly(texname))) continue;
|
|
// create surface
|
|
ctex = new C4Surface();
|
|
if (ctex->Read(hGroup, GetExtension(texname)))
|
|
{
|
|
SReplaceChar(texname,'.',0);
|
|
if (AddTexture(texname,ctex)) texnum++;
|
|
else delete ctex;
|
|
}
|
|
else
|
|
{
|
|
delete ctex;
|
|
}
|
|
}
|
|
return texnum;
|
|
}
|
|
|
|
bool C4TextureMap::HasTextures(C4Group &hGroup)
|
|
{
|
|
return hGroup.EntryCount(C4CFN_PNGFiles) || hGroup.EntryCount(C4CFN_BitmapFiles);
|
|
}
|
|
|
|
void C4TextureMap::MoveIndex(BYTE byOldIndex, BYTE byNewIndex)
|
|
{
|
|
Entry[byNewIndex] = Entry[byOldIndex];
|
|
fEntriesAdded = true;
|
|
}
|
|
|
|
int32_t C4TextureMap::GetIndex(const char *szMaterial, const char *szTexture, BOOL fAddIfNotExist, const char *szErrorIfFailed)
|
|
{
|
|
BYTE byIndex;
|
|
// Find existing
|
|
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())
|
|
{
|
|
if (AddEntry(byIndex, szMaterial, szTexture))
|
|
{
|
|
fEntriesAdded=true;
|
|
return byIndex;
|
|
}
|
|
if (szErrorIfFailed) DebugLogF("Error getting MatTex %s-%s for %s from TextureMap: Init failed.", szMaterial, szTexture, szErrorIfFailed);
|
|
return 0;
|
|
}
|
|
// Else, fail
|
|
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;
|
|
}
|
|
|
|
int32_t C4TextureMap::GetIndexMatTex(const char *szMaterialTexture, const char *szDefaultTexture, BOOL fAddIfNotExist, const char *szErrorIfFailed)
|
|
{
|
|
// split material/texture pair
|
|
StdStrBuf Material, Texture;
|
|
Material.CopyUntil(szMaterialTexture, '-');
|
|
Texture.Copy(SSearch(szMaterialTexture, "-"));
|
|
// texture not given or invalid?
|
|
int32_t iMatTex = 0;
|
|
if(Texture.getData())
|
|
if(iMatTex = GetIndex(Material.getData(), Texture.getData(), fAddIfNotExist))
|
|
return iMatTex;
|
|
if(szDefaultTexture)
|
|
if(iMatTex = GetIndex(Material.getData(), szDefaultTexture, fAddIfNotExist))
|
|
return iMatTex;
|
|
// search material
|
|
long iMaterial = ::MaterialMap.Get(szMaterialTexture);
|
|
if (!MatValid(iMaterial))
|
|
{
|
|
if (szErrorIfFailed) DebugLogF("Error getting MatTex for %s: Invalid material", szErrorIfFailed);
|
|
return 0;
|
|
}
|
|
// return default map entry
|
|
return ::MaterialMap.Map[iMaterial].DefaultMatTex;
|
|
}
|
|
|
|
C4Texture * C4TextureMap::GetTexture(const char *szTexture)
|
|
{
|
|
C4Texture *pTexture;
|
|
for (pTexture=FirstTexture; pTexture; pTexture=pTexture->Next)
|
|
if (SEqualNoCase(pTexture->Name,szTexture))
|
|
return pTexture;
|
|
return NULL;
|
|
}
|
|
|
|
bool C4TextureMap::CheckTexture(const char *szTexture)
|
|
{
|
|
C4Texture *pTexture;
|
|
for (pTexture=FirstTexture; pTexture; pTexture=pTexture->Next)
|
|
if (SEqualNoCase(pTexture->Name,szTexture))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
const char* C4TextureMap::GetTexture(int32_t iIndex)
|
|
{
|
|
C4Texture *pTexture;
|
|
int32_t cindex;
|
|
for (pTexture=FirstTexture,cindex=0; pTexture; pTexture=pTexture->Next,cindex++)
|
|
if (cindex==iIndex)
|
|
return pTexture->Name;
|
|
return NULL;
|
|
}
|
|
|
|
void C4TextureMap::Default()
|
|
{
|
|
FirstTexture=NULL;
|
|
fEntriesAdded=false;
|
|
fOverloadMaterials=false;
|
|
fOverloadTextures=false;
|
|
fInitialized = false;
|
|
}
|
|
|
|
void C4TextureMap::StoreMapPalette(BYTE *bypPalette, C4MaterialMap &rMaterial)
|
|
{
|
|
// 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));
|
|
int32_t i;
|
|
for(i = 0; i < C4M_MaxTexIndex; i++)
|
|
{
|
|
// 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;
|
|
}
|
|
// Crosscheck colors, change equal palette entries
|
|
for(i = 0; i < 256; i++) if(fSet[i])
|
|
for(;;)
|
|
{
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
C4TextureMap TextureMap;
|