/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** Confidential - Westwood Studios *** *********************************************************************************************** * * * Project Name : LightMap * * * * $Archive:: /Commando/Code/Tool $* * * * $Author:: Ian_l $* * * * $Modtime:: 6/06/01 5:40p $* * * * $Revision:: 35 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #ifndef _LIGHTMAPPACKER_H #define _LIGHTMAPPACKER_H // Includes. #include "ProceduralTexture.h" #include "StringBuilder.h" #include "Triangle.h" #include "vector.h" #include "vector2.h" #include "matrix3.h" #include "listnode.h" #include "Targa.h" #include "w3d_file.h" #include #include // Defines. #define UNPACKED_TEXEL_BYTE_COUNT 4 // No. of bytes for internal unpacked texel format (ARGB). class ColorSurface : public srColorSurface { public: ColorSurface (srPixelConvert::e_surfaceType pixelformat, short width, short height) : srColorSurface (pixelformat, width, height) {} }; class TrueColorTarga : public Targa { public: // Public functions. TrueColorTarga(); TrueColorTarga (unsigned width, unsigned height, unsigned pixeldepth); TrueColorTarga (unsigned width, unsigned height, unsigned pixeldepth, const W3dRGBStruct &clearcolor); void Reformat (unsigned width, unsigned height, unsigned pixeldepth); void Reformat (unsigned width, unsigned height, unsigned pixeldepth, const W3dRGBStruct &clearcolor); unsigned Width() const {return (Header.Width);} unsigned Height() const {return (Header.Height);} unsigned Pixel_Depth() const {return (Header.PixelDepth);} void Scale (unsigned width, unsigned height) {Scale (*this, width, height);} void Scale (TrueColorTarga &destination) {Scale (destination, destination.Width(), destination.Height());} void Scale (float filtererror) {Scale (*this, filtererror);} void Transpose() {Transpose (*this);} void Pad (unsigned padwidth, unsigned padheight, const W3dRGBStruct &padcolor, DynamicVectorClass &triangleptrs) { Pad (*this, padwidth, padheight, padcolor, triangleptrs); } char *Load (const char *pathname); char *Save (const char *pathname); void Blit (TrueColorTarga &destination, unsigned x, unsigned y); void Scale (TrueColorTarga &destination, unsigned width, unsigned height); int Compare (TrueColorTarga &comparison, float epsilon); int Compare (TrueColorTarga &comparison, unsigned x, unsigned y, float epsilon); bool Fill (const W3dRGBStruct &fillcolor); void Add (TrueColorTarga &targa); void Rasterize (TrueColorTarga &destination, const W3dRGBStruct &fillcolor, unsigned vertexcount, const W3dRGBStruct *vertexcolors, Vector2 *uvs); bool Get_Color (const Vector2 &t, W3dRGBStruct &color) const; bool Set_Color (unsigned x, unsigned y, const W3dRGBStruct &color); protected: enum TransposeEnum { UNTRANSPOSED, TRANSPOSED, TRANSPOSE_COUNT }; struct UnpackedTexelStruct { UnpackedTexelStruct () {} UnpackedTexelStruct (unsigned char r, unsigned char g, unsigned char b, unsigned char a) { Set (r, g, b, a); } void Set (unsigned char r, unsigned char g, unsigned char b, unsigned char a) { Byte [0] = a; Byte [1] = r; Byte [2] = g; Byte [3] = b; } unsigned char Alpha() const {return (Byte [0]);} unsigned char Red() const {return (Byte [1]);} unsigned char Green() const {return (Byte [2]);} unsigned char Blue() const {return (Byte [3]);} // Equality operator. bool operator == (const UnpackedTexelStruct &t) { ASSERT (sizeof (unsigned long) == sizeof (Byte)); return (*((unsigned long*) Byte) == *((unsigned long*) t.Byte)); } // Inequality operator. bool operator != (const UnpackedTexelStruct &t) { return (!(*this == t)); } // Addition operator. UnpackedTexelStruct operator += (UnpackedTexelStruct t) { Byte [0] = MIN (((unsigned) Byte [0]) + ((unsigned) t.Byte [0]), (unsigned) UCHAR_MAX); Byte [1] = MIN (((unsigned) Byte [1]) + ((unsigned) t.Byte [1]), (unsigned) UCHAR_MAX); Byte [2] = MIN (((unsigned) Byte [2]) + ((unsigned) t.Byte [2]), (unsigned) UCHAR_MAX); Byte [3] = MIN (((unsigned) Byte [3]) + ((unsigned) t.Byte [3]), (unsigned) UCHAR_MAX); return (*this); } // Public data. unsigned char Byte [UNPACKED_TEXEL_BYTE_COUNT]; }; struct PointStruct { // Equality operator. bool operator == (const PointStruct &p) { return ((X == p.X) && (Y == p.Y)); } // Inequality operator. bool operator != (const PointStruct &p) { return (!(*this == p)); } // Public data. int X; int Y; }; void Clear (const UnpackedTexelStruct &cleartexel); private: // Inlines. unsigned char *Get_Texel (int x, int y) const; UnpackedTexelStruct *Unpack_Texel (unsigned char *packedtexelptr, unsigned packedbytespertexel, UnpackedTexelStruct &unpackedtexel) const; void Pack_Texel (const UnpackedTexelStruct &unpackedtexel, unsigned char *packedtexelptr, unsigned packedbytespertexel); // Static functions. static srPixelConvert::e_surfaceType Pixel_Format (unsigned pixeldepth); void Scale (TrueColorTarga &destination, float filtererror); void Transpose (TrueColorTarga &destination); bool Fill_Four_Connected (unsigned x, unsigned y, const UnpackedTexelStruct &filltexel, DynamicVectorClass &fourconnectedarray); void Pad (TrueColorTarga &destination, unsigned verticalpadthickness, unsigned horizontalpadthickness, const W3dRGBStruct &padcolor, DynamicVectorClass &triangleptrs); }; class Page : public TrueColorTarga { public: // Public functions. Page (unsigned bitdepth, const W3dRGBStruct &clearcolor); ~Page (); bool Pack (TrueColorTarga &targa, float epsilon, DynamicVectorClass &triangleptrs); float Packing_Efficiency() const { return (((float) UsedTexelCount) / (((unsigned) Width()) * ((unsigned) Height()))); } float Replica_Efficiency() const { return (((float) ReplicaTexelCount) / (((unsigned) Width()) * ((unsigned) Height()))); } private: class Region : public GenericNode { public: // Public functions. Region () {} Region (const Region ®ion) { Set (region.X0, region.Y0, region.X1, region.Y1); } unsigned Width () {return (X1 - X0 + 1);} unsigned Height() {return (Y1 - Y0 + 1);} void Set (unsigned x0, unsigned y0, unsigned x1, unsigned y1) { X0 = x0; Y0 = y0; X1 = x1; Y1 = y1; } bool Accomodates (unsigned width, unsigned height, Region &targetregion); bool Intersects (const Region &targetregion, unsigned &subregioncount, Region **subregions); // Public data. unsigned X0, Y0; unsigned X1, Y1; }; // Private functions. bool Replica_Region (TrueColorTarga &targa, float epsilon, Region &replicaregion); bool Lowest_Cost_Region (TrueColorTarga &targa, unsigned &lowestcost, Region &lowestcostregion); void Insert_Region (const Region &insertionregion); bool Contains (const Region &testregion); // Statistics functions. void Reset_Statistics(); // Private data. GenericList VacantRegionList; GenericList UsedRegionList; // Statistics counters. unsigned AssetCount; unsigned UsedTexelCount; unsigned ReplicaTexelCount; }; class TargaLoader { public: // Public functions. TargaLoader (float samplerate, W3dRGBStruct fillcolor); ~TargaLoader(); TrueColorTarga *Load (const Triangle &triangle); private: struct TargaCacheStruct { TargaCacheStruct() { Ptr = NULL; } ~TargaCacheStruct() { if (Ptr != NULL) delete Ptr; } // Equality operator. bool operator == (const TargaCacheStruct &t) { return ((ID == t.ID) && (Ptr == t.Ptr)); } // Inequality operator. bool operator != (const TargaCacheStruct &t) { return (!(*this == t)); } // Data. unsigned ID; TrueColorTarga *Ptr; }; // Private functions. TargaLoader() {} unsigned Index (unsigned id) { return (id % ((unsigned) Cache.Length())); } // Private data. VectorClass Cache; // A simple array of targacache objects. DynamicVectorClass CleanupList; // List of targas that have been displaced from the cache but cannot // yet be deleted because they may still be referenced. float SampleRate; // No. of texels per unit length. W3dRGBStruct FillColor; // Color to replace during fill operations. }; class TrianglePacker { public: TrianglePacker (const PackingTriangle *principaltriangleptr, const DynamicVectorClass &adjtriangles, unsigned groupid, unsigned edgeblendthickness, float samplerate, W3dRGBStruct fillcolor); DynamicVectorClass &Principal_Triangles() {return (PrincipalTriangles);} // Query functions. unsigned Width() const {return (UpperBound.U - LowerBound.U);} unsigned Height() const {return (UpperBound.V - LowerBound.V);} // Operations. TrianglePacker *Merge (const TrianglePacker &trianglepacker); void Rasterize (TargaLoader &targaloader, ProceduralTexture *proceduraltexture, TrueColorTarga &rasterizedtarga); // Statistics. unsigned Adjacent_Face_Count() const { return (AdjacentTriangles [ADJACENT_PROJECTION_COMMON].Count() + AdjacentTriangles [ADJACENT_PROJECTION_VALID].Count() + AdjacentTriangles [ADJACENT_PROJECTION_NONE].Count()); } unsigned Blended_Face_Count() const { return (AdjacentTriangles [ADJACENT_PROJECTION_COMMON].Count() + AdjacentTriangles [ADJACENT_PROJECTION_VALID].Count()); } float Edge_Blend_Area() const; private: enum ProjectionEnum { PROJECTION_Y_POSITIVE, PROJECTION_Y_NEGATIVE, PROJECTION_Z_POSITIVE, PROJECTION_Z_NEGATIVE, PROJECTION_X_POSITIVE, PROJECTION_X_NEGATIVE, PROJECTION_COUNT }; enum AdjacentProjectionEnum { ADJACENT_PROJECTION_COMMON, // Adjacent triangles that use the same projection as this object. ADJACENT_PROJECTION_VALID, // Adjacent triangles that map to a different projection but have non-zero area if object's projection is used. ADJACENT_PROJECTION_NONE, // Adjacent triangles that cannot be projected using object's projection. ADJACENT_PROJECTION_COUNT }; class ProjectionTriangle { public: // Public functions. ProjectionTriangle() {} ProjectionTriangle (const Vector3 *points, const Vector2 *sourceuvs, const TrueColorTarga *sourcetargaptr, const Vector2 *projectionuvs); // Public data. Vector3 Points [Triangle::VERTICES_COUNT]; Vector2 SourceUVs [Triangle::VERTICES_COUNT]; TrueColorTarga *SourceTargaPtr; Vector2 ProjectionUVs [Triangle::VERTICES_COUNT]; }; class SampleSurface { public: // Public functions. SampleSurface (unsigned width, unsigned height, ProceduralTexture *blendtexture = NULL); bool Sample (const Vector2 &samplepoint, const ProjectionTriangle &projectiontriangle, unsigned priority); bool Sample (float alpha, float beta, const ProjectionTriangle &projectiontriangle, unsigned priority); bool Get_Color (unsigned x, unsigned y, W3dRGBStruct &color) { SampleStruct *sampleptr; ASSERT ((x < Width) && (y < Height)); sampleptr = Surface + (y * Width + x); if (sampleptr->Count > 0) { float oocount; unsigned r, g, b; oocount = 1.0f / sampleptr->Count; r = sampleptr->Red * oocount; g = sampleptr->Green * oocount; b = sampleptr->Blue * oocount; color.Set ((uint8) MIN (r, UCHAR_MAX), (uint8) MIN (g, UCHAR_MAX), (uint8) MIN (b, UCHAR_MAX)); return (true); } else { return (false); } } unsigned Priority (unsigned x, unsigned y) { SampleStruct *sampleptr; ASSERT ((x < Width) && (y < Height)); sampleptr = Surface + (y * Width + x); return (sampleptr->Priority); } float Sampling_Ratio() {return (((float) SampledTexelCount) / ((float) (Width * Height)));} private: struct SampleStruct { unsigned Red; unsigned Green; unsigned Blue; unsigned Count; unsigned Priority; }; SampleSurface() {} unsigned Width; unsigned Height; unsigned SampledTexelCount; SampleStruct *Surface; ProceduralTexture *BlendTexture; }; // Private functions. TrianglePacker() {} TrianglePacker (const TrianglePacker &trianglepacker); // Copy constructor. void Set_Bounds(); bool Can_Project (const Vector3 &normal); Vector2 Project (const Vector3 &point) const; // void Get_Projection_Triangle (const Triangle &triangle, ProjectionTriangleStruct &projectiontriangle); bool Sample (const Vector2 &samplepoint, const ProjectionTriangle &projectiontriangle, W3dRGBStruct &color); static ProjectionEnum Get_Projection (const Vector3 &normal); // Private data. ProjectionEnum Projection; // Texture projection to be used for rasterization. unsigned GroupID; unsigned EdgeBlendThickness; float SampleRate; // No. of texels per unit length. W3dRGBStruct FillColor; // Color to replace during fill operations. DynamicVectorClass PrincipalTriangles; // Set of triangles that will be rasterized in their entirety (ie. not edge blended). DynamicVectorClass AdjacentTriangles [ADJACENT_PROJECTION_COUNT]; // Set of triangles that will be edge-blended. Vector2 LowerBound; // Lower bound of all principal triangle points projected onto the texel plane. Vector2 UpperBound; // Upper bound of all principal triangle points projected onto the texel plane. }; class LightmapPacker { public: enum ThicknessEnum { EDGE_BLEND_THICKNESS = 2 // No. of texels to pad around every packed map. // NOTE: The edge blend thickness is designed to prevent // bleeding between adjacent maps under mip-mapping. The // no, of allowable mip-maps is dependant upon this // thickness value. }; enum StatisticsEnum { STATISTICS_PAGE_FORMAT, STATISTICS_LIGHTMAPS_PROCESSED, STATISTICS_ADJACENT_FACE_BLEND_PERCENTAGE, STATISTICS_EDGE_BLEND_EFFICIENCY, STATISTICS_SCALING_EFFICIENCY, STATISTICS_PAGES_CREATED, STATISTICS_PACKING_EFFICIENCY, STATISTICS_CULLING_EFFICIENCY, STATISTICS_TEXTURE_SWAP_EFFICIENCY, STATISTICS_OVERSIZE_LIGHTMAPS, STATISTICS_COUNT, STATISTICS_STRING_SIZE = 32 // Size of statistic string in bytes. }; LightmapPacker(); Finish(); ~LightmapPacker(); void Submit (PackingTriangle *principaltriangleptr, const DynamicVectorClass &adjtriangles); TrianglePacker *Merge (TrianglePacker *trianglepackerptr); void Pack (ProceduralTexture *proceduraltexture = NULL); void Pack (TrueColorTarga &targa, DynamicVectorClass &triangleptrs); static const char *Lightmap_Pathname (unsigned pageindex); static const char *Asset_Directory(); static const char *Asset_Directory (const char *filename); static void Delete_Assets(); static void Copy_Assets (const char *pathname); static const char *Get_Statistic (unsigned index) {ASSERT (index < STATISTICS_COUNT); return (_Statistics [index]);} private: // Statistics functions. void Reset_Statistics(); void Update_Statistics (const Page &page); void Update_Statistics (const TrianglePacker &trianglepacker); void Collate_Statistics(); W3dRGBStruct FillColor; // Color used to pad unused texels in lightmaps. float ScaleFactor; // Global lightmap scaling factor (0.0...1.0). float FilterError; // Maximum allowed variance between a color component of original // lightmap and that of packed lightmap, expressed as a fraction // of color component's total range ie. a setting of 0.0 will // allow no variance (packed lightmap must match original) and // a setting of 1.0 allows complete variance. float SampleRate; // Sample rate used by rasterizers (such as Edge_Blend()) in texels per unit length. unsigned PageBitDepth; // Bit depth of page texels. int CurrentPageIndex; // Current page (-1 indicates no page exists). DynamicVectorClass TrianglePackerPtrs; DynamicVectorClass PagePtrs; TextureNameNode *PlaceholderTextureNameNodePtr; // Statistics counters. unsigned FaceCount; unsigned LightmapCount; unsigned AdjacentFaceCount; unsigned BlendedFaceCount; double EdgeBlendAreaSum; __int64 UnscaledTexelCount; __int64 ScaledTexelCount; double PackingEfficiencySum; double ReplicaEfficiencySum; unsigned TextureSwapCount; unsigned OversizeCount; unsigned AllFillColorCount; static unsigned _BasePageIndex; static char _Statistics [STATISTICS_COUNT][STATISTICS_STRING_SIZE]; }; /*********************************************************************************************** * TrueColorTarga::Get_Color -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/27/00 IML : Created. * *=============================================================================================*/ inline bool TrueColorTarga::Get_Color (const Vector2 &t, W3dRGBStruct &color) const { int x, y; unsigned char *texelptr; UnpackedTexelStruct unpackedtexel; x = t.U * Width(); y = t.V * Height(); texelptr = Get_Texel (x, y); if (texelptr == NULL) return (false); Unpack_Texel (texelptr, TGA_BytesPerPixel (Pixel_Depth()), unpackedtexel); color.Set (unpackedtexel.Red(), unpackedtexel.Green(), unpackedtexel.Blue()); return (true); } /*********************************************************************************************** * TrueColorTarga::Set_Color -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 06/27/00 IML : Created. * *=============================================================================================*/ inline bool TrueColorTarga::Set_Color (unsigned x, unsigned y, const W3dRGBStruct &color) { UnpackedTexelStruct unpackedtexel (color.R, color.G, color.B, 0); unsigned char *texelptr; texelptr = Get_Texel (x, y); if (texelptr != NULL) { Pack_Texel (unpackedtexel, texelptr, TGA_BytesPerPixel (Pixel_Depth())); return (true); } else { return (false); } } /*********************************************************************************************** * TrueColorTarga::Get_Texel -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/27/99 IML : Created. * *=============================================================================================*/ inline unsigned char *TrueColorTarga::Get_Texel (int x, int y) const { // Check for (x, y) out of range. if ((x < 0) || (x >= ((int) Width ()))) return (NULL); if ((y < 0) || (y >= ((int) Height()))) return (NULL); return ((unsigned char*) GetImage()) + (((Width() * y) + x) * TGA_BytesPerPixel (Pixel_Depth())); } /*********************************************************************************************** * TrueColorTarga::Unpack_Texel -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/27/99 IML : Created. * *=============================================================================================*/ inline TrueColorTarga::UnpackedTexelStruct *TrueColorTarga::Unpack_Texel (unsigned char *packedtexelptr, unsigned packedbytespertexel, UnpackedTexelStruct &unpackedtexel) const { switch (packedbytespertexel) { case 4: // Packed format same as unpacked format. ASSERT (sizeof (UnpackedTexelStruct) == 4); unpackedtexel = *((UnpackedTexelStruct*) packedtexelptr); break; case 3: // Unpack to ordering ARGB. Set Alpha to zero. unpackedtexel.Byte [0] = 0; // Alpha unpackedtexel.Byte [1] = *(packedtexelptr + 0); // Red unpackedtexel.Byte [2] = *(packedtexelptr + 1); // Green unpackedtexel.Byte [3] = *(packedtexelptr + 2); // Blue break; case 2: { static unsigned char _alpha [2] = {0x00, 0xff}; unsigned short packedtexel = *((unsigned short*) packedtexelptr); // Unpack to ordering ARGB. Bit replicate Alpha. unpackedtexel.Byte [0] = _alpha [packedtexel >> 15]; // Alpha unpackedtexel.Byte [1] = ((packedtexel & 0x7c00) >> 7) | 0x3; // Red unpackedtexel.Byte [2] = ((packedtexel & 0x03e0) >> 2) | 0x3; // Green unpackedtexel.Byte [3] = ((packedtexel & 0x001f) << 3) | 0x3; // Blue } break; default: // Unrecognized byte count. ASSERT (0); break; } return (&unpackedtexel); } /*********************************************************************************************** * TrueColorTarga::Pack_Texel -- * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 9/27/99 IML : Created. * *=============================================================================================*/ inline void TrueColorTarga::Pack_Texel (const UnpackedTexelStruct &unpackedtexel, unsigned char *packedtexelptr, unsigned packedbytespertexel) { switch (packedbytespertexel) { case 4: *(packedtexelptr + 0) = unpackedtexel.Byte [0]; *(packedtexelptr + 1) = unpackedtexel.Byte [1]; *(packedtexelptr + 2) = unpackedtexel.Byte [2]; *(packedtexelptr + 3) = unpackedtexel.Byte [3]; break; case 3: // Pack to ordering RGB. Lose Alpha. *(packedtexelptr + 0) = unpackedtexel.Byte [1]; *(packedtexelptr + 1) = unpackedtexel.Byte [2]; *(packedtexelptr + 2) = unpackedtexel.Byte [3]; break; case 2: { unsigned a, r, g, b; // Pack to ordering ARGB. Round Alpha, Red, Green, Blue. a = unpackedtexel.Byte [0] >> 7; r = MIN (0x1f, (unpackedtexel.Byte [1] >> 3) + ((unpackedtexel.Byte [1] & 0x4) >> 2)); g = MIN (0x1f, (unpackedtexel.Byte [2] >> 3) + ((unpackedtexel.Byte [2] & 0x4) >> 2)); b = MIN (0x1f, (unpackedtexel.Byte [3] >> 3) + ((unpackedtexel.Byte [3] & 0x4) >> 2)); *((unsigned short*) packedtexelptr) = ((a << 15) | (r << 10) | (g << 5) | b); } break; default: // Unrecognized byte count. ASSERT (0); break; } } #endif // LIGHTMAPPACKER_H