1 /* This file is part of the Spring engine (GPL v2 or later), see LICENSE.html */
2
3
4 #include <cctype>
5 #include <set>
6 #include <sstream>
7
8 #include "3DOTextureHandler.h"
9 #include "Rendering/GlobalRendering.h"
10 #include "Rendering/ShadowHandler.h"
11 #include "Rendering/UnitDrawer.h"
12 #include "Rendering/Textures/Bitmap.h"
13 #include "Rendering/Textures/IAtlasAllocator.h"
14 #include "Rendering/Textures/TextureAtlas.h"
15 #include "TAPalette.h"
16 #include "System/Exceptions.h"
17 #include "System/Util.h"
18 #include "System/type2.h"
19 #include "System/FileSystem/FileHandler.h"
20 #include "System/FileSystem/FileSystem.h"
21 #include "System/FileSystem/SimpleParser.h"
22 #include "System/Log/ILog.h"
23
24 //////////////////////////////////////////////////////////////////////
25 // Construction/Destruction
26 //////////////////////////////////////////////////////////////////////
27
28 C3DOTextureHandler* texturehandler3DO = NULL;
29
30 struct TexFile {
31 CBitmap tex; ///< same format as s3o's
32 CBitmap tex2; ///< same format as s3o's
33 std::string name;
34 };
35
36
C3DOTextureHandler()37 C3DOTextureHandler::C3DOTextureHandler()
38 {
39 std::vector<TexFile*> texfiles = LoadTexFiles();
40
41 // TODO: make this use TextureAtlas directly
42 CTextureAtlas atlas;
43 atlas.SetName("3DOModelTextureAtlas");
44
45 IAtlasAllocator* atlasAlloc = atlas.GetAllocator();
46
47 // NOTE: most Intels report maxTextureSize=2048, some even 1024 (!)
48 atlasAlloc->SetNonPowerOfTwo(globalRendering->supportNPOTs);
49 atlasAlloc->SetMaxSize(std::min(globalRendering->maxTextureSize, 2048), std::min(globalRendering->maxTextureSize, 2048));
50
51 // default for 3DO primitives that point to non-existing textures
52 textures["___dummy___"] = UnitTexture(0.0f, 0.0f, 1.0f, 1.0f);
53
54 for (std::vector<TexFile*>::iterator it = texfiles.begin(); it != texfiles.end(); ++it) {
55 atlasAlloc->AddEntry((*it)->name, int2((*it)->tex.xsize, (*it)->tex.ysize));
56 }
57
58 const bool allocated = atlasAlloc->Allocate();
59 const int2 curAtlasSize = atlasAlloc->GetAtlasSize();
60 const int2 maxAtlasSize = atlasAlloc->GetMaxSize();
61
62 if (!allocated) {
63 // either the algorithm simply failed to fit all
64 // textures or the textures would really not fit
65 LOG_L(L_WARNING,
66 "[%s] failed to allocate 3DO texture-atlas memory (size=%dx%d max=%dx%d)",
67 __FUNCTION__,
68 curAtlasSize.x, curAtlasSize.y,
69 maxAtlasSize.x, maxAtlasSize.y
70 );
71 return;
72 } else {
73 assert(curAtlasSize.x <= maxAtlasSize.x);
74 assert(curAtlasSize.y <= maxAtlasSize.y);
75 }
76
77 unsigned char* bigtex1 = new unsigned char[curAtlasSize.x * curAtlasSize.y * 4];
78 unsigned char* bigtex2 = new unsigned char[curAtlasSize.x * curAtlasSize.y * 4];
79
80 for (int a = 0; a < (curAtlasSize.x * curAtlasSize.y); ++a) {
81 bigtex1[a*4 + 0] = 128;
82 bigtex1[a*4 + 1] = 128;
83 bigtex1[a*4 + 2] = 128;
84 bigtex1[a*4 + 3] = 0;
85
86 bigtex2[a*4 + 0] = 0;
87 bigtex2[a*4 + 1] = 128;
88 bigtex2[a*4 + 2] = 0;
89 bigtex2[a*4 + 3] = 255;
90 }
91
92 for (std::vector<TexFile*>::iterator it = texfiles.begin(); it != texfiles.end(); ++it) {
93 CBitmap& curtex1 = (*it)->tex;
94 CBitmap& curtex2 = (*it)->tex2;
95
96 const size_t foundx = atlasAlloc->GetEntry((*it)->name).x;
97 const size_t foundy = atlasAlloc->GetEntry((*it)->name).y;
98 const float4 texCoords = atlasAlloc->GetTexCoords((*it)->name);
99
100 for (int y = 0; y < curtex1.ysize; ++y) {
101 for (int x = 0; x < curtex1.xsize; ++x) {
102 for (int col = 0; col < 4; ++col) {
103 bigtex1[(((foundy + y) * curAtlasSize.x + (foundx + x)) * 4) + col] = curtex1.mem[(((y * curtex1.xsize) + x) * 4) + col];
104 bigtex2[(((foundy + y) * curAtlasSize.x + (foundx + x)) * 4) + col] = curtex2.mem[(((y * curtex1.xsize) + x) * 4) + col];
105 }
106 }
107 }
108
109 textures[(*it)->name] = UnitTexture(texCoords);
110
111 delete (*it);
112 }
113
114 const int maxMipMaps = atlasAlloc->GetMaxMipMaps();
115
116 {
117 glGenTextures(1, &atlas3do1);
118 glBindTexture(GL_TEXTURE_2D, atlas3do1);
119 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
120 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (maxMipMaps > 0) ? GL_LINEAR_MIPMAP_NEAREST : GL_LINEAR);
121 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
122 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
123 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxMipMaps);
124 if (maxMipMaps > 0) {
125 glBuildMipmaps(GL_TEXTURE_2D, GL_RGBA8, curAtlasSize.x, curAtlasSize.y, GL_RGBA, GL_UNSIGNED_BYTE, bigtex1); //FIXME disable texcompression
126 } else {
127 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, curAtlasSize.x, curAtlasSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, bigtex1);
128 }
129 }
130 {
131 glGenTextures(1, &atlas3do2);
132 glBindTexture(GL_TEXTURE_2D, atlas3do2);
133 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
134 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (maxMipMaps > 0) ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
135 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
136 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
137 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, maxMipMaps);
138 if (maxMipMaps > 0) {
139 glBuildMipmaps(GL_TEXTURE_2D, GL_RGBA8, curAtlasSize.x, curAtlasSize.y, GL_RGBA, GL_UNSIGNED_BYTE, bigtex2); //FIXME disable texcompression
140 } else {
141 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, curAtlasSize.x, curAtlasSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, bigtex2);
142 }
143 }
144
145 if (CTextureAtlas::GetDebug()) {
146 CBitmap tex1(bigtex1, curAtlasSize.x, curAtlasSize.y);
147 CBitmap tex2(bigtex2, curAtlasSize.x, curAtlasSize.y);
148
149 tex1.Save(atlas.GetName() + "-1-" + IntToString(curAtlasSize.x) + "x" + IntToString(curAtlasSize.y) + ".png");
150 tex2.Save(atlas.GetName() + "-2-" + IntToString(curAtlasSize.x) + "x" + IntToString(curAtlasSize.y) + ".png");
151 }
152
153 delete[] bigtex1;
154 delete[] bigtex2;
155 }
156
~C3DOTextureHandler()157 C3DOTextureHandler::~C3DOTextureHandler()
158 {
159 glDeleteTextures(1, &atlas3do1);
160 glDeleteTextures(1, &atlas3do2);
161 }
162
163
LoadTexFiles()164 std::vector<TexFile*> C3DOTextureHandler::LoadTexFiles()
165 {
166 CFileHandler teamTexFile("unittextures/tatex/teamtex.txt");
167 CFileHandler paletteFile("unittextures/tatex/palette.pal");
168
169 CSimpleParser parser(teamTexFile);
170
171 std::set<std::string> teamTexes;
172 while (!parser.Eof()) {
173 teamTexes.insert(StringToLower(parser.GetCleanLine()));
174 }
175
176 std::vector<TexFile*> texfiles;
177
178 const std::vector<std::string>& filesBMP = CFileHandler::FindFiles("unittextures/tatex/", "*.bmp");
179 std::vector<std::string> files = CFileHandler::FindFiles("unittextures/tatex/", "*.tga");
180 files.insert(files.end(), filesBMP.begin(), filesBMP.end());
181
182 std::set<string> usedNames;
183 for (std::vector<std::string>::iterator fi = files.begin(); fi != files.end(); ++fi) {
184 const std::string& s = *fi;
185 const std::string s2 = StringToLower(FileSystem::GetBasename(s));
186
187 // avoid duplicate names and give tga images priority
188 if (usedNames.find(s2) != usedNames.end()) {
189 continue;
190 }
191 usedNames.insert(s2);
192
193 if(teamTexes.find(s2) == teamTexes.end()){
194 TexFile* tex = CreateTex(s, s2, false);
195 texfiles.push_back(tex);
196 } else {
197 TexFile* tex = CreateTex(s, s2, true);
198 texfiles.push_back(tex);
199 }
200 }
201
202 if (paletteFile.FileExists()) {
203 palette.Init(paletteFile);
204 }
205
206 for (unsigned a = 0; a < CTAPalette::NUM_PALETTE_ENTRIES; ++a) {
207 TexFile* tex = new TexFile();
208 tex->name = "ta_color" + IntToString(a, "%i");
209 tex->tex.Alloc(1, 1);
210 tex->tex.mem[0] = palette[a][0];
211 tex->tex.mem[1] = palette[a][1];
212 tex->tex.mem[2] = palette[a][2];
213 tex->tex.mem[3] = 0; // teamcolor
214
215 tex->tex2.Alloc(1, 1);
216 tex->tex2.mem[0] = 0; // self illum
217 tex->tex2.mem[1] = 30; // reflectivity
218 tex->tex2.mem[2] = 0;
219 tex->tex2.mem[3] = 255;
220
221 texfiles.push_back(tex);
222 }
223
224 return texfiles;
225 }
226
227
Get3DOTexture(const std::string & name)228 C3DOTextureHandler::UnitTexture* C3DOTextureHandler::Get3DOTexture(const std::string& name)
229 {
230 std::map<std::string, UnitTexture>::iterator tti;
231 if ((tti = textures.find(name)) != textures.end()) {
232 return &tti->second;
233 }
234
235 // unknown texture
236 return NULL;
237 }
238
Set3doAtlases() const239 void C3DOTextureHandler::Set3doAtlases() const
240 {
241 glActiveTexture(GL_TEXTURE1);
242 glBindTexture(GL_TEXTURE_2D, atlas3do2);
243 glActiveTexture(GL_TEXTURE0);
244 glBindTexture(GL_TEXTURE_2D, atlas3do1);
245 }
246
CreateTex(const std::string & name,const std::string & name2,bool teamcolor)247 TexFile* C3DOTextureHandler::CreateTex(const std::string& name, const std::string& name2, bool teamcolor)
248 {
249 TexFile* tex = new TexFile;
250 tex->tex.Load(name, 30);
251 tex->name = name2;
252
253 tex->tex2.Alloc(tex->tex.xsize, tex->tex.ysize);
254
255 CBitmap* tex1 = &tex->tex;
256 CBitmap* tex2 = &tex->tex2;
257
258 for (int a = 0; a < (tex1->ysize * tex1->xsize); ++a) {
259 tex2->mem[a*4 + 0] = 0;
260 tex2->mem[a*4 + 1] = tex1->mem[a*4 + 3]; // move reflectivity to texture2
261 tex2->mem[a*4 + 2] = 0;
262 tex2->mem[a*4 + 3] = 255;
263
264 tex1->mem[a*4 + 3] = 0;
265
266 if (teamcolor) {
267 //purple = teamcolor
268 if ((tex1->mem[a*4] == tex1->mem[a*4 + 2]) && (tex1->mem[a*4+1] == 0)) {
269 unsigned char lum = tex1->mem[a*4];
270 tex1->mem[a*4 + 0] = 0;
271 tex1->mem[a*4 + 1] = 0;
272 tex1->mem[a*4 + 2] = 0;
273 tex1->mem[a*4 + 3] = (unsigned char)(std::min(255.0f, lum * 1.5f));
274 }
275 }
276 }
277
278 return tex;
279 }
280
281