mirror of
https://github.com/OpenTrespasser/JurassicParkTrespasser.git
synced 2024-12-18 14:41:56 +00:00
1617 lines
42 KiB
C++
1617 lines
42 KiB
C++
/***********************************************************************************************
|
|
*
|
|
* Copyright © DreamWorks Interactive. 1996
|
|
*
|
|
* Implementation of Texture.hpp
|
|
*
|
|
***********************************************************************************************
|
|
*
|
|
* $Log:: /JP2_PC/Source/Lib/Renderer/Texture.cpp $
|
|
*
|
|
* 110 98.09.10 2:02p Mmouni
|
|
* Added ComputeSolidColour() method to CTexture.
|
|
*
|
|
*
|
|
* 109 9/10/98 12:52a Pkeet
|
|
* Disabled the extra deletion code.
|
|
*
|
|
* 108 9/10/98 12:41a Pkeet
|
|
* Added destructor functions to the overloaded delete member function.
|
|
*
|
|
* 107 9/09/98 11:59a Pkeet
|
|
* Made texturing tracking work.
|
|
*
|
|
* 106 9/08/98 8:54p Rwyatt
|
|
* Added a class specific delete so textures can exist in either normal memory of fast heap
|
|
* memory
|
|
*
|
|
* 105 9/05/98 10:35p Agrant
|
|
* get rid of symbol length warning
|
|
*
|
|
* 104 98.08.31 9:34p Mmouni
|
|
* Added support for directly specified alpha colours.
|
|
*
|
|
* 103 8/29/98 9:33p Pkeet
|
|
* Implemented the 'PurgeD3D' function.
|
|
*
|
|
* 102 8/27/98 1:52p Asouth
|
|
* loop variable re-scoped
|
|
*
|
|
* 101 98.08.23 4:03p Mmouni
|
|
* Changed mip-map threshold.
|
|
*
|
|
* 100 8/21/98 2:25a Rwyatt
|
|
* Added a terminal error in VER_TEST builds for textures missing in the swap file.
|
|
*
|
|
* 99 8/19/98 2:33p Rwyatt
|
|
* Fixed merge fuck up
|
|
*
|
|
* 98 8/19/98 1:31p Rwyatt
|
|
* Textures allocated at load are put into a fast heap, therefore texturesr have a ZeroRefs
|
|
* function so they can delete themselves correctly when their ref count gets to zero.
|
|
*
|
|
* 97 8/18/98 6:11p Pkeet
|
|
* Added the 'bNoLowRes' flag. Added the 'PurgeD3D' member function.
|
|
*
|
|
* 96 8/16/98 11:29p Pkeet
|
|
* Enabled tracking only for textures with rasters.
|
|
*
|
|
* 95 8/16/98 5:48p Pkeet
|
|
* Set necessary features for the default constructor.
|
|
*
|
|
* 94 8/16/98 4:33p Pkeet
|
|
* Added a default constructor.
|
|
*
|
|
* 93 8/15/98 6:06p Mmouni
|
|
* Fixed error "==" instead of "=".
|
|
*
|
|
* 92 8/13/98 1:39p Rwyatt
|
|
* Added memory logs for rasters and textures
|
|
*
|
|
* 91 98.07.30 6:16p Mmouni
|
|
* Adjusted mip-map threshold to 1.2 (slightly biased towards higher resolution).
|
|
*
|
|
* 90 98.07.30 11:43a Mmouni
|
|
* Switched to area based mip-mapping.
|
|
*
|
|
* 89 7/29/98 8:41p Pkeet
|
|
* Added the 'bLargeSizes' member variable.
|
|
*
|
|
* 88 7/29/98 10:49a Rwyatt
|
|
* Added a render flag for textures that have had mip maps generated.
|
|
*
|
|
* 87 7/27/98 8:43p Pkeet
|
|
* Added the 'iGetBestLinkedMipLevel' member function.
|
|
*
|
|
* 86 98.07.27 7:10p Mmouni
|
|
* Made it so that textures that don't get mip-mapped have the flag set that causes them no to
|
|
* get paged out.
|
|
*
|
|
* 85 7/26/98 7:39p Pkeet
|
|
* Simplified colour conversions to Direct3D format.
|
|
*
|
|
* 84 7/23/98 10:07p Pkeet
|
|
* Inverted the alpha colour for hardware.
|
|
*
|
|
* 83 98.07.17 6:42p Mmouni
|
|
* Now sets flags for 16-bit textures based on the pixel format.
|
|
*
|
|
* 82 98.07.10 4:43p Mmouni
|
|
* Curved bump-map mip-maps are now expanded by one pixel.
|
|
*
|
|
* 81 7/02/98 7:11p Rwyatt
|
|
* New VM allocation stratergy for curved bump maps
|
|
*
|
|
* 80 6/08/98 8:00p Mlange
|
|
* Removed unused constructor.
|
|
*
|
|
* 79 98.05.18 3:00p Mmouni
|
|
* Fixed problem with flat shaded color for 16-bit bump maps.
|
|
*
|
|
* 78 98.05.17 6:08p Mmouni
|
|
* Fixed assert in GenerateMipLevels for textures without bitmaps when a swap file was
|
|
* available.
|
|
*
|
|
* 77 5/11/98 12:21p Mlange
|
|
* Added constructor.
|
|
*
|
|
* 76 4/21/98 2:53p Rwyatt
|
|
* The GenerateMips function now takes a parameter that specifies how many mip maps you want.
|
|
* The lowest three mips, no matter what their size, are in non-pageable memory.
|
|
*
|
|
* 75 3/19/98 5:03p Mlange
|
|
* Made CTexture::iGetNumMipLevels() an inline function.
|
|
*
|
|
**********************************************************************************************/
|
|
|
|
#include "Common.hpp"
|
|
#include "Lib/W95/WinInclude.hpp"
|
|
#include "Texture.hpp"
|
|
|
|
#include "Lib/Renderer/LightBlend.hpp"
|
|
#include "Lib/Sys/W95/Render.hpp"
|
|
#include "Lib/View/Raster.hpp"
|
|
#include "Lib/Renderer/Primitives/FastBump.hpp"
|
|
#include "Lib/Renderer/Primitives/FastBumpTable.hpp"
|
|
#include "Lib/Sys/Profile.hpp"
|
|
#include "Lib/View/RasterFile.hpp"
|
|
#include "Lib/Loader/TextureManager.hpp"
|
|
#include "Lib/Loader/ImageLoader.hpp"
|
|
#include "Lib/Sys/VirtualMem.hpp"
|
|
#include "Lib/W95/Direct3D.hpp"
|
|
#include "Lib/Sys/DebugConsole.hpp"
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
#include <memory.h>
|
|
int iTextureCount = 0; // Count of the total number of textures created.
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
|
|
#pragma warning(disable: 4786)
|
|
|
|
// Add this pragma to make sure seterfDEFAULT is initialised before use.
|
|
#pragma warning(disable:4073)
|
|
#pragma init_seg(lib)
|
|
|
|
|
|
extern CProfileStat psTextureConstruct;
|
|
extern CProfileStat psTextureClut;
|
|
|
|
//
|
|
// Module specific functions.
|
|
//
|
|
|
|
//*********************************************************************************************
|
|
//
|
|
inline uint8 uAverage8
|
|
(
|
|
rptr<CRaster> pras, // Raster to sample from.
|
|
int i_x_corner, // Corner in units of i_dim.
|
|
int i_y_corner,
|
|
int i_dim, // Sample size.
|
|
int i_width_source,
|
|
int i_height_source,
|
|
int i_stride_source,
|
|
uint8* pu1_source,
|
|
CColour* aclr,
|
|
bool b_trans
|
|
)
|
|
//
|
|
// Returns a pixel representing the average colour of the pixels at the square defined by
|
|
// i_x_corner * i_dim, i_y_corner * i_dim, i_x_corner * i_dim + i_dim and i_y_corner * i_dim +
|
|
// i_dim.
|
|
//
|
|
//**********************************
|
|
{
|
|
CColour clr_out;
|
|
CColour clr;
|
|
int i_red = 0;
|
|
int i_green = 0;
|
|
int i_blue = 0;
|
|
int i_num_opaque = 0;
|
|
int i_num_transparent = 0;
|
|
|
|
// Get the real corner.
|
|
i_x_corner *= i_dim;
|
|
i_y_corner *= i_dim;
|
|
|
|
// Get the end corners.
|
|
int i_x_corner_end = i_x_corner + i_dim;
|
|
int i_y_corner_end = i_y_corner + i_dim;
|
|
|
|
if (i_x_corner_end > i_width_source)
|
|
i_x_corner_end = i_width_source;
|
|
|
|
if (i_y_corner_end > i_height_source)
|
|
i_y_corner_end = i_height_source;
|
|
|
|
pu1_source += i_y_corner * i_stride_source;
|
|
|
|
// Loop.
|
|
for (int i_y = i_y_corner; i_y < i_y_corner_end; ++i_y)
|
|
{
|
|
for (int i_x = i_x_corner; i_x < i_x_corner_end; ++i_x)
|
|
{
|
|
// Extract the pixel index value.
|
|
uint u1 = pu1_source[i_x];
|
|
|
|
// If the colour is transparent, don't include it in the average.
|
|
if (u1)
|
|
{
|
|
// Get the rgb colour.
|
|
//clr = pras->pxf.clrFromPixel(u1);
|
|
clr = aclr[u1];
|
|
|
|
// Sum rgb values.
|
|
i_red += clr.u1Red;
|
|
i_green += clr.u1Green;
|
|
i_blue += clr.u1Blue;
|
|
++i_num_opaque;
|
|
}
|
|
else
|
|
{
|
|
++i_num_transparent;
|
|
}
|
|
}
|
|
pu1_source += i_stride_source;
|
|
}
|
|
|
|
// If there are more transparent than opaque pixels, return transparency.
|
|
if ((i_num_transparent > i_num_opaque) && (b_trans))
|
|
return 0;
|
|
|
|
if (i_num_opaque == 0)
|
|
return 0;
|
|
|
|
// Otherwise return the average colour.
|
|
clr.u1Red = uint8(i_red / i_num_opaque);
|
|
clr.u1Green = uint8(i_green / i_num_opaque);
|
|
clr.u1Blue = uint8(i_blue / i_num_opaque);
|
|
|
|
// Insert averaged value.
|
|
return pras->pxf.pixFromColour(clr);
|
|
}
|
|
|
|
|
|
//*********************************************************************************************
|
|
inline CBumpAnglePair bangAverage
|
|
(
|
|
rptr<CRaster> pras, // Raster to sample from.
|
|
int i_x_corner, // Corner in units of i_dim.
|
|
int i_y_corner,
|
|
int i_dim, // Sample size.
|
|
bool b_transparent // texture is transparent
|
|
)
|
|
//
|
|
// Returns a pixel representing the average colour of the pixels at the square defined by
|
|
// i_x_corner * i_dim, i_y_corner * i_dim, i_x_corner * i_dim + i_dim and i_y_corner * i_dim +
|
|
// i_dim as well as the bump angle defined by the average of the normals of that region.
|
|
//
|
|
//**********************************
|
|
{
|
|
CColour clr_out;
|
|
CColour clr;
|
|
int i_red = 0;
|
|
int i_green = 0;
|
|
int i_blue = 0;
|
|
int i_num_opaque = 0;
|
|
int i_num_transparent = 0;
|
|
CVector3<> v3_normal(0.0f, 0.0f, 0.0f);
|
|
|
|
// Get the real corner.
|
|
i_x_corner *= i_dim;
|
|
i_y_corner *= i_dim;
|
|
|
|
// Loop.
|
|
for (int i_x = i_x_corner; i_x < i_x_corner + i_dim; ++i_x)
|
|
for (int i_y = i_y_corner; i_y < i_y_corner + i_dim; ++i_y)
|
|
{
|
|
// Extract the pixel index value.
|
|
CBumpAnglePair bang = pras->pixGet(i_x, i_y);
|
|
|
|
// Extract the colour value.
|
|
uint8 u1 = bang.u1GetColour();
|
|
|
|
// If the colour is transparent, don't include it in the average.
|
|
if (u1)
|
|
{
|
|
// Get the rgb colour.
|
|
clr = pras->pxf.clrFromPixel(bang);
|
|
|
|
// Sum rgb values.
|
|
i_red += clr.u1Red;
|
|
i_green += clr.u1Green;
|
|
i_blue += clr.u1Blue;
|
|
|
|
// Get the normal and sum it.
|
|
v3_normal += bang.dir3MakeNormalFast();
|
|
|
|
// Increment the opaque count.
|
|
++i_num_opaque;
|
|
}
|
|
else
|
|
{
|
|
++i_num_transparent;
|
|
}
|
|
}
|
|
|
|
// If there are more transparent than opaque pixels, return transparency.
|
|
if ((i_num_transparent > i_num_opaque) && (b_transparent))
|
|
return 0;
|
|
|
|
if (i_num_opaque == 0)
|
|
return 0;
|
|
|
|
// Otherwise return the average colour.
|
|
clr.u1Red = uint8(i_red / i_num_opaque);
|
|
clr.u1Green = uint8(i_green / i_num_opaque);
|
|
clr.u1Blue = uint8(i_blue / i_num_opaque);
|
|
|
|
// Get the average normal.
|
|
if (v3_normal.tX == 0.0f && v3_normal.tY == 0.0f && v3_normal.tZ == 0.0f)
|
|
v3_normal = CVector3<>(0.0f, 1.0f, 0.0f);
|
|
CDir3<> dir3 = CDir3<>(v3_normal);
|
|
|
|
// Create a return value.
|
|
CBumpAnglePair bang_ret = pras->pxf.pixFromColour(clr);
|
|
|
|
// Convert the average normal to a bump angle and add it to the return value.
|
|
bang_ret.SetBump(dir3, false);
|
|
|
|
return bang_ret;
|
|
}
|
|
|
|
|
|
//*****************************************************************************************
|
|
//
|
|
TPixel pixAverage
|
|
(
|
|
rptr<CRaster> pras // Any raster.
|
|
)
|
|
//
|
|
// Returns:
|
|
// A pixel representing the average colour of the raster.
|
|
//
|
|
//**********************************
|
|
{
|
|
// Add up all the colours, storing in uint32s for range and precision.
|
|
uint32 u4_red = 0;
|
|
uint32 u4_green = 0;
|
|
uint32 u4_blue = 0;
|
|
uint32 u4_count = 0;
|
|
|
|
pras->Lock();
|
|
int i_y;
|
|
for (i_y = 0; i_y < pras->iHeight; i_y += 4)
|
|
{
|
|
int i_index = pras->iIndex(0, i_y);
|
|
for (int i_x = i_y & 3; i_x < pras->iWidth; i_x += 2)
|
|
{
|
|
// Get the pixels.
|
|
TPixel pix = pras->pixGet(i_index + i_x);
|
|
|
|
// Don't include transparent pixels in the count.
|
|
if (pix)
|
|
{
|
|
CColour clr = pras->clrFromPixel(pix);
|
|
u4_red += clr.u1Red;
|
|
u4_green += clr.u1Green;
|
|
u4_blue += clr.u1Blue;
|
|
u4_count++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If no opaque pixels are found, get desperate.
|
|
if (u4_count == 0)
|
|
{
|
|
for (i_y = 0; i_y < pras->iHeight; i_y++)
|
|
{
|
|
int i_index = pras->iIndex(0, i_y);
|
|
for (int i_x = 0; i_x < pras->iWidth; i_x++)
|
|
{
|
|
// Get the pixels.
|
|
TPixel pix = pras->pixGet(i_index + i_x);
|
|
|
|
// Don't include transparent pixels in the count.
|
|
if (pix)
|
|
{
|
|
CColour clr = pras->clrFromPixel(pix);
|
|
u4_red += clr.u1Red;
|
|
u4_green += clr.u1Green;
|
|
u4_blue += clr.u1Blue;
|
|
u4_count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
pras->Unlock();
|
|
|
|
if (u4_count)
|
|
{
|
|
// Divide by the number of pixels counted.
|
|
u4_red /= u4_count;
|
|
u4_green /= u4_count;
|
|
u4_blue /= u4_count;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise, make a nice, average colour.
|
|
u4_red = 128;
|
|
u4_green = 128;
|
|
u4_blue = 128;
|
|
}
|
|
|
|
// Convert colour to a pixel in this raster.
|
|
return pras->pixFromColour(CColour((int)u4_red, (int)u4_green, (int)u4_blue));
|
|
}
|
|
|
|
|
|
//
|
|
// Module specific function prototypes.
|
|
//
|
|
|
|
//*********************************************************************************************
|
|
void Average8(rptr<CRaster> pras_dest, rptr<CRaster> pras_source, int i_shift, bool b_trans);
|
|
|
|
//*********************************************************************************************
|
|
void AverageBump(rptr<CRaster> pras_dest, rptr<CRaster> pras_source, int i_shift, bool b_trans);
|
|
|
|
//*********************************************************************************************
|
|
void GrowBumpEdges(rptr<CRaster> pras_new);
|
|
|
|
|
|
//
|
|
// Class implementations.
|
|
//
|
|
|
|
//*********************************************************************************************
|
|
//
|
|
// CTexture implementation.
|
|
//
|
|
|
|
//
|
|
// The render flags used by default. Enable only those that are common, and will not be
|
|
// automatically enabled by the texture attributes.
|
|
//
|
|
|
|
const CSet<ERenderFeature> seterfDEFAULT =
|
|
Set(erfPERSPECTIVE) + erfZ_BUFFER + erfTRAPEZOIDS + erfSUBPIXEL +
|
|
erfLIGHT + erfLIGHT_SHADE + erfFOG + erfFOG_SHADE;
|
|
|
|
//*****************************************************************************************
|
|
// Default constructor.
|
|
CTexture::CTexture()
|
|
{
|
|
bNoLowRes = false;
|
|
bLargeSizes = false;
|
|
bDirectAlpha = false;
|
|
seterfFeatures = Set(erfTRAPEZOIDS);
|
|
ppcePalClut = 0;
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// Initialise with a raster and a solid colour.
|
|
CTexture::CTexture(rptr<CRaster> pras, const CMaterial* pmat, TTexPix tp_solid, CSet<ERenderFeature> seterf)
|
|
: seterfFeatures(seterf), tpSolid(tp_solid), bLargeSizes(false), bNoLowRes(false), bDirectAlpha(false)
|
|
{
|
|
CCycleTimer ctmr;
|
|
|
|
Assert(tpSolid != 0);
|
|
|
|
CCycleTimer ctmr_clut;
|
|
if (pras->pxf.ppalAttached)
|
|
{
|
|
ppcePalClut = pcdbMain.ppceAddEntry(pras->pxf.ppalAttached, pmat);
|
|
Assert(ppcePalClut);
|
|
pras->pxf.ppalAttached = ppcePalClut->ppalPalette;
|
|
Assert(pras->pxf.ppalAttached);
|
|
}
|
|
else
|
|
ppcePalClut = pcdbMain.pceMainPalClut;
|
|
|
|
psTextureClut.Add(ctmr_clut(),0);
|
|
|
|
// Create the bump map table here.
|
|
if (seterfFeatures[erfBUMP] && !ppcePalClut->pBumpTable)
|
|
ppcePalClut->pBumpTable = new CBumpTable(ppcePalClut->pclutClut);
|
|
|
|
//
|
|
// Attach the texture as a single mip level.
|
|
//
|
|
Assert(aprasTextures.uLen == 0);
|
|
aprasTextures << pras;
|
|
|
|
// Set the texture features.
|
|
UpdateFeatures();
|
|
|
|
// Set the colour for use by hardware.
|
|
SetD3DColour();
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
strTextureBmpName = 0;
|
|
aiMipUseCount = 0;
|
|
++iTextureCount;
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
psTextureConstruct.Add(ctmr());
|
|
|
|
MEMLOG_ADD_COUNTER(emlCTexture,1);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// Initialise with a raster; solid colour is calculated automatically.
|
|
CTexture::CTexture(rptr<CRaster> pras, const CMaterial* pmat, CSet<ERenderFeature> seterf)
|
|
{
|
|
new(this) CTexture(pras, pmat, pixAverage(pras), seterf);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// Initialise with a palette and a solid colour.
|
|
CTexture::CTexture(CPal* ppal, TTexPix tp_solid, const CColour& clr_d3d, const CMaterial* pmat)
|
|
: seterfFeatures(seterfDEFAULT), tpSolid(tp_solid), bLargeSizes(false),
|
|
bNoLowRes(false), bDirectAlpha(false)
|
|
{
|
|
CCycleTimer ctmr;
|
|
Assert(tpSolid != 0);
|
|
Assert(ppal);
|
|
|
|
CCycleTimer ctmr_clut;
|
|
ppcePalClut = pcdbMain.ppceAddEntry(ppal, pmat);
|
|
Assert(ppcePalClut);
|
|
psTextureClut.Add(ctmr(),0);
|
|
|
|
d3dpixColour = clr_d3d.d3dcolGetD3DColour();
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
strTextureBmpName = 0;
|
|
aiMipUseCount = 0;
|
|
++iTextureCount;
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
seterfFeatures[erfSPECULAR] = pmat->rvSpecular != 0;
|
|
|
|
// Check for pre-lit surface. If no diffuse or specular component, there is no need for vertex lighting.
|
|
if (pmat->rvDiffuse == 0 && pmat->rvSpecular == 0)
|
|
seterfFeatures[erfLIGHT_SHADE] = 0;
|
|
|
|
#if VER_DEBUG
|
|
Validate();
|
|
#endif
|
|
|
|
psTextureConstruct.Add(ctmr());
|
|
|
|
MEMLOG_ADD_COUNTER(emlCTexture,1);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// Initialise with a solid colour (using a default palette).
|
|
CTexture::CTexture(CColour clr_solid, const CMaterial* pmat)
|
|
: seterfFeatures(seterfDEFAULT), bLargeSizes(false), bNoLowRes(false), bDirectAlpha(false)
|
|
{
|
|
CCycleTimer ctmr;
|
|
|
|
// Set the Direct3D colour.
|
|
d3dpixColour = clr_solid.d3dcolGetD3DColour();
|
|
|
|
// Use the default palette, adding a new clut for material if necessary.
|
|
Assert(pcdbMain.pceMainPalClut);
|
|
|
|
ppcePalClut = pcdbMain.ppceAddEntry(pcdbMain.pceMainPalClut->ppalPalette, pmat);
|
|
Assert(ppcePalClut);
|
|
|
|
// And look up the colour therein.
|
|
tpSolid = ppcePalClut->ppalPalette->u1MatchEntry(clr_solid);
|
|
Assert(tpSolid != 0);
|
|
|
|
|
|
// Set the colour for use by hardware.
|
|
const float fInv255 = 1.0f / 255.0f;
|
|
|
|
//
|
|
// To do:
|
|
// Make this operation faster using a custom macro.
|
|
//
|
|
|
|
// For now, set the colour to red.
|
|
d3dpixColour = clr_solid.d3dcolGetD3DColour();
|
|
|
|
seterfFeatures[erfSPECULAR] = pmat->rvSpecular != 0;
|
|
|
|
// Check for pre-lit surface. If no diffuse or specular component, there is no need for vertex lighting.
|
|
if (pmat->rvDiffuse == 0 && pmat->rvSpecular == 0)
|
|
seterfFeatures[erfLIGHT_SHADE] = 0;
|
|
|
|
#if VER_DEBUG
|
|
Validate();
|
|
#endif
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
strTextureBmpName = 0;
|
|
aiMipUseCount = 0;
|
|
++iTextureCount;
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
psTextureConstruct.Add(ctmr());
|
|
|
|
MEMLOG_ADD_COUNTER(emlCTexture,1);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// Initialise with an alpha colour index.
|
|
CTexture::CTexture(int i_alpha_color)
|
|
: seterfFeatures(seterfDEFAULT + erfALPHA_COLOUR), iAlphaColour(i_alpha_color),
|
|
bLargeSizes(false), bNoLowRes(false), bDirectAlpha(false)
|
|
{
|
|
CCycleTimer ctmr;
|
|
|
|
CLightBlend::SLightBlendSettings* plbs; // Pointer to the settings for the alpha.
|
|
|
|
// Turn off all lighting for any special surfaces.
|
|
seterfFeatures[erfLIGHT][erfLIGHT_SHADE][erfSPECULAR] = 0;
|
|
|
|
#if VER_DEBUG
|
|
Validate();
|
|
#endif
|
|
|
|
ppcePalClut = 0;
|
|
|
|
//
|
|
// Set the alpha colour value.
|
|
//
|
|
|
|
// Get the light blend settings for the alpha colour.
|
|
plbs = &lbAlphaConstant.lpsSettings[iAlphaColour];
|
|
Assert(plbs);
|
|
|
|
// Use the D3D colour macro.
|
|
d3dpixColour = D3DRGBA
|
|
(
|
|
plbs->fRed(),
|
|
plbs->fGreen(),
|
|
plbs->fBlue(),
|
|
plbs->fAlpha
|
|
);
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
strTextureBmpName = 0;
|
|
aiMipUseCount = 0;
|
|
++iTextureCount;
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
psTextureConstruct.Add(ctmr());
|
|
|
|
MEMLOG_ADD_COUNTER(emlCTexture,1);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
// Initialise with colour + alpha.
|
|
CTexture::CTexture(int i_red, int i_green, int i_blue, int i_alpha)
|
|
: seterfFeatures(seterfDEFAULT + erfALPHA_COLOUR), bLargeSizes(false),
|
|
bNoLowRes(false), bDirectAlpha(true)
|
|
{
|
|
CCycleTimer ctmr;
|
|
|
|
// Turn off all lighting for any special surfaces.
|
|
seterfFeatures[erfLIGHT][erfLIGHT_SHADE][erfSPECULAR] = 0;
|
|
|
|
#if VER_DEBUG
|
|
Validate();
|
|
#endif
|
|
|
|
ppcePalClut = 0;
|
|
|
|
// Set the solid version of the alpha colour value.
|
|
tpSolid = prasMainScreen->pixFromColour(CColour(i_red, i_green, i_blue));
|
|
|
|
// Use the D3D colour macro.
|
|
d3dpixColour = D3DRGBA
|
|
(
|
|
i_red / 255.0,
|
|
i_green / 255.0,
|
|
i_blue / 255.0,
|
|
i_alpha / 255.0
|
|
);
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
strTextureBmpName = 0;
|
|
aiMipUseCount = 0;
|
|
++iTextureCount;
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
psTextureConstruct.Add(ctmr());
|
|
|
|
MEMLOG_ADD_COUNTER(emlCTexture,1);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
CTexture::~CTexture()
|
|
{
|
|
PurgeD3D();
|
|
for (uint u = 0; u < aprasTextures.uLen; ++u)
|
|
aprasTextures[u] = rptr0;
|
|
#if bTRACK_TEXTURE_USAGE
|
|
if (aprasTextures.uLen)
|
|
DeleteTracking();
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
MEMLOG_SUB_COUNTER(emlCTexture,1);
|
|
}
|
|
|
|
//******************************************************************************************
|
|
// If this texture is not in the image loader fast heap then delete its memory
|
|
void CTexture::operator delete(void* pv)
|
|
{
|
|
if (CLoadImageDirectory::bLoadHeapAllocation(pv))
|
|
{
|
|
/*
|
|
CTexture* ptex = ((CTexture*)pv);
|
|
ptex->PurgeD3D();
|
|
for (uint u = 0; u < ptex->aprasTextures.uLen; ++u)
|
|
ptex->aprasTextures[u] = rptr0;
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
::delete pv;
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::PurgeD3D()
|
|
{
|
|
for (int i = 0; i < iGetNumMipLevels(); ++i)
|
|
{
|
|
// Break the raster's link.
|
|
prasGetTexture(i)->Link();
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::SetD3DColour()
|
|
{
|
|
const float fInv255 = 1.0f / 255.0f;
|
|
|
|
Assert(tpSolid != 0);
|
|
Assert(prasMainScreen);
|
|
|
|
// Use the texture raster to convert the value.
|
|
if (aprasTextures[0])
|
|
{
|
|
CColour clr = aprasTextures[0]->pxf.clrFromPixel(tpSolid);
|
|
d3dpixColour = clr.d3dcolGetD3DColour();
|
|
}
|
|
else
|
|
{
|
|
// Use bright red to show errors.
|
|
d3dpixColour = D3DRGB(1, 0, 0);
|
|
}
|
|
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
int CTexture::iSelectMipLevel(float fScreenArea, float fTextureArea) const
|
|
{
|
|
// If there are no mip levels to choose from, just use the top mip level.
|
|
if (aprasTextures.uLen <= 1)
|
|
{
|
|
#if bTRACK_TEXTURE_USAGE
|
|
// Increment tracking information for the selected mip level.
|
|
++aiMipUseCount[0];
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Use the smallest mipmap if required.
|
|
if (emuMipUse == emuSMALLEST)
|
|
{
|
|
#if bTRACK_TEXTURE_USAGE
|
|
// Increment tracking information for the selected mip level.
|
|
++aiMipUseCount[aprasTextures.uLen - 1];
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
return aprasTextures.uLen - 1;
|
|
}
|
|
|
|
fScreenArea *= fMipmapThreshold;
|
|
|
|
for (uint u = 0; u < aprasTextures.uLen; ++u)
|
|
{
|
|
// Calculate the texture area for this mip level.
|
|
float f_tex_area = fTextureArea * aprasTextures[u]->fWidth * aprasTextures[u]->fHeight;
|
|
|
|
// Break if the area of the texels is less than the area of the screen.
|
|
if (f_tex_area < fScreenArea)
|
|
{
|
|
// Do not use the largest mipmap if not allowed.
|
|
if (emuMipUse == emuNO_LARGEST)
|
|
{
|
|
if (u == 0)
|
|
u = 1;
|
|
}
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
// Increment tracking information for the selected mip level.
|
|
++aiMipUseCount[u];
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
return u;
|
|
}
|
|
}
|
|
|
|
uint u_retval = aprasTextures.uLen - 1;
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
// Increment tracking information for the selected mip level.
|
|
++aiMipUseCount[u_retval];
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
return u_retval;
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::AddMipLevel(rptr<CRaster> pras)
|
|
{
|
|
// Create a new array one larger than the old array.
|
|
aprasTextures << pras;
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::DestroyMipLevels()
|
|
{
|
|
// Do nothing if there is only one mip level.
|
|
if (aprasTextures.uLen == 1)
|
|
return;
|
|
|
|
for (int i = aprasTextures.uLen - 1; i > 0; --i)
|
|
{
|
|
aprasTextures[i] = rptr0;
|
|
}
|
|
|
|
aprasTextures.uLen = 1;
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::ReassignMipLevel(rptr<CRaster> pras, int i_miplevel)
|
|
{
|
|
Assert(aprasTextures.uLen > i_miplevel);
|
|
|
|
// Reassign the raster.
|
|
aprasTextures[i_miplevel] = pras;
|
|
}
|
|
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::ComputeSolidColour()
|
|
{
|
|
// Compute solid colour from smalled mip.
|
|
if (aprasTextures.uLen)
|
|
tpSolid = pixAverage(aprasTextures[aprasTextures.uLen-1]);
|
|
}
|
|
|
|
#if bSTIPPLE_MIP_LEVELS
|
|
|
|
//*********************************************************************************************
|
|
static void StippleMip(rptr<CRaster> pras_new, int i_mip_level, bool bBumpMap)
|
|
{
|
|
uint8* src;
|
|
|
|
// mip non pageable mip levels are in the smallest mip colour
|
|
if ((i_mip_level>=3) || (i_mip_level == (int)eptSMALLEST))
|
|
i_mip_level == 5;
|
|
|
|
pras_new->Lock();
|
|
|
|
// copy the source raster into the reference raster
|
|
|
|
int x, y;
|
|
|
|
if (bBumpMap)
|
|
{
|
|
//
|
|
// A CBumpAnglePair raster is a bump map so we need to extract the colour element,
|
|
// this is usually the top byte but we will use the class just to be sure.
|
|
// The resulting data is 8 bits per pixel and uses the palette from the
|
|
// raster.
|
|
//
|
|
CBumpAnglePair bang;
|
|
|
|
for (y = 0; y<pras_new->iHeight; y+=1)
|
|
{
|
|
for (x = (y&1); x<pras_new->iWidth-(y&1); x+=2)
|
|
{
|
|
bang.br = pras_new->pixGet(x, y);
|
|
// (!(b_transparent && br.u1Colour() == 0))
|
|
if (bang.u1GetColour() != 0)
|
|
{
|
|
bang.SetColour(255-i_mip_level);
|
|
pras_new->PutPixel(x,y,bang.br);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else switch (pras_new->iPixelBits)
|
|
{
|
|
case 8:
|
|
for (y = 0; y<pras_new->iHeight; y+=1)
|
|
{
|
|
src = (uint8*)pras_new->pAddress(0,y);
|
|
for (x = 0 + (y&1); x<pras_new->iWidth-(y&1); x+=2)
|
|
{
|
|
if (src[x] != 0)
|
|
src[x] = 255 - i_mip_level;
|
|
}
|
|
//src = ((uint8*)src) + pras_new->iLineBytes();
|
|
}
|
|
break;
|
|
}
|
|
|
|
pras_new->Unlock();
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::GenerateMipLevels
|
|
(
|
|
uint32 u4_smallest
|
|
)
|
|
{
|
|
CCycleTimer ctmr;
|
|
CCycleTimer ctmr_all;
|
|
char str_mip_name[128];
|
|
bool b_imaged;
|
|
int i_mip_level;
|
|
uint32 u4_mips;
|
|
|
|
// If there is no raster, or there already are multiple rasters, do nothing.
|
|
if (iGetNumMipLevels() != 1)
|
|
return;
|
|
|
|
// This texture already has its mips created.
|
|
if (seterfFeatures[erfMIPS_CREATED])
|
|
return;
|
|
|
|
// This texture has been processed for mip maps.
|
|
seterfFeatures[erfMIPS_CREATED] = true;
|
|
|
|
|
|
b_imaged = CLoadImageDirectory::bImageValid() &&
|
|
gtxmTexMan.pvmeTextures->bImageAddressValid(aprasTextures[0]->pSurface);
|
|
|
|
u4_mips = 0;
|
|
|
|
i_mip_level = (int)eptTOP_LEVEL;
|
|
|
|
if (seterfFeatures[erfCURVED])
|
|
{
|
|
if ((aprasTextures[0]->iWidth<16) || (aprasTextures[0]->iHeight<16))
|
|
{
|
|
i_mip_level = (int)eptSMALLEST;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (u4_smallest < u4NONPAGEABLE_MIPS)
|
|
{
|
|
i_mip_level = (int)eptSMALLEST;
|
|
}
|
|
}
|
|
|
|
|
|
/* if ((aprasTextures[0]->iWidth<16) || (aprasTextures[0]->iHeight<16))
|
|
{
|
|
// width or height are less than 16 which means that no mip maps
|
|
// will get generated for this texture, therefore both of the
|
|
// above checks will fail and the texture will be packed twice
|
|
// and this will generate a hash collision in the image directory.
|
|
// If texture 0 is already packed, do nothing.
|
|
if (gtxmTexMan.pvmeTextures->bVirtualAddressValid(aprasTextures[0]->pSurface))
|
|
return;
|
|
}*/
|
|
|
|
// Since we do not mip non-8 bit non-bumpmap textures, make sure that they will not
|
|
// be pageable.
|
|
if (prasGetTexture()->iPixelBits != 8 && !seterfFeatures[erfBUMP])
|
|
i_mip_level = (int)eptSMALLEST;
|
|
|
|
|
|
//
|
|
// If this texture is not imaged then pack it as usual
|
|
//
|
|
if (!b_imaged)
|
|
{
|
|
// pack the original texture, if it is too big or already packed then this will return
|
|
// what was passed in to it...
|
|
aprasTextures[0] = gtxmTexMan.prasPackTexture( aprasTextures[0],
|
|
seterfFeatures[erfCURVED],
|
|
(ETexturePackTypes)i_mip_level );
|
|
#if bSTIPPLE_MIP_LEVELS
|
|
StippleMip(aprasTextures[0], 0, seterfFeatures[erfBUMP]);
|
|
#endif
|
|
gtxmTexMan.AddToPackLog(rptr_this(this),
|
|
aprasTextures[0],
|
|
(uint64)u4HashValue);
|
|
}
|
|
|
|
// Get the width and the height for the next mip level.
|
|
int i_width = prasGetTexture()->iWidth >> 1;
|
|
int i_height = prasGetTexture()->iHeight >> 1;
|
|
int i_pixel_size = prasGetTexture()->iPixelBits;
|
|
|
|
// For the time being, do nothing with non-8 bit non-bumpmap textures.
|
|
if (i_pixel_size != 8 && !seterfFeatures[erfBUMP])
|
|
return;
|
|
|
|
// Choose the minimum dimension for determining mip levels.
|
|
int i_dimension = Min(i_width, i_height);
|
|
|
|
// Create a unique hash value for this texture.
|
|
// the 64 bit hash value is the parent hash value with the width and height encoded.
|
|
|
|
int i_shift = 2;
|
|
|
|
// Build mipmaps while they are worth building.
|
|
while (i_dimension >= 8)
|
|
{
|
|
// Create the raster for the new mip level.
|
|
rptr<CRaster> pras_new;
|
|
bool b_found;
|
|
uint64 u8_hash = ((uint64)u4HashValue) |
|
|
(((uint64)i_width)<<32) | // 10 bits at pos32 for X Size
|
|
(((uint64)i_height)<<42); // 10 bits at pos42 for y Size
|
|
|
|
u4_mips++;
|
|
if (u4_mips>u4_smallest)
|
|
return;
|
|
|
|
if (aprasTextures[0]->pxf.ppalAttached)
|
|
{
|
|
// if the parent has a CLUT add convert its unique ID into
|
|
// a byte and merge it with the mips hash value.
|
|
uint32 u4_palid = aprasTextures[0]->pxf.ppalAttached->u4GetHashValue();
|
|
|
|
// convert the 32 bit pal ID into a byte
|
|
u4_palid ^= (u4_palid>>15);
|
|
u4_palid &= 0x0000ffff;
|
|
u4_palid ^= (u4_palid>>8);
|
|
u4_palid &= 0x00000fff;
|
|
|
|
u8_hash |= ((uint64)u4_palid)<<52; // 12 bits at pos52 for pal ID.
|
|
}
|
|
|
|
// Once we have the packed a smallest texture, all child mips after that are also smallest.
|
|
if (i_mip_level != (int)eptSMALLEST)
|
|
{
|
|
i_mip_level++;
|
|
|
|
if (seterfFeatures[erfCURVED])
|
|
{
|
|
if ((aprasTextures[0]->iWidth<16) || (aprasTextures[0]->iHeight<16))
|
|
{
|
|
i_mip_level = (int)eptSMALLEST;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((uint32)i_mip_level >= (uint32)(u4_smallest-3))
|
|
{
|
|
i_mip_level = (int)eptSMALLEST;
|
|
}
|
|
}
|
|
}
|
|
|
|
wsprintf(str_mip_name,"Mip/%x%x.mip", (uint32)(u8_hash >> 32), (uint32)(u8_hash & 0xffffffff) );
|
|
|
|
b_found = false;
|
|
|
|
// if we are using an image file and this texture is in it, try to locate the mip maps
|
|
if (b_imaged)
|
|
{
|
|
ctmr();
|
|
SDirectoryFileChunk* pdfc;
|
|
pdfc = CLoadImageDirectory::plidImageDir->mapChunk[u8_hash];
|
|
|
|
AlwaysAssert(pdfc);
|
|
|
|
// check the parameters from the directory file are correct
|
|
if (pdfc)
|
|
{
|
|
Assert((int32)pdfc->u4Width == i_width);
|
|
Assert((int32)pdfc->u4Height == i_height);
|
|
|
|
void* pv_raster = CLoadImageDirectory::pvAllocate( sizeof(CRasterMem) );
|
|
pras_new = rptr_cast( CRaster, rptr_new(pv_raster) CRasterMem(
|
|
((char*)gtxmTexMan.pvmeTextures->pvGetBase()) + pdfc->u4VMOffset,
|
|
pdfc->u4Width,
|
|
pdfc->u4Height,
|
|
pdfc->u4Bits,
|
|
pdfc->u4Stride,
|
|
NULL,
|
|
emtTexManVirtual) );
|
|
|
|
if (pras_new)
|
|
{
|
|
// copy the pxf format of the parent to the mip
|
|
pras_new->pxf = prasGetTexture()->pxf;
|
|
b_found = true;
|
|
}
|
|
}
|
|
extern CProfileStat psNewRaster;
|
|
psNewRaster.Add(ctmr(), 0);
|
|
}
|
|
|
|
// if we have not managed to located the mip in the image file load it the usuall way
|
|
if (!b_found)
|
|
{
|
|
#if VER_TEST
|
|
if (CLoadImageDirectory::bImageValid())
|
|
{
|
|
char str_buffer[1024];
|
|
sprintf(str_buffer,
|
|
"%s\n\nWarning: Mip map '%s' Not in swap file..\n",
|
|
__FILE__,
|
|
str_mip_name );
|
|
|
|
bTerminalError(ERROR_ASSERTFAIL, true, str_buffer, __LINE__);
|
|
}
|
|
#endif
|
|
pras_new = rptr_cast(CRaster, rptr_new CRasterMem
|
|
(
|
|
i_width,
|
|
i_height,
|
|
i_pixel_size,
|
|
0,
|
|
&prasGetTexture()->pxf
|
|
));
|
|
|
|
// Try and load a pregenerated mipmap.
|
|
if (!bLoadRasterMip(str_mip_name, pras_new))
|
|
{
|
|
dprintf("Create Mip: %s\n",str_mip_name);
|
|
|
|
// Copy the texture over and resample.
|
|
if (seterfFeatures[erfBUMP])
|
|
{
|
|
AverageBump(pras_new, prasGetTexture(), i_shift, seterfFeatures[erfTRANSPARENT]);
|
|
|
|
// Grow bump-map edges.
|
|
if (seterfFeatures[erfCURVED])
|
|
GrowBumpEdges(pras_new);
|
|
}
|
|
else switch (i_pixel_size)
|
|
{
|
|
case 8:
|
|
Average8(pras_new, prasGetTexture(), i_shift, seterfFeatures[erfTRANSPARENT]);
|
|
break;
|
|
|
|
default:
|
|
Assert(0);
|
|
}
|
|
|
|
// Cache the mip level to a file.
|
|
bSaveRasterMip(str_mip_name, pras_new);
|
|
}
|
|
|
|
#if bSTIPPLE_MIP_LEVELS
|
|
StippleMip(pras_new, i_mip_level, seterfFeatures[erfBUMP]);
|
|
#endif
|
|
|
|
// pack the mip, this will relocate the raster in the image file log after the
|
|
// raster has been packed.
|
|
pras_new = gtxmTexMan.prasPackTexture(pras_new,
|
|
seterfFeatures[erfCURVED], (ETexturePackTypes)i_mip_level);
|
|
|
|
gtxmTexMan.AddToPackLog(rptr_this(this), pras_new, u8_hash);
|
|
|
|
// keep track of the correct mip stat, the check is based on the bit depth...
|
|
MEMLOG_ADD_COUNTER((seterfFeatures[erfBUMP] ? emlBumpMipCount : emlTextureMipCount), 1);
|
|
}
|
|
|
|
ctmr_all();
|
|
// Add the mip level to the existing structure.
|
|
AddMipLevel(pras_new);
|
|
extern CProfileStat psPreImageMip;
|
|
psPreImageMip.Add(ctmr_all(),0);
|
|
|
|
//
|
|
// Generate values for the next mip level.
|
|
//
|
|
i_width >>= 1;
|
|
i_height >>= 1;
|
|
i_dimension >>= 1;
|
|
i_shift <<= 1;
|
|
}
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
// Add tracking information if required.
|
|
CreateTracking();
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
}
|
|
|
|
//******************************************************************************************
|
|
void CTexture::UpdateFeatures()
|
|
{
|
|
if (seterfFeatures[erfOCCLUDE])
|
|
{
|
|
// Occlusion precludes all other features.
|
|
seterfFeatures = Set(erfOCCLUDE);
|
|
return;
|
|
}
|
|
|
|
// Set flag if a texture is present.
|
|
bool b_is_texture = iGetNumMipLevels() > 0;
|
|
|
|
// Automatically set some flag values.
|
|
seterfFeatures[erfTEXTURE] = b_is_texture;
|
|
if (!b_is_texture)
|
|
seterfFeatures[erfTRANSPARENT][erfBUMP] = false;
|
|
|
|
//
|
|
// A bit of a hack here. Set some flags for any 16-bit texture that is not a bump
|
|
// map based on the pixel format of the map.
|
|
//
|
|
if (b_is_texture && prasGetTexture()->iPixelBits == 16 && !seterfFeatures[erfBUMP])
|
|
{
|
|
if (prasGetTexture()->pxf.cposG == 0x00F0)
|
|
seterfFeatures[erfALPHA_COLOUR] = 1;
|
|
else
|
|
seterfFeatures[erfCOPY] = 1;
|
|
}
|
|
|
|
if (seterfFeatures[erfBUMP])
|
|
// Bump mapping is currently imcompatible with light shading.
|
|
seterfFeatures[erfLIGHT_SHADE] = 0;
|
|
|
|
if (ppcePalClut)
|
|
{
|
|
const CMaterial* pmat = ppcePalClut->pmatMaterial;
|
|
|
|
seterfFeatures[erfSPECULAR] = pmat->rvSpecular != 0;
|
|
|
|
// Check for pre-lit surface. If no diffuse or specular component, there is no need for vertex lighting.
|
|
if (pmat->rvDiffuse == 0 && pmat->rvSpecular == 0)
|
|
seterfFeatures[erfLIGHT_SHADE] = 0;
|
|
}
|
|
else
|
|
seterfFeatures[erfSPECULAR] = 0;
|
|
|
|
// Turn off all lighting for any special surfaces.
|
|
if (seterfFeatures[erfCOPY][erfALPHA_COLOUR][erfOCCLUDE] ||
|
|
!seterfFeatures[erfLIGHT])
|
|
seterfFeatures[erfLIGHT][erfLIGHT_SHADE][erfSPECULAR] = 0;
|
|
|
|
#if VER_DEBUG
|
|
Validate();
|
|
#endif
|
|
}
|
|
|
|
//******************************************************************************************
|
|
void CTexture::Validate() const
|
|
{
|
|
// To do: further implement this function.
|
|
|
|
// If this is a bump map, make sure that the bump bit depth matches the raster.
|
|
if (seterfFeatures[erfBUMP])
|
|
{
|
|
Assert(prasGetTexture()->iPixelBits == iBUMPMAP_RESOLUTION);
|
|
}
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
uint32 CTexture::u4GetConstColour(int i_ramp, int i_fog) const
|
|
{
|
|
// If there is no raster, use the average pixel value.
|
|
if (iGetNumMipLevels() < 1)
|
|
{
|
|
// If there is a clut for the texture, use it.
|
|
if (ppcePalClut)
|
|
return ppcePalClut->pclutClut->Convert(tpSolid & 0xFF, i_ramp, i_fog);
|
|
|
|
// Default colour.
|
|
return tpSolid;
|
|
}
|
|
|
|
// Switch based on the pixel bit depth.
|
|
if (seterfFeatures[erfBUMP])
|
|
{
|
|
if (ppcePalClut)
|
|
return ppcePalClut->pclutClut->Convert(((CBumpAnglePair&)tpSolid).u1GetColour(), i_ramp, i_fog);
|
|
}
|
|
else switch (prasGetTexture()->iPixelBits)
|
|
{
|
|
case 8:
|
|
if (ppcePalClut)
|
|
return ppcePalClut->pclutClut->Convert(tpSolid & 0xFF, i_ramp, i_fog);
|
|
break;
|
|
}
|
|
|
|
// Default colour.
|
|
return u2FogSolidCol[i_fog];
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::SetTextureName(const char* str_texture_bmp_name)
|
|
{
|
|
#if bTRACK_TEXTURE_USAGE
|
|
|
|
// Delete any existing strings.
|
|
if (strTextureBmpName)
|
|
free(strTextureBmpName);
|
|
|
|
// Do nothing if no string is given.
|
|
if (str_texture_bmp_name == 0)
|
|
return;
|
|
|
|
// Allocate memory and copy the string to the new memory location.
|
|
strTextureBmpName = _strdup(str_texture_bmp_name);
|
|
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
}
|
|
|
|
#if bTRACK_TEXTURE_USAGE
|
|
|
|
//*****************************************************************************************
|
|
//
|
|
void CTexture::CreateTracking
|
|
(
|
|
)
|
|
//
|
|
// Sets up required tracking structures and initializes them.
|
|
//
|
|
//**************************************
|
|
{
|
|
// Create and zero memory for mip use count.
|
|
aiMipUseCount = new int[aprasTextures.uLen];
|
|
memset(aiMipUseCount, 0, sizeof(int) * aprasTextures.uLen);
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
//
|
|
void CTexture::DeleteTracking
|
|
(
|
|
)
|
|
//
|
|
// If a the name exists in the 'strTextureBmpName' member variable, the name and texture
|
|
// usage data will be output to a log. If the log does not exist, this member function
|
|
// will generate one.
|
|
//
|
|
//**************************************
|
|
{
|
|
// Delete any existing strings.
|
|
if (strTextureBmpName)
|
|
free(strTextureBmpName);
|
|
|
|
// Delete count array.
|
|
delete[] aiMipUseCount;
|
|
|
|
// Decrement the texture count, and close the output file if it is zero.
|
|
--iTextureCount;
|
|
}
|
|
|
|
//*****************************************************************************************
|
|
void CTexture::OutputTextureUse(CConsoleBuffer& con) const
|
|
{
|
|
// Dump information about the texture.
|
|
if (strTextureBmpName && aiMipUseCount)
|
|
{
|
|
// Count the total mip use.
|
|
int i_count = 0;
|
|
for (uint u = 0; u < aprasTextures.uLen; ++u)
|
|
{
|
|
i_count += aiMipUseCount[u];
|
|
}
|
|
i_count = Max(i_count, 1);
|
|
|
|
// Print the mip use.
|
|
con.Print
|
|
(
|
|
"\n%s: %ld, %ld\n\n",
|
|
strTextureBmpName,
|
|
aprasTextures.uLen,
|
|
i_count
|
|
);
|
|
for (u = 0; u < aprasTextures.uLen; ++u)
|
|
{
|
|
float f_percent = float(aiMipUseCount[u]) / float(i_count) * 100.0f;
|
|
con.Print
|
|
(
|
|
"\t%ldx%ld\t%1.1f\t%ld\n",
|
|
aprasTextures[u]->iWidth,
|
|
aprasTextures[u]->iHeight,
|
|
f_percent,
|
|
aiMipUseCount[u]
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//AlwaysAssert(0);
|
|
}
|
|
}
|
|
|
|
#endif // bTRACK_TEXTURE_USAGE
|
|
|
|
//*****************************************************************************************
|
|
int CTexture::iGetBestLinkedMipLevel(int i_miplevel) const
|
|
{
|
|
int i_test_level;
|
|
|
|
// Prevent bad requests.
|
|
if (i_miplevel < 0 || i_miplevel >= iGetNumMipLevels())
|
|
i_miplevel = 0;
|
|
|
|
// Is the mip level requested actually available?
|
|
if (aprasTextures[i_miplevel]->prasLink)
|
|
return i_miplevel;
|
|
|
|
// Look for higher resolution mip levels.
|
|
for (i_test_level = i_miplevel - 1; i_test_level >= 0; --i_test_level)
|
|
{
|
|
if (aprasTextures[i_test_level]->prasLink)
|
|
return i_test_level;
|
|
}
|
|
|
|
// Look for lower resolution mip levels.
|
|
for (i_test_level = i_miplevel + 1; i_test_level < iGetNumMipLevels(); ++i_test_level)
|
|
{
|
|
if (aprasTextures[i_test_level]->prasLink)
|
|
return i_test_level;
|
|
}
|
|
|
|
// Return a code indicating failure.
|
|
return -1;
|
|
}
|
|
|
|
|
|
//
|
|
// Implementation of module specific functions.
|
|
//
|
|
|
|
//*********************************************************************************************
|
|
void Average8(rptr<CRaster> pras_dest, rptr<CRaster> pras_source, int i_shift, bool b_trans)
|
|
{
|
|
// Store the pixel format.
|
|
CPixelFormat pxf = pras_source->pxf;
|
|
|
|
// Get the attached palette.
|
|
CColour* aclr = pras_source->pxf.ppalAttached->aclrPalette.atArray;
|
|
|
|
// Obtain the width, height, stride and a pointer to the surface.
|
|
int i_width_source = pras_source->iWidth;
|
|
int i_height_source = pras_source->iHeight;
|
|
int i_stride_source = pras_source->iLinePixels;
|
|
uint8* pu1_source = (uint8*)pras_source->pSurface;
|
|
|
|
// Loop.
|
|
for (int i_dest_x = 0; i_dest_x < pras_dest->iWidth; ++i_dest_x)
|
|
for (int i_dest_y = 0; i_dest_y < pras_dest->iHeight; ++i_dest_y)
|
|
{
|
|
// Insert averaged value.
|
|
uint8 u1 = uAverage8
|
|
(
|
|
pras_source,
|
|
i_dest_x,
|
|
i_dest_y,
|
|
i_shift,
|
|
i_width_source,
|
|
i_height_source,
|
|
i_stride_source,
|
|
pu1_source,
|
|
aclr,
|
|
b_trans
|
|
);
|
|
pras_dest->PutPixel(i_dest_x, i_dest_y, u1);
|
|
}
|
|
}
|
|
|
|
//*********************************************************************************************
|
|
void AverageBump(rptr<CRaster> pras_dest, rptr<CRaster> pras_source, int i_shift, bool b_trans)
|
|
{
|
|
// Store the pixel format.
|
|
CPixelFormat pxf = pras_source->pxf;
|
|
|
|
int i_line_dest = pras_dest->iLinePixels;
|
|
int i_line_source = pras_source->iLinePixels;
|
|
TBumpRes* pbr_dest = (TBumpRes*)pras_dest->pSurface;
|
|
|
|
uint32 i_index_dest;
|
|
|
|
// Loop.
|
|
for (int i_dest_x = 0; i_dest_x < pras_dest->iWidth; ++i_dest_x)
|
|
for (int i_dest_y = 0; i_dest_y < pras_dest->iHeight; ++i_dest_y)
|
|
{
|
|
// Insert averaged value.
|
|
CBumpAnglePair bang = bangAverage(pras_source, i_dest_x, i_dest_y, i_shift, b_trans);
|
|
|
|
// For some reason, put pixel did not work.
|
|
i_index_dest = i_dest_y * i_line_dest + i_dest_x;
|
|
pbr_dest[i_index_dest] = bang.br;
|
|
}
|
|
}
|
|
|
|
//*********************************************************************************************
|
|
void GrowBumpEdges(rptr<CRaster> pras_new)
|
|
{
|
|
CBumpAnglePair bang;
|
|
|
|
//
|
|
// Grow the curved area by 1 pixel in X and Y.
|
|
//
|
|
pras_new->Lock();
|
|
|
|
//
|
|
// PASS 1: Scans the bit map horizontally growing the pixels out by 1.
|
|
//
|
|
for (int y = 0; y < pras_new->iHeight; y++)
|
|
{
|
|
bool b_col = false;
|
|
for (int x = 0; x < pras_new->iWidth; x++)
|
|
{
|
|
// Get pixel.
|
|
bang.br = pras_new->pixGet(x,y);
|
|
|
|
if (b_col)
|
|
{
|
|
if (bang.u1GetColour() == 0)
|
|
{
|
|
// Colour to no colour.
|
|
|
|
// Copy previous pixel to this pixel.
|
|
bang.br = pras_new->pixGet(x-1,y);
|
|
pras_new->PutPixel(x,y,bang.br);
|
|
|
|
b_col = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bang.u1GetColour() != 0)
|
|
{
|
|
// No colour to colour.
|
|
if (x > 0)
|
|
{
|
|
// Copy this pixel to previous pixel.
|
|
pras_new->PutPixel(x-1,y,bang.br);
|
|
}
|
|
|
|
b_col = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// PASS 2: Scans the bit map vertically growing the pixels out by 1.
|
|
//
|
|
for (int x = 0; x < pras_new->iWidth; x++)
|
|
{
|
|
bool b_col = false;
|
|
|
|
for (int y = 0; y < pras_new->iHeight; y++)
|
|
{
|
|
// Get pixel.
|
|
bang.br = pras_new->pixGet(x,y);
|
|
|
|
if (b_col)
|
|
{
|
|
if (bang.u1GetColour() == 0)
|
|
{
|
|
// Colour to no colour.
|
|
|
|
// Copy previous pixel to this pixel.
|
|
bang.br = pras_new->pixGet(x,y-1);
|
|
pras_new->PutPixel(x,y,bang.br);
|
|
|
|
b_col = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (bang.u1GetColour() != 0)
|
|
{
|
|
// No colour to colour.
|
|
if (y > 0)
|
|
{
|
|
// Copy this pixel to previous pixel.
|
|
pras_new->PutPixel(x,y-1,bang.br);
|
|
}
|
|
|
|
b_col = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pras_new->Unlock();
|
|
}
|
|
|
|
|
|
//
|
|
// Static variables.
|
|
//
|
|
float CTexture::fMipmapThreshold = 1.44f; // Default multiplier (higher gives higher resolution textures).
|
|
EMipUse CTexture::emuMipUse = emuNORMAL; // Mipmap use type.
|