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