forked from nillerusr/source-engine
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimagepacker.cpp
169 lines (143 loc) · 4.03 KB
/
imagepacker.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
//=====================================================================================//
#include "imagepacker.h"
#include "materialsystem_global.h"
#include "IHardwareConfigInternal.h"
// NOTE: This has to be the last file included
#include "tier0/memdbgon.h"
float CImagePacker::GetEfficiency( void )
{
return ( float )m_AreaUsed / ( float )( m_MaxLightmapWidth * CeilPow2( m_MinimumHeight ) );
}
bool CImagePacker::Reset( int nSortId, int maxLightmapWidth, int maxLightmapHeight )
{
int i;
Assert( maxLightmapWidth <= MAX_MAX_LIGHTMAP_WIDTH );
m_MaxLightmapWidth = maxLightmapWidth;
m_MaxLightmapHeight = maxLightmapHeight;
m_MaxBlockWidth = maxLightmapWidth + 1;
m_MaxBlockHeight = maxLightmapHeight + 1;
m_nSortID = nSortId;
m_AreaUsed = 0;
m_MinimumHeight = -1;
for( i = 0; i < m_MaxLightmapWidth; i++ )
{
m_pLightmapWavefront[i] = -1;
}
return true;
}
inline int CImagePacker::GetMaxYIndex( int firstX, int width )
{
int maxY = -1;
int maxYIndex = 0;
for( int x = firstX; x < firstX + width; ++x )
{
// NOTE: Want the equals here since we'll never be able to fit
// in between the multiple instances of maxY
if( m_pLightmapWavefront[x] >= maxY )
{
maxY = m_pLightmapWavefront[x];
maxYIndex = x;
}
}
return maxYIndex;
}
//#define ADD_ONE_TEXEL_BORDER
bool CImagePacker::AddBlock( int width, int height,
int *returnX, int *returnY )
{
#ifdef ADD_ONE_TEXEL_BORDER
width += 2;
height += 2;
width = clamp( width, m_MaxLightmapWidth );
height = clamp( height, m_MaxLightmapHeight );
#endif
// If we've already determined that a block this big couldn't fit
// then blow off checking again...
if ( ( width >= m_MaxBlockWidth ) && ( height >= m_MaxBlockHeight ) )
return false;
int bestX = -1;
int maxYIdx;
int outerX = 0;
int outerMinY = m_MaxLightmapHeight;
int lastX = m_MaxLightmapWidth - width;
int lastMaxYVal = -2;
while (outerX <= lastX)
{
// Skip all tiles that have the last Y value, these
// aren't going to change our min Y value
if (m_pLightmapWavefront[outerX] == lastMaxYVal)
{
++outerX;
continue;
}
maxYIdx = GetMaxYIndex( outerX, width );
lastMaxYVal = m_pLightmapWavefront[maxYIdx];
if (outerMinY > lastMaxYVal)
{
outerMinY = lastMaxYVal;
bestX = outerX;
// Early out for the first row...
// if (outerMinY == -1)
// break;
}
outerX = maxYIdx + 1;
}
if( bestX == -1 )
{
// If we failed to add it, remember the block size that failed
// *only if both dimensions are smaller*!!
// Just because a 1x10 block failed, doesn't mean a 10x1 block will fail
if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
{
m_MaxBlockWidth = width;
m_MaxBlockHeight = height;
}
return false;
}
// Set the return positions for the block.
*returnX = bestX;
*returnY = outerMinY + 1;
// Check if it actually fit height-wise.
// hack
// if( *returnY + height > maxLightmapHeight )
if( *returnY + height >= m_MaxLightmapHeight - 1 )
{
if ( ( width <= m_MaxBlockWidth ) && ( height <= m_MaxBlockHeight ) )
{
m_MaxBlockWidth = width;
m_MaxBlockHeight = height;
}
return false;
}
// It fit!
// Keep up with the smallest possible size for the image so far.
if( *returnY + height > m_MinimumHeight )
m_MinimumHeight = *returnY + height;
// Update the wavefront info.
int x;
for( x = bestX; x < bestX + width; x++ )
{
m_pLightmapWavefront[x] = outerMinY + height;
}
// AddBlockToLightmapImage( *returnX, *returnY, width, height );
m_AreaUsed += width * height;
#ifdef ADD_ONE_TEXEL_BORDER
*returnX++;
*returnY++;
#endif
return true;
}
void CImagePacker::GetMinimumDimensions( int *pReturnWidth, int *pReturnHeight )
{
*pReturnWidth = CeilPow2( m_MaxLightmapWidth );
*pReturnHeight = CeilPow2( m_MinimumHeight );
int aspect = *pReturnWidth / *pReturnHeight;
if (aspect > HardwareConfig()->MaxTextureAspectRatio())
{
*pReturnHeight = *pReturnWidth / HardwareConfig()->MaxTextureAspectRatio();
}
}