1.0.2対応
改行コード修正
@@ -1,63 +1,63 @@ | ||
1 | -#ifndef _RASTERIZER_CLASS_ | |
2 | -#define _RASTERIZER_CLASS_ | |
3 | -/* | |
4 | - | |
5 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - Rasterizer Implementation Base Class | |
23 | - by Loren Petrich, | |
24 | - August 7, 2000 | |
25 | - | |
26 | - To be subclassed for specific rasterizers (software, OpenGL, etc.) | |
27 | -*/ | |
28 | - | |
29 | -#include "render.h" | |
30 | -#ifdef HAVE_OPENGL | |
31 | -#include "OGL_Render.h" | |
32 | -#endif | |
33 | - | |
34 | - | |
35 | -class RasterizerClass | |
36 | -{ | |
37 | -public: | |
38 | - | |
39 | - // Sets the rasterizer's view data; | |
40 | - // be sure to call it before doing any rendering | |
41 | - virtual void SetView(view_data& View) {} | |
42 | - | |
43 | - // Sets the rasterizer so that it will start rendering foreground objects | |
44 | - // like weapons in hand | |
45 | - virtual void SetForeground() {} | |
46 | - | |
47 | - // Sets the view of a foreground object; | |
48 | - // parameter is whether it is horizontally reflected | |
49 | - virtual void SetForegroundView(bool HorizReflect) {} | |
50 | - | |
51 | - // Rendering calls | |
52 | - virtual void Begin() {} | |
53 | - virtual void End() {} | |
54 | - | |
55 | - virtual void texture_horizontal_polygon(polygon_definition& textured_polygon) {} | |
56 | - | |
57 | - virtual void texture_vertical_polygon(polygon_definition& textured_polygon) {} | |
58 | - | |
59 | - virtual void texture_rectangle(rectangle_definition& textured_rectangle) {} | |
60 | -}; | |
61 | - | |
62 | - | |
63 | -#endif | |
1 | +#ifndef _RASTERIZER_CLASS_ | |
2 | +#define _RASTERIZER_CLASS_ | |
3 | +/* | |
4 | + | |
5 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + Rasterizer Implementation Base Class | |
23 | + by Loren Petrich, | |
24 | + August 7, 2000 | |
25 | + | |
26 | + To be subclassed for specific rasterizers (software, OpenGL, etc.) | |
27 | +*/ | |
28 | + | |
29 | +#include "render.h" | |
30 | +#ifdef HAVE_OPENGL | |
31 | +#include "OGL_Render.h" | |
32 | +#endif | |
33 | + | |
34 | + | |
35 | +class RasterizerClass | |
36 | +{ | |
37 | +public: | |
38 | + | |
39 | + // Sets the rasterizer's view data; | |
40 | + // be sure to call it before doing any rendering | |
41 | + virtual void SetView(view_data& View) {} | |
42 | + | |
43 | + // Sets the rasterizer so that it will start rendering foreground objects | |
44 | + // like weapons in hand | |
45 | + virtual void SetForeground() {} | |
46 | + | |
47 | + // Sets the view of a foreground object; | |
48 | + // parameter is whether it is horizontally reflected | |
49 | + virtual void SetForegroundView(bool HorizReflect) {} | |
50 | + | |
51 | + // Rendering calls | |
52 | + virtual void Begin() {} | |
53 | + virtual void End() {} | |
54 | + | |
55 | + virtual void texture_horizontal_polygon(polygon_definition& textured_polygon) {} | |
56 | + | |
57 | + virtual void texture_vertical_polygon(polygon_definition& textured_polygon) {} | |
58 | + | |
59 | + virtual void texture_rectangle(rectangle_definition& textured_rectangle) {} | |
60 | +}; | |
61 | + | |
62 | + | |
63 | +#endif |
@@ -1,489 +1,495 @@ | ||
1 | -/* | |
2 | - | |
3 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | - OpenGL Substitute-Texture-Definition File | |
21 | - by Loren Petrich, | |
22 | - March 12, 2000 | |
23 | - | |
24 | - This contains implementations of functions for handling | |
25 | - the OpenGL substitute textures for the walls and the sprites | |
26 | -*/ | |
27 | - | |
28 | -#include "cseries.h" | |
29 | -#include "OGL_Subst_Texture_Def.h" | |
30 | -#include "Logging.h" | |
31 | - | |
32 | -#include <set> | |
33 | -#include <string> | |
34 | -#include <boost/unordered_map.hpp> | |
35 | - | |
36 | -#ifdef HAVE_OPENGL | |
37 | - | |
38 | -// Texture-options stuff; | |
39 | -// defaults for whatever might need them | |
40 | -static OGL_TextureOptions DefaultTextureOptions; | |
41 | - | |
42 | -typedef std::pair<short, short> TOKey; | |
43 | -typedef boost::unordered_map<TOKey, OGL_TextureOptions> TOHash; | |
44 | -static TOHash Collections[NUMBER_OF_COLLECTIONS]; | |
45 | - | |
46 | -// Deletes a collection's texture-options sequences | |
47 | -void TODelete(short Collection) | |
48 | -{ | |
49 | - Collections[Collection].clear(); | |
50 | -} | |
51 | - | |
52 | -// Deletes all of them | |
53 | -static void TODelete_All() | |
54 | -{ | |
55 | - for (int c = 0; c < NUMBER_OF_COLLECTIONS; c++) TODelete(c); | |
56 | -} | |
57 | - | |
58 | -int OGL_CountTextures(short Collection) | |
59 | -{ | |
60 | - return Collections[Collection].size(); | |
61 | -} | |
62 | - | |
63 | -extern void OGL_ProgressCallback(int); | |
64 | - | |
65 | -void OGL_LoadTextures(short Collection) | |
66 | -{ | |
67 | - | |
68 | - for (TOHash::iterator it = Collections[Collection].begin(); it != Collections[Collection].end(); ++it) | |
69 | - { | |
70 | - it->second.Load(); | |
71 | - OGL_ProgressCallback(1); | |
72 | - | |
73 | - } | |
74 | -} | |
75 | - | |
76 | - | |
77 | -void OGL_UnloadTextures(short Collection) | |
78 | -{ | |
79 | - for (TOHash::iterator it = Collections[Collection].begin(); it != Collections[Collection].end(); ++it) | |
80 | - { | |
81 | - it->second.Unload(); | |
82 | - } | |
83 | -} | |
84 | - | |
85 | - | |
86 | -OGL_TextureOptions *OGL_GetTextureOptions(short Collection, short CLUT, short Bitmap) | |
87 | -{ | |
88 | - TOHash::iterator it = Collections[Collection].find(TOKey(CLUT, Bitmap)); | |
89 | - if (it != Collections[Collection].end()) | |
90 | - { | |
91 | - return &it->second; | |
92 | - } | |
93 | - | |
94 | - it = Collections[Collection].find(TOKey(ALL_CLUTS, Bitmap)); | |
95 | - if (it != Collections[Collection].end()) | |
96 | - { | |
97 | - return &it->second; | |
98 | - } | |
99 | - | |
100 | - return &DefaultTextureOptions; | |
101 | -} | |
102 | - | |
103 | - | |
104 | -class XML_TO_ClearParser: public XML_ElementParser | |
105 | -{ | |
106 | - bool IsPresent; | |
107 | - short Collection; | |
108 | - | |
109 | -public: | |
110 | - bool Start(); | |
111 | - bool HandleAttribute(const char *Tag, const char *Value); | |
112 | - bool AttributesDone(); | |
113 | - | |
114 | - XML_TO_ClearParser(): XML_ElementParser("txtr_clear") {} | |
115 | -}; | |
116 | - | |
117 | -bool XML_TO_ClearParser::Start() | |
118 | -{ | |
119 | - IsPresent = false; | |
120 | - return true; | |
121 | -} | |
122 | - | |
123 | -bool XML_TO_ClearParser::HandleAttribute(const char *Tag, const char *Value) | |
124 | -{ | |
125 | - if (StringsEqual(Tag,"coll")) | |
126 | - { | |
127 | - if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
128 | - { | |
129 | - IsPresent = true; | |
130 | - return true; | |
131 | - } | |
132 | - else return false; | |
133 | - } | |
134 | - UnrecognizedTag(); | |
135 | - return false; | |
136 | -} | |
137 | - | |
138 | -bool XML_TO_ClearParser::AttributesDone() | |
139 | -{ | |
140 | - if (IsPresent) | |
141 | - TODelete(Collection); | |
142 | - else | |
143 | - TODelete_All(); | |
144 | - | |
145 | - return true; | |
146 | -} | |
147 | - | |
148 | -static XML_TO_ClearParser TO_ClearParser; | |
149 | - | |
150 | - | |
151 | -class XML_TextureOptionsParser: public XML_ElementParser | |
152 | -{ | |
153 | - bool CollIsPresent, BitmapIsPresent; | |
154 | - short Collection, CLUT, Bitmap; | |
155 | - | |
156 | - std::set<std::string> Attributes; | |
157 | - | |
158 | - OGL_TextureOptions Data; | |
159 | - | |
160 | - bool _HandleAttribute(const char *Tag, const char *Value); | |
161 | - | |
162 | -public: | |
163 | - bool Start(); | |
164 | - bool HandleAttribute(const char *Tag, const char *Value); | |
165 | - bool AttributesDone(); | |
166 | - bool ResetValues(); | |
167 | - | |
168 | - XML_TextureOptionsParser(): XML_ElementParser("texture") {} | |
169 | -}; | |
170 | - | |
171 | -bool XML_TextureOptionsParser::Start() | |
172 | -{ | |
173 | - Data = DefaultTextureOptions; | |
174 | - CollIsPresent = BitmapIsPresent = false; | |
175 | - CLUT = ALL_CLUTS; | |
176 | - Attributes.clear(); | |
177 | - | |
178 | - return true; | |
179 | -} | |
180 | - | |
181 | -bool XML_TextureOptionsParser::_HandleAttribute(const char *Tag, const char *Value) | |
182 | -{ | |
183 | - if (StringsEqual(Tag,"coll")) | |
184 | - { | |
185 | - if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
186 | - { | |
187 | - CollIsPresent = true; | |
188 | - return true; | |
189 | - } | |
190 | - else return false; | |
191 | - } | |
192 | - else if (StringsEqual(Tag,"clut")) | |
193 | - { | |
194 | - return ReadBoundedInt16Value(Value,CLUT,short(ALL_CLUTS),short(SILHOUETTE_BITMAP_SET)); | |
195 | - } | |
196 | - else if (StringsEqual(Tag,"bitmap")) | |
197 | - { | |
198 | - if (ReadBoundedInt16Value(Value,Bitmap,0,MAXIMUM_SHAPES_PER_COLLECTION-1)) | |
199 | - { | |
200 | - BitmapIsPresent = true; | |
201 | - return true; | |
202 | - } | |
203 | - else return false; | |
204 | - } | |
205 | - else if (StringsEqual(Tag,"opac_type")) | |
206 | - { | |
207 | - return ReadBoundedInt16Value(Value,Data.OpacityType,0,OGL_NUMBER_OF_OPACITY_TYPES-1); | |
208 | - } | |
209 | - else if (StringsEqual(Tag,"opac_scale")) | |
210 | - { | |
211 | - return ReadFloatValue(Value,Data.OpacityScale); | |
212 | - } | |
213 | - else if (StringsEqual(Tag,"opac_shift")) | |
214 | - { | |
215 | - return ReadFloatValue(Value,Data.OpacityShift); | |
216 | - } | |
217 | - else if (StringsEqual(Tag,"void_visible")) | |
218 | - { | |
219 | - return ReadBooleanValueAsBool(Value,Data.VoidVisible); | |
220 | - } | |
221 | - else if (StringsEqual(Tag,"normal_image")) | |
222 | - { | |
223 | - Data.NormalColors.SetNameWithPath(Value); | |
224 | - return true; | |
225 | - } | |
226 | - else if (StringsEqual(Tag,"offset_image")) | |
227 | - { | |
228 | - Data.OffsetMap.SetNameWithPath(Value); | |
229 | - return true; | |
230 | - } | |
231 | - else if (StringsEqual(Tag,"normal_mask")) | |
232 | - { | |
233 | - Data.NormalMask.SetNameWithPath(Value); | |
234 | - return true; | |
235 | - } | |
236 | - else if (StringsEqual(Tag,"glow_image")) | |
237 | - { | |
238 | - Data.GlowColors.SetNameWithPath(Value); | |
239 | - return true; | |
240 | - } | |
241 | - else if (StringsEqual(Tag,"glow_mask")) | |
242 | - { | |
243 | - Data.GlowMask.SetNameWithPath(Value); | |
244 | - return true; | |
245 | - } | |
246 | - else if (StringsEqual(Tag,"normal_blend")) | |
247 | - { | |
248 | - return ReadBoundedInt16Value(Value,Data.NormalBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
249 | - } | |
250 | - else if (StringsEqual(Tag,"glow_blend")) | |
251 | - { | |
252 | - return ReadBoundedInt16Value(Value,Data.GlowBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
253 | - } | |
254 | - else if (StringsEqual(Tag, "image_scale")) | |
255 | - { | |
256 | - logWarning("Ignoring deprecated image_scale tag"); | |
257 | - return true; | |
258 | - } | |
259 | - else if (StringsEqual(Tag, "x_offset")) | |
260 | - { | |
261 | - logWarning("Ignoring deprecated x_offset tag"); | |
262 | - return true; | |
263 | - } | |
264 | - else if (StringsEqual(Tag, "y_offset")) | |
265 | - { | |
266 | - logWarning("Ignoring deprecated y_offset tag"); | |
267 | - return true; | |
268 | - } | |
269 | - else if (StringsEqual(Tag,"shape_width")) | |
270 | - { | |
271 | - logWarning("Ignoring deprecated shape_width tag"); | |
272 | - return true; | |
273 | - } | |
274 | - else if (StringsEqual(Tag,"shape_height")) | |
275 | - { | |
276 | - logWarning("Ignoring deprecated shape_height tag"); | |
277 | - return true; | |
278 | - } | |
279 | - else if (StringsEqual(Tag,"offset_x")) | |
280 | - { | |
281 | - logWarning("Ignoring deprecated offset_x tag"); | |
282 | - return true; | |
283 | - } | |
284 | - else if (StringsEqual(Tag,"offset_y")) | |
285 | - { | |
286 | - logWarning("Ignoring deprecated offset_y tag"); | |
287 | - return true; | |
288 | - } | |
289 | - else if (StringsEqual(Tag,"actual_height")) | |
290 | - { | |
291 | - return ReadInt16Value(Value, Data.actual_height); | |
292 | - } | |
293 | - else if (StringsEqual(Tag, "actual_width")) | |
294 | - { | |
295 | - return ReadInt16Value(Value, Data.actual_width); | |
296 | - } | |
297 | - else if (StringsEqual(Tag, "type")) | |
298 | - { | |
299 | - return ReadInt16Value(Value, Data.Type); | |
300 | - } | |
301 | - else if (StringsEqual(Tag, "normal_premultiply")) | |
302 | - { | |
303 | - return ReadBooleanValueAsBool(Value, Data.NormalIsPremultiplied); | |
304 | - } | |
305 | - else if (StringsEqual(Tag, "glow_premultiply")) | |
306 | - { | |
307 | - return ReadBooleanValueAsBool(Value, Data.GlowIsPremultiplied); | |
308 | - } | |
309 | - else if (StringsEqual(Tag,"normal_bloom_scale")) | |
310 | - { | |
311 | - return ReadFloatValue(Value,Data.BloomScale); | |
312 | - } | |
313 | - else if (StringsEqual(Tag,"normal_bloom_shift")) | |
314 | - { | |
315 | - return ReadFloatValue(Value,Data.BloomShift); | |
316 | - } | |
317 | - else if (StringsEqual(Tag,"glow_bloom_scale")) | |
318 | - { | |
319 | - return ReadFloatValue(Value,Data.GlowBloomScale); | |
320 | - } | |
321 | - else if (StringsEqual(Tag,"glow_bloom_shift")) | |
322 | - { | |
323 | - return ReadFloatValue(Value,Data.GlowBloomShift); | |
324 | - } | |
325 | - else if (StringsEqual(Tag,"landscape_bloom")) | |
326 | - { | |
327 | - return ReadFloatValue(Value,Data.LandscapeBloom); | |
328 | - } | |
329 | - else if (StringsEqual(Tag,"minimum_glow_intensity")) | |
330 | - { | |
331 | - return ReadFloatValue(Value,Data.MinGlowIntensity); | |
332 | - } | |
333 | - UnrecognizedTag(); | |
334 | - return false; | |
335 | -} | |
336 | - | |
337 | -bool XML_TextureOptionsParser::HandleAttribute(const char* Tag, const char* Value) | |
338 | -{ | |
339 | - if (_HandleAttribute(Tag, Value)) | |
340 | - { | |
341 | - Attributes.insert(Tag); | |
342 | - return true; | |
343 | - } | |
344 | - return false; | |
345 | -} | |
346 | - | |
347 | -bool XML_TextureOptionsParser::AttributesDone() | |
348 | -{ | |
349 | - // Verify... | |
350 | - if (!CollIsPresent || !BitmapIsPresent) | |
351 | - { | |
352 | - AttribsMissing(); | |
353 | - return false; | |
354 | - } | |
355 | - | |
356 | - TOHash::iterator it = Collections[Collection].find(TOKey(CLUT, Bitmap)); | |
357 | - if (it == Collections[Collection].end()) | |
358 | - { | |
359 | - Collections[Collection][TOKey(CLUT, Bitmap)] = Data; | |
360 | - return true; | |
361 | - } | |
362 | - | |
363 | - if (Attributes.count("opac_type")) | |
364 | - { | |
365 | - it->second.OpacityType = Data.OpacityType; | |
366 | - } | |
367 | - | |
368 | - if (Attributes.count("opac_scale")) | |
369 | - { | |
370 | - it->second.OpacityScale = Data.OpacityScale; | |
371 | - } | |
372 | - | |
373 | - if (Attributes.count("opac_shift")) | |
374 | - { | |
375 | - it->second.OpacityShift = Data.OpacityShift; | |
376 | - } | |
377 | - | |
378 | - if (Attributes.count("void_visible")) | |
379 | - { | |
380 | - it->second.VoidVisible = Data.VoidVisible; | |
381 | - } | |
382 | - | |
383 | - if (Attributes.count("normal_image")) | |
384 | - { | |
385 | - it->second.NormalColors = Data.NormalColors; | |
386 | - } | |
387 | - | |
388 | - if (Attributes.count("offset_image")) | |
389 | - { | |
390 | - it->second.OffsetMap = Data.OffsetMap; | |
391 | - } | |
392 | - | |
393 | - if (Attributes.count("normal_mask")) | |
394 | - { | |
395 | - it->second.NormalMask = Data.NormalMask; | |
396 | - } | |
397 | - | |
398 | - if (Attributes.count("glow_image")) | |
399 | - { | |
400 | - it->second.GlowColors = Data.GlowColors; | |
401 | - } | |
402 | - | |
403 | - if (Attributes.count("glow_mask")) | |
404 | - { | |
405 | - it->second.GlowMask = Data.GlowMask; | |
406 | - } | |
407 | - | |
408 | - if (Attributes.count("normal_blend")) | |
409 | - { | |
410 | - it->second.NormalBlend = Data.NormalBlend; | |
411 | - } | |
412 | - | |
413 | - if (Attributes.count("glow_blend")) | |
414 | - { | |
415 | - it->second.GlowBlend = Data.GlowBlend; | |
416 | - } | |
417 | - | |
418 | - if (Attributes.count("actual_height")) | |
419 | - { | |
420 | - it->second.actual_height = Data.actual_height; | |
421 | - } | |
422 | - | |
423 | - if (Attributes.count("actual_width")) | |
424 | - { | |
425 | - it->second.actual_width = Data.actual_width; | |
426 | - } | |
427 | - | |
428 | - if (Attributes.count("type")) | |
429 | - { | |
430 | - it->second.Type = Data.Type; | |
431 | - } | |
432 | - | |
433 | - if (Attributes.count("normal_premultiply")) | |
434 | - { | |
435 | - it->second.NormalIsPremultiplied = Data.NormalIsPremultiplied; | |
436 | - } | |
437 | - | |
438 | - if (Attributes.count("glow_premultiply")) | |
439 | - { | |
440 | - it->second.GlowIsPremultiplied = Data.GlowIsPremultiplied; | |
441 | - } | |
442 | - | |
443 | - if (Attributes.count("normal_bloom_scale")) | |
444 | - { | |
445 | - it->second.BloomScale = Data.BloomScale; | |
446 | - } | |
447 | - | |
448 | - if (Attributes.count("normal_bloom_shift")) | |
449 | - { | |
450 | - it->second.BloomShift = Data.BloomShift; | |
451 | - } | |
452 | - | |
453 | - if (Attributes.count("glow_bloom_scale")) | |
454 | - { | |
455 | - it->second.GlowBloomScale = Data.GlowBloomScale; | |
456 | - } | |
457 | - | |
458 | - if (Attributes.count("glow_bloom_shift")) | |
459 | - { | |
460 | - it->second.GlowBloomShift = Data.GlowBloomShift; | |
461 | - } | |
462 | - | |
463 | - if (Attributes.count("landscape_bloom")) | |
464 | - { | |
465 | - it->second.LandscapeBloom = Data.LandscapeBloom; | |
466 | - } | |
467 | - | |
468 | - if (Attributes.count("minimum_glow_intensity")) | |
469 | - { | |
470 | - it->second.MinGlowIntensity = Data.MinGlowIntensity; | |
471 | - } | |
472 | - | |
473 | - return true; | |
474 | -} | |
475 | - | |
476 | -bool XML_TextureOptionsParser::ResetValues() | |
477 | -{ | |
478 | - TODelete_All(); | |
479 | - return true; | |
480 | -} | |
481 | - | |
482 | -static XML_TextureOptionsParser TextureOptionsParser; | |
483 | - | |
484 | - | |
485 | -// XML-parser support: | |
486 | -XML_ElementParser *TextureOptions_GetParser() {return &TextureOptionsParser;} | |
487 | -XML_ElementParser *TO_Clear_GetParser() {return &TO_ClearParser;} | |
488 | - | |
489 | -#endif | |
1 | +/* | |
2 | + | |
3 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | + OpenGL Substitute-Texture-Definition File | |
21 | + by Loren Petrich, | |
22 | + March 12, 2000 | |
23 | + | |
24 | + This contains implementations of functions for handling | |
25 | + the OpenGL substitute textures for the walls and the sprites | |
26 | +*/ | |
27 | + | |
28 | +#include "cseries.h" | |
29 | +#include "OGL_Subst_Texture_Def.h" | |
30 | +#include "Logging.h" | |
31 | + | |
32 | +#include <set> | |
33 | +#include <string> | |
34 | +#include <boost/unordered_map.hpp> | |
35 | + | |
36 | +#ifdef HAVE_OPENGL | |
37 | + | |
38 | +// Texture-options stuff; | |
39 | +// defaults for whatever might need them | |
40 | +static OGL_TextureOptions DefaultTextureOptions; | |
41 | + | |
42 | +typedef std::pair<short, short> TOKey; | |
43 | +typedef boost::unordered_map<TOKey, OGL_TextureOptions> TOHash; | |
44 | +static TOHash Collections[NUMBER_OF_COLLECTIONS]; | |
45 | + | |
46 | +// Deletes a collection's texture-options sequences | |
47 | +void TODelete(short Collection) | |
48 | +{ | |
49 | + Collections[Collection].clear(); | |
50 | +} | |
51 | + | |
52 | +// Deletes all of them | |
53 | +static void TODelete_All() | |
54 | +{ | |
55 | + for (int c = 0; c < NUMBER_OF_COLLECTIONS; c++) TODelete(c); | |
56 | +} | |
57 | + | |
58 | +int OGL_CountTextures(short Collection) | |
59 | +{ | |
60 | + return Collections[Collection].size(); | |
61 | +} | |
62 | + | |
63 | +extern void OGL_ProgressCallback(int); | |
64 | + | |
65 | +void OGL_LoadTextures(short Collection) | |
66 | +{ | |
67 | + | |
68 | + for (TOHash::iterator it = Collections[Collection].begin(); it != Collections[Collection].end(); ++it) | |
69 | + { | |
70 | + it->second.Load(); | |
71 | + OGL_ProgressCallback(1); | |
72 | + | |
73 | + } | |
74 | +} | |
75 | + | |
76 | + | |
77 | +void OGL_UnloadTextures(short Collection) | |
78 | +{ | |
79 | + for (TOHash::iterator it = Collections[Collection].begin(); it != Collections[Collection].end(); ++it) | |
80 | + { | |
81 | + it->second.Unload(); | |
82 | + } | |
83 | +} | |
84 | + | |
85 | + | |
86 | +OGL_TextureOptions *OGL_GetTextureOptions(short Collection, short CLUT, short Bitmap) | |
87 | +{ | |
88 | + TOHash::iterator it = Collections[Collection].find(TOKey(CLUT, Bitmap)); | |
89 | + if (it != Collections[Collection].end()) | |
90 | + { | |
91 | + return &it->second; | |
92 | + } | |
93 | + | |
94 | + it = Collections[Collection].find(TOKey(ALL_CLUTS, Bitmap)); | |
95 | + if (it != Collections[Collection].end()) | |
96 | + { | |
97 | + return &it->second; | |
98 | + } | |
99 | + | |
100 | + return &DefaultTextureOptions; | |
101 | +} | |
102 | + | |
103 | + | |
104 | +class XML_TO_ClearParser: public XML_ElementParser | |
105 | +{ | |
106 | + bool IsPresent; | |
107 | + short Collection; | |
108 | + | |
109 | +public: | |
110 | + bool Start(); | |
111 | + bool HandleAttribute(const char *Tag, const char *Value); | |
112 | + bool AttributesDone(); | |
113 | + | |
114 | + XML_TO_ClearParser(): XML_ElementParser("txtr_clear") {} | |
115 | +}; | |
116 | + | |
117 | +bool XML_TO_ClearParser::Start() | |
118 | +{ | |
119 | + IsPresent = false; | |
120 | + return true; | |
121 | +} | |
122 | + | |
123 | +bool XML_TO_ClearParser::HandleAttribute(const char *Tag, const char *Value) | |
124 | +{ | |
125 | + if (StringsEqual(Tag,"coll")) | |
126 | + { | |
127 | + if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
128 | + { | |
129 | + IsPresent = true; | |
130 | + return true; | |
131 | + } | |
132 | + else return false; | |
133 | + } | |
134 | + UnrecognizedTag(); | |
135 | + return false; | |
136 | +} | |
137 | + | |
138 | +bool XML_TO_ClearParser::AttributesDone() | |
139 | +{ | |
140 | + if (IsPresent) | |
141 | + TODelete(Collection); | |
142 | + else | |
143 | + TODelete_All(); | |
144 | + | |
145 | + return true; | |
146 | +} | |
147 | + | |
148 | +static XML_TO_ClearParser TO_ClearParser; | |
149 | + | |
150 | + | |
151 | +class XML_TextureOptionsParser: public XML_ElementParser | |
152 | +{ | |
153 | + bool CollIsPresent, BitmapIsPresent; | |
154 | + short Collection, CLUT, Bitmap; | |
155 | + | |
156 | + std::set<std::string> Attributes; | |
157 | + | |
158 | + OGL_TextureOptions Data; | |
159 | + | |
160 | + bool _HandleAttribute(const char *Tag, const char *Value); | |
161 | + | |
162 | +public: | |
163 | + bool Start(); | |
164 | + bool HandleAttribute(const char *Tag, const char *Value); | |
165 | + bool AttributesDone(); | |
166 | + bool ResetValues(); | |
167 | + | |
168 | + XML_TextureOptionsParser(): XML_ElementParser("texture") {} | |
169 | +}; | |
170 | + | |
171 | +bool XML_TextureOptionsParser::Start() | |
172 | +{ | |
173 | + Data = DefaultTextureOptions; | |
174 | + CollIsPresent = BitmapIsPresent = false; | |
175 | + CLUT = ALL_CLUTS; | |
176 | + Attributes.clear(); | |
177 | + | |
178 | + return true; | |
179 | +} | |
180 | + | |
181 | +bool XML_TextureOptionsParser::_HandleAttribute(const char *Tag, const char *Value) | |
182 | +{ | |
183 | + if (StringsEqual(Tag,"coll")) | |
184 | + { | |
185 | + if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
186 | + { | |
187 | + CollIsPresent = true; | |
188 | + return true; | |
189 | + } | |
190 | + else return false; | |
191 | + } | |
192 | + else if (StringsEqual(Tag,"clut")) | |
193 | + { | |
194 | + return ReadBoundedInt16Value(Value,CLUT,short(ALL_CLUTS),short(SILHOUETTE_BITMAP_SET)); | |
195 | + } | |
196 | + else if (StringsEqual(Tag,"bitmap")) | |
197 | + { | |
198 | + if (ReadBoundedInt16Value(Value,Bitmap,0,MAXIMUM_SHAPES_PER_COLLECTION-1)) | |
199 | + { | |
200 | + BitmapIsPresent = true; | |
201 | + return true; | |
202 | + } | |
203 | + else return false; | |
204 | + } | |
205 | + else if (StringsEqual(Tag,"opac_type")) | |
206 | + { | |
207 | + return ReadBoundedInt16Value(Value,Data.OpacityType,0,OGL_NUMBER_OF_OPACITY_TYPES-1); | |
208 | + } | |
209 | + else if (StringsEqual(Tag,"opac_scale")) | |
210 | + { | |
211 | + return ReadFloatValue(Value,Data.OpacityScale); | |
212 | + } | |
213 | + else if (StringsEqual(Tag,"opac_shift")) | |
214 | + { | |
215 | + return ReadFloatValue(Value,Data.OpacityShift); | |
216 | + } | |
217 | + else if (StringsEqual(Tag,"void_visible")) | |
218 | + { | |
219 | + return ReadBooleanValueAsBool(Value,Data.VoidVisible); | |
220 | + } | |
221 | + else if (StringsEqual(Tag,"normal_image")) | |
222 | + { | |
223 | + Data.NormalColors.SetNameWithPath(Value); | |
224 | + return true; | |
225 | + } | |
226 | + else if (StringsEqual(Tag,"offset_image")) | |
227 | + { | |
228 | + Data.OffsetMap.SetNameWithPath(Value); | |
229 | + return true; | |
230 | + } | |
231 | + else if (StringsEqual(Tag,"normal_mask")) | |
232 | + { | |
233 | + Data.NormalMask.SetNameWithPath(Value); | |
234 | + return true; | |
235 | + } | |
236 | + else if (StringsEqual(Tag,"glow_image")) | |
237 | + { | |
238 | + Data.GlowColors.SetNameWithPath(Value); | |
239 | + return true; | |
240 | + } | |
241 | + else if (StringsEqual(Tag,"glow_mask")) | |
242 | + { | |
243 | + Data.GlowMask.SetNameWithPath(Value); | |
244 | + return true; | |
245 | + } | |
246 | + else if (StringsEqual(Tag,"normal_blend")) | |
247 | + { | |
248 | + return ReadBoundedInt16Value(Value,Data.NormalBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
249 | + } | |
250 | + else if (StringsEqual(Tag,"glow_blend")) | |
251 | + { | |
252 | + return ReadBoundedInt16Value(Value,Data.GlowBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
253 | + } | |
254 | + else if (StringsEqual(Tag, "image_scale")) | |
255 | + { | |
256 | + logWarning("Ignoring deprecated image_scale tag"); | |
257 | + return true; | |
258 | + } | |
259 | + else if (StringsEqual(Tag,"shape_width")) | |
260 | + { | |
261 | + return ReadInt16Value(Value,Data.shape_width); | |
262 | + } | |
263 | + else if (StringsEqual(Tag,"shape_height")) | |
264 | + { | |
265 | + return ReadInt16Value(Value,Data.shape_height); | |
266 | + } | |
267 | + else if (StringsEqual(Tag,"offset_x")) | |
268 | + { | |
269 | + return ReadInt16Value(Value,Data.offset_x); | |
270 | + } | |
271 | + else if (StringsEqual(Tag,"offset_y")) | |
272 | + { | |
273 | + return ReadInt16Value(Value,Data.offset_y); | |
274 | + } | |
275 | + else if (StringsEqual(Tag,"actual_height")) | |
276 | + { | |
277 | + return ReadInt16Value(Value, Data.actual_height); | |
278 | + } | |
279 | + else if (StringsEqual(Tag, "actual_width")) | |
280 | + { | |
281 | + return ReadInt16Value(Value, Data.actual_width); | |
282 | + } | |
283 | + else if (StringsEqual(Tag, "type")) | |
284 | + { | |
285 | + return ReadInt16Value(Value, Data.Type); | |
286 | + } | |
287 | + else if (StringsEqual(Tag, "normal_premultiply")) | |
288 | + { | |
289 | + return ReadBooleanValueAsBool(Value, Data.NormalIsPremultiplied); | |
290 | + } | |
291 | + else if (StringsEqual(Tag, "glow_premultiply")) | |
292 | + { | |
293 | + return ReadBooleanValueAsBool(Value, Data.GlowIsPremultiplied); | |
294 | + } | |
295 | + else if (StringsEqual(Tag,"normal_bloom_scale")) | |
296 | + { | |
297 | + return ReadFloatValue(Value,Data.BloomScale); | |
298 | + } | |
299 | + else if (StringsEqual(Tag,"normal_bloom_shift")) | |
300 | + { | |
301 | + return ReadFloatValue(Value,Data.BloomShift); | |
302 | + } | |
303 | + else if (StringsEqual(Tag,"glow_bloom_scale")) | |
304 | + { | |
305 | + return ReadFloatValue(Value,Data.GlowBloomScale); | |
306 | + } | |
307 | + else if (StringsEqual(Tag,"glow_bloom_shift")) | |
308 | + { | |
309 | + return ReadFloatValue(Value,Data.GlowBloomShift); | |
310 | + } | |
311 | + else if (StringsEqual(Tag,"landscape_bloom")) | |
312 | + { | |
313 | + return ReadFloatValue(Value,Data.LandscapeBloom); | |
314 | + } | |
315 | + else if (StringsEqual(Tag,"minimum_glow_intensity")) | |
316 | + { | |
317 | + return ReadFloatValue(Value,Data.MinGlowIntensity); | |
318 | + } | |
319 | + UnrecognizedTag(); | |
320 | + return false; | |
321 | +} | |
322 | + | |
323 | +bool XML_TextureOptionsParser::HandleAttribute(const char* Tag, const char* Value) | |
324 | +{ | |
325 | + if (_HandleAttribute(Tag, Value)) | |
326 | + { | |
327 | + Attributes.insert(Tag); | |
328 | + return true; | |
329 | + } | |
330 | + return false; | |
331 | +} | |
332 | + | |
333 | +bool XML_TextureOptionsParser::AttributesDone() | |
334 | +{ | |
335 | + // Verify... | |
336 | + if (!CollIsPresent || !BitmapIsPresent) | |
337 | + { | |
338 | + AttribsMissing(); | |
339 | + return false; | |
340 | + } | |
341 | + | |
342 | + TOHash::iterator it = Collections[Collection].find(TOKey(CLUT, Bitmap)); | |
343 | + if (it == Collections[Collection].end()) | |
344 | + { | |
345 | + Collections[Collection][TOKey(CLUT, Bitmap)] = Data; | |
346 | + return true; | |
347 | + } | |
348 | + | |
349 | + if (Attributes.count("opac_type")) | |
350 | + { | |
351 | + it->second.OpacityType = Data.OpacityType; | |
352 | + } | |
353 | + | |
354 | + if (Attributes.count("opac_scale")) | |
355 | + { | |
356 | + it->second.OpacityScale = Data.OpacityScale; | |
357 | + } | |
358 | + | |
359 | + if (Attributes.count("opac_shift")) | |
360 | + { | |
361 | + it->second.OpacityShift = Data.OpacityShift; | |
362 | + } | |
363 | + | |
364 | + if (Attributes.count("void_visible")) | |
365 | + { | |
366 | + it->second.VoidVisible = Data.VoidVisible; | |
367 | + } | |
368 | + | |
369 | + if (Attributes.count("normal_image")) | |
370 | + { | |
371 | + it->second.NormalColors = Data.NormalColors; | |
372 | + } | |
373 | + | |
374 | + if (Attributes.count("offset_image")) | |
375 | + { | |
376 | + it->second.OffsetMap = Data.OffsetMap; | |
377 | + } | |
378 | + | |
379 | + if (Attributes.count("normal_mask")) | |
380 | + { | |
381 | + it->second.NormalMask = Data.NormalMask; | |
382 | + } | |
383 | + | |
384 | + if (Attributes.count("glow_image")) | |
385 | + { | |
386 | + it->second.GlowColors = Data.GlowColors; | |
387 | + } | |
388 | + | |
389 | + if (Attributes.count("glow_mask")) | |
390 | + { | |
391 | + it->second.GlowMask = Data.GlowMask; | |
392 | + } | |
393 | + | |
394 | + if (Attributes.count("normal_blend")) | |
395 | + { | |
396 | + it->second.NormalBlend = Data.NormalBlend; | |
397 | + } | |
398 | + | |
399 | + if (Attributes.count("glow_blend")) | |
400 | + { | |
401 | + it->second.GlowBlend = Data.GlowBlend; | |
402 | + } | |
403 | + | |
404 | + if (Attributes.count("shape_width")) | |
405 | + { | |
406 | + it->second.shape_width = Data.shape_width; | |
407 | + } | |
408 | + | |
409 | + if (Attributes.count("shape_height")) | |
410 | + { | |
411 | + it->second.shape_height = Data.shape_height; | |
412 | + } | |
413 | + | |
414 | + if (Attributes.count("offset_x")) | |
415 | + { | |
416 | + it->second.offset_x = Data.offset_x; | |
417 | + } | |
418 | + | |
419 | + if (Attributes.count("offset_y")) | |
420 | + { | |
421 | + it->second.offset_y = Data.offset_y; | |
422 | + } | |
423 | + | |
424 | + if (Attributes.count("actual_height")) | |
425 | + { | |
426 | + it->second.actual_height = Data.actual_height; | |
427 | + } | |
428 | + | |
429 | + if (Attributes.count("actual_width")) | |
430 | + { | |
431 | + it->second.actual_width = Data.actual_width; | |
432 | + } | |
433 | + | |
434 | + if (Attributes.count("type")) | |
435 | + { | |
436 | + it->second.Type = Data.Type; | |
437 | + } | |
438 | + | |
439 | + if (Attributes.count("normal_premultiply")) | |
440 | + { | |
441 | + it->second.NormalIsPremultiplied = Data.NormalIsPremultiplied; | |
442 | + } | |
443 | + | |
444 | + if (Attributes.count("glow_premultiply")) | |
445 | + { | |
446 | + it->second.GlowIsPremultiplied = Data.GlowIsPremultiplied; | |
447 | + } | |
448 | + | |
449 | + if (Attributes.count("normal_bloom_scale")) | |
450 | + { | |
451 | + it->second.BloomScale = Data.BloomScale; | |
452 | + } | |
453 | + | |
454 | + if (Attributes.count("normal_bloom_shift")) | |
455 | + { | |
456 | + it->second.BloomShift = Data.BloomShift; | |
457 | + } | |
458 | + | |
459 | + if (Attributes.count("glow_bloom_scale")) | |
460 | + { | |
461 | + it->second.GlowBloomScale = Data.GlowBloomScale; | |
462 | + } | |
463 | + | |
464 | + if (Attributes.count("glow_bloom_shift")) | |
465 | + { | |
466 | + it->second.GlowBloomShift = Data.GlowBloomShift; | |
467 | + } | |
468 | + | |
469 | + if (Attributes.count("landscape_bloom")) | |
470 | + { | |
471 | + it->second.LandscapeBloom = Data.LandscapeBloom; | |
472 | + } | |
473 | + | |
474 | + if (Attributes.count("minimum_glow_intensity")) | |
475 | + { | |
476 | + it->second.MinGlowIntensity = Data.MinGlowIntensity; | |
477 | + } | |
478 | + | |
479 | + return true; | |
480 | +} | |
481 | + | |
482 | +bool XML_TextureOptionsParser::ResetValues() | |
483 | +{ | |
484 | + TODelete_All(); | |
485 | + return true; | |
486 | +} | |
487 | + | |
488 | +static XML_TextureOptionsParser TextureOptionsParser; | |
489 | + | |
490 | + | |
491 | +// XML-parser support: | |
492 | +XML_ElementParser *TextureOptions_GetParser() {return &TextureOptionsParser;} | |
493 | +XML_ElementParser *TO_Clear_GetParser() {return &TO_ClearParser;} | |
494 | + | |
495 | +#endif |
@@ -1,69 +1,69 @@ | ||
1 | -#ifndef _RASTERIZER_OPENGL_CLASS_ | |
2 | -#define _RASTERIZER_OPENGL_CLASS_ | |
3 | -/* | |
4 | - | |
5 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - Rasterizer OpenGL impementation | |
23 | - by Loren Petrich, | |
24 | - August 7, 2000 | |
25 | - | |
26 | - As it says... will need to rewrite OGL_Render to make it truly object-oriented | |
27 | -*/ | |
28 | - | |
29 | -#include "Rasterizer.h" | |
30 | - | |
31 | - | |
32 | -class Rasterizer_OGL_Class: public RasterizerClass | |
33 | -{ | |
34 | -public: | |
35 | - | |
36 | - // Sets the rasterizer's view data; | |
37 | - // be sure to call it before doing any rendering | |
38 | - virtual void SetView(view_data& View) {OGL_SetView(View);} | |
39 | - | |
40 | - // Sets the rasterizer so that it will start rendering foreground objects | |
41 | - // like weapons in hand | |
42 | - virtual void SetForeground() {OGL_SetForeground();} | |
43 | - | |
44 | - // Sets the view of a foreground object; | |
45 | - // parameter is whether it is horizontally reflected | |
46 | - virtual void SetForegroundView(bool HorizReflect) {OGL_SetForegroundView(HorizReflect);} | |
47 | - | |
48 | - // Rendering calls | |
49 | - void Begin() {OGL_StartMain();} | |
50 | - void End() {OGL_EndMain();} | |
51 | - | |
52 | - void texture_horizontal_polygon(polygon_definition& textured_polygon) | |
53 | - { | |
54 | - OGL_RenderWall(textured_polygon,false); | |
55 | - } | |
56 | - | |
57 | - void texture_vertical_polygon(polygon_definition& textured_polygon) | |
58 | - { | |
59 | - OGL_RenderWall(textured_polygon,true); | |
60 | - } | |
61 | - | |
62 | - void texture_rectangle(rectangle_definition& textured_rectangle) | |
63 | - { | |
64 | - OGL_RenderSprite(textured_rectangle); | |
65 | - } | |
66 | -}; | |
67 | - | |
68 | - | |
69 | -#endif | |
1 | +#ifndef _RASTERIZER_OPENGL_CLASS_ | |
2 | +#define _RASTERIZER_OPENGL_CLASS_ | |
3 | +/* | |
4 | + | |
5 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + Rasterizer OpenGL impementation | |
23 | + by Loren Petrich, | |
24 | + August 7, 2000 | |
25 | + | |
26 | + As it says... will need to rewrite OGL_Render to make it truly object-oriented | |
27 | +*/ | |
28 | + | |
29 | +#include "Rasterizer.h" | |
30 | + | |
31 | + | |
32 | +class Rasterizer_OGL_Class: public RasterizerClass | |
33 | +{ | |
34 | +public: | |
35 | + | |
36 | + // Sets the rasterizer's view data; | |
37 | + // be sure to call it before doing any rendering | |
38 | + virtual void SetView(view_data& View) {OGL_SetView(View);} | |
39 | + | |
40 | + // Sets the rasterizer so that it will start rendering foreground objects | |
41 | + // like weapons in hand | |
42 | + virtual void SetForeground() {OGL_SetForeground();} | |
43 | + | |
44 | + // Sets the view of a foreground object; | |
45 | + // parameter is whether it is horizontally reflected | |
46 | + virtual void SetForegroundView(bool HorizReflect) {OGL_SetForegroundView(HorizReflect);} | |
47 | + | |
48 | + // Rendering calls | |
49 | + void Begin() {OGL_StartMain();} | |
50 | + void End() {OGL_EndMain();} | |
51 | + | |
52 | + void texture_horizontal_polygon(polygon_definition& textured_polygon) | |
53 | + { | |
54 | + OGL_RenderWall(textured_polygon,false); | |
55 | + } | |
56 | + | |
57 | + void texture_vertical_polygon(polygon_definition& textured_polygon) | |
58 | + { | |
59 | + OGL_RenderWall(textured_polygon,true); | |
60 | + } | |
61 | + | |
62 | + void texture_rectangle(rectangle_definition& textured_rectangle) | |
63 | + { | |
64 | + OGL_RenderSprite(textured_rectangle); | |
65 | + } | |
66 | +}; | |
67 | + | |
68 | + | |
69 | +#endif |
@@ -1,105 +1,105 @@ | ||
1 | -#ifndef _RENDER_SORT_POLYGONS_CLASS_ | |
2 | -#define _RENDER_SORT_POLYGONS_CLASS_ | |
3 | -/* | |
4 | - | |
5 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - Rendering Polygon-Sorting Class | |
23 | - by Loren Petrich, | |
24 | - August 6, 2000 | |
25 | - | |
26 | - Defines a class for sorting the polygons into appropriate depth order; from render.c | |
27 | - Works from RenderVisTree stuff. | |
28 | - | |
29 | - Made [view_data *view] a member and removed it as an argument | |
30 | - | |
31 | -Oct 13, 2000 | |
32 | - LP: replaced ResizableList with STL vector class | |
33 | - | |
34 | -Oct 13, 2000 | |
35 | - LP: replaced GrowableLists and ResizableLists with STL vectors | |
36 | -*/ | |
37 | - | |
38 | -#include <vector> | |
39 | -#include "world.h" | |
40 | -#include "render.h" | |
41 | -#include "RenderVisTree.h" | |
42 | - | |
43 | - | |
44 | -/* ---------- sorted nodes */ | |
45 | - | |
46 | -struct sorted_node_data | |
47 | -{ | |
48 | - short polygon_index; | |
49 | - | |
50 | - struct render_object_data *interior_objects; | |
51 | - struct render_object_data *exterior_objects; | |
52 | - | |
53 | - struct clipping_window_data *clipping_windows; | |
54 | -}; | |
55 | - | |
56 | - | |
57 | -class RenderSortPolyClass | |
58 | -{ | |
59 | - // Auxiliary data and routines: | |
60 | - | |
61 | - void initialize_sorted_render_tree(); | |
62 | - | |
63 | - clipping_window_data *build_clipping_windows(node_data *ChainBegin); | |
64 | - | |
65 | - void calculate_vertical_clip_data(line_clip_data **accumulated_line_clips, | |
66 | - size_t accumulated_line_clip_count, clipping_window_data *window, short x0, short x1); | |
67 | - | |
68 | -public: | |
69 | - | |
70 | - /* converts map polygon indexes to sorted nodes (only valid if _polygon_is_visible) */ | |
71 | - vector<sorted_node_data *> polygon_index_to_sorted_node; | |
72 | - | |
73 | - // LP additions: growable list of sorted nodes | |
74 | - // Length changed in initialize_sorted_render_tree() and sort_render_tree() | |
75 | - // When being built, the render objects are yet to be listed | |
76 | - vector<sorted_node_data> SortedNodes; | |
77 | - | |
78 | - // LP addition: growable lists of accumulations of endpoint and line clips | |
79 | - // used in build_clipping_windows() | |
80 | - vector<endpoint_clip_data *> AccumulatedEndpointClips; | |
81 | - vector<line_clip_data *> AccumulatedLineClips; | |
82 | - | |
83 | - // Pointers to view and calculated visibility tree | |
84 | - view_data *view; | |
85 | - RenderVisTreeClass *RVPtr; | |
86 | - | |
87 | - // Resizes all the objects defined inside; | |
88 | - // the resizing is lazy | |
89 | - void Resize(size_t NumPolygons); | |
90 | - | |
91 | - // Does the sorting | |
92 | - void sort_render_tree(); | |
93 | - | |
94 | - // Inits everything | |
95 | - RenderSortPolyClass(); | |
96 | -}; | |
97 | - | |
98 | -// Historical note: cause of too-many-transparent-line errors | |
99 | -// LP addition: node-alias growable list | |
100 | -// Only used in sort_render_tree() | |
101 | -// Suppressed as unnecessary because of node_data polygon-sorted-tree structure | |
102 | -// static GrowableList<node_data *> NodeAliases(32); | |
103 | - | |
104 | - | |
105 | -#endif | |
1 | +#ifndef _RENDER_SORT_POLYGONS_CLASS_ | |
2 | +#define _RENDER_SORT_POLYGONS_CLASS_ | |
3 | +/* | |
4 | + | |
5 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + Rendering Polygon-Sorting Class | |
23 | + by Loren Petrich, | |
24 | + August 6, 2000 | |
25 | + | |
26 | + Defines a class for sorting the polygons into appropriate depth order; from render.c | |
27 | + Works from RenderVisTree stuff. | |
28 | + | |
29 | + Made [view_data *view] a member and removed it as an argument | |
30 | + | |
31 | +Oct 13, 2000 | |
32 | + LP: replaced ResizableList with STL vector class | |
33 | + | |
34 | +Oct 13, 2000 | |
35 | + LP: replaced GrowableLists and ResizableLists with STL vectors | |
36 | +*/ | |
37 | + | |
38 | +#include <vector> | |
39 | +#include "world.h" | |
40 | +#include "render.h" | |
41 | +#include "RenderVisTree.h" | |
42 | + | |
43 | + | |
44 | +/* ---------- sorted nodes */ | |
45 | + | |
46 | +struct sorted_node_data | |
47 | +{ | |
48 | + short polygon_index; | |
49 | + | |
50 | + struct render_object_data *interior_objects; | |
51 | + struct render_object_data *exterior_objects; | |
52 | + | |
53 | + struct clipping_window_data *clipping_windows; | |
54 | +}; | |
55 | + | |
56 | + | |
57 | +class RenderSortPolyClass | |
58 | +{ | |
59 | + // Auxiliary data and routines: | |
60 | + | |
61 | + void initialize_sorted_render_tree(); | |
62 | + | |
63 | + clipping_window_data *build_clipping_windows(node_data *ChainBegin); | |
64 | + | |
65 | + void calculate_vertical_clip_data(line_clip_data **accumulated_line_clips, | |
66 | + size_t accumulated_line_clip_count, clipping_window_data *window, short x0, short x1); | |
67 | + | |
68 | +public: | |
69 | + | |
70 | + /* converts map polygon indexes to sorted nodes (only valid if _polygon_is_visible) */ | |
71 | + vector<sorted_node_data *> polygon_index_to_sorted_node; | |
72 | + | |
73 | + // LP additions: growable list of sorted nodes | |
74 | + // Length changed in initialize_sorted_render_tree() and sort_render_tree() | |
75 | + // When being built, the render objects are yet to be listed | |
76 | + vector<sorted_node_data> SortedNodes; | |
77 | + | |
78 | + // LP addition: growable lists of accumulations of endpoint and line clips | |
79 | + // used in build_clipping_windows() | |
80 | + vector<endpoint_clip_data *> AccumulatedEndpointClips; | |
81 | + vector<line_clip_data *> AccumulatedLineClips; | |
82 | + | |
83 | + // Pointers to view and calculated visibility tree | |
84 | + view_data *view; | |
85 | + RenderVisTreeClass *RVPtr; | |
86 | + | |
87 | + // Resizes all the objects defined inside; | |
88 | + // the resizing is lazy | |
89 | + void Resize(size_t NumPolygons); | |
90 | + | |
91 | + // Does the sorting | |
92 | + void sort_render_tree(); | |
93 | + | |
94 | + // Inits everything | |
95 | + RenderSortPolyClass(); | |
96 | +}; | |
97 | + | |
98 | +// Historical note: cause of too-many-transparent-line errors | |
99 | +// LP addition: node-alias growable list | |
100 | +// Only used in sort_render_tree() | |
101 | +// Suppressed as unnecessary because of node_data polygon-sorted-tree structure | |
102 | +// static GrowableList<node_data *> NodeAliases(32); | |
103 | + | |
104 | + | |
105 | +#endif |
@@ -1,51 +1,51 @@ | ||
1 | -#ifndef __SHAPE_DEFINITIONS_H | |
2 | -#define __SHAPE_DEFINITIONS_H | |
3 | -/* | |
4 | -SHAPE_DEFINITIONS.H | |
5 | - | |
6 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
7 | - and the "Aleph One" developers. | |
8 | - | |
9 | - This program is free software; you can redistribute it and/or modify | |
10 | - it under the terms of the GNU General Public License as published by | |
11 | - the Free Software Foundation; either version 3 of the License, or | |
12 | - (at your option) any later version. | |
13 | - | |
14 | - This program is distributed in the hope that it will be useful, | |
15 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | - GNU General Public License for more details. | |
18 | - | |
19 | - This license is contained in the file "COPYING", | |
20 | - which is included with this source code; it is available online at | |
21 | - http://www.gnu.org/licenses/gpl.html | |
22 | - | |
23 | -Tuesday, August 29, 1995 9:56:56 AM (Jason) | |
24 | - | |
25 | -Aug 14, 2000 (Loren Petrich): | |
26 | - Turned collection and shading-table handles into pointers, | |
27 | - because handles are needlessly MacOS-specific, | |
28 | - and because these are variable-format objects. | |
29 | -*/ | |
30 | - | |
31 | -/* ---------- structures */ | |
32 | - | |
33 | -struct collection_header /* 32 bytes on disk */ | |
34 | -{ | |
35 | - int16 status; | |
36 | - uint16 flags; | |
37 | - | |
38 | - int32 offset, length; | |
39 | - int32 offset16, length16; | |
40 | - | |
41 | - // LP: handles to pointers | |
42 | - collection_definition *collection; | |
43 | - byte *shading_tables; | |
44 | -}; | |
45 | -const int SIZEOF_collection_header = 32; | |
46 | - | |
47 | -/* ---------- globals */ | |
48 | - | |
49 | -static struct collection_header collection_headers[MAXIMUM_COLLECTIONS]; | |
50 | - | |
51 | -#endif | |
1 | +#ifndef __SHAPE_DEFINITIONS_H | |
2 | +#define __SHAPE_DEFINITIONS_H | |
3 | +/* | |
4 | +SHAPE_DEFINITIONS.H | |
5 | + | |
6 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
7 | + and the "Aleph One" developers. | |
8 | + | |
9 | + This program is free software; you can redistribute it and/or modify | |
10 | + it under the terms of the GNU General Public License as published by | |
11 | + the Free Software Foundation; either version 3 of the License, or | |
12 | + (at your option) any later version. | |
13 | + | |
14 | + This program is distributed in the hope that it will be useful, | |
15 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | + GNU General Public License for more details. | |
18 | + | |
19 | + This license is contained in the file "COPYING", | |
20 | + which is included with this source code; it is available online at | |
21 | + http://www.gnu.org/licenses/gpl.html | |
22 | + | |
23 | +Tuesday, August 29, 1995 9:56:56 AM (Jason) | |
24 | + | |
25 | +Aug 14, 2000 (Loren Petrich): | |
26 | + Turned collection and shading-table handles into pointers, | |
27 | + because handles are needlessly MacOS-specific, | |
28 | + and because these are variable-format objects. | |
29 | +*/ | |
30 | + | |
31 | +/* ---------- structures */ | |
32 | + | |
33 | +struct collection_header /* 32 bytes on disk */ | |
34 | +{ | |
35 | + int16 status; | |
36 | + uint16 flags; | |
37 | + | |
38 | + int32 offset, length; | |
39 | + int32 offset16, length16; | |
40 | + | |
41 | + // LP: handles to pointers | |
42 | + collection_definition *collection; | |
43 | + byte *shading_tables; | |
44 | +}; | |
45 | +const int SIZEOF_collection_header = 32; | |
46 | + | |
47 | +/* ---------- globals */ | |
48 | + | |
49 | +static struct collection_header collection_headers[MAXIMUM_COLLECTIONS]; | |
50 | + | |
51 | +#endif |
@@ -1,637 +1,637 @@ | ||
1 | -/* | |
2 | - | |
3 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | - OpenGL Renderer, | |
21 | - by Loren Petrich, | |
22 | - March 12, 2000 | |
23 | - | |
24 | - This contains implementations of functions intended for finding out OpenGL's presence | |
25 | - in the host system, for setting parameters for OpenGL rendering, | |
26 | - and for deciding whether to use OpenGL for rendering. | |
27 | - | |
28 | - June 11, 2000 | |
29 | - | |
30 | - Had added XML parsing before that; most recently, added "opac_shift". | |
31 | - | |
32 | - Made semitransparency optional if the void is on one side of the texture | |
33 | - | |
34 | -Oct 13, 2000 (Loren Petrich) | |
35 | - Converted the OpenGL-addition accounting into Standard Template Library vectors | |
36 | - | |
37 | -Nov 12, 2000 (Loren Petrich): | |
38 | - Implemented texture substitution, also moved pixel-opacity editing into here; | |
39 | - the code is carefully constructed to assume RGBA byte order whether integers are | |
40 | - big- or little-endian. | |
41 | - | |
42 | -Nov 18, 2000 (Loren Petrich): | |
43 | - Added support for glow mapping; constrained it to only be present | |
44 | - when a normal texture is present, and to have the same size | |
45 | - | |
46 | -Nov 26, 2000 (Loren Petrich): | |
47 | - Added system for reloading textures only when their filenames change. | |
48 | - | |
49 | -Dec 17, 2000 (Loren Petrich): | |
50 | - Eliminated fog parameters from the preferences; | |
51 | - there is still a "fog present" switch, which is used to indicate | |
52 | - whether fog will not be suppressed. | |
53 | - | |
54 | -Apr 27, 2001 (Loren Petrich): | |
55 | - Modified the OpenGL fog support so as to enable below-liquid fogs | |
56 | - | |
57 | -Jul 8, 2001 (Loren Petrich): | |
58 | - Made it possible to read in silhouette bitmaps; one can now use the silhouette index | |
59 | - as a MML color-table index | |
60 | - | |
61 | -Aug 21, 2001 (Loren Petrich): | |
62 | - Adding support for 3D-model inhabitant objects | |
63 | - | |
64 | -Jan 25, 2002 (Br'fin (Jeremy Parsons)): | |
65 | - Added TARGET_API_MAC_CARBON for OpenGL.h, AGL.h | |
66 | - Removed QuickDraw3D support from Carbon | |
67 | - | |
68 | -Feb 5, 2002 (Br'fin (Jeremy Parsons)): | |
69 | - Refined OGL default preferences for Carbon | |
70 | -*/ | |
71 | - | |
72 | -#include <vector> | |
73 | -#include <string.h> | |
74 | -#include <math.h> | |
75 | -#include "cseries.h" | |
76 | - | |
77 | -#ifdef HAVE_OPENGL | |
78 | - | |
79 | -#ifdef __MVCPP__ | |
80 | -#include <windows.h> | |
81 | -#endif | |
82 | - | |
83 | -#include "OGL_Headers.h" | |
84 | -#include "OGL_Shader.h" | |
85 | - | |
86 | -#endif | |
87 | - | |
88 | -#include "shape_descriptors.h" | |
89 | -#include "OGL_Setup.h" | |
90 | -#include "ColorParser.h" | |
91 | -#include "OGL_LoadScreen.h" | |
92 | -#include "progress.h" | |
93 | - | |
94 | -// Whether or not OpenGL is present and usable | |
95 | -static bool _OGL_IsPresent = false; | |
96 | - | |
97 | -bool Using_sRGB = false; | |
98 | -bool Wanting_sRGB = false; | |
99 | -bool Bloom_sRGB = false; | |
100 | -bool npotTextures = false; // non-power-of-two | |
101 | - | |
102 | -// Initializer | |
103 | -bool OGL_Initialize() | |
104 | -{ | |
105 | -#ifdef HAVE_OPENGL | |
106 | -#if defined(mac) | |
107 | - // Cribbed from Apple's DrawSprocket documentation; | |
108 | - // look for OpenGL function | |
109 | - return (_OGL_IsPresent = ((Ptr)aglChoosePixelFormat != (Ptr)kUnresolvedCFragSymbolAddress)); | |
110 | - // return (_OGL_IsPresent = ((Ptr)glBegin != (Ptr)kUnresolvedCFragSymbolAddress)); | |
111 | -#elif defined(SDL) | |
112 | - // nothing to do | |
113 | -#if defined(__WIN32__) | |
114 | -// glewInit(); | |
115 | -#endif | |
116 | - | |
117 | - return _OGL_IsPresent = true; | |
118 | -#else | |
119 | -#error OGL_Initialize() not implemented for this platform | |
120 | -#endif | |
121 | -#else | |
122 | - return false; | |
123 | -#endif | |
124 | -} | |
125 | - | |
126 | -// Test for presence | |
127 | -bool OGL_IsPresent() {return _OGL_IsPresent;} | |
128 | - | |
129 | -bool OGL_CheckExtension(const std::string extension) { | |
130 | -#ifdef HAVE_OPENGL | |
131 | -#ifdef __WIN32__ | |
132 | - return glewIsSupported(extension.c_str()); | |
133 | -#else | |
134 | - char *extensions = (char *) glGetString(GL_EXTENSIONS); | |
135 | - if (!extensions) return false; | |
136 | - | |
137 | - while (*extensions) | |
138 | - { | |
139 | - unsigned int length = strcspn(extensions, " "); | |
140 | - | |
141 | - if (length == extension.size() && | |
142 | - strncmp(extension.c_str(), extensions, length) == 0) { | |
143 | - return true; | |
144 | - } | |
145 | - | |
146 | - extensions += length + 1; | |
147 | - } | |
148 | -#endif | |
149 | -#endif | |
150 | - return false; | |
151 | -} | |
152 | - | |
153 | -static int ogl_progress; | |
154 | -static int total_ogl_progress; | |
155 | -static bool show_ogl_progress = false; | |
156 | -static int32 last_update_tick; | |
157 | - | |
158 | -extern bool OGL_ClearScreen(); | |
159 | - | |
160 | -#ifdef HAVE_OPENGL | |
161 | -void OGL_StartProgress(int total_progress) | |
162 | -{ | |
163 | - ogl_progress = 0; | |
164 | - total_ogl_progress = total_progress; | |
165 | - if (!OGL_LoadScreen::instance()->Start()) | |
166 | - { | |
167 | - OGL_ClearScreen(); | |
168 | - open_progress_dialog(_loading, true); | |
169 | - } | |
170 | - show_ogl_progress = true; | |
171 | - last_update_tick = SDL_GetTicks(); | |
172 | -} | |
173 | - | |
174 | -void OGL_ProgressCallback(int delta_progress) | |
175 | -{ | |
176 | - if (!show_ogl_progress) return; | |
177 | - ogl_progress += delta_progress; | |
178 | - { | |
179 | - int32 current_ticks = SDL_GetTicks(); | |
180 | - if (current_ticks > last_update_tick + 33) | |
181 | - { | |
182 | - if (OGL_LoadScreen::instance()->Use()) | |
183 | - OGL_LoadScreen::instance()->Progress(100 * ogl_progress / total_ogl_progress); | |
184 | - else | |
185 | - draw_progress_bar(ogl_progress, total_ogl_progress); | |
186 | - last_update_tick = current_ticks; | |
187 | - } | |
188 | - } | |
189 | -} | |
190 | - | |
191 | -void OGL_StopProgress() | |
192 | -{ | |
193 | - show_ogl_progress = false; | |
194 | - if (OGL_LoadScreen::instance()->Use()) | |
195 | - OGL_LoadScreen::instance()->Stop(); | |
196 | - else | |
197 | - close_progress_dialog(); | |
198 | -} | |
199 | -#endif | |
200 | - | |
201 | -// Sensible defaults for the fog: | |
202 | -static OGL_FogData FogData[OGL_NUMBER_OF_FOG_TYPES] = | |
203 | -{ | |
204 | - {{0x8000,0x8000,0x8000},8,false,true}, | |
205 | - {{0x8000,0x8000,0x8000},8,false,true} | |
206 | -}; | |
207 | - | |
208 | - | |
209 | -// For flat landscapes: | |
210 | -const RGBColor DefaultLscpColors[4][2] = | |
211 | -{ | |
212 | - { | |
213 | - {0xffff, 0xffff, 0x6666}, // Day | |
214 | - {0x3333, 0x9999, 0xffff}, | |
215 | - }, | |
216 | - { | |
217 | - {0x1818, 0x1818, 0x1010}, // Night | |
218 | - {0x0808, 0x0808, 0x1010}, | |
219 | - }, | |
220 | - { | |
221 | - {0x6666, 0x6666, 0x6666}, // Moon | |
222 | - {0x0000, 0x0000, 0x0000}, | |
223 | - }, | |
224 | - { | |
225 | - {0x0000, 0x0000, 0x0000}, // Outer Space | |
226 | - {0x0000, 0x0000, 0x0000}, | |
227 | - }, | |
228 | -}; | |
229 | - | |
230 | - | |
231 | -// Set defaults | |
232 | -void OGL_SetDefaults(OGL_ConfigureData& Data) | |
233 | -{ | |
234 | - for (int k=0; k<OGL_NUMBER_OF_TEXTURE_TYPES; k++) | |
235 | - { | |
236 | - OGL_Texture_Configure& TxtrData = Data.TxtrConfigList[k]; | |
237 | - TxtrData.NearFilter = 1; // GL_LINEAR | |
238 | - if (k == OGL_Txtr_Wall || k == OGL_Txtr_Inhabitant) | |
239 | - TxtrData.FarFilter = 5; // GL_LINEAR_MIPMAP_LINEAR | |
240 | - else | |
241 | - TxtrData.FarFilter = 1; // GL_LINEAR | |
242 | - TxtrData.Resolution = 0; // 1x | |
243 | - TxtrData.ColorFormat = 0; // 32-bit color | |
244 | - TxtrData.MaxSize = 0; // Unlimited | |
245 | - } | |
246 | - | |
247 | - Data.ModelConfig.NearFilter = 1; | |
248 | - Data.ModelConfig.FarFilter = 5; | |
249 | - Data.ModelConfig.Resolution = 0; | |
250 | - Data.ModelConfig.ColorFormat = 0; | |
251 | - Data.ModelConfig.MaxSize = 0; | |
252 | - | |
253 | - // Reasonable default flags ("static" effect causes massive slowdown, so we turn it off) | |
254 | - Data.Flags = OGL_Flag_FlatStatic | OGL_Flag_Fader | OGL_Flag_Map | | |
255 | - OGL_Flag_HUD | OGL_Flag_LiqSeeThru | OGL_Flag_3D_Models | OGL_Flag_ZBuffer | | |
256 | - OGL_Flag_Fog; | |
257 | - | |
258 | - Data.AnisotropyLevel = 0.0; // off | |
259 | - Data.Multisamples = 0; // off | |
260 | - | |
261 | - Data.VoidColor = rgb_black; // Self-explanatory | |
262 | - for (int il=0; il<4; il++) | |
263 | - for (int ie=0; ie<2; ie++) | |
264 | - Data.LscpColors[il][ie] = DefaultLscpColors[il][ie]; | |
265 | - | |
266 | - Data.GeForceFix = false; | |
267 | - Data.WaitForVSync = true; | |
268 | - Data.Use_sRGB = false; | |
269 | - Data.Use_NPOT = false; | |
270 | -} | |
271 | - | |
272 | - | |
273 | -inline bool StringPresent(vector<char>& String) | |
274 | -{ | |
275 | - return (String.size() > 1); | |
276 | -} | |
277 | - | |
278 | -#ifdef HAVE_OPENGL | |
279 | - | |
280 | -GLint glMaxTextureSize = 0; | |
281 | -bool hasS3TC = false; | |
282 | - | |
283 | -void OGL_TextureOptionsBase::Load() | |
284 | -{ | |
285 | - FileSpecifier File; | |
286 | - | |
287 | - GLint maxTextureSize = glMaxTextureSize; | |
288 | - if (GetMaxSize()) | |
289 | - { | |
290 | - maxTextureSize = MIN(maxTextureSize, GetMaxSize()); | |
291 | - } | |
292 | - | |
293 | - int flags = npotTextures ? 0 : ImageLoader_ResizeToPowersOfTwo; | |
294 | - | |
295 | - if (Type >= 0 && Type < OGL_NUMBER_OF_TEXTURE_TYPES && Get_OGL_ConfigureData().TxtrConfigList[Type].FarFilter > 1 /* GL_LINEAR */) | |
296 | - { | |
297 | - flags |= ImageLoader_LoadMipMaps; | |
298 | - } | |
299 | - | |
300 | - if (hasS3TC) | |
301 | - { | |
302 | - flags |= ImageLoader_CanUseDXTC; | |
303 | - } | |
304 | - | |
305 | - if (Get_OGL_ConfigureData().GeForceFix) | |
306 | - { | |
307 | - flags |= ImageLoader_LoadDXTC1AsDXTC3; | |
308 | - } | |
309 | - | |
310 | - // Load the normal image with alpha channel | |
311 | - | |
312 | - // Check to see if loading needs to be done; | |
313 | - // it does not need to be if an image is present. | |
314 | - if (NormalImg.IsPresent()) return; | |
315 | - | |
316 | - NormalImg.Clear(); | |
317 | - | |
318 | - // Load the normal image if it has a filename specified for it | |
319 | - if (NormalColors != FileSpecifier() && NormalColors.Exists()) | |
320 | - { | |
321 | - if (!NormalImg.LoadFromFile(NormalColors,ImageLoader_Colors, flags | (NormalIsPremultiplied ? ImageLoader_ImageIsAlreadyPremultiplied : 0), actual_width, actual_height, maxTextureSize)) | |
322 | - { | |
323 | - // A texture must have a normal colored part | |
324 | - return; | |
325 | - } | |
326 | - } | |
327 | - else | |
328 | - { | |
329 | - return; | |
330 | - } | |
331 | - | |
332 | - // load a heightmap | |
333 | - if(OffsetMap != FileSpecifier() && OffsetMap.Exists()) { | |
334 | - if(!OffsetImg.LoadFromFile(OffsetMap, ImageLoader_Colors, flags | (NormalIsPremultiplied ? ImageLoader_ImageIsAlreadyPremultiplied : 0), actual_width, actual_height, maxTextureSize)) { | |
335 | - return; | |
336 | - } | |
337 | - } | |
338 | - | |
339 | - // Load the normal mask if it has a filename specified for it | |
340 | - if (NormalMask != FileSpecifier() && NormalMask.Exists()) | |
341 | - { | |
342 | - NormalImg.LoadFromFile(NormalMask,ImageLoader_Opacity, flags, actual_width, actual_height, maxTextureSize); | |
343 | - } | |
344 | - | |
345 | - if (maxTextureSize) | |
346 | - { | |
347 | - while (NormalImg.GetWidth() > maxTextureSize || NormalImg.GetHeight() > maxTextureSize) | |
348 | - { | |
349 | - if (!NormalImg.Minify()) break; | |
350 | - } | |
351 | - | |
352 | - if(OffsetImg.IsPresent()) { | |
353 | - while (OffsetImg.GetWidth() > maxTextureSize || OffsetImg.GetHeight() > maxTextureSize) { | |
354 | - if(!OffsetImg.Minify()) { break; } | |
355 | - } | |
356 | - } | |
357 | - } | |
358 | - | |
359 | - // Load the glow image with alpha channel | |
360 | - if (!GlowImg.IsPresent()) | |
361 | - { | |
362 | - GlowImg.Clear(); | |
363 | - | |
364 | - // Load the glow image if it has a filename specified for it | |
365 | - if (GlowColors != FileSpecifier() && GlowColors.Exists()) | |
366 | - { | |
367 | - if (GlowImg.LoadFromFile(GlowColors,ImageLoader_Colors, flags | (GlowIsPremultiplied ? ImageLoader_ImageIsAlreadyPremultiplied : 0), actual_width, actual_height, maxTextureSize)) | |
368 | - { | |
369 | - | |
370 | - // Load the glow mask if it has a | |
371 | - // filename specified for it; only | |
372 | - // loaded if an image has been loaded | |
373 | - // for it | |
374 | - if (GlowMask != FileSpecifier() && GlowMask.Exists()) | |
375 | - { | |
376 | - GlowImg.LoadFromFile(GlowMask,ImageLoader_Opacity, flags, actual_width, actual_height, maxTextureSize); | |
377 | - } | |
378 | - } | |
379 | - } | |
380 | - } | |
381 | - | |
382 | - if (GlowImg.IsPresent() && maxTextureSize) | |
383 | - { | |
384 | - while (GlowImg.GetWidth() > maxTextureSize || GlowImg.GetHeight() > maxTextureSize) | |
385 | - { | |
386 | - if (!GlowImg.Minify()) break; | |
387 | - } | |
388 | - } | |
389 | - | |
390 | - // The rest of the code is made simpler by these constraints: | |
391 | - // that the glow texture only be present if the normal texture is also present, | |
392 | - // and that the normal and glow textures have the same dimensions | |
393 | - if (NormalImg.IsPresent()) | |
394 | - { | |
395 | - int W0 = NormalImg.GetWidth(); | |
396 | - int W1 = GlowImg.GetWidth(); | |
397 | - int H0 = NormalImg.GetHeight(); | |
398 | - int H1 = GlowImg.GetHeight(); | |
399 | - if ((W1 != W0) || (H1 != H0)) GlowImg.Clear(); | |
400 | - } | |
401 | - else | |
402 | - { | |
403 | - GlowImg.Clear(); | |
404 | - } | |
405 | - | |
406 | -} | |
407 | - | |
408 | -void OGL_TextureOptionsBase::Unload() | |
409 | -{ | |
410 | - NormalImg.Clear(); | |
411 | - GlowImg.Clear(); | |
412 | - OffsetImg.Clear(); | |
413 | -} | |
414 | - | |
415 | -int OGL_TextureOptionsBase::GetMaxSize() | |
416 | -{ | |
417 | - if (Type >= 0 && Type < OGL_NUMBER_OF_TEXTURE_TYPES) | |
418 | - { | |
419 | - return Get_OGL_ConfigureData().TxtrConfigList[Type].MaxSize; | |
420 | - } | |
421 | - else | |
422 | - return 0; // Unlimited | |
423 | -} | |
424 | -#endif | |
425 | - | |
426 | -#ifdef HAVE_OPENGL | |
427 | - | |
428 | -int OGL_CountModelsImages(short Collection) | |
429 | -{ | |
430 | - return OGL_CountTextures(Collection) + OGL_CountModels(Collection); | |
431 | -} | |
432 | - | |
433 | -// for managing the model and image loading and unloading | |
434 | -void OGL_LoadModelsImages(short Collection) | |
435 | -{ | |
436 | - assert(Collection >= 0 && Collection < MAXIMUM_COLLECTIONS); | |
437 | - | |
438 | - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTextureSize); | |
439 | - hasS3TC = OGL_CheckExtension("GL_ARB_texture_compression") && OGL_CheckExtension("GL_EXT_texture_compression_s3tc"); | |
440 | - | |
441 | - // For wall/sprite images | |
442 | - OGL_LoadTextures(Collection); | |
443 | - | |
444 | - // For models, skins | |
445 | - bool UseModels = TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_3D_Models) ? true : false; | |
446 | - if (UseModels) | |
447 | - OGL_LoadModels(Collection); | |
448 | - else | |
449 | - OGL_UnloadModels(Collection); | |
450 | -} | |
451 | - | |
452 | -void OGL_UnloadModelsImages(short Collection) | |
453 | -{ | |
454 | - assert(Collection >= 0 && Collection < MAXIMUM_COLLECTIONS); | |
455 | - | |
456 | - // For wall/sprite images | |
457 | - OGL_UnloadTextures(Collection); | |
458 | - | |
459 | - // For models, skins | |
460 | - OGL_UnloadModels(Collection); | |
461 | -} | |
462 | - | |
463 | -#else | |
464 | - | |
465 | -void OGL_LoadModelsImages(short) | |
466 | -{ | |
467 | -} | |
468 | - | |
469 | -void OGL_UnloadModelsImages(short) | |
470 | -{ | |
471 | -} | |
472 | - | |
473 | -#endif // def HAVE_OPENGL | |
474 | - | |
475 | - | |
476 | -OGL_FogData *OGL_GetFogData(int Type) | |
477 | -{ | |
478 | - return GetMemberWithBounds(FogData,Type,OGL_NUMBER_OF_FOG_TYPES); | |
479 | -} | |
480 | - | |
481 | - | |
482 | -// XML-parsing stuff | |
483 | -OGL_FogData *OriginalFogData = NULL; | |
484 | -class XML_FogParser: public XML_ElementParser | |
485 | -{ | |
486 | - bool IsPresent[3]; | |
487 | - bool FogPresent, AffectsLandscapes; | |
488 | - float Depth; | |
489 | - short Type; | |
490 | - | |
491 | -public: | |
492 | - bool Start(); | |
493 | - bool HandleAttribute(const char *Tag, const char *Value); | |
494 | - bool AttributesDone(); | |
495 | - bool ResetValues(); | |
496 | - | |
497 | - XML_FogParser(): XML_ElementParser("fog") {} | |
498 | -}; | |
499 | - | |
500 | -bool XML_FogParser::Start() | |
501 | -{ | |
502 | - // back up old values first | |
503 | - if (!OriginalFogData) { | |
504 | - OriginalFogData = (OGL_FogData *) malloc(sizeof(OGL_FogData) * OGL_NUMBER_OF_FOG_TYPES); | |
505 | - assert(OriginalFogData); | |
506 | - for (unsigned i = 0; i < OGL_NUMBER_OF_FOG_TYPES; i++) | |
507 | - OriginalFogData[i] = FogData[i]; | |
508 | - } | |
509 | - | |
510 | - IsPresent[0] = IsPresent[1] = IsPresent[2] = false; | |
511 | - Type = 0; | |
512 | - return true; | |
513 | -} | |
514 | - | |
515 | -bool XML_FogParser::HandleAttribute(const char *Tag, const char *Value) | |
516 | -{ | |
517 | - if (StringsEqual(Tag,"on")) | |
518 | - { | |
519 | - if (ReadBooleanValueAsBool(Value,FogPresent)) | |
520 | - { | |
521 | - IsPresent[0] = true; | |
522 | - return true; | |
523 | - } | |
524 | - else return false; | |
525 | - } | |
526 | - else if (StringsEqual(Tag,"depth")) | |
527 | - { | |
528 | - if (ReadFloatValue(Value,Depth)) | |
529 | - { | |
530 | - IsPresent[1] = true; | |
531 | - return true; | |
532 | - } | |
533 | - else return false; | |
534 | - } | |
535 | - if (StringsEqual(Tag,"landscapes")) | |
536 | - { | |
537 | - if (ReadBooleanValueAsBool(Value,AffectsLandscapes)) | |
538 | - { | |
539 | - IsPresent[2] = true; | |
540 | - return true; | |
541 | - } | |
542 | - else return false; | |
543 | - } | |
544 | - else if (StringsEqual(Tag,"type")) | |
545 | - { | |
546 | - return ReadBoundedInt16Value(Value,Type,0,OGL_NUMBER_OF_FOG_TYPES-1); | |
547 | - } | |
548 | - UnrecognizedTag(); | |
549 | - return false; | |
550 | -} | |
551 | - | |
552 | -bool XML_FogParser::AttributesDone() | |
553 | -{ | |
554 | - OGL_FogData& Data = FogData[Type]; | |
555 | - if (IsPresent[0]) Data.IsPresent = FogPresent; | |
556 | - if (IsPresent[1]) Data.Depth = Depth; | |
557 | - if (IsPresent[2]) Data.AffectsLandscapes = AffectsLandscapes; | |
558 | - Color_SetArray(&Data.Color); | |
559 | - return true; | |
560 | -} | |
561 | - | |
562 | -bool XML_FogParser::ResetValues() | |
563 | -{ | |
564 | - if (OriginalFogData) { | |
565 | - for (unsigned i = 0; i < OGL_NUMBER_OF_FOG_TYPES; i++) | |
566 | - FogData[i] = OriginalFogData[i]; | |
567 | - free(OriginalFogData); | |
568 | - OriginalFogData = NULL; | |
569 | - } | |
570 | - return true; | |
571 | -} | |
572 | - | |
573 | -static XML_FogParser FogParser; | |
574 | - | |
575 | -static XML_ElementParser OpenGL_Parser("opengl"); | |
576 | - | |
577 | - | |
578 | -// XML-parser support: | |
579 | -XML_ElementParser *OpenGL_GetParser() | |
580 | -{ | |
581 | -#ifdef HAVE_OPENGL | |
582 | - OpenGL_Parser.AddChild(TextureOptions_GetParser()); | |
583 | - OpenGL_Parser.AddChild(Shader_GetParser()); | |
584 | - OpenGL_Parser.AddChild(TO_Clear_GetParser()); | |
585 | - | |
586 | - OpenGL_Parser.AddChild(ModelData_GetParser()); | |
587 | - OpenGL_Parser.AddChild(Mdl_Clear_GetParser()); | |
588 | -#endif | |
589 | - | |
590 | - FogParser.AddChild(Color_GetParser()); | |
591 | - OpenGL_Parser.AddChild(&FogParser); | |
592 | - | |
593 | - return &OpenGL_Parser; | |
594 | -} | |
595 | - | |
596 | -#ifdef HAVE_OPENGL | |
597 | -/* These don't belong here */ | |
598 | -void SglColor3f(GLfloat r, GLfloat g, GLfloat b) { | |
599 | - GLfloat ov[3] = {sRGB_frob(r), sRGB_frob(g), sRGB_frob(b)}; | |
600 | - glColor3fv(ov); | |
601 | -} | |
602 | - | |
603 | -void SglColor3fv(const GLfloat* iv) { | |
604 | - GLfloat ov[3] = {sRGB_frob(iv[0]), sRGB_frob(iv[1]), sRGB_frob(iv[2])}; | |
605 | - glColor3fv(ov); | |
606 | -} | |
607 | - | |
608 | -void SglColor3ub(GLubyte r, GLubyte g, GLubyte b) { | |
609 | - GLfloat ov[3] = {sRGB_frob(r*(1.f/255.f)), sRGB_frob(g*(1.f/255.f)), sRGB_frob(b*(1.f/255.f))}; | |
610 | - glColor3fv(ov); | |
611 | -} | |
612 | - | |
613 | -void SglColor3us(GLushort r, GLushort g, GLushort b) { | |
614 | - GLfloat ov[3] = {sRGB_frob(r*(1.f/65535.f)), sRGB_frob(g*(1.f/65535.f)), sRGB_frob(b*(1.f/65535.f))}; | |
615 | - glColor3fv(ov); | |
616 | -} | |
617 | - | |
618 | -void SglColor3usv(const GLushort* iv) { | |
619 | - GLfloat ov[3] = {sRGB_frob(iv[0]*(1.f/65535.f)), sRGB_frob(iv[1]*(1.f/65535.f)), sRGB_frob(iv[2]*(1.f/65535.f))}; | |
620 | - glColor3fv(ov); | |
621 | -} | |
622 | - | |
623 | -void SglColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { | |
624 | - GLfloat ov[4] = {sRGB_frob(r), sRGB_frob(g), sRGB_frob(b), a}; | |
625 | - glColor4fv(ov); | |
626 | -} | |
627 | - | |
628 | -void SglColor4fv(const GLfloat* iv) { | |
629 | - GLfloat ov[4] = {sRGB_frob(iv[0]), sRGB_frob(iv[1]), sRGB_frob(iv[2]), iv[3]}; | |
630 | - glColor4fv(ov); | |
631 | -} | |
632 | - | |
633 | -void SglColor4usv(const GLushort* iv) { | |
634 | - GLfloat ov[4] = {sRGB_frob(iv[0]*(1.f/65535.f)), sRGB_frob(iv[1]*(1.f/65535.f)), sRGB_frob(iv[2]*(1.f/65535.f)), iv[3]*(1.f/65535.f)}; | |
635 | - glColor4fv(ov); | |
636 | -} | |
637 | -#endif | |
1 | +/* | |
2 | + | |
3 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | + OpenGL Renderer, | |
21 | + by Loren Petrich, | |
22 | + March 12, 2000 | |
23 | + | |
24 | + This contains implementations of functions intended for finding out OpenGL's presence | |
25 | + in the host system, for setting parameters for OpenGL rendering, | |
26 | + and for deciding whether to use OpenGL for rendering. | |
27 | + | |
28 | + June 11, 2000 | |
29 | + | |
30 | + Had added XML parsing before that; most recently, added "opac_shift". | |
31 | + | |
32 | + Made semitransparency optional if the void is on one side of the texture | |
33 | + | |
34 | +Oct 13, 2000 (Loren Petrich) | |
35 | + Converted the OpenGL-addition accounting into Standard Template Library vectors | |
36 | + | |
37 | +Nov 12, 2000 (Loren Petrich): | |
38 | + Implemented texture substitution, also moved pixel-opacity editing into here; | |
39 | + the code is carefully constructed to assume RGBA byte order whether integers are | |
40 | + big- or little-endian. | |
41 | + | |
42 | +Nov 18, 2000 (Loren Petrich): | |
43 | + Added support for glow mapping; constrained it to only be present | |
44 | + when a normal texture is present, and to have the same size | |
45 | + | |
46 | +Nov 26, 2000 (Loren Petrich): | |
47 | + Added system for reloading textures only when their filenames change. | |
48 | + | |
49 | +Dec 17, 2000 (Loren Petrich): | |
50 | + Eliminated fog parameters from the preferences; | |
51 | + there is still a "fog present" switch, which is used to indicate | |
52 | + whether fog will not be suppressed. | |
53 | + | |
54 | +Apr 27, 2001 (Loren Petrich): | |
55 | + Modified the OpenGL fog support so as to enable below-liquid fogs | |
56 | + | |
57 | +Jul 8, 2001 (Loren Petrich): | |
58 | + Made it possible to read in silhouette bitmaps; one can now use the silhouette index | |
59 | + as a MML color-table index | |
60 | + | |
61 | +Aug 21, 2001 (Loren Petrich): | |
62 | + Adding support for 3D-model inhabitant objects | |
63 | + | |
64 | +Jan 25, 2002 (Br'fin (Jeremy Parsons)): | |
65 | + Added TARGET_API_MAC_CARBON for OpenGL.h, AGL.h | |
66 | + Removed QuickDraw3D support from Carbon | |
67 | + | |
68 | +Feb 5, 2002 (Br'fin (Jeremy Parsons)): | |
69 | + Refined OGL default preferences for Carbon | |
70 | +*/ | |
71 | + | |
72 | +#include <vector> | |
73 | +#include <string.h> | |
74 | +#include <math.h> | |
75 | +#include "cseries.h" | |
76 | + | |
77 | +#ifdef HAVE_OPENGL | |
78 | + | |
79 | +#ifdef __MVCPP__ | |
80 | +#include <windows.h> | |
81 | +#endif | |
82 | + | |
83 | +#include "OGL_Headers.h" | |
84 | +#include "OGL_Shader.h" | |
85 | + | |
86 | +#endif | |
87 | + | |
88 | +#include "shape_descriptors.h" | |
89 | +#include "OGL_Setup.h" | |
90 | +#include "ColorParser.h" | |
91 | +#include "OGL_LoadScreen.h" | |
92 | +#include "progress.h" | |
93 | + | |
94 | +// Whether or not OpenGL is present and usable | |
95 | +static bool _OGL_IsPresent = false; | |
96 | + | |
97 | +bool Using_sRGB = false; | |
98 | +bool Wanting_sRGB = false; | |
99 | +bool Bloom_sRGB = false; | |
100 | +bool npotTextures = false; // non-power-of-two | |
101 | + | |
102 | +// Initializer | |
103 | +bool OGL_Initialize() | |
104 | +{ | |
105 | +#ifdef HAVE_OPENGL | |
106 | +#if defined(mac) | |
107 | + // Cribbed from Apple's DrawSprocket documentation; | |
108 | + // look for OpenGL function | |
109 | + return (_OGL_IsPresent = ((Ptr)aglChoosePixelFormat != (Ptr)kUnresolvedCFragSymbolAddress)); | |
110 | + // return (_OGL_IsPresent = ((Ptr)glBegin != (Ptr)kUnresolvedCFragSymbolAddress)); | |
111 | +#elif defined(SDL) | |
112 | + // nothing to do | |
113 | +#if defined(__WIN32__) | |
114 | +// glewInit(); | |
115 | +#endif | |
116 | + | |
117 | + return _OGL_IsPresent = true; | |
118 | +#else | |
119 | +#error OGL_Initialize() not implemented for this platform | |
120 | +#endif | |
121 | +#else | |
122 | + return false; | |
123 | +#endif | |
124 | +} | |
125 | + | |
126 | +// Test for presence | |
127 | +bool OGL_IsPresent() {return _OGL_IsPresent;} | |
128 | + | |
129 | +bool OGL_CheckExtension(const std::string extension) { | |
130 | +#ifdef HAVE_OPENGL | |
131 | +#ifdef __WIN32__ | |
132 | + return glewIsSupported(extension.c_str()); | |
133 | +#else | |
134 | + char *extensions = (char *) glGetString(GL_EXTENSIONS); | |
135 | + if (!extensions) return false; | |
136 | + | |
137 | + while (*extensions) | |
138 | + { | |
139 | + unsigned int length = strcspn(extensions, " "); | |
140 | + | |
141 | + if (length == extension.size() && | |
142 | + strncmp(extension.c_str(), extensions, length) == 0) { | |
143 | + return true; | |
144 | + } | |
145 | + | |
146 | + extensions += length + 1; | |
147 | + } | |
148 | +#endif | |
149 | +#endif | |
150 | + return false; | |
151 | +} | |
152 | + | |
153 | +static int ogl_progress; | |
154 | +static int total_ogl_progress; | |
155 | +static bool show_ogl_progress = false; | |
156 | +static int32 last_update_tick; | |
157 | + | |
158 | +extern bool OGL_ClearScreen(); | |
159 | + | |
160 | +#ifdef HAVE_OPENGL | |
161 | +void OGL_StartProgress(int total_progress) | |
162 | +{ | |
163 | + ogl_progress = 0; | |
164 | + total_ogl_progress = total_progress; | |
165 | + if (!OGL_LoadScreen::instance()->Start()) | |
166 | + { | |
167 | + OGL_ClearScreen(); | |
168 | + open_progress_dialog(_loading, true); | |
169 | + } | |
170 | + show_ogl_progress = true; | |
171 | + last_update_tick = SDL_GetTicks(); | |
172 | +} | |
173 | + | |
174 | +void OGL_ProgressCallback(int delta_progress) | |
175 | +{ | |
176 | + if (!show_ogl_progress) return; | |
177 | + ogl_progress += delta_progress; | |
178 | + { | |
179 | + int32 current_ticks = SDL_GetTicks(); | |
180 | + if (current_ticks > last_update_tick + 33) | |
181 | + { | |
182 | + if (OGL_LoadScreen::instance()->Use()) | |
183 | + OGL_LoadScreen::instance()->Progress(100 * ogl_progress / total_ogl_progress); | |
184 | + else | |
185 | + draw_progress_bar(ogl_progress, total_ogl_progress); | |
186 | + last_update_tick = current_ticks; | |
187 | + } | |
188 | + } | |
189 | +} | |
190 | + | |
191 | +void OGL_StopProgress() | |
192 | +{ | |
193 | + show_ogl_progress = false; | |
194 | + if (OGL_LoadScreen::instance()->Use()) | |
195 | + OGL_LoadScreen::instance()->Stop(); | |
196 | + else | |
197 | + close_progress_dialog(); | |
198 | +} | |
199 | +#endif | |
200 | + | |
201 | +// Sensible defaults for the fog: | |
202 | +static OGL_FogData FogData[OGL_NUMBER_OF_FOG_TYPES] = | |
203 | +{ | |
204 | + {{0x8000,0x8000,0x8000},8,false,true}, | |
205 | + {{0x8000,0x8000,0x8000},8,false,true} | |
206 | +}; | |
207 | + | |
208 | + | |
209 | +// For flat landscapes: | |
210 | +const RGBColor DefaultLscpColors[4][2] = | |
211 | +{ | |
212 | + { | |
213 | + {0xffff, 0xffff, 0x6666}, // Day | |
214 | + {0x3333, 0x9999, 0xffff}, | |
215 | + }, | |
216 | + { | |
217 | + {0x1818, 0x1818, 0x1010}, // Night | |
218 | + {0x0808, 0x0808, 0x1010}, | |
219 | + }, | |
220 | + { | |
221 | + {0x6666, 0x6666, 0x6666}, // Moon | |
222 | + {0x0000, 0x0000, 0x0000}, | |
223 | + }, | |
224 | + { | |
225 | + {0x0000, 0x0000, 0x0000}, // Outer Space | |
226 | + {0x0000, 0x0000, 0x0000}, | |
227 | + }, | |
228 | +}; | |
229 | + | |
230 | + | |
231 | +// Set defaults | |
232 | +void OGL_SetDefaults(OGL_ConfigureData& Data) | |
233 | +{ | |
234 | + for (int k=0; k<OGL_NUMBER_OF_TEXTURE_TYPES; k++) | |
235 | + { | |
236 | + OGL_Texture_Configure& TxtrData = Data.TxtrConfigList[k]; | |
237 | + TxtrData.NearFilter = 1; // GL_LINEAR | |
238 | + if (k == OGL_Txtr_Wall || k == OGL_Txtr_Inhabitant) | |
239 | + TxtrData.FarFilter = 5; // GL_LINEAR_MIPMAP_LINEAR | |
240 | + else | |
241 | + TxtrData.FarFilter = 1; // GL_LINEAR | |
242 | + TxtrData.Resolution = 0; // 1x | |
243 | + TxtrData.ColorFormat = 0; // 32-bit color | |
244 | + TxtrData.MaxSize = 0; // Unlimited | |
245 | + } | |
246 | + | |
247 | + Data.ModelConfig.NearFilter = 1; | |
248 | + Data.ModelConfig.FarFilter = 5; | |
249 | + Data.ModelConfig.Resolution = 0; | |
250 | + Data.ModelConfig.ColorFormat = 0; | |
251 | + Data.ModelConfig.MaxSize = 0; | |
252 | + | |
253 | + // Reasonable default flags ("static" effect causes massive slowdown, so we turn it off) | |
254 | + Data.Flags = OGL_Flag_FlatStatic | OGL_Flag_Fader | OGL_Flag_Map | | |
255 | + OGL_Flag_HUD | OGL_Flag_LiqSeeThru | OGL_Flag_3D_Models | OGL_Flag_ZBuffer | | |
256 | + OGL_Flag_Fog; | |
257 | + | |
258 | + Data.AnisotropyLevel = 0.0; // off | |
259 | + Data.Multisamples = 0; // off | |
260 | + | |
261 | + Data.VoidColor = rgb_black; // Self-explanatory | |
262 | + for (int il=0; il<4; il++) | |
263 | + for (int ie=0; ie<2; ie++) | |
264 | + Data.LscpColors[il][ie] = DefaultLscpColors[il][ie]; | |
265 | + | |
266 | + Data.GeForceFix = false; | |
267 | + Data.WaitForVSync = true; | |
268 | + Data.Use_sRGB = false; | |
269 | + Data.Use_NPOT = false; | |
270 | +} | |
271 | + | |
272 | + | |
273 | +inline bool StringPresent(vector<char>& String) | |
274 | +{ | |
275 | + return (String.size() > 1); | |
276 | +} | |
277 | + | |
278 | +#ifdef HAVE_OPENGL | |
279 | + | |
280 | +GLint glMaxTextureSize = 0; | |
281 | +bool hasS3TC = false; | |
282 | + | |
283 | +void OGL_TextureOptionsBase::Load() | |
284 | +{ | |
285 | + FileSpecifier File; | |
286 | + | |
287 | + GLint maxTextureSize = glMaxTextureSize; | |
288 | + if (GetMaxSize()) | |
289 | + { | |
290 | + maxTextureSize = MIN(maxTextureSize, GetMaxSize()); | |
291 | + } | |
292 | + | |
293 | + int flags = npotTextures ? 0 : ImageLoader_ResizeToPowersOfTwo; | |
294 | + | |
295 | + if (Type >= 0 && Type < OGL_NUMBER_OF_TEXTURE_TYPES && Get_OGL_ConfigureData().TxtrConfigList[Type].FarFilter > 1 /* GL_LINEAR */) | |
296 | + { | |
297 | + flags |= ImageLoader_LoadMipMaps; | |
298 | + } | |
299 | + | |
300 | + if (hasS3TC) | |
301 | + { | |
302 | + flags |= ImageLoader_CanUseDXTC; | |
303 | + } | |
304 | + | |
305 | + if (Get_OGL_ConfigureData().GeForceFix) | |
306 | + { | |
307 | + flags |= ImageLoader_LoadDXTC1AsDXTC3; | |
308 | + } | |
309 | + | |
310 | + // Load the normal image with alpha channel | |
311 | + | |
312 | + // Check to see if loading needs to be done; | |
313 | + // it does not need to be if an image is present. | |
314 | + if (NormalImg.IsPresent()) return; | |
315 | + | |
316 | + NormalImg.Clear(); | |
317 | + | |
318 | + // Load the normal image if it has a filename specified for it | |
319 | + if (NormalColors != FileSpecifier() && NormalColors.Exists()) | |
320 | + { | |
321 | + if (!NormalImg.LoadFromFile(NormalColors,ImageLoader_Colors, flags | (NormalIsPremultiplied ? ImageLoader_ImageIsAlreadyPremultiplied : 0), actual_width, actual_height, maxTextureSize)) | |
322 | + { | |
323 | + // A texture must have a normal colored part | |
324 | + return; | |
325 | + } | |
326 | + } | |
327 | + else | |
328 | + { | |
329 | + return; | |
330 | + } | |
331 | + | |
332 | + // load a heightmap | |
333 | + if(OffsetMap != FileSpecifier() && OffsetMap.Exists()) { | |
334 | + if(!OffsetImg.LoadFromFile(OffsetMap, ImageLoader_Colors, flags | (NormalIsPremultiplied ? ImageLoader_ImageIsAlreadyPremultiplied : 0), actual_width, actual_height, maxTextureSize)) { | |
335 | + return; | |
336 | + } | |
337 | + } | |
338 | + | |
339 | + // Load the normal mask if it has a filename specified for it | |
340 | + if (NormalMask != FileSpecifier() && NormalMask.Exists()) | |
341 | + { | |
342 | + NormalImg.LoadFromFile(NormalMask,ImageLoader_Opacity, flags, actual_width, actual_height, maxTextureSize); | |
343 | + } | |
344 | + | |
345 | + if (maxTextureSize) | |
346 | + { | |
347 | + while (NormalImg.GetWidth() > maxTextureSize || NormalImg.GetHeight() > maxTextureSize) | |
348 | + { | |
349 | + if (!NormalImg.Minify()) break; | |
350 | + } | |
351 | + | |
352 | + if(OffsetImg.IsPresent()) { | |
353 | + while (OffsetImg.GetWidth() > maxTextureSize || OffsetImg.GetHeight() > maxTextureSize) { | |
354 | + if(!OffsetImg.Minify()) { break; } | |
355 | + } | |
356 | + } | |
357 | + } | |
358 | + | |
359 | + // Load the glow image with alpha channel | |
360 | + if (!GlowImg.IsPresent()) | |
361 | + { | |
362 | + GlowImg.Clear(); | |
363 | + | |
364 | + // Load the glow image if it has a filename specified for it | |
365 | + if (GlowColors != FileSpecifier() && GlowColors.Exists()) | |
366 | + { | |
367 | + if (GlowImg.LoadFromFile(GlowColors,ImageLoader_Colors, flags | (GlowIsPremultiplied ? ImageLoader_ImageIsAlreadyPremultiplied : 0), actual_width, actual_height, maxTextureSize)) | |
368 | + { | |
369 | + | |
370 | + // Load the glow mask if it has a | |
371 | + // filename specified for it; only | |
372 | + // loaded if an image has been loaded | |
373 | + // for it | |
374 | + if (GlowMask != FileSpecifier() && GlowMask.Exists()) | |
375 | + { | |
376 | + GlowImg.LoadFromFile(GlowMask,ImageLoader_Opacity, flags, actual_width, actual_height, maxTextureSize); | |
377 | + } | |
378 | + } | |
379 | + } | |
380 | + } | |
381 | + | |
382 | + if (GlowImg.IsPresent() && maxTextureSize) | |
383 | + { | |
384 | + while (GlowImg.GetWidth() > maxTextureSize || GlowImg.GetHeight() > maxTextureSize) | |
385 | + { | |
386 | + if (!GlowImg.Minify()) break; | |
387 | + } | |
388 | + } | |
389 | + | |
390 | + // The rest of the code is made simpler by these constraints: | |
391 | + // that the glow texture only be present if the normal texture is also present, | |
392 | + // and that the normal and glow textures have the same dimensions | |
393 | + if (NormalImg.IsPresent()) | |
394 | + { | |
395 | + int W0 = NormalImg.GetWidth(); | |
396 | + int W1 = GlowImg.GetWidth(); | |
397 | + int H0 = NormalImg.GetHeight(); | |
398 | + int H1 = GlowImg.GetHeight(); | |
399 | + if ((W1 != W0) || (H1 != H0)) GlowImg.Clear(); | |
400 | + } | |
401 | + else | |
402 | + { | |
403 | + GlowImg.Clear(); | |
404 | + } | |
405 | + | |
406 | +} | |
407 | + | |
408 | +void OGL_TextureOptionsBase::Unload() | |
409 | +{ | |
410 | + NormalImg.Clear(); | |
411 | + GlowImg.Clear(); | |
412 | + OffsetImg.Clear(); | |
413 | +} | |
414 | + | |
415 | +int OGL_TextureOptionsBase::GetMaxSize() | |
416 | +{ | |
417 | + if (Type >= 0 && Type < OGL_NUMBER_OF_TEXTURE_TYPES) | |
418 | + { | |
419 | + return Get_OGL_ConfigureData().TxtrConfigList[Type].MaxSize; | |
420 | + } | |
421 | + else | |
422 | + return 0; // Unlimited | |
423 | +} | |
424 | +#endif | |
425 | + | |
426 | +#ifdef HAVE_OPENGL | |
427 | + | |
428 | +int OGL_CountModelsImages(short Collection) | |
429 | +{ | |
430 | + return OGL_CountTextures(Collection) + OGL_CountModels(Collection); | |
431 | +} | |
432 | + | |
433 | +// for managing the model and image loading and unloading | |
434 | +void OGL_LoadModelsImages(short Collection) | |
435 | +{ | |
436 | + assert(Collection >= 0 && Collection < MAXIMUM_COLLECTIONS); | |
437 | + | |
438 | + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glMaxTextureSize); | |
439 | + hasS3TC = OGL_CheckExtension("GL_ARB_texture_compression") && OGL_CheckExtension("GL_EXT_texture_compression_s3tc"); | |
440 | + | |
441 | + // For wall/sprite images | |
442 | + OGL_LoadTextures(Collection); | |
443 | + | |
444 | + // For models, skins | |
445 | + bool UseModels = TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_3D_Models) ? true : false; | |
446 | + if (UseModels) | |
447 | + OGL_LoadModels(Collection); | |
448 | + else | |
449 | + OGL_UnloadModels(Collection); | |
450 | +} | |
451 | + | |
452 | +void OGL_UnloadModelsImages(short Collection) | |
453 | +{ | |
454 | + assert(Collection >= 0 && Collection < MAXIMUM_COLLECTIONS); | |
455 | + | |
456 | + // For wall/sprite images | |
457 | + OGL_UnloadTextures(Collection); | |
458 | + | |
459 | + // For models, skins | |
460 | + OGL_UnloadModels(Collection); | |
461 | +} | |
462 | + | |
463 | +#else | |
464 | + | |
465 | +void OGL_LoadModelsImages(short) | |
466 | +{ | |
467 | +} | |
468 | + | |
469 | +void OGL_UnloadModelsImages(short) | |
470 | +{ | |
471 | +} | |
472 | + | |
473 | +#endif // def HAVE_OPENGL | |
474 | + | |
475 | + | |
476 | +OGL_FogData *OGL_GetFogData(int Type) | |
477 | +{ | |
478 | + return GetMemberWithBounds(FogData,Type,OGL_NUMBER_OF_FOG_TYPES); | |
479 | +} | |
480 | + | |
481 | + | |
482 | +// XML-parsing stuff | |
483 | +OGL_FogData *OriginalFogData = NULL; | |
484 | +class XML_FogParser: public XML_ElementParser | |
485 | +{ | |
486 | + bool IsPresent[3]; | |
487 | + bool FogPresent, AffectsLandscapes; | |
488 | + float Depth; | |
489 | + short Type; | |
490 | + | |
491 | +public: | |
492 | + bool Start(); | |
493 | + bool HandleAttribute(const char *Tag, const char *Value); | |
494 | + bool AttributesDone(); | |
495 | + bool ResetValues(); | |
496 | + | |
497 | + XML_FogParser(): XML_ElementParser("fog") {} | |
498 | +}; | |
499 | + | |
500 | +bool XML_FogParser::Start() | |
501 | +{ | |
502 | + // back up old values first | |
503 | + if (!OriginalFogData) { | |
504 | + OriginalFogData = (OGL_FogData *) malloc(sizeof(OGL_FogData) * OGL_NUMBER_OF_FOG_TYPES); | |
505 | + assert(OriginalFogData); | |
506 | + for (unsigned i = 0; i < OGL_NUMBER_OF_FOG_TYPES; i++) | |
507 | + OriginalFogData[i] = FogData[i]; | |
508 | + } | |
509 | + | |
510 | + IsPresent[0] = IsPresent[1] = IsPresent[2] = false; | |
511 | + Type = 0; | |
512 | + return true; | |
513 | +} | |
514 | + | |
515 | +bool XML_FogParser::HandleAttribute(const char *Tag, const char *Value) | |
516 | +{ | |
517 | + if (StringsEqual(Tag,"on")) | |
518 | + { | |
519 | + if (ReadBooleanValueAsBool(Value,FogPresent)) | |
520 | + { | |
521 | + IsPresent[0] = true; | |
522 | + return true; | |
523 | + } | |
524 | + else return false; | |
525 | + } | |
526 | + else if (StringsEqual(Tag,"depth")) | |
527 | + { | |
528 | + if (ReadFloatValue(Value,Depth)) | |
529 | + { | |
530 | + IsPresent[1] = true; | |
531 | + return true; | |
532 | + } | |
533 | + else return false; | |
534 | + } | |
535 | + if (StringsEqual(Tag,"landscapes")) | |
536 | + { | |
537 | + if (ReadBooleanValueAsBool(Value,AffectsLandscapes)) | |
538 | + { | |
539 | + IsPresent[2] = true; | |
540 | + return true; | |
541 | + } | |
542 | + else return false; | |
543 | + } | |
544 | + else if (StringsEqual(Tag,"type")) | |
545 | + { | |
546 | + return ReadBoundedInt16Value(Value,Type,0,OGL_NUMBER_OF_FOG_TYPES-1); | |
547 | + } | |
548 | + UnrecognizedTag(); | |
549 | + return false; | |
550 | +} | |
551 | + | |
552 | +bool XML_FogParser::AttributesDone() | |
553 | +{ | |
554 | + OGL_FogData& Data = FogData[Type]; | |
555 | + if (IsPresent[0]) Data.IsPresent = FogPresent; | |
556 | + if (IsPresent[1]) Data.Depth = Depth; | |
557 | + if (IsPresent[2]) Data.AffectsLandscapes = AffectsLandscapes; | |
558 | + Color_SetArray(&Data.Color); | |
559 | + return true; | |
560 | +} | |
561 | + | |
562 | +bool XML_FogParser::ResetValues() | |
563 | +{ | |
564 | + if (OriginalFogData) { | |
565 | + for (unsigned i = 0; i < OGL_NUMBER_OF_FOG_TYPES; i++) | |
566 | + FogData[i] = OriginalFogData[i]; | |
567 | + free(OriginalFogData); | |
568 | + OriginalFogData = NULL; | |
569 | + } | |
570 | + return true; | |
571 | +} | |
572 | + | |
573 | +static XML_FogParser FogParser; | |
574 | + | |
575 | +static XML_ElementParser OpenGL_Parser("opengl"); | |
576 | + | |
577 | + | |
578 | +// XML-parser support: | |
579 | +XML_ElementParser *OpenGL_GetParser() | |
580 | +{ | |
581 | +#ifdef HAVE_OPENGL | |
582 | + OpenGL_Parser.AddChild(TextureOptions_GetParser()); | |
583 | + OpenGL_Parser.AddChild(Shader_GetParser()); | |
584 | + OpenGL_Parser.AddChild(TO_Clear_GetParser()); | |
585 | + | |
586 | + OpenGL_Parser.AddChild(ModelData_GetParser()); | |
587 | + OpenGL_Parser.AddChild(Mdl_Clear_GetParser()); | |
588 | +#endif | |
589 | + | |
590 | + FogParser.AddChild(Color_GetParser()); | |
591 | + OpenGL_Parser.AddChild(&FogParser); | |
592 | + | |
593 | + return &OpenGL_Parser; | |
594 | +} | |
595 | + | |
596 | +#ifdef HAVE_OPENGL | |
597 | +/* These don't belong here */ | |
598 | +void SglColor3f(GLfloat r, GLfloat g, GLfloat b) { | |
599 | + GLfloat ov[3] = {sRGB_frob(r), sRGB_frob(g), sRGB_frob(b)}; | |
600 | + glColor3fv(ov); | |
601 | +} | |
602 | + | |
603 | +void SglColor3fv(const GLfloat* iv) { | |
604 | + GLfloat ov[3] = {sRGB_frob(iv[0]), sRGB_frob(iv[1]), sRGB_frob(iv[2])}; | |
605 | + glColor3fv(ov); | |
606 | +} | |
607 | + | |
608 | +void SglColor3ub(GLubyte r, GLubyte g, GLubyte b) { | |
609 | + GLfloat ov[3] = {sRGB_frob(r*(1.f/255.f)), sRGB_frob(g*(1.f/255.f)), sRGB_frob(b*(1.f/255.f))}; | |
610 | + glColor3fv(ov); | |
611 | +} | |
612 | + | |
613 | +void SglColor3us(GLushort r, GLushort g, GLushort b) { | |
614 | + GLfloat ov[3] = {sRGB_frob(r*(1.f/65535.f)), sRGB_frob(g*(1.f/65535.f)), sRGB_frob(b*(1.f/65535.f))}; | |
615 | + glColor3fv(ov); | |
616 | +} | |
617 | + | |
618 | +void SglColor3usv(const GLushort* iv) { | |
619 | + GLfloat ov[3] = {sRGB_frob(iv[0]*(1.f/65535.f)), sRGB_frob(iv[1]*(1.f/65535.f)), sRGB_frob(iv[2]*(1.f/65535.f))}; | |
620 | + glColor3fv(ov); | |
621 | +} | |
622 | + | |
623 | +void SglColor4f(GLfloat r, GLfloat g, GLfloat b, GLfloat a) { | |
624 | + GLfloat ov[4] = {sRGB_frob(r), sRGB_frob(g), sRGB_frob(b), a}; | |
625 | + glColor4fv(ov); | |
626 | +} | |
627 | + | |
628 | +void SglColor4fv(const GLfloat* iv) { | |
629 | + GLfloat ov[4] = {sRGB_frob(iv[0]), sRGB_frob(iv[1]), sRGB_frob(iv[2]), iv[3]}; | |
630 | + glColor4fv(ov); | |
631 | +} | |
632 | + | |
633 | +void SglColor4usv(const GLushort* iv) { | |
634 | + GLfloat ov[4] = {sRGB_frob(iv[0]*(1.f/65535.f)), sRGB_frob(iv[1]*(1.f/65535.f)), sRGB_frob(iv[2]*(1.f/65535.f)), iv[3]*(1.f/65535.f)}; | |
635 | + glColor4fv(ov); | |
636 | +} | |
637 | +#endif |
@@ -1,1396 +1,1396 @@ | ||
1 | -/* | |
2 | -SCOTTISH_TEXTURES.C | |
3 | - | |
4 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
5 | - and the "Aleph One" developers. | |
6 | - | |
7 | - This program is free software; you can redistribute it and/or modify | |
8 | - it under the terms of the GNU General Public License as published by | |
9 | - the Free Software Foundation; either version 3 of the License, or | |
10 | - (at your option) any later version. | |
11 | - | |
12 | - This program is distributed in the hope that it will be useful, | |
13 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | - GNU General Public License for more details. | |
16 | - | |
17 | - This license is contained in the file "COPYING", | |
18 | - which is included with this source code; it is available online at | |
19 | - http://www.gnu.org/licenses/gpl.html | |
20 | - | |
21 | -Wednesday, April 20, 1994 9:35:36 AM | |
22 | - | |
23 | -this is not your fatherユs texture mapping library. | |
24 | -(in fact it isnユt yours either, dillweed) | |
25 | - | |
26 | -Wednesday, April 20, 1994 3:39:21 PM | |
27 | - vertical repeats would be difficult because it would require testing repeats in the | |
28 | - innermost loop of the pixel mapper (a compare and branch we can do without). | |
29 | -Saturday, April 23, 1994 10:42:41 AM | |
30 | - (on the plane to santa clara) finished the slower version of the trapezoid mapper (we | |
31 | - need to handle stretching with a degenerate switch statement like marathon used to) but | |
32 | - the whole sampling process is now mathematically correct except for the squared function | |
33 | - we use to calculate the x texture position and the shading table (but this is accurate to | |
34 | - within 1/64k and doesn't accumulate error so who cares). | |
35 | -Sunday, April 24, 1994 10:12:47 AM | |
36 | - (waiting for the CGDC to start at 9:00 PST) added all polygon stuff. it struck me this | |
37 | - morning that clipping against the view cone must be deterministic (that is, line segments | |
38 | - of polygons and line segments of walls must be clipped in the same manner) or our | |
39 | - edges won't meet up. ordered dither darkening will look really cool but will be slow in c. | |
40 | -Sunday, April 24, 1994 11:21:47 PM | |
41 | - still need transparent trapezoids, dither darkening, faster DDA for trapezoid mapping. | |
42 | -Wednesday, April 27, 1994 9:49:55 AM | |
43 | - i'm just looking for one divine hammer (to bang it all day). solid polygons are currently | |
44 | - unaffected by darkening. i'm not entirely certain we'll even use them. | |
45 | -Sunday, May 8, 1994 8:32:11 AM | |
46 | - LISPユs lexical contours kick C firmly and painfully in the ass. everything is fast now | |
47 | - except the landscape mapper which has just been routed and is in full retreat. | |
48 | -Friday, May 13, 1994 10:05:08 AM | |
49 | - low-level unification of trapezoids and rectangles, transparent runs in shapes are run-length | |
50 | - encoded now. maintaining run tables was slower than generating d, delta_d and delta_d_prime | |
51 | - and using them on the fly. | |
52 | -Wednesday, May 18, 1994 2:16:26 PM | |
53 | - scope matters (at WWDC). | |
54 | -Sunday, May 22, 1994 12:32:02 PM | |
55 | - drawing things in column order to cached (i.e., non-screen) memory is like crapping in the | |
56 | - data cache, right? maybe drawing rectangles in column-order wasn't such a great idea after all. | |
57 | - it also occurs to me that i know nothing about how to order instructions for the ユ040 pipelines. | |
58 | -Thursday, June 16, 1994 9:56:14 PM | |
59 | - modified _render_textured_polygon_line to handle elevation. | |
60 | -Thursday, July 7, 1994 1:23:09 PM | |
61 | - changed MAXIMUM_SCRATCH_TABLE_ENTRIES from 4k to 1200. Modified render code to work as well, | |
62 | - now the problem is floor/ceiling matching with trapezoids, which should fall out with the | |
63 | - rewrite... | |
64 | -Tuesday, July 26, 1994 3:42:16 PM | |
65 | - OBSOLETEユed nearly the entire file (fixed_pixels are no more). rewriting texture_rectangle. | |
66 | - will do 16bit mapping, soon. a while ago i rewrote everything in 68k. | |
67 | -Friday, September 16, 1994 6:03:11 PM (Jason') | |
68 | - texture_rectangle() now respects top and bottom clips | |
69 | -Tuesday, September 20, 1994 9:58:30 PM (Jason') | |
70 | - if weユre so close to a rectangle that n>LARGEST_N then we donユt draw anything | |
71 | -Wednesday, October 26, 1994 3:18:59 PM (Jason) | |
72 | - for non-convex or otherwise weird lines (dx<=0, dy<=0) we donユt draw anything (somebodyユll | |
73 | - notice that for sure). | |
74 | -Friday, November 4, 1994 7:35:48 PM (Jason') | |
75 | - pretexture_horizontal_polygon_lines() now respects the (x,y) polygon origin and uses z as height. | |
76 | - | |
77 | -Jan 30, 2000 (Loren Petrich): | |
78 | - Added some typecasts | |
79 | - | |
80 | -Feb. 4, 2000 (Loren Petrich): | |
81 | - Changed halt() to assert(false) for better debugging | |
82 | - | |
83 | -Mar 24, 2000 (Loren Petrich): | |
84 | - Using a special "landscape yaw" for the landscape texturing, so that the landscape center | |
85 | - will stay put. | |
86 | - | |
87 | -May 23, 2000 (Loren Petrich): | |
88 | - Adding support for different size scales for landscapes | |
89 | - | |
90 | -Jul 6, 2000 (Loren Petrich): | |
91 | - Added some slop to MAXIMUM_SCRATCH_TABLE_ENTRIES, because displays are now bigger; | |
92 | - its size got upped by 2 | |
93 | - | |
94 | -Aug 9, 2000 (Loren Petrich): | |
95 | - Rasterizer_SW object introduced (software subclass of rasterizer object) | |
96 | - | |
97 | -May 16, 2002 (Woody Zenfell): | |
98 | - MSVC doesn't like "void f(); void g() { return f(); }"... fixed. | |
99 | -*/ | |
100 | - | |
101 | -/* | |
102 | -rectangle shrinking has vertical error and appears to randomly shear the bitmap | |
103 | -pretexture_horizontal_polygon_lines() has integer error in large height cases | |
104 | - | |
105 | -_static_transfer doesnユt work for ceilings and floors (because they call the wall mapper) | |
106 | -build_y_table and build_x_table could both be sped up in nearly-horizontal and nearly-vertical cases (respectively) | |
107 | -_pretexture_vertical_polygon_lines() takes up to half the time _texture_vertical_polygon_lines() does | |
108 | -not only that, but texture_horizontal_polygon() is actually faster than texture_vertical_polygon() | |
109 | - | |
110 | -//calculate_shading_table() needs to be inlined in a macro | |
111 | -*/ | |
112 | - | |
113 | -#include "cseries.h" | |
114 | -#if defined(__GNUC__) | |
115 | -#ifndef DEBUG_FAST_CODE | |
116 | -#undef DEBUG | |
117 | -#undef assert | |
118 | -#define assert(x) | |
119 | -#undef vassert | |
120 | -#define vassert(x...) | |
121 | -#undef csprintf | |
122 | -#define csprintf(x...) | |
123 | -#undef vhalt | |
124 | -#define vhalt(x...) | |
125 | -#endif | |
126 | -#endif | |
127 | -#include "render.h" | |
128 | -#include "Rasterizer_SW.h" | |
129 | - | |
130 | -#include <stdlib.h> | |
131 | -#include <limits.h> | |
132 | - | |
133 | -#include "preferences.h" | |
134 | -#include "SW_Texture_Extras.h" | |
135 | - | |
136 | - | |
137 | -#ifdef env68k | |
138 | -#pragma segment texture | |
139 | -#endif | |
140 | - | |
141 | -#ifdef env68k | |
142 | -#define EXTERNAL | |
143 | -#endif | |
144 | - | |
145 | -/* ---------- constants */ | |
146 | - | |
147 | -// LP change: boosted to cope with big displays | |
148 | -#define MAXIMUM_SCRATCH_TABLE_ENTRIES 2048 | |
149 | -#define MAXIMUM_PRECALCULATION_TABLE_ENTRY_SIZE (MAX(sizeof(_vertical_polygon_data), sizeof(_horizontal_polygon_line_data))) | |
150 | - | |
151 | -#define SHADE_TO_SHADING_TABLE_INDEX(shade) ((shade)>>(FIXED_FRACTIONAL_BITS-shading_table_fractional_bits)) | |
152 | -#define DEPTH_TO_SHADE(d) (((_fixed)(d))<<(FIXED_FRACTIONAL_BITS-WORLD_FRACTIONAL_BITS-3)) | |
153 | - | |
154 | -#define LARGEST_N 24 | |
155 | - | |
156 | -/* ---------- texture horizontal polygon */ | |
157 | - | |
158 | -#define HORIZONTAL_WIDTH_SHIFT 7 /* 128 (8 for 256) */ | |
159 | -#define HORIZONTAL_HEIGHT_SHIFT 7 /* 128 */ | |
160 | -#define HORIZONTAL_FREE_BITS (32-TRIG_SHIFT-WORLD_FRACTIONAL_BITS) | |
161 | -#define HORIZONTAL_WIDTH_DOWNSHIFT (32-HORIZONTAL_WIDTH_SHIFT) | |
162 | -#define HORIZONTAL_HEIGHT_DOWNSHIFT (32-HORIZONTAL_HEIGHT_SHIFT) | |
163 | - | |
164 | -struct _horizontal_polygon_line_header | |
165 | -{ | |
166 | - int32 y_downshift; | |
167 | -}; | |
168 | - | |
169 | -struct _horizontal_polygon_line_data | |
170 | -{ | |
171 | - uint32 source_x, source_y; | |
172 | - uint32 source_dx, source_dy; | |
173 | - | |
174 | - void *shading_table; | |
175 | -}; | |
176 | - | |
177 | -/* ---------- texture vertical polygon */ | |
178 | - | |
179 | -#define VERTICAL_TEXTURE_WIDTH 128 | |
180 | -#define VERTICAL_TEXTURE_WIDTH_BITS 7 | |
181 | -#define VERTICAL_TEXTURE_WIDTH_FRACTIONAL_BITS (FIXED_FRACTIONAL_BITS-VERTICAL_TEXTURE_WIDTH_BITS) | |
182 | -#define VERTICAL_TEXTURE_ONE (1<<VERTICAL_TEXTURE_WIDTH_FRACTIONAL_BITS) | |
183 | -#define VERTICAL_TEXTURE_FREE_BITS FIXED_FRACTIONAL_BITS | |
184 | -#define VERTICAL_TEXTURE_DOWNSHIFT (32-VERTICAL_TEXTURE_WIDTH_BITS) | |
185 | - | |
186 | -//AS: Seven! It's Everywhere! | |
187 | -#define HORIZONTAL_WIDTH_SHIFT 7 /* 128 (8 for 256) */ | |
188 | -#define HORIZONTAL_HEIGHT_SHIFT 7 /* 128 */ | |
189 | -#define HORIZONTAL_FREE_BITS (32-TRIG_SHIFT-WORLD_FRACTIONAL_BITS) | |
190 | -#define HORIZONTAL_WIDTH_DOWNSHIFT (32-HORIZONTAL_WIDTH_SHIFT) | |
191 | -#define HORIZONTAL_HEIGHT_DOWNSHIFT (32-HORIZONTAL_HEIGHT_SHIFT) | |
192 | - | |
193 | -struct _vertical_polygon_data | |
194 | -{ | |
195 | - int16 downshift; | |
196 | - int16 x0; | |
197 | - int16 width; | |
198 | - | |
199 | - int16 pad; | |
200 | -}; | |
201 | - | |
202 | -struct _vertical_polygon_line_data | |
203 | -{ | |
204 | - void *shading_table; | |
205 | - pixel8 *texture; | |
206 | - int32 texture_y, texture_dy; | |
207 | -}; | |
208 | - | |
209 | -/* ---------- macros */ | |
210 | - | |
211 | -// i0 + i1 == MAX(i0, i1) + MIN(i0, i1)/2 | |
212 | -//#define calculate_shading_table(result, view, shading_tables, depth, ambient_shade) | |
213 | -static void calculate_shading_table(void * &result,view_data *view, void *shading_tables, short depth,_fixed ambient_shade) | |
214 | -{ | |
215 | - short table_index; | |
216 | - _fixed shade; | |
217 | - | |
218 | - if ((ambient_shade)<0) | |
219 | - { | |
220 | - table_index= SHADE_TO_SHADING_TABLE_INDEX(-(ambient_shade)); | |
221 | - } | |
222 | - else | |
223 | - { | |
224 | - shade= (view)->maximum_depth_intensity - DEPTH_TO_SHADE(depth); | |
225 | - shade= PIN(shade, 0, FIXED_ONE); | |
226 | - table_index= SHADE_TO_SHADING_TABLE_INDEX((ambient_shade>shade) ? (ambient_shade + (shade>>1)) : (shade + (ambient_shade>>1))); | |
227 | - } | |
228 | - | |
229 | - switch (bit_depth) | |
230 | - { | |
231 | - case 8: result= ((byte*)(shading_tables)) + MAXIMUM_SHADING_TABLE_INDEXES*sizeof(pixel8)* | |
232 | - CEILING(table_index, number_of_shading_tables-1); break; | |
233 | - case 16: result= ((byte*)(shading_tables)) + MAXIMUM_SHADING_TABLE_INDEXES*sizeof(pixel16)* | |
234 | - CEILING(table_index, number_of_shading_tables-1); break; | |
235 | - case 32: result= ((byte*)(shading_tables)) + MAXIMUM_SHADING_TABLE_INDEXES*sizeof(pixel32)* | |
236 | - CEILING(table_index, number_of_shading_tables-1); break; | |
237 | - } | |
238 | -} | |
239 | - | |
240 | -/* ---------- globals */ | |
241 | - | |
242 | -/* these tables are used by the polygon rasterizer (to store the x-coordinates of the left and | |
243 | - right lines of the current polygon), the trapezoid rasterizer (to store the y-coordinates | |
244 | - of the top and bottom of the current trapezoid) and the rectangle mapper (for itユs | |
245 | - vertical and if necessary horizontal distortion tables). these are not necessary as | |
246 | - globals, just as global storage. */ | |
247 | -static short *scratch_table0 = NULL, *scratch_table1 = NULL; | |
248 | -static void *precalculation_table = NULL; | |
249 | - | |
250 | -static uint16 texture_random_seed= 6906; | |
251 | - | |
252 | -/* ---------- private prototypes */ | |
253 | - | |
254 | -static void _pretexture_horizontal_polygon_lines(struct polygon_definition *polygon, | |
255 | - struct bitmap_definition *screen, struct view_data *view, struct _horizontal_polygon_line_data *data, | |
256 | - short y0, short *x0_table, short *x1_table, short line_count); | |
257 | - | |
258 | -static void _pretexture_vertical_polygon_lines(struct polygon_definition *polygon, | |
259 | - struct bitmap_definition *screen, struct view_data *view, struct _vertical_polygon_data *data, | |
260 | - short x0, short *y0_table, short *y1_table, short line_count); | |
261 | - | |
262 | -static short *build_x_table(short *table, short x0, short y0, short x1, short y1); | |
263 | -static short *build_y_table(short *table, short x0, short y0, short x1, short y1); | |
264 | - | |
265 | -static void _prelandscape_horizontal_polygon_lines(struct polygon_definition *polygon, | |
266 | - struct bitmap_definition *screen, struct view_data *view, struct _horizontal_polygon_line_data *data, | |
267 | - short y0, short *x0_table, short *x1_table, short line_count); | |
268 | - | |
269 | -/* ---------- code */ | |
270 | - | |
271 | - | |
272 | -// LP addition: | |
273 | -// Find the next lower power of 2, and return the exponent | |
274 | -//AS: p isn't needed | |
275 | -inline int NextLowerExponent(int n) | |
276 | -{ | |
277 | - int xp = 0; | |
278 | - while(n > 1) {n >>= 1; xp++;} | |
279 | - return xp; | |
280 | -} | |
281 | - | |
282 | -#include "low_level_textures.h" | |
283 | - | |
284 | -/* set aside memory at launch for two line tables (remember, we precalculate all the y-values | |
285 | - for trapezoids and two lines worth of x-values for polygons before mapping them) */ | |
286 | -void allocate_texture_tables( | |
287 | - void) | |
288 | -{ | |
289 | - scratch_table0= new short[MAXIMUM_SCRATCH_TABLE_ENTRIES]; | |
290 | - scratch_table1= new short[MAXIMUM_SCRATCH_TABLE_ENTRIES]; | |
291 | - precalculation_table= (void*)new char[MAXIMUM_PRECALCULATION_TABLE_ENTRY_SIZE*MAXIMUM_SCRATCH_TABLE_ENTRIES]; | |
292 | - assert(scratch_table0&&scratch_table1&&precalculation_table); | |
293 | -} | |
294 | - | |
295 | -void Rasterizer_SW_Class::texture_horizontal_polygon(polygon_definition& textured_polygon) | |
296 | -{ | |
297 | - polygon_definition *polygon = &textured_polygon; // Reference to pointer | |
298 | - short vertex, highest_vertex, lowest_vertex; | |
299 | - point2d *vertices= polygon->vertices; | |
300 | - | |
301 | - assert(polygon->vertex_count>=MINIMUM_VERTICES_PER_SCREEN_POLYGON&&polygon->vertex_count<MAXIMUM_VERTICES_PER_SCREEN_POLYGON); | |
302 | - | |
303 | - /* if we get static, tinted or landscaped transfer modes punt to the vertical polygon mapper */ | |
304 | - if (polygon->transfer_mode == _static_transfer) { | |
305 | - texture_vertical_polygon(textured_polygon); | |
306 | - return; | |
307 | - } | |
308 | - | |
309 | - /* locate the vertically highest (closest to zero) and lowest (farthest from zero) vertices */ | |
310 | - highest_vertex= lowest_vertex= 0; | |
311 | - for (vertex= 0; vertex<polygon->vertex_count; ++vertex) | |
312 | - { | |
313 | - if (!(vertices[vertex].x>=0&&vertices[vertex].x<=screen->width&&vertices[vertex].y>=0&&vertices[vertex].y<=screen->height)) | |
314 | - { | |
315 | - // dprintf("vertex #%d/#%d out of bounds:;dm %x %x;g;", vertex, polygon->vertex_count, polygon->vertices, polygon->vertex_count*sizeof(point2d)); | |
316 | - return; | |
317 | - } | |
318 | - if (vertices[vertex].y<vertices[highest_vertex].y) highest_vertex= vertex; | |
319 | - else if (vertices[vertex].y>vertices[lowest_vertex].y) lowest_vertex= vertex; | |
320 | - } | |
321 | - | |
322 | - /* if this polygon is not a horizontal line, draw it */ | |
323 | - if (highest_vertex!=lowest_vertex) | |
324 | - { | |
325 | - short left_line_count, right_line_count, total_line_count; | |
326 | - short aggregate_left_line_count, aggregate_right_line_count, aggregate_total_line_count; | |
327 | - short left_vertex, right_vertex; | |
328 | - short *left_table= scratch_table0, *right_table= scratch_table1; | |
329 | - | |
330 | - left_line_count= right_line_count= 0; /* zero counts so the left and right lines get initialized */ | |
331 | - aggregate_left_line_count= aggregate_right_line_count= 0; /* weユve precalculated nothing initially */ | |
332 | - left_vertex= right_vertex= highest_vertex; /* both sides start at the highest vertex */ | |
333 | - total_line_count= vertices[lowest_vertex].y-vertices[highest_vertex].y; /* calculate vertical line count */ | |
334 | - | |
335 | - assert(total_line_count<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* make sure we have enough scratch space */ | |
336 | - | |
337 | - /* precalculate high and low y-coordinates for every x-coordinate */ | |
338 | - aggregate_total_line_count= total_line_count; | |
339 | - while (total_line_count>0) | |
340 | - { | |
341 | - | |
342 | - /* if weユre out of scan lines on the left side, get a new vertex and build a table | |
343 | - of x-coordinates so we can walk toward the new vertex */ | |
344 | - if (left_line_count<=0) | |
345 | - { | |
346 | - do /* counter-clockwise vertex search */ | |
347 | - { | |
348 | - vertex= left_vertex ? (left_vertex-1) : (polygon->vertex_count-1); | |
349 | - left_line_count= vertices[vertex].y-vertices[left_vertex].y; | |
350 | - if (!build_x_table(left_table+aggregate_left_line_count, vertices[left_vertex].x, vertices[left_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
351 | - aggregate_left_line_count+= left_line_count; | |
352 | - left_vertex= vertex; | |
353 | -// dprintf("add %d left", left_line_count); | |
354 | - } | |
355 | - while (!left_line_count); | |
356 | - } | |
357 | - | |
358 | - /* if weユre out of scan lines on the right side, get a new vertex and build a table | |
359 | - of x-coordinates so we can walk toward the new vertex */ | |
360 | - if (right_line_count<=0) | |
361 | - { | |
362 | - do /* clockwise vertex search */ | |
363 | - { | |
364 | - vertex= (right_vertex==polygon->vertex_count-1) ? 0 : (right_vertex+1); | |
365 | - right_line_count= vertices[vertex].y-vertices[right_vertex].y; | |
366 | - if (!build_x_table(right_table+aggregate_right_line_count, vertices[right_vertex].x, vertices[right_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
367 | - aggregate_right_line_count+= right_line_count; | |
368 | - right_vertex= vertex; | |
369 | -// dprintf("add %d right", right_line_count); | |
370 | - } | |
371 | - while (!right_line_count); | |
372 | - } | |
373 | - //AS: moving delta declaration up to where it's needed. Isn't C++ wonderful? | |
374 | - /* advance by the minimum of left_line_count and right_line_count */ | |
375 | - short delta= MIN(left_line_count, right_line_count); | |
376 | - assert(delta); | |
377 | -// dprintf("tc=%d lc=%d rc=%d delta=%d", total_line_count, left_line_count, right_line_count, delta); | |
378 | - total_line_count-= delta; | |
379 | - left_line_count-= delta; | |
380 | - right_line_count-= delta; | |
381 | - | |
382 | - assert(delta||!total_line_count); /* if our delta is zero, weユd better be out of lines */ | |
383 | - } | |
384 | - | |
385 | - /* make sure every coordinate is accounted for in our tables */ | |
386 | - assert(aggregate_right_line_count==aggregate_total_line_count); | |
387 | - assert(aggregate_left_line_count==aggregate_total_line_count); | |
388 | - | |
389 | - /* precalculate mode-specific data */ | |
390 | - switch (polygon->transfer_mode) | |
391 | - { | |
392 | - case _textured_transfer: | |
393 | - _pretexture_horizontal_polygon_lines(polygon, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
394 | - vertices[highest_vertex].y, left_table, right_table, | |
395 | - aggregate_total_line_count); | |
396 | - break; | |
397 | - | |
398 | - case _big_landscaped_transfer: | |
399 | - _prelandscape_horizontal_polygon_lines(polygon, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
400 | - vertices[highest_vertex].y, left_table, right_table, | |
401 | - aggregate_total_line_count); | |
402 | - break; | |
403 | - | |
404 | - default: | |
405 | - vhalt(csprintf(temporary, "horizontal_polygons dont support mode #%d", polygon->transfer_mode)); | |
406 | - } | |
407 | - | |
408 | - /* render all lines */ | |
409 | - switch (bit_depth) | |
410 | - { | |
411 | - case 8: | |
412 | - switch (polygon->transfer_mode) | |
413 | - { | |
414 | - | |
415 | - case _textured_transfer: | |
416 | - texture_horizontal_polygon_lines<pixel8, _sw_alpha_off>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
417 | - vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
418 | - break; | |
419 | - case _big_landscaped_transfer: | |
420 | - landscape_horizontal_polygon_lines<pixel8>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
421 | - vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
422 | - break; | |
423 | - | |
424 | - default: | |
425 | - assert(false); | |
426 | - break; | |
427 | - } | |
428 | - break; | |
429 | - | |
430 | - case 16: | |
431 | - switch (polygon->transfer_mode) | |
432 | - { | |
433 | - case _textured_transfer: | |
434 | - { | |
435 | - SW_Texture *sw_texture = 0; | |
436 | - if (graphics_preferences->software_alpha_blending) | |
437 | - { | |
438 | - sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
439 | - } | |
440 | - if (sw_texture && !polygon->VoidPresent && sw_texture->opac_type()) | |
441 | - { | |
442 | - if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) { | |
443 | - texture_horizontal_polygon_lines<pixel16, _sw_alpha_fast>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
444 | - } | |
445 | - else if (graphics_preferences->software_alpha_blending == _sw_alpha_nice) { | |
446 | - texture_horizontal_polygon_lines<pixel16, _sw_alpha_nice>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *) precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count, sw_texture->opac_table()); | |
447 | - } | |
448 | - } else { | |
449 | - texture_horizontal_polygon_lines<pixel16, _sw_alpha_off>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
450 | - vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
451 | - } | |
452 | - } | |
453 | - break; | |
454 | - | |
455 | - case _big_landscaped_transfer: | |
456 | - landscape_horizontal_polygon_lines<pixel16>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
457 | - vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
458 | - break; | |
459 | - default: | |
460 | - assert(false); | |
461 | - break; | |
462 | - } | |
463 | - break; | |
464 | - | |
465 | - case 32: | |
466 | - switch (polygon->transfer_mode) | |
467 | - { | |
468 | - case _textured_transfer: | |
469 | - { | |
470 | - SW_Texture *sw_texture = 0; | |
471 | - if (graphics_preferences->software_alpha_blending) | |
472 | - { | |
473 | - sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
474 | - } | |
475 | - if (sw_texture && sw_texture->opac_type() && !polygon->VoidPresent) | |
476 | - { | |
477 | - if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) | |
478 | - { | |
479 | - texture_horizontal_polygon_lines<pixel32, _sw_alpha_fast>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
480 | - } | |
481 | - else if (graphics_preferences->software_alpha_blending = _sw_alpha_nice) | |
482 | - { | |
483 | - texture_horizontal_polygon_lines<pixel32, _sw_alpha_nice>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *) precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count, sw_texture->opac_table()); | |
484 | - } | |
485 | - } | |
486 | - else | |
487 | - { | |
488 | - texture_horizontal_polygon_lines<pixel32, _sw_alpha_off>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
489 | - vertices[highest_vertex].y, left_table, right_table, | |
490 | - aggregate_total_line_count); | |
491 | - } | |
492 | - } | |
493 | - break; | |
494 | - case _big_landscaped_transfer: | |
495 | - landscape_horizontal_polygon_lines<pixel32>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
496 | - vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
497 | - break; | |
498 | - | |
499 | - default: | |
500 | - assert(false); | |
501 | - break; | |
502 | - } | |
503 | - break; | |
504 | - | |
505 | - default: | |
506 | - assert(false); | |
507 | - break; | |
508 | - } | |
509 | - } | |
510 | -} | |
511 | - | |
512 | -void Rasterizer_SW_Class::texture_vertical_polygon(polygon_definition& textured_polygon) | |
513 | -{ | |
514 | - polygon_definition *polygon = &textured_polygon; // Reference to pointer | |
515 | - short vertex, highest_vertex, lowest_vertex; | |
516 | - point2d *vertices= polygon->vertices; | |
517 | - | |
518 | - assert(polygon->vertex_count>=MINIMUM_VERTICES_PER_SCREEN_POLYGON&&polygon->vertex_count<MAXIMUM_VERTICES_PER_SCREEN_POLYGON); | |
519 | - | |
520 | - if (polygon->transfer_mode == _big_landscaped_transfer) { | |
521 | - texture_horizontal_polygon(textured_polygon); | |
522 | - return; | |
523 | - } | |
524 | - | |
525 | - /* locate the horizontally highest (closest to zero) and lowest (farthest from zero) vertices */ | |
526 | - highest_vertex= lowest_vertex= 0; | |
527 | - for (vertex=1;vertex<polygon->vertex_count;++vertex) | |
528 | - { | |
529 | - if (vertices[vertex].x<vertices[highest_vertex].x) highest_vertex= vertex; | |
530 | - if (vertices[vertex].x>vertices[lowest_vertex].x) lowest_vertex= vertex; | |
531 | - } | |
532 | - | |
533 | - for (vertex=0;vertex<polygon->vertex_count;++vertex) | |
534 | - { | |
535 | - if (!(vertices[vertex].x>=0&&vertices[vertex].x<=screen->width&&vertices[vertex].y>=0&&vertices[vertex].y<=screen->height)) | |
536 | - { | |
537 | -// dprintf("vertex #%d/#%d out of bounds:;dm %x %x;g;", vertex, polygon->vertex_count, polygon->vertices, polygon->vertex_count*sizeof(point2d)); | |
538 | - return; | |
539 | - } | |
540 | - } | |
541 | - | |
542 | - /* if this polygon is not a vertical line, draw it */ | |
543 | - if (highest_vertex!=lowest_vertex) | |
544 | - { | |
545 | - short left_line_count, right_line_count, total_line_count; | |
546 | - short aggregate_left_line_count, aggregate_right_line_count, aggregate_total_line_count; | |
547 | - short left_vertex, right_vertex; | |
548 | - short *left_table= scratch_table0, *right_table= scratch_table1; | |
549 | - | |
550 | - left_line_count= right_line_count= 0; /* zero counts so the left and right lines get initialized */ | |
551 | - aggregate_left_line_count= aggregate_right_line_count= 0; /* weユve precalculated nothing initially */ | |
552 | - left_vertex= right_vertex= highest_vertex; /* both sides start at the highest vertex */ | |
553 | - total_line_count= vertices[lowest_vertex].x-vertices[highest_vertex].x; /* calculate vertical line count */ | |
554 | - | |
555 | - assert(total_line_count<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* make sure we have enough scratch space */ | |
556 | - | |
557 | - /* precalculate high and low y-coordinates for every x-coordinate */ | |
558 | - aggregate_total_line_count= total_line_count; | |
559 | - while (total_line_count>0) | |
560 | - { | |
561 | - /* if weユre out of scan lines on the left side, get a new vertex and build a table | |
562 | - of y-coordinates so we can walk toward the new vertex */ | |
563 | - if (left_line_count<=0) | |
564 | - { | |
565 | - do /* clockwise vertex search */ | |
566 | - { | |
567 | - vertex= (left_vertex==polygon->vertex_count-1) ? 0 : (left_vertex+1); | |
568 | - left_line_count= vertices[vertex].x-vertices[left_vertex].x; | |
569 | -// dprintf("left line (%d,%d) to (%d,%d) for %d points", vertices[left_vertex].x, vertices[left_vertex].y, vertices[vertex].x, vertices[vertex].y, left_line_count); | |
570 | - if (!build_y_table(left_table+aggregate_left_line_count, vertices[left_vertex].x, vertices[left_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
571 | - aggregate_left_line_count+= left_line_count; | |
572 | - left_vertex= vertex; | |
573 | - } | |
574 | - while (!left_line_count); | |
575 | - } | |
576 | - | |
577 | - /* if weユre out of scan lines on the right side, get a new vertex and build a table | |
578 | - of y-coordinates so we can walk toward the new vertex */ | |
579 | - if (right_line_count<=0) | |
580 | - { | |
581 | - do /* counter-clockwise vertex search */ | |
582 | - { | |
583 | - vertex= right_vertex ? (right_vertex-1) : (polygon->vertex_count-1); | |
584 | - right_line_count= vertices[vertex].x-vertices[right_vertex].x; | |
585 | -// dprintf("right line (%d,%d) to (%d,%d) for %d points", vertices[right_vertex].x, vertices[right_vertex].y, vertices[vertex].x, vertices[vertex].y, right_line_count); | |
586 | - if (!build_y_table(right_table+aggregate_right_line_count, vertices[right_vertex].x, vertices[right_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
587 | - aggregate_right_line_count+= right_line_count; | |
588 | - right_vertex= vertex; | |
589 | - } | |
590 | - while (!right_line_count); | |
591 | - } | |
592 | - | |
593 | - /* advance by the minimum of left_line_count and right_line_count */ | |
594 | - short delta= MIN(left_line_count, right_line_count); | |
595 | - assert(delta); | |
596 | - total_line_count-= delta; | |
597 | - left_line_count-= delta; | |
598 | - right_line_count-= delta; | |
599 | - | |
600 | - assert(delta||!total_line_count); /* if our delta is zero, weユd better be out of lines */ | |
601 | - } | |
602 | - | |
603 | - /* make sure every coordinate is accounted for in our tables */ | |
604 | - assert(aggregate_right_line_count==aggregate_total_line_count); | |
605 | - assert(aggregate_left_line_count==aggregate_total_line_count); | |
606 | - | |
607 | - /* precalculate mode-specific data */ | |
608 | - | |
609 | - if ((polygon->transfer_mode == _textured_transfer) || (polygon->transfer_mode == _static_transfer)) | |
610 | - { | |
611 | - _pretexture_vertical_polygon_lines(polygon, screen, view, (struct _vertical_polygon_data *)precalculation_table, vertices[highest_vertex].x, left_table, right_table, aggregate_total_line_count); | |
612 | - } | |
613 | - else vhalt(csprintf(temporary, "vertical_polygons dont support mode #%d", polygon->transfer_mode)); | |
614 | - | |
615 | - /* render all lines */ | |
616 | - switch (bit_depth) | |
617 | - { | |
618 | - case 8: | |
619 | - switch (polygon->transfer_mode) | |
620 | - { | |
621 | - case _textured_transfer: | |
622 | - if (polygon->texture->flags&_TRANSPARENT_BIT) | |
623 | - texture_vertical_polygon_lines<pixel8, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
624 | - else | |
625 | - texture_vertical_polygon_lines<pixel8, _sw_alpha_off, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
626 | - break; | |
627 | - case _static_transfer: | |
628 | - if (polygon->texture->flags&_TRANSPARENT_BIT) | |
629 | - randomize_vertical_polygon_lines<pixel8, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
630 | - else | |
631 | - randomize_vertical_polygon_lines<pixel8, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
632 | - break; | |
633 | - | |
634 | - default: | |
635 | - assert(false); | |
636 | - break; | |
637 | - } | |
638 | - break; | |
639 | - | |
640 | - case 16: | |
641 | - switch (polygon->transfer_mode) | |
642 | - { | |
643 | - case _textured_transfer: | |
644 | - { | |
645 | - SW_Texture *sw_texture =0 ; | |
646 | - if (graphics_preferences->software_alpha_blending) | |
647 | - { | |
648 | - sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
649 | - } | |
650 | - if (sw_texture && !polygon->VoidPresent && sw_texture->opac_type()) | |
651 | - { | |
652 | - if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) { | |
653 | - if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
654 | - texture_vertical_polygon_lines<pixel16, _sw_alpha_fast, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
655 | - } else { | |
656 | - texture_vertical_polygon_lines<pixel16, _sw_alpha_fast, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
657 | - } | |
658 | - } | |
659 | - else if (graphics_preferences->software_alpha_blending == _sw_alpha_nice) { | |
660 | - if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
661 | - texture_vertical_polygon_lines<pixel16, _sw_alpha_nice, true>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
662 | - } else { | |
663 | - texture_vertical_polygon_lines<pixel16, _sw_alpha_nice, false>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
664 | - } | |
665 | - } | |
666 | - } else { | |
667 | - if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
668 | - texture_vertical_polygon_lines<pixel16, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
669 | - } else { | |
670 | - texture_vertical_polygon_lines<pixel16, _sw_alpha_off, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
671 | - } | |
672 | - } | |
673 | - } | |
674 | - break; | |
675 | - case _static_transfer: | |
676 | - if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
677 | - randomize_vertical_polygon_lines<pixel16, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
678 | - } else { | |
679 | - randomize_vertical_polygon_lines<pixel16, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
680 | - } | |
681 | - break; | |
682 | - default: | |
683 | - assert(false); | |
684 | - break; | |
685 | - } | |
686 | - break; | |
687 | - | |
688 | - case 32: | |
689 | - switch (polygon->transfer_mode) | |
690 | - { | |
691 | - case _textured_transfer: | |
692 | - { | |
693 | - SW_Texture *sw_texture = 0; | |
694 | - if (graphics_preferences->software_alpha_blending) | |
695 | - { | |
696 | - sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
697 | - } | |
698 | - if (sw_texture && !polygon->VoidPresent && sw_texture->opac_type()) | |
699 | - { | |
700 | - if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) { | |
701 | - if (polygon->texture->flags&_TRANSPARENT_BIT) | |
702 | - texture_vertical_polygon_lines<pixel32, _sw_alpha_fast, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
703 | - else | |
704 | - texture_vertical_polygon_lines<pixel32, _sw_alpha_fast, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
705 | - } | |
706 | - else if (graphics_preferences->software_alpha_blending == _sw_alpha_nice) | |
707 | - { | |
708 | - if (polygon->texture->flags & _TRANSPARENT_BIT) | |
709 | - texture_vertical_polygon_lines<pixel32, _sw_alpha_nice, true>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
710 | - else | |
711 | - texture_vertical_polygon_lines<pixel32, _sw_alpha_nice, false>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
712 | - } | |
713 | - } else { | |
714 | - if (polygon->texture->flags & _TRANSPARENT_BIT) | |
715 | - texture_vertical_polygon_lines<pixel32, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
716 | - else | |
717 | - texture_vertical_polygon_lines<pixel32, _sw_alpha_off, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
718 | - } | |
719 | - break; | |
720 | - } | |
721 | - case _static_transfer: | |
722 | - if (polygon->texture->flags & _TRANSPARENT_BIT) | |
723 | - randomize_vertical_polygon_lines<pixel32, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
724 | - else | |
725 | - randomize_vertical_polygon_lines<pixel32, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
726 | - break; | |
727 | - | |
728 | - default: | |
729 | - assert(false); | |
730 | - break; | |
731 | - } | |
732 | - break; | |
733 | - | |
734 | - default: | |
735 | - assert(false); | |
736 | - break; | |
737 | - } | |
738 | - } | |
739 | -} | |
740 | - | |
741 | -void Rasterizer_SW_Class::texture_rectangle(rectangle_definition& textured_rectangle) | |
742 | -{ | |
743 | - rectangle_definition *rectangle = &textured_rectangle; // Reference to pointer | |
744 | - | |
745 | - if (rectangle->x0<rectangle->x1 && rectangle->y0<rectangle->y1) | |
746 | - { | |
747 | - /* subsume screen boundaries into clipping parameters */ | |
748 | - if (rectangle->clip_left<0) rectangle->clip_left= 0; | |
749 | - if (rectangle->clip_right>screen->width) rectangle->clip_right= screen->width; | |
750 | - if (rectangle->clip_top<0) rectangle->clip_top= 0; | |
751 | - if (rectangle->clip_bottom>screen->height) rectangle->clip_bottom= screen->height; | |
752 | - | |
753 | - /* subsume left and right sides of the rectangle into clipping parameters */ | |
754 | - if (rectangle->clip_left<rectangle->x0) rectangle->clip_left= rectangle->x0; | |
755 | - if (rectangle->clip_right>rectangle->x1) rectangle->clip_right= rectangle->x1; | |
756 | - if (rectangle->clip_top<rectangle->y0) rectangle->clip_top= rectangle->y0; | |
757 | - if (rectangle->clip_bottom>rectangle->y1) rectangle->clip_bottom= rectangle->y1; | |
758 | - | |
759 | - /* only continue if we have a non-empty rectangle, at least some of which is on the screen */ | |
760 | - if (rectangle->clip_left<rectangle->clip_right && rectangle->clip_top<rectangle->clip_bottom && | |
761 | - rectangle->clip_right>0 && rectangle->clip_left<screen->width && | |
762 | - rectangle->clip_bottom>0 && rectangle->clip_top<screen->height) | |
763 | - { | |
764 | - short delta; /* scratch */ | |
765 | - short screen_width= rectangle->x1-rectangle->x0; | |
766 | - short screen_height= rectangle->y1-rectangle->y0; | |
767 | - short screen_x= rectangle->x0; | |
768 | - struct bitmap_definition *texture= rectangle->texture; | |
769 | - | |
770 | - short *y0_table= scratch_table0, *y1_table= scratch_table1; | |
771 | - struct _vertical_polygon_data *header= (struct _vertical_polygon_data *)precalculation_table; | |
772 | - struct _vertical_polygon_line_data *data= (struct _vertical_polygon_line_data *) (header+1); | |
773 | - | |
774 | - _fixed texture_dx= INTEGER_TO_FIXED(texture->width)/screen_width; | |
775 | - _fixed texture_x= texture_dx>>1; | |
776 | - | |
777 | - _fixed texture_dy= INTEGER_TO_FIXED(texture->height)/screen_height; | |
778 | - _fixed texture_y0= 0; | |
779 | - _fixed texture_y1; | |
780 | - | |
781 | - if (texture_dx&&texture_dy) | |
782 | - { | |
783 | - /* handle horizontal mirroring */ | |
784 | - if (rectangle->flip_horizontal) | |
785 | - { | |
786 | - texture_dx= -texture_dx; | |
787 | - texture_x= INTEGER_TO_FIXED(texture->width)+(texture_dx>>1); | |
788 | - } | |
789 | - | |
790 | - /* left clipping */ | |
791 | - if ((delta= rectangle->clip_left-rectangle->x0)>0) | |
792 | - { | |
793 | - texture_x+= delta*texture_dx; | |
794 | - screen_width-= delta; | |
795 | - screen_x= rectangle->clip_left; | |
796 | - } | |
797 | - /* right clipping */ | |
798 | - if ((delta= rectangle->x1-rectangle->clip_right)>0) | |
799 | - { | |
800 | - screen_width-= delta; | |
801 | - } | |
802 | - | |
803 | - /* top clipping */ | |
804 | - if ((delta= rectangle->clip_top-rectangle->y0)>0) | |
805 | - { | |
806 | - texture_y0+= delta*texture_dy; | |
807 | - screen_height-= delta; | |
808 | - } | |
809 | - | |
810 | - /* bottom clipping */ | |
811 | - if ((delta= rectangle->y1-rectangle->clip_bottom)>0) | |
812 | - { | |
813 | - screen_height-= delta; | |
814 | - } | |
815 | - | |
816 | - texture_y1= texture_y0 + screen_height*texture_dy; | |
817 | - | |
818 | - header->downshift= FIXED_FRACTIONAL_BITS; | |
819 | - header->width= screen_width; | |
820 | - header->x0= screen_x; | |
821 | - | |
822 | - /* calculate shading table, once */ | |
823 | - void *shading_table = NULL; | |
824 | - switch (rectangle->transfer_mode) | |
825 | - { | |
826 | - case _textured_transfer: | |
827 | - if (!(rectangle->flags&_SHADELESS_BIT)) | |
828 | - { | |
829 | - // LP change: | |
830 | - // Made this more long-distance friendly | |
831 | - calculate_shading_table(shading_table, view, rectangle->shading_tables, (short)MIN(rectangle->depth, SHRT_MAX), rectangle->ambient_shade); | |
832 | - break; | |
833 | - } | |
834 | - /* if shadeless, fall through to a single shading table, ignoring depth */ | |
835 | - case _tinted_transfer: | |
836 | - case _static_transfer: | |
837 | - shading_table= rectangle->shading_tables; | |
838 | - break; | |
839 | - | |
840 | - default: | |
841 | - vhalt(csprintf(temporary, "rectangles dont support mode #%d", rectangle->transfer_mode)); | |
842 | - } | |
843 | - | |
844 | - for (; screen_width; --screen_width) | |
845 | - { | |
846 | - byte *read= texture->row_addresses[FIXED_INTEGERAL_PART(texture_x)]; | |
847 | - // CB: first/last are stored in big-endian order | |
848 | - uint16 first = *read++ << 8; | |
849 | - first |= *read++; | |
850 | - uint16 last = *read++ << 8; | |
851 | - last |= *read++; | |
852 | - _fixed texture_y= texture_y0; | |
853 | - short y0= rectangle->clip_top, y1= rectangle->clip_bottom; | |
854 | - | |
855 | - if (FIXED_INTEGERAL_PART(texture_y0)<first) | |
856 | - { | |
857 | - delta= (INTEGER_TO_FIXED(first) - texture_y0)/texture_dy + 1; | |
858 | - vassert(delta>=0, csprintf(temporary, "[%x,%x] カ=%x (#%d,#%d)", texture_y0, texture_y1, texture_dy, first, last)); | |
859 | - | |
860 | - y0= MIN(y1, y0+delta); | |
861 | - texture_y+= delta*texture_dy; | |
862 | - } | |
863 | - | |
864 | - if (FIXED_INTEGERAL_PART(texture_y1)>last) | |
865 | - { | |
866 | - delta= (texture_y1 - INTEGER_TO_FIXED(last))/texture_dy + 1; | |
867 | - vassert(delta>=0, csprintf(temporary, "[%x,%x] カ=%x (#%d,#%d)", texture_y0, texture_y1, texture_dy, first, last)); | |
868 | - | |
869 | - y1= MAX(y0, y1-delta); | |
870 | - } | |
871 | - | |
872 | - data->texture_y= texture_y - INTEGER_TO_FIXED(first); | |
873 | - data->texture_dy= texture_dy; | |
874 | - data->shading_table= shading_table; | |
875 | - data->texture= (unsigned char *)read; | |
876 | - | |
877 | - texture_x+= texture_dx; | |
878 | - data+= 1; | |
879 | - | |
880 | - *y0_table++= y0; | |
881 | - *y1_table++= y1; | |
882 | - | |
883 | - assert(y0<=y1); | |
884 | - assert(y0>=0 && y1>=0); | |
885 | - assert(y0<=screen->height); | |
886 | - assert(y1<=screen->height); | |
887 | - } | |
888 | - | |
889 | - switch (bit_depth) | |
890 | - { | |
891 | - case 8: | |
892 | - switch (rectangle->transfer_mode) | |
893 | - { | |
894 | - case _textured_transfer: | |
895 | - texture_vertical_polygon_lines<pixel8, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
896 | - scratch_table0, scratch_table1); | |
897 | - break; | |
898 | - | |
899 | - case _static_transfer: | |
900 | - randomize_vertical_polygon_lines<pixel8, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
901 | - scratch_table0, scratch_table1, rectangle->transfer_data); | |
902 | - break; | |
903 | - | |
904 | - case _tinted_transfer: | |
905 | - tint_vertical_polygon_lines<pixel8>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
906 | - scratch_table0, scratch_table1, rectangle->transfer_data); | |
907 | - break; | |
908 | - | |
909 | - default: | |
910 | - assert(false); | |
911 | - break; | |
912 | - } | |
913 | - break; | |
914 | - | |
915 | - case 16: | |
916 | - switch (rectangle->transfer_mode) | |
917 | - { | |
918 | - case _textured_transfer: | |
919 | - texture_vertical_polygon_lines<pixel16, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, scratch_table0, scratch_table1); | |
920 | - break; | |
921 | - | |
922 | - case _static_transfer: | |
923 | - randomize_vertical_polygon_lines<pixel16, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
924 | - scratch_table0, scratch_table1, rectangle->transfer_data); | |
925 | - break; | |
926 | - | |
927 | - case _tinted_transfer: | |
928 | - tint_vertical_polygon_lines<pixel16>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
929 | - scratch_table0, scratch_table1, rectangle->transfer_data); | |
930 | - break; | |
931 | - | |
932 | - default: | |
933 | - assert(false); | |
934 | - break; | |
935 | - } | |
936 | - break; | |
937 | - | |
938 | - case 32: | |
939 | - switch (rectangle->transfer_mode) | |
940 | - { | |
941 | - case _textured_transfer: | |
942 | - texture_vertical_polygon_lines<pixel32, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
943 | - scratch_table0, scratch_table1); | |
944 | - break; | |
945 | - | |
946 | - case _static_transfer: | |
947 | - randomize_vertical_polygon_lines<pixel32, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
948 | - scratch_table0, scratch_table1, rectangle->transfer_data); | |
949 | - break; | |
950 | - | |
951 | - case _tinted_transfer: | |
952 | - tint_vertical_polygon_lines<pixel32>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
953 | - scratch_table0, scratch_table1, rectangle->transfer_data); | |
954 | - break; | |
955 | - | |
956 | - default: | |
957 | - assert(false); | |
958 | - break; | |
959 | - } | |
960 | - break; | |
961 | - | |
962 | - default: | |
963 | - assert(false); | |
964 | - break; | |
965 | - } | |
966 | - } | |
967 | - } | |
968 | - } | |
969 | -} | |
970 | - | |
971 | -/* ---------- private code */ | |
972 | - | |
973 | -#if 0 | |
974 | - | |
975 | -#define LANDSCAPE_REPEATS 12 | |
976 | -static void preprocess_landscaped_polygon( | |
977 | - struct polygon_definition *polygon, | |
978 | - struct view_data *view) | |
979 | -{ | |
980 | - polygon->origin.x= (world_distance) ((10000*LANDSCAPE_REPEATS*WORLD_ONE)/(2*31415)); | |
981 | - polygon->origin.y= -(((LANDSCAPE_REPEATS*WORLD_ONE*view->yaw)>>ANGULAR_BITS)&(WORLD_ONE-1)); | |
982 | - polygon->origin.z= 0; | |
983 | - | |
984 | - polygon->vector.i= 0; | |
985 | - polygon->vector.j= WORLD_ONE; | |
986 | - polygon->vector.k= -WORLD_ONE; | |
987 | - | |
988 | - polygon->ambient_shade= FIXED_ONE; | |
989 | -} | |
990 | - | |
991 | -#endif | |
992 | - | |
993 | -/* starting at x0 and for line_count vertical lines between *y0 and *y1, precalculate all the | |
994 | - information _texture_vertical_polygon_lines will need to work */ | |
995 | -static void _pretexture_vertical_polygon_lines( | |
996 | - struct polygon_definition *polygon, | |
997 | - struct bitmap_definition *screen, | |
998 | - struct view_data *view, | |
999 | - struct _vertical_polygon_data *data, | |
1000 | - short x0, | |
1001 | - short *y0_table, | |
1002 | - short *y1_table, | |
1003 | - short line_count) | |
1004 | -{ | |
1005 | - short screen_x= x0-view->half_screen_width; | |
1006 | - int32 dz0= view->world_to_screen_y*polygon->origin.z; | |
1007 | - int32 unadjusted_ty_denominator= view->world_to_screen_y*polygon->vector.k; | |
1008 | - int32 tx_numerator, tx_denominator, tx_numerator_delta, tx_denominator_delta; | |
1009 | - struct _vertical_polygon_line_data *line= (struct _vertical_polygon_line_data *) (data+1); | |
1010 | - | |
1011 | - (void) (screen); | |
1012 | - | |
1013 | - assert(sizeof(struct _vertical_polygon_line_data)<=MAXIMUM_PRECALCULATION_TABLE_ENTRY_SIZE); | |
1014 | - | |
1015 | - data->downshift= VERTICAL_TEXTURE_DOWNSHIFT; | |
1016 | - data->x0= x0; | |
1017 | - data->width= line_count; | |
1018 | - | |
1019 | - /* calculate and rescale tx_numerator, tx_denominator, etc. */ | |
1020 | - tx_numerator= view->world_to_screen_x*polygon->origin.y - screen_x*polygon->origin.x; | |
1021 | - tx_denominator= screen_x*polygon->vector.i - view->world_to_screen_x*polygon->vector.j; | |
1022 | - tx_numerator_delta= -polygon->origin.x; | |
1023 | - tx_denominator_delta= polygon->vector.i; | |
1024 | - | |
1025 | - while (--line_count>=0) | |
1026 | - { | |
1027 | - _fixed tx; | |
1028 | - // LP change: made this quantity more long-distance friendly; | |
1029 | - // have to avoid doing INTEGER_TO_FIXED on this one, however | |
1030 | - int32 world_x; | |
1031 | - short x0, y0= *y0_table++; | |
1032 | - short screen_y0= view->half_screen_height-y0+view->dtanpitch; | |
1033 | - int32 ty_numerator, ty_denominator; | |
1034 | - _fixed ty, ty_delta; | |
1035 | - | |
1036 | - /* would our precision be greater here if we shifted the numerator up to $7FFFFFFF and | |
1037 | - then downshifted only the numerator? too bad we canユt use BFFFO in 68k */ | |
1038 | - { | |
1039 | - int32 adjusted_tx_denominator= tx_denominator; | |
1040 | - int32 adjusted_tx_numerator= tx_numerator; | |
1041 | - | |
1042 | - while (adjusted_tx_numerator>((1<<(31-VERTICAL_TEXTURE_WIDTH_BITS))-1) || | |
1043 | - adjusted_tx_numerator<((-1)<<(31-VERTICAL_TEXTURE_WIDTH_BITS))) | |
1044 | - { | |
1045 | - adjusted_tx_numerator>>= 1, adjusted_tx_denominator>>= 1; | |
1046 | - } | |
1047 | - if (!adjusted_tx_denominator) adjusted_tx_denominator= 1; /* -1 will still be -1 */ | |
1048 | - x0= ((adjusted_tx_numerator<<VERTICAL_TEXTURE_WIDTH_BITS)/adjusted_tx_denominator)&(VERTICAL_TEXTURE_WIDTH-1); | |
1049 | - | |
1050 | - while (adjusted_tx_numerator>INT16_MAX||adjusted_tx_numerator<INT16_MIN) | |
1051 | - { | |
1052 | - adjusted_tx_numerator>>= 1, adjusted_tx_denominator>>= 1; | |
1053 | - } | |
1054 | - if (!adjusted_tx_denominator) adjusted_tx_denominator= 1; /* -1 will still be -1 */ | |
1055 | - tx= INTEGER_TO_FIXED(adjusted_tx_numerator)/adjusted_tx_denominator; | |
1056 | - } | |
1057 | - | |
1058 | - world_x= polygon->origin.x + ((tx*polygon->vector.i)>>FIXED_FRACTIONAL_BITS); | |
1059 | - if (world_x<0) world_x= -world_x; /* it is mostly unclear what weユre supposed to do with negative x values */ | |
1060 | - | |
1061 | - /* calculate and rescale ty_numerator, ty_denominator and calculate ty */ | |
1062 | - ty_numerator= world_x*screen_y0 - dz0; | |
1063 | - ty_denominator= unadjusted_ty_denominator; | |
1064 | - while (ty_numerator>INT16_MAX||ty_numerator<INT16_MIN) | |
1065 | - { | |
1066 | - ty_numerator>>= 1, ty_denominator>>= 1; | |
1067 | - } | |
1068 | - if (!ty_denominator) ty_denominator= 1; /* -1 will still be -1 */ | |
1069 | - ty= INTEGER_TO_FIXED(ty_numerator)/ty_denominator; | |
1070 | - | |
1071 | - // LP change: | |
1072 | - // Use the same reduction hack used earlier, | |
1073 | - // because otherwise, INTEGER_TO_FIXED would cause world_x to wrap around. | |
1074 | - int32 adjusted_world_x = world_x; | |
1075 | - int32 adjusted_ty_denominator = unadjusted_ty_denominator>>8; | |
1076 | - | |
1077 | - // LP: remember that world_x is always >= 0 | |
1078 | - while(adjusted_world_x > INT16_MAX) | |
1079 | - { | |
1080 | - adjusted_world_x >>= 1; adjusted_ty_denominator >>= 1; | |
1081 | - } | |
1082 | - if (!adjusted_ty_denominator) adjusted_ty_denominator= 1; /* -1 will still be -1 */ | |
1083 | - ty_delta= - INTEGER_TO_FIXED(adjusted_world_x)/adjusted_ty_denominator; | |
1084 | - | |
1085 | - vassert(ty_delta>=0, csprintf(temporary, "ty_delta=W2F(%d)/%d=%d", world_x, unadjusted_ty_denominator, ty_delta)); | |
1086 | - | |
1087 | - /* calculate the shading table for this column */ | |
1088 | - if (polygon->flags&_SHADELESS_BIT) | |
1089 | - { | |
1090 | - line->shading_table= polygon->shading_tables; | |
1091 | - } | |
1092 | - else | |
1093 | - { | |
1094 | - // LP change: made this more long-distance friendly | |
1095 | - calculate_shading_table(line->shading_table, view, polygon->shading_tables, (short)MIN(world_x, SHRT_MAX), polygon->ambient_shade); | |
1096 | - // calculate_shading_table(line->shading_table, view, polygon->shading_tables, world_x, polygon->ambient_shade); | |
1097 | - } | |
1098 | - | |
1099 | -// if (ty_delta) | |
1100 | - { | |
1101 | - /* calculate texture_y and texture_dy (floor-mapper style) */ | |
1102 | -// data->n= VERTICAL_TEXTURE_DOWNSHIFT; | |
1103 | - line->texture_y= ty<<VERTICAL_TEXTURE_FREE_BITS; | |
1104 | - line->texture_dy= ty_delta<<(VERTICAL_TEXTURE_FREE_BITS-8); | |
1105 | - line->texture= polygon->texture->row_addresses[x0]; | |
1106 | - | |
1107 | - line+= 1; | |
1108 | - } | |
1109 | - | |
1110 | - tx_numerator+= tx_numerator_delta; | |
1111 | - tx_denominator+= tx_denominator_delta; | |
1112 | - | |
1113 | - screen_x+= 1; | |
1114 | - } | |
1115 | -} | |
1116 | - | |
1117 | -static void _pretexture_horizontal_polygon_lines( | |
1118 | - struct polygon_definition *polygon, | |
1119 | - struct bitmap_definition *screen, | |
1120 | - struct view_data *view, | |
1121 | - struct _horizontal_polygon_line_data *data, | |
1122 | - short y0, | |
1123 | - short *x0_table, | |
1124 | - short *x1_table, | |
1125 | - short line_count) | |
1126 | -{ | |
1127 | - int32 hcosine, dhcosine; | |
1128 | - int32 hsine, dhsine; | |
1129 | - int32 hworld_to_screen; | |
1130 | - bool higher_precision= polygon->origin.z>-WORLD_ONE && polygon->origin.z<WORLD_ONE; | |
1131 | - | |
1132 | - (void) (screen); | |
1133 | - | |
1134 | - /* precalculate a bunch of multiplies */ | |
1135 | - hcosine= cosine_table[view->yaw]; | |
1136 | - hsine= sine_table[view->yaw]; | |
1137 | - if (higher_precision) | |
1138 | - { | |
1139 | - hcosine*= polygon->origin.z; | |
1140 | - hsine*= polygon->origin.z; | |
1141 | - } | |
1142 | - hworld_to_screen= polygon->origin.z*view->world_to_screen_y; | |
1143 | - dhcosine= view->world_to_screen_y*hcosine; | |
1144 | - dhsine= view->world_to_screen_y*hsine; | |
1145 | - | |
1146 | - while ((line_count-=1)>=0) | |
1147 | - { | |
1148 | - // LP change: made this more long-distance-friendly | |
1149 | - int32 depth; | |
1150 | - // world_distance depth; | |
1151 | - short screen_x, screen_y; | |
1152 | - short x0= *x0_table++; | |
1153 | - | |
1154 | - /* calculate screen_x,screen_y */ | |
1155 | - screen_x= x0-view->half_screen_width; | |
1156 | - screen_y= view->half_screen_height-y0+view->dtanpitch; | |
1157 | - if (!screen_y) screen_y= 1; /* this will avoid division by zero and won't change rendering */ | |
1158 | - | |
1159 | - /* calculate source_x, source_y, source_dx, source_dy */ | |
1160 | - | |
1161 | - int32 source_x, source_y, source_dx, source_dy; | |
1162 | - | |
1163 | - /* calculate texture origins and deltas (source_x,source_dx,source_y,source_dy) */ | |
1164 | - if (higher_precision) | |
1165 | - { | |
1166 | - source_x= (dhcosine - screen_x*hsine)/screen_y + (polygon->origin.x<<TRIG_SHIFT); | |
1167 | - source_dx= - hsine/screen_y; | |
1168 | - source_y= (screen_x*hcosine + dhsine)/screen_y + (polygon->origin.y<<TRIG_SHIFT); | |
1169 | - source_dy= hcosine/screen_y; | |
1170 | - } | |
1171 | - else | |
1172 | - { | |
1173 | - source_x= ((dhcosine - screen_x*hsine)/screen_y)*polygon->origin.z + (polygon->origin.x<<TRIG_SHIFT); | |
1174 | - source_dx= - (hsine*polygon->origin.z)/screen_y; | |
1175 | - source_y= ((screen_x*hcosine + dhsine)/screen_y)*polygon->origin.z + (polygon->origin.y<<TRIG_SHIFT); | |
1176 | - source_dy= (hcosine*polygon->origin.z)/screen_y; | |
1177 | - } | |
1178 | - | |
1179 | - /* voodoo so x,y texture wrapping is handled automatically by downshifting | |
1180 | - (subtract one from HORIZONTAL_FREE_BITS to double scale) */ | |
1181 | - data->source_x= source_x<<HORIZONTAL_FREE_BITS, data->source_dx= source_dx<<HORIZONTAL_FREE_BITS; | |
1182 | - data->source_y= source_y<<HORIZONTAL_FREE_BITS, data->source_dy= source_dy<<HORIZONTAL_FREE_BITS; | |
1183 | - | |
1184 | - | |
1185 | - /* get shading table (with absolute value of depth) */ | |
1186 | - if ((depth= hworld_to_screen/screen_y)<0) depth= -depth; | |
1187 | - if (polygon->flags&_SHADELESS_BIT) | |
1188 | - { | |
1189 | - data->shading_table= polygon->shading_tables; | |
1190 | - } | |
1191 | - else | |
1192 | - { | |
1193 | - calculate_shading_table(data->shading_table, view, polygon->shading_tables, (short)MIN(depth, SHRT_MAX), polygon->ambient_shade); | |
1194 | - } | |
1195 | - | |
1196 | - data++; | |
1197 | - y0++; | |
1198 | - } | |
1199 | -} | |
1200 | - | |
1201 | - | |
1202 | -// height must be determined emperically (texture is vertically centered at 0。) | |
1203 | -// #define LANDSCAPE_REPEAT_BITS 1 | |
1204 | -static void _prelandscape_horizontal_polygon_lines( | |
1205 | - struct polygon_definition *polygon, | |
1206 | - struct bitmap_definition *screen, | |
1207 | - struct view_data *view, | |
1208 | - struct _horizontal_polygon_line_data *data, | |
1209 | - short y0, | |
1210 | - short *x0_table, | |
1211 | - short *x1_table, | |
1212 | - short line_count) | |
1213 | -{ | |
1214 | - // LP change: made this more general: | |
1215 | - short landscape_width_bits= NextLowerExponent(polygon->texture->height); | |
1216 | - short texture_height= polygon->texture->width; | |
1217 | - _fixed ambient_shade= FIXED_ONE; // MPW C died if we passed the constant directly to the macro | |
1218 | - | |
1219 | - // Get the landscape-texturing options | |
1220 | - LandscapeOptions *LandOpts = View_GetLandscapeOptions(polygon->ShapeDesc); | |
1221 | - | |
1222 | - // LP change: separate horizontal and vertical pixel deltas: | |
1223 | - // LP change: using a "landscape yaw" that's at the left edge of the screen. | |
1224 | - _fixed first_horizontal_pixel= (view->landscape_yaw + LandOpts->Azimuth)<<(landscape_width_bits+(LandOpts->HorizExp)+FIXED_FRACTIONAL_BITS-ANGULAR_BITS); | |
1225 | - _fixed horizontal_pixel_delta= (view->half_cone<<(1+landscape_width_bits+(LandOpts->HorizExp)+FIXED_FRACTIONAL_BITS-ANGULAR_BITS))/view->standard_screen_width; | |
1226 | - _fixed vertical_pixel_delta= (view->half_cone<<(1+landscape_width_bits+(LandOpts->VertExp)+FIXED_FRACTIONAL_BITS-ANGULAR_BITS))/view->standard_screen_width; | |
1227 | - short landscape_free_bits= 32-FIXED_FRACTIONAL_BITS-landscape_width_bits; | |
1228 | - | |
1229 | - (void) (screen); | |
1230 | - | |
1231 | - /* calculate the shading table */ | |
1232 | - void *shading_table = NULL; | |
1233 | - if (polygon->flags&_SHADELESS_BIT) | |
1234 | - { | |
1235 | - shading_table= polygon->shading_tables; | |
1236 | - } | |
1237 | - else | |
1238 | - { | |
1239 | - calculate_shading_table(shading_table, view, polygon->shading_tables, 0, ambient_shade); | |
1240 | - } | |
1241 | - | |
1242 | - // Find the height to repeat over; use value used for OpenGL texture setup | |
1243 | - short texture_width= polygon->texture->height; | |
1244 | - short repeat_texture_height = texture_width >> LandOpts->OGL_AspRatExp; | |
1245 | - | |
1246 | - short height_reduced = texture_height - 1; | |
1247 | - short height_shift = texture_height >> 1; | |
1248 | - short height_repeat_mask = repeat_texture_height - 1; | |
1249 | - short height_repeat_shift = repeat_texture_height >> 1; | |
1250 | - | |
1251 | - y0-= view->half_screen_height + view->dtanpitch; /* back to virtual screen coordinates */ | |
1252 | - while ((line_count-= 1)>=0) | |
1253 | - { | |
1254 | - short x0= *x0_table++; | |
1255 | - | |
1256 | - data->shading_table= shading_table; | |
1257 | - // LP change: using vertical pixel delta | |
1258 | - // Also using vertical repeat if selected; | |
1259 | - // fold the height into the range (-repeat_height/2, repeat_height) | |
1260 | - short y_txtr_offset= FIXED_INTEGERAL_PART(y0*vertical_pixel_delta); | |
1261 | - if (LandOpts->VertRepeat) | |
1262 | - y_txtr_offset = ((y_txtr_offset + height_repeat_shift) & height_repeat_mask) - | |
1263 | - height_repeat_shift; | |
1264 | - data->source_y= texture_height - PIN(y_txtr_offset + height_shift, 0, height_reduced) - 1; | |
1265 | - // LP change: using horizontal pixel delta | |
1266 | - data->source_x= (first_horizontal_pixel + x0*horizontal_pixel_delta)<<landscape_free_bits; | |
1267 | - data->source_dx= horizontal_pixel_delta<<landscape_free_bits; | |
1268 | - | |
1269 | - data+= 1; | |
1270 | - y0+= 1; | |
1271 | - } | |
1272 | -} | |
1273 | - | |
1274 | -/* y0<y1; this is for vertical polygons */ | |
1275 | -static short *build_x_table( | |
1276 | - short *table, | |
1277 | - short x0, | |
1278 | - short y0, | |
1279 | - short x1, | |
1280 | - short y1) | |
1281 | -{ | |
1282 | - short dx, dy, adx, ady; /* 'a' prefix means absolute value */ | |
1283 | - short x, y; /* x,y screen positions */ | |
1284 | - short d, delta_d, d_max; /* descriminator, delta_descriminator, descriminator_maximum */ | |
1285 | - short *record; | |
1286 | - | |
1287 | - /* calculate SGN(dx),SGN(dy) and the absolute values of dx,dy */ | |
1288 | - dx= x1-x0, adx= ABS(dx), dx= SGN(dx); | |
1289 | - dy= y1-y0, ady= ABS(dy), dy= SGN(dy); | |
1290 | - | |
1291 | - assert(ady<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* can't overflow table */ | |
1292 | - if (dy>0) | |
1293 | - { | |
1294 | - /* setup initial (x,y) location and initialize a pointer to our table */ | |
1295 | - x= x0, y= y0; | |
1296 | - record= table; | |
1297 | - | |
1298 | - if (adx>=ady) | |
1299 | - { | |
1300 | - /* x-dominant line (we need to record x every time y changes) */ | |
1301 | - | |
1302 | - d= adx-ady, delta_d= - 2*ady, d_max= 2*adx; | |
1303 | - while ((adx-=1)>=0) | |
1304 | - { | |
1305 | - if (d<0) y+= 1, d+= d_max, *record++= x, ady-= 1; | |
1306 | - x+= dx, d+= delta_d; | |
1307 | - } | |
1308 | - if (ady==1) *record++= x; else assert(!ady); | |
1309 | - } | |
1310 | - else | |
1311 | - { | |
1312 | - /* y-dominant line (we need to record x every iteration) */ | |
1313 | - | |
1314 | - d= ady-adx, delta_d= - 2*adx, d_max= 2*ady; | |
1315 | - while ((ady-=1)>=0) | |
1316 | - { | |
1317 | - if (d<0) x+= dx, d+= d_max; | |
1318 | - *record++= x; | |
1319 | - y+= 1, d+= delta_d; | |
1320 | - } | |
1321 | - } | |
1322 | - } | |
1323 | - else | |
1324 | - { | |
1325 | - /* canユt build a table for negative dy */ | |
1326 | - if (dy<0) return NULL; | |
1327 | - } | |
1328 | - | |
1329 | - return table; | |
1330 | -} | |
1331 | - | |
1332 | -/* x0<x1; this is for horizontal polygons */ | |
1333 | -static short *build_y_table( | |
1334 | - short *table, | |
1335 | - short x0, | |
1336 | - short y0, | |
1337 | - short x1, | |
1338 | - short y1) | |
1339 | -{ | |
1340 | - short dx, dy, adx, ady; /* 'a' prefix means absolute value */ | |
1341 | - short x, y; /* x,y screen positions */ | |
1342 | - short d, delta_d, d_max; /* descriminator, delta_descriminator, descriminator_maximum */ | |
1343 | - short *record; | |
1344 | - | |
1345 | - /* calculate SGN(dx),SGN(dy) and the absolute values of dx,dy */ | |
1346 | - dx= x1-x0, adx= ABS(dx), dx= SGN(dx); | |
1347 | - dy= y1-y0, ady= ABS(dy), dy= SGN(dy); | |
1348 | - | |
1349 | - assert(adx<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* can't overflow table */ | |
1350 | - if (dx>=0) /* vertical lines allowed */ | |
1351 | - { | |
1352 | - /* setup initial (x,y) location and initialize a pointer to our table */ | |
1353 | - if (dy>=0) | |
1354 | - { | |
1355 | - x= x0, y= y0; | |
1356 | - record= table; | |
1357 | - } | |
1358 | - else | |
1359 | - { | |
1360 | - x= x1, y= y1; | |
1361 | - record= table+adx; | |
1362 | - } | |
1363 | - | |
1364 | - if (adx>=ady) | |
1365 | - { | |
1366 | - /* x-dominant line (we need to record y every iteration) */ | |
1367 | - | |
1368 | - d= adx-ady, delta_d= - 2*ady, d_max= 2*adx; | |
1369 | - while ((adx-=1)>=0) | |
1370 | - { | |
1371 | - if (d<0) y+= 1, d+= d_max; | |
1372 | - if (dy>=0) *record++= y; else *--record= y; | |
1373 | - x+= dx, d+= delta_d; | |
1374 | - } | |
1375 | - } | |
1376 | - else | |
1377 | - { | |
1378 | - /* y-dominant line (we need to record y every time x changes) */ | |
1379 | - | |
1380 | - d= ady-adx, delta_d= - 2*adx, d_max= 2*ady; | |
1381 | - while ((ady-=1)>=0) | |
1382 | - { | |
1383 | - if (d<0) { x+= dx, d+= d_max, adx-= 1; if (dy>=0) *record++= y; else *--record= y; } | |
1384 | - y+= 1, d+= delta_d; | |
1385 | - } | |
1386 | - if (adx==1) if (dy>=0) *record++= y; else *--record= y; else assert(!adx); | |
1387 | - } | |
1388 | - } | |
1389 | - else | |
1390 | - { | |
1391 | - /* canユt build a table for a negative dx */ | |
1392 | - return NULL; | |
1393 | - } | |
1394 | - | |
1395 | - return table; | |
1396 | -} | |
1 | +/* | |
2 | +SCOTTISH_TEXTURES.C | |
3 | + | |
4 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
5 | + and the "Aleph One" developers. | |
6 | + | |
7 | + This program is free software; you can redistribute it and/or modify | |
8 | + it under the terms of the GNU General Public License as published by | |
9 | + the Free Software Foundation; either version 3 of the License, or | |
10 | + (at your option) any later version. | |
11 | + | |
12 | + This program is distributed in the hope that it will be useful, | |
13 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + GNU General Public License for more details. | |
16 | + | |
17 | + This license is contained in the file "COPYING", | |
18 | + which is included with this source code; it is available online at | |
19 | + http://www.gnu.org/licenses/gpl.html | |
20 | + | |
21 | +Wednesday, April 20, 1994 9:35:36 AM | |
22 | + | |
23 | +this is not your fatherユs texture mapping library. | |
24 | +(in fact it isnユt yours either, dillweed) | |
25 | + | |
26 | +Wednesday, April 20, 1994 3:39:21 PM | |
27 | + vertical repeats would be difficult because it would require testing repeats in the | |
28 | + innermost loop of the pixel mapper (a compare and branch we can do without). | |
29 | +Saturday, April 23, 1994 10:42:41 AM | |
30 | + (on the plane to santa clara) finished the slower version of the trapezoid mapper (we | |
31 | + need to handle stretching with a degenerate switch statement like marathon used to) but | |
32 | + the whole sampling process is now mathematically correct except for the squared function | |
33 | + we use to calculate the x texture position and the shading table (but this is accurate to | |
34 | + within 1/64k and doesn't accumulate error so who cares). | |
35 | +Sunday, April 24, 1994 10:12:47 AM | |
36 | + (waiting for the CGDC to start at 9:00 PST) added all polygon stuff. it struck me this | |
37 | + morning that clipping against the view cone must be deterministic (that is, line segments | |
38 | + of polygons and line segments of walls must be clipped in the same manner) or our | |
39 | + edges won't meet up. ordered dither darkening will look really cool but will be slow in c. | |
40 | +Sunday, April 24, 1994 11:21:47 PM | |
41 | + still need transparent trapezoids, dither darkening, faster DDA for trapezoid mapping. | |
42 | +Wednesday, April 27, 1994 9:49:55 AM | |
43 | + i'm just looking for one divine hammer (to bang it all day). solid polygons are currently | |
44 | + unaffected by darkening. i'm not entirely certain we'll even use them. | |
45 | +Sunday, May 8, 1994 8:32:11 AM | |
46 | + LISPユs lexical contours kick C firmly and painfully in the ass. everything is fast now | |
47 | + except the landscape mapper which has just been routed and is in full retreat. | |
48 | +Friday, May 13, 1994 10:05:08 AM | |
49 | + low-level unification of trapezoids and rectangles, transparent runs in shapes are run-length | |
50 | + encoded now. maintaining run tables was slower than generating d, delta_d and delta_d_prime | |
51 | + and using them on the fly. | |
52 | +Wednesday, May 18, 1994 2:16:26 PM | |
53 | + scope matters (at WWDC). | |
54 | +Sunday, May 22, 1994 12:32:02 PM | |
55 | + drawing things in column order to cached (i.e., non-screen) memory is like crapping in the | |
56 | + data cache, right? maybe drawing rectangles in column-order wasn't such a great idea after all. | |
57 | + it also occurs to me that i know nothing about how to order instructions for the ユ040 pipelines. | |
58 | +Thursday, June 16, 1994 9:56:14 PM | |
59 | + modified _render_textured_polygon_line to handle elevation. | |
60 | +Thursday, July 7, 1994 1:23:09 PM | |
61 | + changed MAXIMUM_SCRATCH_TABLE_ENTRIES from 4k to 1200. Modified render code to work as well, | |
62 | + now the problem is floor/ceiling matching with trapezoids, which should fall out with the | |
63 | + rewrite... | |
64 | +Tuesday, July 26, 1994 3:42:16 PM | |
65 | + OBSOLETEユed nearly the entire file (fixed_pixels are no more). rewriting texture_rectangle. | |
66 | + will do 16bit mapping, soon. a while ago i rewrote everything in 68k. | |
67 | +Friday, September 16, 1994 6:03:11 PM (Jason') | |
68 | + texture_rectangle() now respects top and bottom clips | |
69 | +Tuesday, September 20, 1994 9:58:30 PM (Jason') | |
70 | + if weユre so close to a rectangle that n>LARGEST_N then we donユt draw anything | |
71 | +Wednesday, October 26, 1994 3:18:59 PM (Jason) | |
72 | + for non-convex or otherwise weird lines (dx<=0, dy<=0) we donユt draw anything (somebodyユll | |
73 | + notice that for sure). | |
74 | +Friday, November 4, 1994 7:35:48 PM (Jason') | |
75 | + pretexture_horizontal_polygon_lines() now respects the (x,y) polygon origin and uses z as height. | |
76 | + | |
77 | +Jan 30, 2000 (Loren Petrich): | |
78 | + Added some typecasts | |
79 | + | |
80 | +Feb. 4, 2000 (Loren Petrich): | |
81 | + Changed halt() to assert(false) for better debugging | |
82 | + | |
83 | +Mar 24, 2000 (Loren Petrich): | |
84 | + Using a special "landscape yaw" for the landscape texturing, so that the landscape center | |
85 | + will stay put. | |
86 | + | |
87 | +May 23, 2000 (Loren Petrich): | |
88 | + Adding support for different size scales for landscapes | |
89 | + | |
90 | +Jul 6, 2000 (Loren Petrich): | |
91 | + Added some slop to MAXIMUM_SCRATCH_TABLE_ENTRIES, because displays are now bigger; | |
92 | + its size got upped by 2 | |
93 | + | |
94 | +Aug 9, 2000 (Loren Petrich): | |
95 | + Rasterizer_SW object introduced (software subclass of rasterizer object) | |
96 | + | |
97 | +May 16, 2002 (Woody Zenfell): | |
98 | + MSVC doesn't like "void f(); void g() { return f(); }"... fixed. | |
99 | +*/ | |
100 | + | |
101 | +/* | |
102 | +rectangle shrinking has vertical error and appears to randomly shear the bitmap | |
103 | +pretexture_horizontal_polygon_lines() has integer error in large height cases | |
104 | + | |
105 | +_static_transfer doesnユt work for ceilings and floors (because they call the wall mapper) | |
106 | +build_y_table and build_x_table could both be sped up in nearly-horizontal and nearly-vertical cases (respectively) | |
107 | +_pretexture_vertical_polygon_lines() takes up to half the time _texture_vertical_polygon_lines() does | |
108 | +not only that, but texture_horizontal_polygon() is actually faster than texture_vertical_polygon() | |
109 | + | |
110 | +//calculate_shading_table() needs to be inlined in a macro | |
111 | +*/ | |
112 | + | |
113 | +#include "cseries.h" | |
114 | +#if defined(__GNUC__) | |
115 | +#ifndef DEBUG_FAST_CODE | |
116 | +#undef DEBUG | |
117 | +#undef assert | |
118 | +#define assert(x) | |
119 | +#undef vassert | |
120 | +#define vassert(x...) | |
121 | +#undef csprintf | |
122 | +#define csprintf(x...) | |
123 | +#undef vhalt | |
124 | +#define vhalt(x...) | |
125 | +#endif | |
126 | +#endif | |
127 | +#include "render.h" | |
128 | +#include "Rasterizer_SW.h" | |
129 | + | |
130 | +#include <stdlib.h> | |
131 | +#include <limits.h> | |
132 | + | |
133 | +#include "preferences.h" | |
134 | +#include "SW_Texture_Extras.h" | |
135 | + | |
136 | + | |
137 | +#ifdef env68k | |
138 | +#pragma segment texture | |
139 | +#endif | |
140 | + | |
141 | +#ifdef env68k | |
142 | +#define EXTERNAL | |
143 | +#endif | |
144 | + | |
145 | +/* ---------- constants */ | |
146 | + | |
147 | +// LP change: boosted to cope with big displays | |
148 | +#define MAXIMUM_SCRATCH_TABLE_ENTRIES 2048 | |
149 | +#define MAXIMUM_PRECALCULATION_TABLE_ENTRY_SIZE (MAX(sizeof(_vertical_polygon_data), sizeof(_horizontal_polygon_line_data))) | |
150 | + | |
151 | +#define SHADE_TO_SHADING_TABLE_INDEX(shade) ((shade)>>(FIXED_FRACTIONAL_BITS-shading_table_fractional_bits)) | |
152 | +#define DEPTH_TO_SHADE(d) (((_fixed)(d))<<(FIXED_FRACTIONAL_BITS-WORLD_FRACTIONAL_BITS-3)) | |
153 | + | |
154 | +#define LARGEST_N 24 | |
155 | + | |
156 | +/* ---------- texture horizontal polygon */ | |
157 | + | |
158 | +#define HORIZONTAL_WIDTH_SHIFT 7 /* 128 (8 for 256) */ | |
159 | +#define HORIZONTAL_HEIGHT_SHIFT 7 /* 128 */ | |
160 | +#define HORIZONTAL_FREE_BITS (32-TRIG_SHIFT-WORLD_FRACTIONAL_BITS) | |
161 | +#define HORIZONTAL_WIDTH_DOWNSHIFT (32-HORIZONTAL_WIDTH_SHIFT) | |
162 | +#define HORIZONTAL_HEIGHT_DOWNSHIFT (32-HORIZONTAL_HEIGHT_SHIFT) | |
163 | + | |
164 | +struct _horizontal_polygon_line_header | |
165 | +{ | |
166 | + int32 y_downshift; | |
167 | +}; | |
168 | + | |
169 | +struct _horizontal_polygon_line_data | |
170 | +{ | |
171 | + uint32 source_x, source_y; | |
172 | + uint32 source_dx, source_dy; | |
173 | + | |
174 | + void *shading_table; | |
175 | +}; | |
176 | + | |
177 | +/* ---------- texture vertical polygon */ | |
178 | + | |
179 | +#define VERTICAL_TEXTURE_WIDTH 128 | |
180 | +#define VERTICAL_TEXTURE_WIDTH_BITS 7 | |
181 | +#define VERTICAL_TEXTURE_WIDTH_FRACTIONAL_BITS (FIXED_FRACTIONAL_BITS-VERTICAL_TEXTURE_WIDTH_BITS) | |
182 | +#define VERTICAL_TEXTURE_ONE (1<<VERTICAL_TEXTURE_WIDTH_FRACTIONAL_BITS) | |
183 | +#define VERTICAL_TEXTURE_FREE_BITS FIXED_FRACTIONAL_BITS | |
184 | +#define VERTICAL_TEXTURE_DOWNSHIFT (32-VERTICAL_TEXTURE_WIDTH_BITS) | |
185 | + | |
186 | +//AS: Seven! It's Everywhere! | |
187 | +#define HORIZONTAL_WIDTH_SHIFT 7 /* 128 (8 for 256) */ | |
188 | +#define HORIZONTAL_HEIGHT_SHIFT 7 /* 128 */ | |
189 | +#define HORIZONTAL_FREE_BITS (32-TRIG_SHIFT-WORLD_FRACTIONAL_BITS) | |
190 | +#define HORIZONTAL_WIDTH_DOWNSHIFT (32-HORIZONTAL_WIDTH_SHIFT) | |
191 | +#define HORIZONTAL_HEIGHT_DOWNSHIFT (32-HORIZONTAL_HEIGHT_SHIFT) | |
192 | + | |
193 | +struct _vertical_polygon_data | |
194 | +{ | |
195 | + int16 downshift; | |
196 | + int16 x0; | |
197 | + int16 width; | |
198 | + | |
199 | + int16 pad; | |
200 | +}; | |
201 | + | |
202 | +struct _vertical_polygon_line_data | |
203 | +{ | |
204 | + void *shading_table; | |
205 | + pixel8 *texture; | |
206 | + int32 texture_y, texture_dy; | |
207 | +}; | |
208 | + | |
209 | +/* ---------- macros */ | |
210 | + | |
211 | +// i0 + i1 == MAX(i0, i1) + MIN(i0, i1)/2 | |
212 | +//#define calculate_shading_table(result, view, shading_tables, depth, ambient_shade) | |
213 | +static void calculate_shading_table(void * &result,view_data *view, void *shading_tables, short depth,_fixed ambient_shade) | |
214 | +{ | |
215 | + short table_index; | |
216 | + _fixed shade; | |
217 | + | |
218 | + if ((ambient_shade)<0) | |
219 | + { | |
220 | + table_index= SHADE_TO_SHADING_TABLE_INDEX(-(ambient_shade)); | |
221 | + } | |
222 | + else | |
223 | + { | |
224 | + shade= (view)->maximum_depth_intensity - DEPTH_TO_SHADE(depth); | |
225 | + shade= PIN(shade, 0, FIXED_ONE); | |
226 | + table_index= SHADE_TO_SHADING_TABLE_INDEX((ambient_shade>shade) ? (ambient_shade + (shade>>1)) : (shade + (ambient_shade>>1))); | |
227 | + } | |
228 | + | |
229 | + switch (bit_depth) | |
230 | + { | |
231 | + case 8: result= ((byte*)(shading_tables)) + MAXIMUM_SHADING_TABLE_INDEXES*sizeof(pixel8)* | |
232 | + CEILING(table_index, number_of_shading_tables-1); break; | |
233 | + case 16: result= ((byte*)(shading_tables)) + MAXIMUM_SHADING_TABLE_INDEXES*sizeof(pixel16)* | |
234 | + CEILING(table_index, number_of_shading_tables-1); break; | |
235 | + case 32: result= ((byte*)(shading_tables)) + MAXIMUM_SHADING_TABLE_INDEXES*sizeof(pixel32)* | |
236 | + CEILING(table_index, number_of_shading_tables-1); break; | |
237 | + } | |
238 | +} | |
239 | + | |
240 | +/* ---------- globals */ | |
241 | + | |
242 | +/* these tables are used by the polygon rasterizer (to store the x-coordinates of the left and | |
243 | + right lines of the current polygon), the trapezoid rasterizer (to store the y-coordinates | |
244 | + of the top and bottom of the current trapezoid) and the rectangle mapper (for itユs | |
245 | + vertical and if necessary horizontal distortion tables). these are not necessary as | |
246 | + globals, just as global storage. */ | |
247 | +static short *scratch_table0 = NULL, *scratch_table1 = NULL; | |
248 | +static void *precalculation_table = NULL; | |
249 | + | |
250 | +static uint16 texture_random_seed= 6906; | |
251 | + | |
252 | +/* ---------- private prototypes */ | |
253 | + | |
254 | +static void _pretexture_horizontal_polygon_lines(struct polygon_definition *polygon, | |
255 | + struct bitmap_definition *screen, struct view_data *view, struct _horizontal_polygon_line_data *data, | |
256 | + short y0, short *x0_table, short *x1_table, short line_count); | |
257 | + | |
258 | +static void _pretexture_vertical_polygon_lines(struct polygon_definition *polygon, | |
259 | + struct bitmap_definition *screen, struct view_data *view, struct _vertical_polygon_data *data, | |
260 | + short x0, short *y0_table, short *y1_table, short line_count); | |
261 | + | |
262 | +static short *build_x_table(short *table, short x0, short y0, short x1, short y1); | |
263 | +static short *build_y_table(short *table, short x0, short y0, short x1, short y1); | |
264 | + | |
265 | +static void _prelandscape_horizontal_polygon_lines(struct polygon_definition *polygon, | |
266 | + struct bitmap_definition *screen, struct view_data *view, struct _horizontal_polygon_line_data *data, | |
267 | + short y0, short *x0_table, short *x1_table, short line_count); | |
268 | + | |
269 | +/* ---------- code */ | |
270 | + | |
271 | + | |
272 | +// LP addition: | |
273 | +// Find the next lower power of 2, and return the exponent | |
274 | +//AS: p isn't needed | |
275 | +inline int NextLowerExponent(int n) | |
276 | +{ | |
277 | + int xp = 0; | |
278 | + while(n > 1) {n >>= 1; xp++;} | |
279 | + return xp; | |
280 | +} | |
281 | + | |
282 | +#include "low_level_textures.h" | |
283 | + | |
284 | +/* set aside memory at launch for two line tables (remember, we precalculate all the y-values | |
285 | + for trapezoids and two lines worth of x-values for polygons before mapping them) */ | |
286 | +void allocate_texture_tables( | |
287 | + void) | |
288 | +{ | |
289 | + scratch_table0= new short[MAXIMUM_SCRATCH_TABLE_ENTRIES]; | |
290 | + scratch_table1= new short[MAXIMUM_SCRATCH_TABLE_ENTRIES]; | |
291 | + precalculation_table= (void*)new char[MAXIMUM_PRECALCULATION_TABLE_ENTRY_SIZE*MAXIMUM_SCRATCH_TABLE_ENTRIES]; | |
292 | + assert(scratch_table0&&scratch_table1&&precalculation_table); | |
293 | +} | |
294 | + | |
295 | +void Rasterizer_SW_Class::texture_horizontal_polygon(polygon_definition& textured_polygon) | |
296 | +{ | |
297 | + polygon_definition *polygon = &textured_polygon; // Reference to pointer | |
298 | + short vertex, highest_vertex, lowest_vertex; | |
299 | + point2d *vertices= polygon->vertices; | |
300 | + | |
301 | + assert(polygon->vertex_count>=MINIMUM_VERTICES_PER_SCREEN_POLYGON&&polygon->vertex_count<MAXIMUM_VERTICES_PER_SCREEN_POLYGON); | |
302 | + | |
303 | + /* if we get static, tinted or landscaped transfer modes punt to the vertical polygon mapper */ | |
304 | + if (polygon->transfer_mode == _static_transfer) { | |
305 | + texture_vertical_polygon(textured_polygon); | |
306 | + return; | |
307 | + } | |
308 | + | |
309 | + /* locate the vertically highest (closest to zero) and lowest (farthest from zero) vertices */ | |
310 | + highest_vertex= lowest_vertex= 0; | |
311 | + for (vertex= 0; vertex<polygon->vertex_count; ++vertex) | |
312 | + { | |
313 | + if (!(vertices[vertex].x>=0&&vertices[vertex].x<=screen->width&&vertices[vertex].y>=0&&vertices[vertex].y<=screen->height)) | |
314 | + { | |
315 | + // dprintf("vertex #%d/#%d out of bounds:;dm %x %x;g;", vertex, polygon->vertex_count, polygon->vertices, polygon->vertex_count*sizeof(point2d)); | |
316 | + return; | |
317 | + } | |
318 | + if (vertices[vertex].y<vertices[highest_vertex].y) highest_vertex= vertex; | |
319 | + else if (vertices[vertex].y>vertices[lowest_vertex].y) lowest_vertex= vertex; | |
320 | + } | |
321 | + | |
322 | + /* if this polygon is not a horizontal line, draw it */ | |
323 | + if (highest_vertex!=lowest_vertex) | |
324 | + { | |
325 | + short left_line_count, right_line_count, total_line_count; | |
326 | + short aggregate_left_line_count, aggregate_right_line_count, aggregate_total_line_count; | |
327 | + short left_vertex, right_vertex; | |
328 | + short *left_table= scratch_table0, *right_table= scratch_table1; | |
329 | + | |
330 | + left_line_count= right_line_count= 0; /* zero counts so the left and right lines get initialized */ | |
331 | + aggregate_left_line_count= aggregate_right_line_count= 0; /* weユve precalculated nothing initially */ | |
332 | + left_vertex= right_vertex= highest_vertex; /* both sides start at the highest vertex */ | |
333 | + total_line_count= vertices[lowest_vertex].y-vertices[highest_vertex].y; /* calculate vertical line count */ | |
334 | + | |
335 | + assert(total_line_count<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* make sure we have enough scratch space */ | |
336 | + | |
337 | + /* precalculate high and low y-coordinates for every x-coordinate */ | |
338 | + aggregate_total_line_count= total_line_count; | |
339 | + while (total_line_count>0) | |
340 | + { | |
341 | + | |
342 | + /* if weユre out of scan lines on the left side, get a new vertex and build a table | |
343 | + of x-coordinates so we can walk toward the new vertex */ | |
344 | + if (left_line_count<=0) | |
345 | + { | |
346 | + do /* counter-clockwise vertex search */ | |
347 | + { | |
348 | + vertex= left_vertex ? (left_vertex-1) : (polygon->vertex_count-1); | |
349 | + left_line_count= vertices[vertex].y-vertices[left_vertex].y; | |
350 | + if (!build_x_table(left_table+aggregate_left_line_count, vertices[left_vertex].x, vertices[left_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
351 | + aggregate_left_line_count+= left_line_count; | |
352 | + left_vertex= vertex; | |
353 | +// dprintf("add %d left", left_line_count); | |
354 | + } | |
355 | + while (!left_line_count); | |
356 | + } | |
357 | + | |
358 | + /* if weユre out of scan lines on the right side, get a new vertex and build a table | |
359 | + of x-coordinates so we can walk toward the new vertex */ | |
360 | + if (right_line_count<=0) | |
361 | + { | |
362 | + do /* clockwise vertex search */ | |
363 | + { | |
364 | + vertex= (right_vertex==polygon->vertex_count-1) ? 0 : (right_vertex+1); | |
365 | + right_line_count= vertices[vertex].y-vertices[right_vertex].y; | |
366 | + if (!build_x_table(right_table+aggregate_right_line_count, vertices[right_vertex].x, vertices[right_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
367 | + aggregate_right_line_count+= right_line_count; | |
368 | + right_vertex= vertex; | |
369 | +// dprintf("add %d right", right_line_count); | |
370 | + } | |
371 | + while (!right_line_count); | |
372 | + } | |
373 | + //AS: moving delta declaration up to where it's needed. Isn't C++ wonderful? | |
374 | + /* advance by the minimum of left_line_count and right_line_count */ | |
375 | + short delta= MIN(left_line_count, right_line_count); | |
376 | + assert(delta); | |
377 | +// dprintf("tc=%d lc=%d rc=%d delta=%d", total_line_count, left_line_count, right_line_count, delta); | |
378 | + total_line_count-= delta; | |
379 | + left_line_count-= delta; | |
380 | + right_line_count-= delta; | |
381 | + | |
382 | + assert(delta||!total_line_count); /* if our delta is zero, weユd better be out of lines */ | |
383 | + } | |
384 | + | |
385 | + /* make sure every coordinate is accounted for in our tables */ | |
386 | + assert(aggregate_right_line_count==aggregate_total_line_count); | |
387 | + assert(aggregate_left_line_count==aggregate_total_line_count); | |
388 | + | |
389 | + /* precalculate mode-specific data */ | |
390 | + switch (polygon->transfer_mode) | |
391 | + { | |
392 | + case _textured_transfer: | |
393 | + _pretexture_horizontal_polygon_lines(polygon, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
394 | + vertices[highest_vertex].y, left_table, right_table, | |
395 | + aggregate_total_line_count); | |
396 | + break; | |
397 | + | |
398 | + case _big_landscaped_transfer: | |
399 | + _prelandscape_horizontal_polygon_lines(polygon, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
400 | + vertices[highest_vertex].y, left_table, right_table, | |
401 | + aggregate_total_line_count); | |
402 | + break; | |
403 | + | |
404 | + default: | |
405 | + vhalt(csprintf(temporary, "horizontal_polygons dont support mode #%d", polygon->transfer_mode)); | |
406 | + } | |
407 | + | |
408 | + /* render all lines */ | |
409 | + switch (bit_depth) | |
410 | + { | |
411 | + case 8: | |
412 | + switch (polygon->transfer_mode) | |
413 | + { | |
414 | + | |
415 | + case _textured_transfer: | |
416 | + texture_horizontal_polygon_lines<pixel8, _sw_alpha_off>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
417 | + vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
418 | + break; | |
419 | + case _big_landscaped_transfer: | |
420 | + landscape_horizontal_polygon_lines<pixel8>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
421 | + vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
422 | + break; | |
423 | + | |
424 | + default: | |
425 | + assert(false); | |
426 | + break; | |
427 | + } | |
428 | + break; | |
429 | + | |
430 | + case 16: | |
431 | + switch (polygon->transfer_mode) | |
432 | + { | |
433 | + case _textured_transfer: | |
434 | + { | |
435 | + SW_Texture *sw_texture = 0; | |
436 | + if (graphics_preferences->software_alpha_blending) | |
437 | + { | |
438 | + sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
439 | + } | |
440 | + if (sw_texture && !polygon->VoidPresent && sw_texture->opac_type()) | |
441 | + { | |
442 | + if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) { | |
443 | + texture_horizontal_polygon_lines<pixel16, _sw_alpha_fast>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
444 | + } | |
445 | + else if (graphics_preferences->software_alpha_blending == _sw_alpha_nice) { | |
446 | + texture_horizontal_polygon_lines<pixel16, _sw_alpha_nice>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *) precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count, sw_texture->opac_table()); | |
447 | + } | |
448 | + } else { | |
449 | + texture_horizontal_polygon_lines<pixel16, _sw_alpha_off>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
450 | + vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
451 | + } | |
452 | + } | |
453 | + break; | |
454 | + | |
455 | + case _big_landscaped_transfer: | |
456 | + landscape_horizontal_polygon_lines<pixel16>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
457 | + vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
458 | + break; | |
459 | + default: | |
460 | + assert(false); | |
461 | + break; | |
462 | + } | |
463 | + break; | |
464 | + | |
465 | + case 32: | |
466 | + switch (polygon->transfer_mode) | |
467 | + { | |
468 | + case _textured_transfer: | |
469 | + { | |
470 | + SW_Texture *sw_texture = 0; | |
471 | + if (graphics_preferences->software_alpha_blending) | |
472 | + { | |
473 | + sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
474 | + } | |
475 | + if (sw_texture && sw_texture->opac_type() && !polygon->VoidPresent) | |
476 | + { | |
477 | + if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) | |
478 | + { | |
479 | + texture_horizontal_polygon_lines<pixel32, _sw_alpha_fast>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
480 | + } | |
481 | + else if (graphics_preferences->software_alpha_blending = _sw_alpha_nice) | |
482 | + { | |
483 | + texture_horizontal_polygon_lines<pixel32, _sw_alpha_nice>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *) precalculation_table, vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count, sw_texture->opac_table()); | |
484 | + } | |
485 | + } | |
486 | + else | |
487 | + { | |
488 | + texture_horizontal_polygon_lines<pixel32, _sw_alpha_off>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
489 | + vertices[highest_vertex].y, left_table, right_table, | |
490 | + aggregate_total_line_count); | |
491 | + } | |
492 | + } | |
493 | + break; | |
494 | + case _big_landscaped_transfer: | |
495 | + landscape_horizontal_polygon_lines<pixel32>(polygon->texture, screen, view, (struct _horizontal_polygon_line_data *)precalculation_table, | |
496 | + vertices[highest_vertex].y, left_table, right_table, aggregate_total_line_count); | |
497 | + break; | |
498 | + | |
499 | + default: | |
500 | + assert(false); | |
501 | + break; | |
502 | + } | |
503 | + break; | |
504 | + | |
505 | + default: | |
506 | + assert(false); | |
507 | + break; | |
508 | + } | |
509 | + } | |
510 | +} | |
511 | + | |
512 | +void Rasterizer_SW_Class::texture_vertical_polygon(polygon_definition& textured_polygon) | |
513 | +{ | |
514 | + polygon_definition *polygon = &textured_polygon; // Reference to pointer | |
515 | + short vertex, highest_vertex, lowest_vertex; | |
516 | + point2d *vertices= polygon->vertices; | |
517 | + | |
518 | + assert(polygon->vertex_count>=MINIMUM_VERTICES_PER_SCREEN_POLYGON&&polygon->vertex_count<MAXIMUM_VERTICES_PER_SCREEN_POLYGON); | |
519 | + | |
520 | + if (polygon->transfer_mode == _big_landscaped_transfer) { | |
521 | + texture_horizontal_polygon(textured_polygon); | |
522 | + return; | |
523 | + } | |
524 | + | |
525 | + /* locate the horizontally highest (closest to zero) and lowest (farthest from zero) vertices */ | |
526 | + highest_vertex= lowest_vertex= 0; | |
527 | + for (vertex=1;vertex<polygon->vertex_count;++vertex) | |
528 | + { | |
529 | + if (vertices[vertex].x<vertices[highest_vertex].x) highest_vertex= vertex; | |
530 | + if (vertices[vertex].x>vertices[lowest_vertex].x) lowest_vertex= vertex; | |
531 | + } | |
532 | + | |
533 | + for (vertex=0;vertex<polygon->vertex_count;++vertex) | |
534 | + { | |
535 | + if (!(vertices[vertex].x>=0&&vertices[vertex].x<=screen->width&&vertices[vertex].y>=0&&vertices[vertex].y<=screen->height)) | |
536 | + { | |
537 | +// dprintf("vertex #%d/#%d out of bounds:;dm %x %x;g;", vertex, polygon->vertex_count, polygon->vertices, polygon->vertex_count*sizeof(point2d)); | |
538 | + return; | |
539 | + } | |
540 | + } | |
541 | + | |
542 | + /* if this polygon is not a vertical line, draw it */ | |
543 | + if (highest_vertex!=lowest_vertex) | |
544 | + { | |
545 | + short left_line_count, right_line_count, total_line_count; | |
546 | + short aggregate_left_line_count, aggregate_right_line_count, aggregate_total_line_count; | |
547 | + short left_vertex, right_vertex; | |
548 | + short *left_table= scratch_table0, *right_table= scratch_table1; | |
549 | + | |
550 | + left_line_count= right_line_count= 0; /* zero counts so the left and right lines get initialized */ | |
551 | + aggregate_left_line_count= aggregate_right_line_count= 0; /* weユve precalculated nothing initially */ | |
552 | + left_vertex= right_vertex= highest_vertex; /* both sides start at the highest vertex */ | |
553 | + total_line_count= vertices[lowest_vertex].x-vertices[highest_vertex].x; /* calculate vertical line count */ | |
554 | + | |
555 | + assert(total_line_count<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* make sure we have enough scratch space */ | |
556 | + | |
557 | + /* precalculate high and low y-coordinates for every x-coordinate */ | |
558 | + aggregate_total_line_count= total_line_count; | |
559 | + while (total_line_count>0) | |
560 | + { | |
561 | + /* if weユre out of scan lines on the left side, get a new vertex and build a table | |
562 | + of y-coordinates so we can walk toward the new vertex */ | |
563 | + if (left_line_count<=0) | |
564 | + { | |
565 | + do /* clockwise vertex search */ | |
566 | + { | |
567 | + vertex= (left_vertex==polygon->vertex_count-1) ? 0 : (left_vertex+1); | |
568 | + left_line_count= vertices[vertex].x-vertices[left_vertex].x; | |
569 | +// dprintf("left line (%d,%d) to (%d,%d) for %d points", vertices[left_vertex].x, vertices[left_vertex].y, vertices[vertex].x, vertices[vertex].y, left_line_count); | |
570 | + if (!build_y_table(left_table+aggregate_left_line_count, vertices[left_vertex].x, vertices[left_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
571 | + aggregate_left_line_count+= left_line_count; | |
572 | + left_vertex= vertex; | |
573 | + } | |
574 | + while (!left_line_count); | |
575 | + } | |
576 | + | |
577 | + /* if weユre out of scan lines on the right side, get a new vertex and build a table | |
578 | + of y-coordinates so we can walk toward the new vertex */ | |
579 | + if (right_line_count<=0) | |
580 | + { | |
581 | + do /* counter-clockwise vertex search */ | |
582 | + { | |
583 | + vertex= right_vertex ? (right_vertex-1) : (polygon->vertex_count-1); | |
584 | + right_line_count= vertices[vertex].x-vertices[right_vertex].x; | |
585 | +// dprintf("right line (%d,%d) to (%d,%d) for %d points", vertices[right_vertex].x, vertices[right_vertex].y, vertices[vertex].x, vertices[vertex].y, right_line_count); | |
586 | + if (!build_y_table(right_table+aggregate_right_line_count, vertices[right_vertex].x, vertices[right_vertex].y, vertices[vertex].x, vertices[vertex].y)) return; | |
587 | + aggregate_right_line_count+= right_line_count; | |
588 | + right_vertex= vertex; | |
589 | + } | |
590 | + while (!right_line_count); | |
591 | + } | |
592 | + | |
593 | + /* advance by the minimum of left_line_count and right_line_count */ | |
594 | + short delta= MIN(left_line_count, right_line_count); | |
595 | + assert(delta); | |
596 | + total_line_count-= delta; | |
597 | + left_line_count-= delta; | |
598 | + right_line_count-= delta; | |
599 | + | |
600 | + assert(delta||!total_line_count); /* if our delta is zero, weユd better be out of lines */ | |
601 | + } | |
602 | + | |
603 | + /* make sure every coordinate is accounted for in our tables */ | |
604 | + assert(aggregate_right_line_count==aggregate_total_line_count); | |
605 | + assert(aggregate_left_line_count==aggregate_total_line_count); | |
606 | + | |
607 | + /* precalculate mode-specific data */ | |
608 | + | |
609 | + if ((polygon->transfer_mode == _textured_transfer) || (polygon->transfer_mode == _static_transfer)) | |
610 | + { | |
611 | + _pretexture_vertical_polygon_lines(polygon, screen, view, (struct _vertical_polygon_data *)precalculation_table, vertices[highest_vertex].x, left_table, right_table, aggregate_total_line_count); | |
612 | + } | |
613 | + else vhalt(csprintf(temporary, "vertical_polygons dont support mode #%d", polygon->transfer_mode)); | |
614 | + | |
615 | + /* render all lines */ | |
616 | + switch (bit_depth) | |
617 | + { | |
618 | + case 8: | |
619 | + switch (polygon->transfer_mode) | |
620 | + { | |
621 | + case _textured_transfer: | |
622 | + if (polygon->texture->flags&_TRANSPARENT_BIT) | |
623 | + texture_vertical_polygon_lines<pixel8, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
624 | + else | |
625 | + texture_vertical_polygon_lines<pixel8, _sw_alpha_off, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
626 | + break; | |
627 | + case _static_transfer: | |
628 | + if (polygon->texture->flags&_TRANSPARENT_BIT) | |
629 | + randomize_vertical_polygon_lines<pixel8, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
630 | + else | |
631 | + randomize_vertical_polygon_lines<pixel8, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
632 | + break; | |
633 | + | |
634 | + default: | |
635 | + assert(false); | |
636 | + break; | |
637 | + } | |
638 | + break; | |
639 | + | |
640 | + case 16: | |
641 | + switch (polygon->transfer_mode) | |
642 | + { | |
643 | + case _textured_transfer: | |
644 | + { | |
645 | + SW_Texture *sw_texture =0 ; | |
646 | + if (graphics_preferences->software_alpha_blending) | |
647 | + { | |
648 | + sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
649 | + } | |
650 | + if (sw_texture && !polygon->VoidPresent && sw_texture->opac_type()) | |
651 | + { | |
652 | + if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) { | |
653 | + if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
654 | + texture_vertical_polygon_lines<pixel16, _sw_alpha_fast, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
655 | + } else { | |
656 | + texture_vertical_polygon_lines<pixel16, _sw_alpha_fast, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
657 | + } | |
658 | + } | |
659 | + else if (graphics_preferences->software_alpha_blending == _sw_alpha_nice) { | |
660 | + if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
661 | + texture_vertical_polygon_lines<pixel16, _sw_alpha_nice, true>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
662 | + } else { | |
663 | + texture_vertical_polygon_lines<pixel16, _sw_alpha_nice, false>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
664 | + } | |
665 | + } | |
666 | + } else { | |
667 | + if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
668 | + texture_vertical_polygon_lines<pixel16, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
669 | + } else { | |
670 | + texture_vertical_polygon_lines<pixel16, _sw_alpha_off, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
671 | + } | |
672 | + } | |
673 | + } | |
674 | + break; | |
675 | + case _static_transfer: | |
676 | + if (polygon->texture->flags & _TRANSPARENT_BIT) { | |
677 | + randomize_vertical_polygon_lines<pixel16, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
678 | + } else { | |
679 | + randomize_vertical_polygon_lines<pixel16, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
680 | + } | |
681 | + break; | |
682 | + default: | |
683 | + assert(false); | |
684 | + break; | |
685 | + } | |
686 | + break; | |
687 | + | |
688 | + case 32: | |
689 | + switch (polygon->transfer_mode) | |
690 | + { | |
691 | + case _textured_transfer: | |
692 | + { | |
693 | + SW_Texture *sw_texture = 0; | |
694 | + if (graphics_preferences->software_alpha_blending) | |
695 | + { | |
696 | + sw_texture = SW_Texture_Extras::instance()->GetTexture(polygon->ShapeDesc); | |
697 | + } | |
698 | + if (sw_texture && !polygon->VoidPresent && sw_texture->opac_type()) | |
699 | + { | |
700 | + if (graphics_preferences->software_alpha_blending == _sw_alpha_fast) { | |
701 | + if (polygon->texture->flags&_TRANSPARENT_BIT) | |
702 | + texture_vertical_polygon_lines<pixel32, _sw_alpha_fast, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
703 | + else | |
704 | + texture_vertical_polygon_lines<pixel32, _sw_alpha_fast, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
705 | + } | |
706 | + else if (graphics_preferences->software_alpha_blending == _sw_alpha_nice) | |
707 | + { | |
708 | + if (polygon->texture->flags & _TRANSPARENT_BIT) | |
709 | + texture_vertical_polygon_lines<pixel32, _sw_alpha_nice, true>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
710 | + else | |
711 | + texture_vertical_polygon_lines<pixel32, _sw_alpha_nice, false>(screen, view, (struct _vertical_polygon_data *) precalculation_table, left_table, right_table, sw_texture->opac_table()); | |
712 | + } | |
713 | + } else { | |
714 | + if (polygon->texture->flags & _TRANSPARENT_BIT) | |
715 | + texture_vertical_polygon_lines<pixel32, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
716 | + else | |
717 | + texture_vertical_polygon_lines<pixel32, _sw_alpha_off, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table); | |
718 | + } | |
719 | + break; | |
720 | + } | |
721 | + case _static_transfer: | |
722 | + if (polygon->texture->flags & _TRANSPARENT_BIT) | |
723 | + randomize_vertical_polygon_lines<pixel32, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
724 | + else | |
725 | + randomize_vertical_polygon_lines<pixel32, false>(screen, view, (struct _vertical_polygon_data *)precalculation_table, left_table, right_table, polygon->transfer_data); | |
726 | + break; | |
727 | + | |
728 | + default: | |
729 | + assert(false); | |
730 | + break; | |
731 | + } | |
732 | + break; | |
733 | + | |
734 | + default: | |
735 | + assert(false); | |
736 | + break; | |
737 | + } | |
738 | + } | |
739 | +} | |
740 | + | |
741 | +void Rasterizer_SW_Class::texture_rectangle(rectangle_definition& textured_rectangle) | |
742 | +{ | |
743 | + rectangle_definition *rectangle = &textured_rectangle; // Reference to pointer | |
744 | + | |
745 | + if (rectangle->x0<rectangle->x1 && rectangle->y0<rectangle->y1) | |
746 | + { | |
747 | + /* subsume screen boundaries into clipping parameters */ | |
748 | + if (rectangle->clip_left<0) rectangle->clip_left= 0; | |
749 | + if (rectangle->clip_right>screen->width) rectangle->clip_right= screen->width; | |
750 | + if (rectangle->clip_top<0) rectangle->clip_top= 0; | |
751 | + if (rectangle->clip_bottom>screen->height) rectangle->clip_bottom= screen->height; | |
752 | + | |
753 | + /* subsume left and right sides of the rectangle into clipping parameters */ | |
754 | + if (rectangle->clip_left<rectangle->x0) rectangle->clip_left= rectangle->x0; | |
755 | + if (rectangle->clip_right>rectangle->x1) rectangle->clip_right= rectangle->x1; | |
756 | + if (rectangle->clip_top<rectangle->y0) rectangle->clip_top= rectangle->y0; | |
757 | + if (rectangle->clip_bottom>rectangle->y1) rectangle->clip_bottom= rectangle->y1; | |
758 | + | |
759 | + /* only continue if we have a non-empty rectangle, at least some of which is on the screen */ | |
760 | + if (rectangle->clip_left<rectangle->clip_right && rectangle->clip_top<rectangle->clip_bottom && | |
761 | + rectangle->clip_right>0 && rectangle->clip_left<screen->width && | |
762 | + rectangle->clip_bottom>0 && rectangle->clip_top<screen->height) | |
763 | + { | |
764 | + short delta; /* scratch */ | |
765 | + short screen_width= rectangle->x1-rectangle->x0; | |
766 | + short screen_height= rectangle->y1-rectangle->y0; | |
767 | + short screen_x= rectangle->x0; | |
768 | + struct bitmap_definition *texture= rectangle->texture; | |
769 | + | |
770 | + short *y0_table= scratch_table0, *y1_table= scratch_table1; | |
771 | + struct _vertical_polygon_data *header= (struct _vertical_polygon_data *)precalculation_table; | |
772 | + struct _vertical_polygon_line_data *data= (struct _vertical_polygon_line_data *) (header+1); | |
773 | + | |
774 | + _fixed texture_dx= INTEGER_TO_FIXED(texture->width)/screen_width; | |
775 | + _fixed texture_x= texture_dx>>1; | |
776 | + | |
777 | + _fixed texture_dy= INTEGER_TO_FIXED(texture->height)/screen_height; | |
778 | + _fixed texture_y0= 0; | |
779 | + _fixed texture_y1; | |
780 | + | |
781 | + if (texture_dx&&texture_dy) | |
782 | + { | |
783 | + /* handle horizontal mirroring */ | |
784 | + if (rectangle->flip_horizontal) | |
785 | + { | |
786 | + texture_dx= -texture_dx; | |
787 | + texture_x= INTEGER_TO_FIXED(texture->width)+(texture_dx>>1); | |
788 | + } | |
789 | + | |
790 | + /* left clipping */ | |
791 | + if ((delta= rectangle->clip_left-rectangle->x0)>0) | |
792 | + { | |
793 | + texture_x+= delta*texture_dx; | |
794 | + screen_width-= delta; | |
795 | + screen_x= rectangle->clip_left; | |
796 | + } | |
797 | + /* right clipping */ | |
798 | + if ((delta= rectangle->x1-rectangle->clip_right)>0) | |
799 | + { | |
800 | + screen_width-= delta; | |
801 | + } | |
802 | + | |
803 | + /* top clipping */ | |
804 | + if ((delta= rectangle->clip_top-rectangle->y0)>0) | |
805 | + { | |
806 | + texture_y0+= delta*texture_dy; | |
807 | + screen_height-= delta; | |
808 | + } | |
809 | + | |
810 | + /* bottom clipping */ | |
811 | + if ((delta= rectangle->y1-rectangle->clip_bottom)>0) | |
812 | + { | |
813 | + screen_height-= delta; | |
814 | + } | |
815 | + | |
816 | + texture_y1= texture_y0 + screen_height*texture_dy; | |
817 | + | |
818 | + header->downshift= FIXED_FRACTIONAL_BITS; | |
819 | + header->width= screen_width; | |
820 | + header->x0= screen_x; | |
821 | + | |
822 | + /* calculate shading table, once */ | |
823 | + void *shading_table = NULL; | |
824 | + switch (rectangle->transfer_mode) | |
825 | + { | |
826 | + case _textured_transfer: | |
827 | + if (!(rectangle->flags&_SHADELESS_BIT)) | |
828 | + { | |
829 | + // LP change: | |
830 | + // Made this more long-distance friendly | |
831 | + calculate_shading_table(shading_table, view, rectangle->shading_tables, (short)MIN(rectangle->depth, SHRT_MAX), rectangle->ambient_shade); | |
832 | + break; | |
833 | + } | |
834 | + /* if shadeless, fall through to a single shading table, ignoring depth */ | |
835 | + case _tinted_transfer: | |
836 | + case _static_transfer: | |
837 | + shading_table= rectangle->shading_tables; | |
838 | + break; | |
839 | + | |
840 | + default: | |
841 | + vhalt(csprintf(temporary, "rectangles dont support mode #%d", rectangle->transfer_mode)); | |
842 | + } | |
843 | + | |
844 | + for (; screen_width; --screen_width) | |
845 | + { | |
846 | + byte *read= texture->row_addresses[FIXED_INTEGERAL_PART(texture_x)]; | |
847 | + // CB: first/last are stored in big-endian order | |
848 | + uint16 first = *read++ << 8; | |
849 | + first |= *read++; | |
850 | + uint16 last = *read++ << 8; | |
851 | + last |= *read++; | |
852 | + _fixed texture_y= texture_y0; | |
853 | + short y0= rectangle->clip_top, y1= rectangle->clip_bottom; | |
854 | + | |
855 | + if (FIXED_INTEGERAL_PART(texture_y0)<first) | |
856 | + { | |
857 | + delta= (INTEGER_TO_FIXED(first) - texture_y0)/texture_dy + 1; | |
858 | + vassert(delta>=0, csprintf(temporary, "[%x,%x] カ=%x (#%d,#%d)", texture_y0, texture_y1, texture_dy, first, last)); | |
859 | + | |
860 | + y0= MIN(y1, y0+delta); | |
861 | + texture_y+= delta*texture_dy; | |
862 | + } | |
863 | + | |
864 | + if (FIXED_INTEGERAL_PART(texture_y1)>last) | |
865 | + { | |
866 | + delta= (texture_y1 - INTEGER_TO_FIXED(last))/texture_dy + 1; | |
867 | + vassert(delta>=0, csprintf(temporary, "[%x,%x] カ=%x (#%d,#%d)", texture_y0, texture_y1, texture_dy, first, last)); | |
868 | + | |
869 | + y1= MAX(y0, y1-delta); | |
870 | + } | |
871 | + | |
872 | + data->texture_y= texture_y - INTEGER_TO_FIXED(first); | |
873 | + data->texture_dy= texture_dy; | |
874 | + data->shading_table= shading_table; | |
875 | + data->texture= (unsigned char *)read; | |
876 | + | |
877 | + texture_x+= texture_dx; | |
878 | + data+= 1; | |
879 | + | |
880 | + *y0_table++= y0; | |
881 | + *y1_table++= y1; | |
882 | + | |
883 | + assert(y0<=y1); | |
884 | + assert(y0>=0 && y1>=0); | |
885 | + assert(y0<=screen->height); | |
886 | + assert(y1<=screen->height); | |
887 | + } | |
888 | + | |
889 | + switch (bit_depth) | |
890 | + { | |
891 | + case 8: | |
892 | + switch (rectangle->transfer_mode) | |
893 | + { | |
894 | + case _textured_transfer: | |
895 | + texture_vertical_polygon_lines<pixel8, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
896 | + scratch_table0, scratch_table1); | |
897 | + break; | |
898 | + | |
899 | + case _static_transfer: | |
900 | + randomize_vertical_polygon_lines<pixel8, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
901 | + scratch_table0, scratch_table1, rectangle->transfer_data); | |
902 | + break; | |
903 | + | |
904 | + case _tinted_transfer: | |
905 | + tint_vertical_polygon_lines<pixel8>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
906 | + scratch_table0, scratch_table1, rectangle->transfer_data); | |
907 | + break; | |
908 | + | |
909 | + default: | |
910 | + assert(false); | |
911 | + break; | |
912 | + } | |
913 | + break; | |
914 | + | |
915 | + case 16: | |
916 | + switch (rectangle->transfer_mode) | |
917 | + { | |
918 | + case _textured_transfer: | |
919 | + texture_vertical_polygon_lines<pixel16, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, scratch_table0, scratch_table1); | |
920 | + break; | |
921 | + | |
922 | + case _static_transfer: | |
923 | + randomize_vertical_polygon_lines<pixel16, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
924 | + scratch_table0, scratch_table1, rectangle->transfer_data); | |
925 | + break; | |
926 | + | |
927 | + case _tinted_transfer: | |
928 | + tint_vertical_polygon_lines<pixel16>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
929 | + scratch_table0, scratch_table1, rectangle->transfer_data); | |
930 | + break; | |
931 | + | |
932 | + default: | |
933 | + assert(false); | |
934 | + break; | |
935 | + } | |
936 | + break; | |
937 | + | |
938 | + case 32: | |
939 | + switch (rectangle->transfer_mode) | |
940 | + { | |
941 | + case _textured_transfer: | |
942 | + texture_vertical_polygon_lines<pixel32, _sw_alpha_off, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
943 | + scratch_table0, scratch_table1); | |
944 | + break; | |
945 | + | |
946 | + case _static_transfer: | |
947 | + randomize_vertical_polygon_lines<pixel32, true>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
948 | + scratch_table0, scratch_table1, rectangle->transfer_data); | |
949 | + break; | |
950 | + | |
951 | + case _tinted_transfer: | |
952 | + tint_vertical_polygon_lines<pixel32>(screen, view, (struct _vertical_polygon_data *)precalculation_table, | |
953 | + scratch_table0, scratch_table1, rectangle->transfer_data); | |
954 | + break; | |
955 | + | |
956 | + default: | |
957 | + assert(false); | |
958 | + break; | |
959 | + } | |
960 | + break; | |
961 | + | |
962 | + default: | |
963 | + assert(false); | |
964 | + break; | |
965 | + } | |
966 | + } | |
967 | + } | |
968 | + } | |
969 | +} | |
970 | + | |
971 | +/* ---------- private code */ | |
972 | + | |
973 | +#if 0 | |
974 | + | |
975 | +#define LANDSCAPE_REPEATS 12 | |
976 | +static void preprocess_landscaped_polygon( | |
977 | + struct polygon_definition *polygon, | |
978 | + struct view_data *view) | |
979 | +{ | |
980 | + polygon->origin.x= (world_distance) ((10000*LANDSCAPE_REPEATS*WORLD_ONE)/(2*31415)); | |
981 | + polygon->origin.y= -(((LANDSCAPE_REPEATS*WORLD_ONE*view->yaw)>>ANGULAR_BITS)&(WORLD_ONE-1)); | |
982 | + polygon->origin.z= 0; | |
983 | + | |
984 | + polygon->vector.i= 0; | |
985 | + polygon->vector.j= WORLD_ONE; | |
986 | + polygon->vector.k= -WORLD_ONE; | |
987 | + | |
988 | + polygon->ambient_shade= FIXED_ONE; | |
989 | +} | |
990 | + | |
991 | +#endif | |
992 | + | |
993 | +/* starting at x0 and for line_count vertical lines between *y0 and *y1, precalculate all the | |
994 | + information _texture_vertical_polygon_lines will need to work */ | |
995 | +static void _pretexture_vertical_polygon_lines( | |
996 | + struct polygon_definition *polygon, | |
997 | + struct bitmap_definition *screen, | |
998 | + struct view_data *view, | |
999 | + struct _vertical_polygon_data *data, | |
1000 | + short x0, | |
1001 | + short *y0_table, | |
1002 | + short *y1_table, | |
1003 | + short line_count) | |
1004 | +{ | |
1005 | + short screen_x= x0-view->half_screen_width; | |
1006 | + int32 dz0= view->world_to_screen_y*polygon->origin.z; | |
1007 | + int32 unadjusted_ty_denominator= view->world_to_screen_y*polygon->vector.k; | |
1008 | + int32 tx_numerator, tx_denominator, tx_numerator_delta, tx_denominator_delta; | |
1009 | + struct _vertical_polygon_line_data *line= (struct _vertical_polygon_line_data *) (data+1); | |
1010 | + | |
1011 | + (void) (screen); | |
1012 | + | |
1013 | + assert(sizeof(struct _vertical_polygon_line_data)<=MAXIMUM_PRECALCULATION_TABLE_ENTRY_SIZE); | |
1014 | + | |
1015 | + data->downshift= VERTICAL_TEXTURE_DOWNSHIFT; | |
1016 | + data->x0= x0; | |
1017 | + data->width= line_count; | |
1018 | + | |
1019 | + /* calculate and rescale tx_numerator, tx_denominator, etc. */ | |
1020 | + tx_numerator= view->world_to_screen_x*polygon->origin.y - screen_x*polygon->origin.x; | |
1021 | + tx_denominator= screen_x*polygon->vector.i - view->world_to_screen_x*polygon->vector.j; | |
1022 | + tx_numerator_delta= -polygon->origin.x; | |
1023 | + tx_denominator_delta= polygon->vector.i; | |
1024 | + | |
1025 | + while (--line_count>=0) | |
1026 | + { | |
1027 | + _fixed tx; | |
1028 | + // LP change: made this quantity more long-distance friendly; | |
1029 | + // have to avoid doing INTEGER_TO_FIXED on this one, however | |
1030 | + int32 world_x; | |
1031 | + short x0, y0= *y0_table++; | |
1032 | + short screen_y0= view->half_screen_height-y0+view->dtanpitch; | |
1033 | + int32 ty_numerator, ty_denominator; | |
1034 | + _fixed ty, ty_delta; | |
1035 | + | |
1036 | + /* would our precision be greater here if we shifted the numerator up to $7FFFFFFF and | |
1037 | + then downshifted only the numerator? too bad we canユt use BFFFO in 68k */ | |
1038 | + { | |
1039 | + int32 adjusted_tx_denominator= tx_denominator; | |
1040 | + int32 adjusted_tx_numerator= tx_numerator; | |
1041 | + | |
1042 | + while (adjusted_tx_numerator>((1<<(31-VERTICAL_TEXTURE_WIDTH_BITS))-1) || | |
1043 | + adjusted_tx_numerator<((-1)<<(31-VERTICAL_TEXTURE_WIDTH_BITS))) | |
1044 | + { | |
1045 | + adjusted_tx_numerator>>= 1, adjusted_tx_denominator>>= 1; | |
1046 | + } | |
1047 | + if (!adjusted_tx_denominator) adjusted_tx_denominator= 1; /* -1 will still be -1 */ | |
1048 | + x0= ((adjusted_tx_numerator<<VERTICAL_TEXTURE_WIDTH_BITS)/adjusted_tx_denominator)&(VERTICAL_TEXTURE_WIDTH-1); | |
1049 | + | |
1050 | + while (adjusted_tx_numerator>INT16_MAX||adjusted_tx_numerator<INT16_MIN) | |
1051 | + { | |
1052 | + adjusted_tx_numerator>>= 1, adjusted_tx_denominator>>= 1; | |
1053 | + } | |
1054 | + if (!adjusted_tx_denominator) adjusted_tx_denominator= 1; /* -1 will still be -1 */ | |
1055 | + tx= INTEGER_TO_FIXED(adjusted_tx_numerator)/adjusted_tx_denominator; | |
1056 | + } | |
1057 | + | |
1058 | + world_x= polygon->origin.x + ((tx*polygon->vector.i)>>FIXED_FRACTIONAL_BITS); | |
1059 | + if (world_x<0) world_x= -world_x; /* it is mostly unclear what weユre supposed to do with negative x values */ | |
1060 | + | |
1061 | + /* calculate and rescale ty_numerator, ty_denominator and calculate ty */ | |
1062 | + ty_numerator= world_x*screen_y0 - dz0; | |
1063 | + ty_denominator= unadjusted_ty_denominator; | |
1064 | + while (ty_numerator>INT16_MAX||ty_numerator<INT16_MIN) | |
1065 | + { | |
1066 | + ty_numerator>>= 1, ty_denominator>>= 1; | |
1067 | + } | |
1068 | + if (!ty_denominator) ty_denominator= 1; /* -1 will still be -1 */ | |
1069 | + ty= INTEGER_TO_FIXED(ty_numerator)/ty_denominator; | |
1070 | + | |
1071 | + // LP change: | |
1072 | + // Use the same reduction hack used earlier, | |
1073 | + // because otherwise, INTEGER_TO_FIXED would cause world_x to wrap around. | |
1074 | + int32 adjusted_world_x = world_x; | |
1075 | + int32 adjusted_ty_denominator = unadjusted_ty_denominator>>8; | |
1076 | + | |
1077 | + // LP: remember that world_x is always >= 0 | |
1078 | + while(adjusted_world_x > INT16_MAX) | |
1079 | + { | |
1080 | + adjusted_world_x >>= 1; adjusted_ty_denominator >>= 1; | |
1081 | + } | |
1082 | + if (!adjusted_ty_denominator) adjusted_ty_denominator= 1; /* -1 will still be -1 */ | |
1083 | + ty_delta= - INTEGER_TO_FIXED(adjusted_world_x)/adjusted_ty_denominator; | |
1084 | + | |
1085 | + vassert(ty_delta>=0, csprintf(temporary, "ty_delta=W2F(%d)/%d=%d", world_x, unadjusted_ty_denominator, ty_delta)); | |
1086 | + | |
1087 | + /* calculate the shading table for this column */ | |
1088 | + if (polygon->flags&_SHADELESS_BIT) | |
1089 | + { | |
1090 | + line->shading_table= polygon->shading_tables; | |
1091 | + } | |
1092 | + else | |
1093 | + { | |
1094 | + // LP change: made this more long-distance friendly | |
1095 | + calculate_shading_table(line->shading_table, view, polygon->shading_tables, (short)MIN(world_x, SHRT_MAX), polygon->ambient_shade); | |
1096 | + // calculate_shading_table(line->shading_table, view, polygon->shading_tables, world_x, polygon->ambient_shade); | |
1097 | + } | |
1098 | + | |
1099 | +// if (ty_delta) | |
1100 | + { | |
1101 | + /* calculate texture_y and texture_dy (floor-mapper style) */ | |
1102 | +// data->n= VERTICAL_TEXTURE_DOWNSHIFT; | |
1103 | + line->texture_y= ty<<VERTICAL_TEXTURE_FREE_BITS; | |
1104 | + line->texture_dy= ty_delta<<(VERTICAL_TEXTURE_FREE_BITS-8); | |
1105 | + line->texture= polygon->texture->row_addresses[x0]; | |
1106 | + | |
1107 | + line+= 1; | |
1108 | + } | |
1109 | + | |
1110 | + tx_numerator+= tx_numerator_delta; | |
1111 | + tx_denominator+= tx_denominator_delta; | |
1112 | + | |
1113 | + screen_x+= 1; | |
1114 | + } | |
1115 | +} | |
1116 | + | |
1117 | +static void _pretexture_horizontal_polygon_lines( | |
1118 | + struct polygon_definition *polygon, | |
1119 | + struct bitmap_definition *screen, | |
1120 | + struct view_data *view, | |
1121 | + struct _horizontal_polygon_line_data *data, | |
1122 | + short y0, | |
1123 | + short *x0_table, | |
1124 | + short *x1_table, | |
1125 | + short line_count) | |
1126 | +{ | |
1127 | + int32 hcosine, dhcosine; | |
1128 | + int32 hsine, dhsine; | |
1129 | + int32 hworld_to_screen; | |
1130 | + bool higher_precision= polygon->origin.z>-WORLD_ONE && polygon->origin.z<WORLD_ONE; | |
1131 | + | |
1132 | + (void) (screen); | |
1133 | + | |
1134 | + /* precalculate a bunch of multiplies */ | |
1135 | + hcosine= cosine_table[view->yaw]; | |
1136 | + hsine= sine_table[view->yaw]; | |
1137 | + if (higher_precision) | |
1138 | + { | |
1139 | + hcosine*= polygon->origin.z; | |
1140 | + hsine*= polygon->origin.z; | |
1141 | + } | |
1142 | + hworld_to_screen= polygon->origin.z*view->world_to_screen_y; | |
1143 | + dhcosine= view->world_to_screen_y*hcosine; | |
1144 | + dhsine= view->world_to_screen_y*hsine; | |
1145 | + | |
1146 | + while ((line_count-=1)>=0) | |
1147 | + { | |
1148 | + // LP change: made this more long-distance-friendly | |
1149 | + int32 depth; | |
1150 | + // world_distance depth; | |
1151 | + short screen_x, screen_y; | |
1152 | + short x0= *x0_table++; | |
1153 | + | |
1154 | + /* calculate screen_x,screen_y */ | |
1155 | + screen_x= x0-view->half_screen_width; | |
1156 | + screen_y= view->half_screen_height-y0+view->dtanpitch; | |
1157 | + if (!screen_y) screen_y= 1; /* this will avoid division by zero and won't change rendering */ | |
1158 | + | |
1159 | + /* calculate source_x, source_y, source_dx, source_dy */ | |
1160 | + | |
1161 | + int32 source_x, source_y, source_dx, source_dy; | |
1162 | + | |
1163 | + /* calculate texture origins and deltas (source_x,source_dx,source_y,source_dy) */ | |
1164 | + if (higher_precision) | |
1165 | + { | |
1166 | + source_x= (dhcosine - screen_x*hsine)/screen_y + (polygon->origin.x<<TRIG_SHIFT); | |
1167 | + source_dx= - hsine/screen_y; | |
1168 | + source_y= (screen_x*hcosine + dhsine)/screen_y + (polygon->origin.y<<TRIG_SHIFT); | |
1169 | + source_dy= hcosine/screen_y; | |
1170 | + } | |
1171 | + else | |
1172 | + { | |
1173 | + source_x= ((dhcosine - screen_x*hsine)/screen_y)*polygon->origin.z + (polygon->origin.x<<TRIG_SHIFT); | |
1174 | + source_dx= - (hsine*polygon->origin.z)/screen_y; | |
1175 | + source_y= ((screen_x*hcosine + dhsine)/screen_y)*polygon->origin.z + (polygon->origin.y<<TRIG_SHIFT); | |
1176 | + source_dy= (hcosine*polygon->origin.z)/screen_y; | |
1177 | + } | |
1178 | + | |
1179 | + /* voodoo so x,y texture wrapping is handled automatically by downshifting | |
1180 | + (subtract one from HORIZONTAL_FREE_BITS to double scale) */ | |
1181 | + data->source_x= source_x<<HORIZONTAL_FREE_BITS, data->source_dx= source_dx<<HORIZONTAL_FREE_BITS; | |
1182 | + data->source_y= source_y<<HORIZONTAL_FREE_BITS, data->source_dy= source_dy<<HORIZONTAL_FREE_BITS; | |
1183 | + | |
1184 | + | |
1185 | + /* get shading table (with absolute value of depth) */ | |
1186 | + if ((depth= hworld_to_screen/screen_y)<0) depth= -depth; | |
1187 | + if (polygon->flags&_SHADELESS_BIT) | |
1188 | + { | |
1189 | + data->shading_table= polygon->shading_tables; | |
1190 | + } | |
1191 | + else | |
1192 | + { | |
1193 | + calculate_shading_table(data->shading_table, view, polygon->shading_tables, (short)MIN(depth, SHRT_MAX), polygon->ambient_shade); | |
1194 | + } | |
1195 | + | |
1196 | + data++; | |
1197 | + y0++; | |
1198 | + } | |
1199 | +} | |
1200 | + | |
1201 | + | |
1202 | +// height must be determined emperically (texture is vertically centered at 0。) | |
1203 | +// #define LANDSCAPE_REPEAT_BITS 1 | |
1204 | +static void _prelandscape_horizontal_polygon_lines( | |
1205 | + struct polygon_definition *polygon, | |
1206 | + struct bitmap_definition *screen, | |
1207 | + struct view_data *view, | |
1208 | + struct _horizontal_polygon_line_data *data, | |
1209 | + short y0, | |
1210 | + short *x0_table, | |
1211 | + short *x1_table, | |
1212 | + short line_count) | |
1213 | +{ | |
1214 | + // LP change: made this more general: | |
1215 | + short landscape_width_bits= NextLowerExponent(polygon->texture->height); | |
1216 | + short texture_height= polygon->texture->width; | |
1217 | + _fixed ambient_shade= FIXED_ONE; // MPW C died if we passed the constant directly to the macro | |
1218 | + | |
1219 | + // Get the landscape-texturing options | |
1220 | + LandscapeOptions *LandOpts = View_GetLandscapeOptions(polygon->ShapeDesc); | |
1221 | + | |
1222 | + // LP change: separate horizontal and vertical pixel deltas: | |
1223 | + // LP change: using a "landscape yaw" that's at the left edge of the screen. | |
1224 | + _fixed first_horizontal_pixel= (view->landscape_yaw + LandOpts->Azimuth)<<(landscape_width_bits+(LandOpts->HorizExp)+FIXED_FRACTIONAL_BITS-ANGULAR_BITS); | |
1225 | + _fixed horizontal_pixel_delta= (view->half_cone<<(1+landscape_width_bits+(LandOpts->HorizExp)+FIXED_FRACTIONAL_BITS-ANGULAR_BITS))/view->standard_screen_width; | |
1226 | + _fixed vertical_pixel_delta= (view->half_cone<<(1+landscape_width_bits+(LandOpts->VertExp)+FIXED_FRACTIONAL_BITS-ANGULAR_BITS))/view->standard_screen_width; | |
1227 | + short landscape_free_bits= 32-FIXED_FRACTIONAL_BITS-landscape_width_bits; | |
1228 | + | |
1229 | + (void) (screen); | |
1230 | + | |
1231 | + /* calculate the shading table */ | |
1232 | + void *shading_table = NULL; | |
1233 | + if (polygon->flags&_SHADELESS_BIT) | |
1234 | + { | |
1235 | + shading_table= polygon->shading_tables; | |
1236 | + } | |
1237 | + else | |
1238 | + { | |
1239 | + calculate_shading_table(shading_table, view, polygon->shading_tables, 0, ambient_shade); | |
1240 | + } | |
1241 | + | |
1242 | + // Find the height to repeat over; use value used for OpenGL texture setup | |
1243 | + short texture_width= polygon->texture->height; | |
1244 | + short repeat_texture_height = texture_width >> LandOpts->OGL_AspRatExp; | |
1245 | + | |
1246 | + short height_reduced = texture_height - 1; | |
1247 | + short height_shift = texture_height >> 1; | |
1248 | + short height_repeat_mask = repeat_texture_height - 1; | |
1249 | + short height_repeat_shift = repeat_texture_height >> 1; | |
1250 | + | |
1251 | + y0-= view->half_screen_height + view->dtanpitch; /* back to virtual screen coordinates */ | |
1252 | + while ((line_count-= 1)>=0) | |
1253 | + { | |
1254 | + short x0= *x0_table++; | |
1255 | + | |
1256 | + data->shading_table= shading_table; | |
1257 | + // LP change: using vertical pixel delta | |
1258 | + // Also using vertical repeat if selected; | |
1259 | + // fold the height into the range (-repeat_height/2, repeat_height) | |
1260 | + short y_txtr_offset= FIXED_INTEGERAL_PART(y0*vertical_pixel_delta); | |
1261 | + if (LandOpts->VertRepeat) | |
1262 | + y_txtr_offset = ((y_txtr_offset + height_repeat_shift) & height_repeat_mask) - | |
1263 | + height_repeat_shift; | |
1264 | + data->source_y= texture_height - PIN(y_txtr_offset + height_shift, 0, height_reduced) - 1; | |
1265 | + // LP change: using horizontal pixel delta | |
1266 | + data->source_x= (first_horizontal_pixel + x0*horizontal_pixel_delta)<<landscape_free_bits; | |
1267 | + data->source_dx= horizontal_pixel_delta<<landscape_free_bits; | |
1268 | + | |
1269 | + data+= 1; | |
1270 | + y0+= 1; | |
1271 | + } | |
1272 | +} | |
1273 | + | |
1274 | +/* y0<y1; this is for vertical polygons */ | |
1275 | +static short *build_x_table( | |
1276 | + short *table, | |
1277 | + short x0, | |
1278 | + short y0, | |
1279 | + short x1, | |
1280 | + short y1) | |
1281 | +{ | |
1282 | + short dx, dy, adx, ady; /* 'a' prefix means absolute value */ | |
1283 | + short x, y; /* x,y screen positions */ | |
1284 | + short d, delta_d, d_max; /* descriminator, delta_descriminator, descriminator_maximum */ | |
1285 | + short *record; | |
1286 | + | |
1287 | + /* calculate SGN(dx),SGN(dy) and the absolute values of dx,dy */ | |
1288 | + dx= x1-x0, adx= ABS(dx), dx= SGN(dx); | |
1289 | + dy= y1-y0, ady= ABS(dy), dy= SGN(dy); | |
1290 | + | |
1291 | + assert(ady<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* can't overflow table */ | |
1292 | + if (dy>0) | |
1293 | + { | |
1294 | + /* setup initial (x,y) location and initialize a pointer to our table */ | |
1295 | + x= x0, y= y0; | |
1296 | + record= table; | |
1297 | + | |
1298 | + if (adx>=ady) | |
1299 | + { | |
1300 | + /* x-dominant line (we need to record x every time y changes) */ | |
1301 | + | |
1302 | + d= adx-ady, delta_d= - 2*ady, d_max= 2*adx; | |
1303 | + while ((adx-=1)>=0) | |
1304 | + { | |
1305 | + if (d<0) y+= 1, d+= d_max, *record++= x, ady-= 1; | |
1306 | + x+= dx, d+= delta_d; | |
1307 | + } | |
1308 | + if (ady==1) *record++= x; else assert(!ady); | |
1309 | + } | |
1310 | + else | |
1311 | + { | |
1312 | + /* y-dominant line (we need to record x every iteration) */ | |
1313 | + | |
1314 | + d= ady-adx, delta_d= - 2*adx, d_max= 2*ady; | |
1315 | + while ((ady-=1)>=0) | |
1316 | + { | |
1317 | + if (d<0) x+= dx, d+= d_max; | |
1318 | + *record++= x; | |
1319 | + y+= 1, d+= delta_d; | |
1320 | + } | |
1321 | + } | |
1322 | + } | |
1323 | + else | |
1324 | + { | |
1325 | + /* canユt build a table for negative dy */ | |
1326 | + if (dy<0) return NULL; | |
1327 | + } | |
1328 | + | |
1329 | + return table; | |
1330 | +} | |
1331 | + | |
1332 | +/* x0<x1; this is for horizontal polygons */ | |
1333 | +static short *build_y_table( | |
1334 | + short *table, | |
1335 | + short x0, | |
1336 | + short y0, | |
1337 | + short x1, | |
1338 | + short y1) | |
1339 | +{ | |
1340 | + short dx, dy, adx, ady; /* 'a' prefix means absolute value */ | |
1341 | + short x, y; /* x,y screen positions */ | |
1342 | + short d, delta_d, d_max; /* descriminator, delta_descriminator, descriminator_maximum */ | |
1343 | + short *record; | |
1344 | + | |
1345 | + /* calculate SGN(dx),SGN(dy) and the absolute values of dx,dy */ | |
1346 | + dx= x1-x0, adx= ABS(dx), dx= SGN(dx); | |
1347 | + dy= y1-y0, ady= ABS(dy), dy= SGN(dy); | |
1348 | + | |
1349 | + assert(adx<MAXIMUM_SCRATCH_TABLE_ENTRIES); /* can't overflow table */ | |
1350 | + if (dx>=0) /* vertical lines allowed */ | |
1351 | + { | |
1352 | + /* setup initial (x,y) location and initialize a pointer to our table */ | |
1353 | + if (dy>=0) | |
1354 | + { | |
1355 | + x= x0, y= y0; | |
1356 | + record= table; | |
1357 | + } | |
1358 | + else | |
1359 | + { | |
1360 | + x= x1, y= y1; | |
1361 | + record= table+adx; | |
1362 | + } | |
1363 | + | |
1364 | + if (adx>=ady) | |
1365 | + { | |
1366 | + /* x-dominant line (we need to record y every iteration) */ | |
1367 | + | |
1368 | + d= adx-ady, delta_d= - 2*ady, d_max= 2*adx; | |
1369 | + while ((adx-=1)>=0) | |
1370 | + { | |
1371 | + if (d<0) y+= 1, d+= d_max; | |
1372 | + if (dy>=0) *record++= y; else *--record= y; | |
1373 | + x+= dx, d+= delta_d; | |
1374 | + } | |
1375 | + } | |
1376 | + else | |
1377 | + { | |
1378 | + /* y-dominant line (we need to record y every time x changes) */ | |
1379 | + | |
1380 | + d= ady-adx, delta_d= - 2*adx, d_max= 2*ady; | |
1381 | + while ((ady-=1)>=0) | |
1382 | + { | |
1383 | + if (d<0) { x+= dx, d+= d_max, adx-= 1; if (dy>=0) *record++= y; else *--record= y; } | |
1384 | + y+= 1, d+= delta_d; | |
1385 | + } | |
1386 | + if (adx==1) if (dy>=0) *record++= y; else *--record= y; else assert(!adx); | |
1387 | + } | |
1388 | + } | |
1389 | + else | |
1390 | + { | |
1391 | + /* canユt build a table for a negative dx */ | |
1392 | + return NULL; | |
1393 | + } | |
1394 | + | |
1395 | + return table; | |
1396 | +} |
@@ -1,82 +1,82 @@ | ||
1 | -#ifndef __SW_TEXTURE_EXTRAS_H | |
2 | -#define __SW_TEXTURE_EXTRAS_H | |
3 | - | |
4 | -/* | |
5 | -SW_TEXTURE_EXTRAS.H | |
6 | - | |
7 | - Copyright (C) 2007 Gregory Smith | |
8 | - | |
9 | - This program is free software; you can redistribute it and/or modify | |
10 | - it under the terms of the GNU General Public License as published by | |
11 | - the Free Software Foundation; either version 3 of the License, or | |
12 | - (at your option) any later version. | |
13 | - | |
14 | - This program is distributed in the hope that it will be useful, | |
15 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | - GNU General Public License for more details. | |
18 | - | |
19 | - This license is contained in the file "COPYING", | |
20 | - which is included with this source code; it is available online at | |
21 | - http://www.gnu.org/licenses/gpl.html | |
22 | - | |
23 | -*/ | |
24 | - | |
25 | -#include "cseries.h" | |
26 | -#include "cstypes.h" | |
27 | -#include "shape_descriptors.h" | |
28 | -#include "XML_ElementParser.h" | |
29 | -#include <vector> | |
30 | - | |
31 | -class SW_Texture | |
32 | -{ | |
33 | -public: | |
34 | - SW_Texture() : m_opac_type(0), m_shape_descriptor(0), m_opac_scale(1.0), m_opac_shift(0.0) { } | |
35 | - void descriptor(shape_descriptor ShapeDesc) { m_shape_descriptor = ShapeDesc; } | |
36 | - int opac_type() { return m_opac_type; } | |
37 | - void opac_type(int new_opac_type) { m_opac_type = new_opac_type; } | |
38 | - | |
39 | - uint8 *opac_table() { | |
40 | - if (m_opac_table.size() == 0) | |
41 | - return 0; | |
42 | - else | |
43 | - return &m_opac_table.front(); | |
44 | - } | |
45 | - | |
46 | - void opac_shift(float new_opac_shift) { m_opac_shift = new_opac_shift; } | |
47 | - void opac_scale(float new_opac_scale) { m_opac_scale = new_opac_scale; } | |
48 | - | |
49 | - void build_opac_table(); | |
50 | - | |
51 | - void clear_opac_table() { | |
52 | - m_opac_table.clear(); | |
53 | - } | |
54 | - | |
55 | -private: | |
56 | - shape_descriptor m_shape_descriptor; | |
57 | - int m_opac_type; | |
58 | - float m_opac_scale; | |
59 | - float m_opac_shift; | |
60 | - std::vector<uint8> m_opac_table; | |
61 | -}; | |
62 | - | |
63 | -class SW_Texture_Extras | |
64 | -{ | |
65 | -public: | |
66 | - static SW_Texture_Extras *instance() { if (!m_instance) m_instance = new SW_Texture_Extras(); return m_instance; } | |
67 | - | |
68 | - SW_Texture *GetTexture(shape_descriptor ShapeDesc); | |
69 | - SW_Texture *AddTexture(shape_descriptor ShapeDesc); | |
70 | - void Load(short Collection); | |
71 | - void Unload(short Collection); | |
72 | - | |
73 | -private: | |
74 | - SW_Texture_Extras() { } | |
75 | - static SW_Texture_Extras *m_instance; | |
76 | - | |
77 | - std::vector<SW_Texture> texture_list[NUMBER_OF_COLLECTIONS]; | |
78 | -}; | |
79 | - | |
80 | -XML_ElementParser *SW_Texture_Extras_GetParser(); | |
81 | - | |
82 | -#endif | |
1 | +#ifndef __SW_TEXTURE_EXTRAS_H | |
2 | +#define __SW_TEXTURE_EXTRAS_H | |
3 | + | |
4 | +/* | |
5 | +SW_TEXTURE_EXTRAS.H | |
6 | + | |
7 | + Copyright (C) 2007 Gregory Smith | |
8 | + | |
9 | + This program is free software; you can redistribute it and/or modify | |
10 | + it under the terms of the GNU General Public License as published by | |
11 | + the Free Software Foundation; either version 3 of the License, or | |
12 | + (at your option) any later version. | |
13 | + | |
14 | + This program is distributed in the hope that it will be useful, | |
15 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
17 | + GNU General Public License for more details. | |
18 | + | |
19 | + This license is contained in the file "COPYING", | |
20 | + which is included with this source code; it is available online at | |
21 | + http://www.gnu.org/licenses/gpl.html | |
22 | + | |
23 | +*/ | |
24 | + | |
25 | +#include "cseries.h" | |
26 | +#include "cstypes.h" | |
27 | +#include "shape_descriptors.h" | |
28 | +#include "XML_ElementParser.h" | |
29 | +#include <vector> | |
30 | + | |
31 | +class SW_Texture | |
32 | +{ | |
33 | +public: | |
34 | + SW_Texture() : m_opac_type(0), m_shape_descriptor(0), m_opac_scale(1.0), m_opac_shift(0.0) { } | |
35 | + void descriptor(shape_descriptor ShapeDesc) { m_shape_descriptor = ShapeDesc; } | |
36 | + int opac_type() { return m_opac_type; } | |
37 | + void opac_type(int new_opac_type) { m_opac_type = new_opac_type; } | |
38 | + | |
39 | + uint8 *opac_table() { | |
40 | + if (m_opac_table.size() == 0) | |
41 | + return 0; | |
42 | + else | |
43 | + return &m_opac_table.front(); | |
44 | + } | |
45 | + | |
46 | + void opac_shift(float new_opac_shift) { m_opac_shift = new_opac_shift; } | |
47 | + void opac_scale(float new_opac_scale) { m_opac_scale = new_opac_scale; } | |
48 | + | |
49 | + void build_opac_table(); | |
50 | + | |
51 | + void clear_opac_table() { | |
52 | + m_opac_table.clear(); | |
53 | + } | |
54 | + | |
55 | +private: | |
56 | + shape_descriptor m_shape_descriptor; | |
57 | + int m_opac_type; | |
58 | + float m_opac_scale; | |
59 | + float m_opac_shift; | |
60 | + std::vector<uint8> m_opac_table; | |
61 | +}; | |
62 | + | |
63 | +class SW_Texture_Extras | |
64 | +{ | |
65 | +public: | |
66 | + static SW_Texture_Extras *instance() { if (!m_instance) m_instance = new SW_Texture_Extras(); return m_instance; } | |
67 | + | |
68 | + SW_Texture *GetTexture(shape_descriptor ShapeDesc); | |
69 | + SW_Texture *AddTexture(shape_descriptor ShapeDesc); | |
70 | + void Load(short Collection); | |
71 | + void Unload(short Collection); | |
72 | + | |
73 | +private: | |
74 | + SW_Texture_Extras() { } | |
75 | + static SW_Texture_Extras *m_instance; | |
76 | + | |
77 | + std::vector<SW_Texture> texture_list[NUMBER_OF_COLLECTIONS]; | |
78 | +}; | |
79 | + | |
80 | +XML_ElementParser *SW_Texture_Extras_GetParser(); | |
81 | + | |
82 | +#endif |
@@ -1,99 +1,99 @@ | ||
1 | -#ifndef _RENDER_PLACE_OBJECTS_CLASS_ | |
2 | -#define _RENDER_PLACE_OBJECTS_CLASS_ | |
3 | -/* | |
4 | - | |
5 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - Rendering Object-Placement Class | |
23 | - by Loren Petrich, | |
24 | - August 6, 2000 | |
25 | - | |
26 | - Defines a class for placing inhabitants in appropriate rendering order; from render.c | |
27 | - Works from RenderSortPoly stuff. | |
28 | - | |
29 | - Made [view_data *view] a member and removed it as an argument | |
30 | - | |
31 | -Oct 13, 2000 | |
32 | - LP: replaced GrowableLists and ResizableLists with STL vectors | |
33 | -*/ | |
34 | - | |
35 | -#include <vector> | |
36 | -#include "world.h" | |
37 | -#include "interface.h" | |
38 | -#include "render.h" | |
39 | -#include "RenderSortPoly.h" | |
40 | - | |
41 | - | |
42 | -/* ---------- render objects */ | |
43 | - | |
44 | -struct render_object_data | |
45 | -{ | |
46 | - struct sorted_node_data *node; /* node we are being drawn inside */ | |
47 | - struct clipping_window_data *clipping_windows; /* our privately calculated clipping window */ | |
48 | - | |
49 | - struct render_object_data *next_object; /* the next object in this chain */ | |
50 | - | |
51 | - struct rectangle_definition rectangle; | |
52 | - | |
53 | - int16 ymedia; | |
54 | -}; | |
55 | - | |
56 | - | |
57 | -class RenderPlaceObjsClass | |
58 | -{ | |
59 | - // Auxiliary data and routines: | |
60 | - | |
61 | - void initialize_render_object_list(); | |
62 | - | |
63 | - render_object_data *build_render_object(long_point3d *origin, | |
64 | - _fixed floor_intensity, _fixed ceiling_intensity, | |
65 | - sorted_node_data **base_nodes, short *base_node_count, | |
66 | - short object_index, float Opacity, long_point3d *rel_origin); | |
67 | - | |
68 | - void sort_render_object_into_tree(render_object_data *new_render_object, | |
69 | - sorted_node_data **base_nodes, short base_node_count); | |
70 | - | |
71 | - short build_base_node_list(short origin_polygon_index, | |
72 | - world_point3d *origin, world_distance left_distance, world_distance right_distance, | |
73 | - sorted_node_data **base_nodes); | |
74 | - | |
75 | - void build_aggregate_render_object_clipping_window(render_object_data *render_object, | |
76 | - sorted_node_data **base_nodes, short base_node_count); | |
77 | - | |
78 | - shape_information_data *rescale_shape_information(shape_information_data *unscaled, | |
79 | - shape_information_data *scaled, uint16 flags); | |
80 | - | |
81 | -public: | |
82 | - | |
83 | - // LP additions: growable list of render objects; these are all the inhabitants | |
84 | - // Length changed in build_render_object() | |
85 | - // keep SortedNodes in sync | |
86 | - vector<render_object_data> RenderObjects; | |
87 | - | |
88 | - // Pointers to view and calculated visibility tree and sorted polygons | |
89 | - view_data *view; | |
90 | - RenderVisTreeClass *RVPtr; | |
91 | - RenderSortPolyClass *RSPtr; | |
92 | - | |
93 | - void build_render_object_list(); | |
94 | - | |
95 | - // Inits everything | |
96 | - RenderPlaceObjsClass(); | |
97 | -}; | |
98 | - | |
99 | -#endif | |
1 | +#ifndef _RENDER_PLACE_OBJECTS_CLASS_ | |
2 | +#define _RENDER_PLACE_OBJECTS_CLASS_ | |
3 | +/* | |
4 | + | |
5 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + Rendering Object-Placement Class | |
23 | + by Loren Petrich, | |
24 | + August 6, 2000 | |
25 | + | |
26 | + Defines a class for placing inhabitants in appropriate rendering order; from render.c | |
27 | + Works from RenderSortPoly stuff. | |
28 | + | |
29 | + Made [view_data *view] a member and removed it as an argument | |
30 | + | |
31 | +Oct 13, 2000 | |
32 | + LP: replaced GrowableLists and ResizableLists with STL vectors | |
33 | +*/ | |
34 | + | |
35 | +#include <vector> | |
36 | +#include "world.h" | |
37 | +#include "interface.h" | |
38 | +#include "render.h" | |
39 | +#include "RenderSortPoly.h" | |
40 | + | |
41 | + | |
42 | +/* ---------- render objects */ | |
43 | + | |
44 | +struct render_object_data | |
45 | +{ | |
46 | + struct sorted_node_data *node; /* node we are being drawn inside */ | |
47 | + struct clipping_window_data *clipping_windows; /* our privately calculated clipping window */ | |
48 | + | |
49 | + struct render_object_data *next_object; /* the next object in this chain */ | |
50 | + | |
51 | + struct rectangle_definition rectangle; | |
52 | + | |
53 | + int16 ymedia; | |
54 | +}; | |
55 | + | |
56 | + | |
57 | +class RenderPlaceObjsClass | |
58 | +{ | |
59 | + // Auxiliary data and routines: | |
60 | + | |
61 | + void initialize_render_object_list(); | |
62 | + | |
63 | + render_object_data *build_render_object(long_point3d *origin, | |
64 | + _fixed floor_intensity, _fixed ceiling_intensity, | |
65 | + sorted_node_data **base_nodes, short *base_node_count, | |
66 | + short object_index, float Opacity, long_point3d *rel_origin); | |
67 | + | |
68 | + void sort_render_object_into_tree(render_object_data *new_render_object, | |
69 | + sorted_node_data **base_nodes, short base_node_count); | |
70 | + | |
71 | + short build_base_node_list(short origin_polygon_index, | |
72 | + world_point3d *origin, world_distance left_distance, world_distance right_distance, | |
73 | + sorted_node_data **base_nodes); | |
74 | + | |
75 | + void build_aggregate_render_object_clipping_window(render_object_data *render_object, | |
76 | + sorted_node_data **base_nodes, short base_node_count); | |
77 | + | |
78 | + shape_information_data *rescale_shape_information(shape_information_data *unscaled, | |
79 | + shape_information_data *scaled, uint16 flags); | |
80 | + | |
81 | +public: | |
82 | + | |
83 | + // LP additions: growable list of render objects; these are all the inhabitants | |
84 | + // Length changed in build_render_object() | |
85 | + // keep SortedNodes in sync | |
86 | + vector<render_object_data> RenderObjects; | |
87 | + | |
88 | + // Pointers to view and calculated visibility tree and sorted polygons | |
89 | + view_data *view; | |
90 | + RenderVisTreeClass *RVPtr; | |
91 | + RenderSortPolyClass *RSPtr; | |
92 | + | |
93 | + void build_render_object_list(); | |
94 | + | |
95 | + // Inits everything | |
96 | + RenderPlaceObjsClass(); | |
97 | +}; | |
98 | + | |
99 | +#endif |
@@ -1,55 +1,55 @@ | ||
1 | -#ifndef _RASTERIZER_SOFTWARE_CLASS_ | |
2 | -#define _RASTERIZER_SOFTWARE_CLASS_ | |
3 | -/* | |
4 | - | |
5 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - Rasterizer software impementation (plugs into scottish_textures files) | |
23 | - by Loren Petrich, | |
24 | - August 7, 2000 | |
25 | - | |
26 | -*/ | |
27 | - | |
28 | -#include "Rasterizer.h" | |
29 | - | |
30 | - | |
31 | -class Rasterizer_SW_Class: public RasterizerClass | |
32 | -{ | |
33 | -public: | |
34 | - | |
35 | - // Pointers to stuff used in scottish_textures: | |
36 | - view_data *view; | |
37 | - // Calling this one "screen" for scottish_textures convenience: | |
38 | - bitmap_definition *screen; | |
39 | - | |
40 | - // Sets the rasterizer's view data; | |
41 | - // be sure to call it before doing any rendering | |
42 | - void SetView(view_data& View) {view = &View;} | |
43 | - | |
44 | - // Rendering calls | |
45 | - // These are defined in scottish_textures.c (too great a name to change) | |
46 | - | |
47 | - void texture_horizontal_polygon(polygon_definition& textured_polygon); | |
48 | - | |
49 | - void texture_vertical_polygon(polygon_definition& textured_polygon); | |
50 | - | |
51 | - void texture_rectangle(rectangle_definition& textured_rectangle); | |
52 | -}; | |
53 | - | |
54 | - | |
55 | -#endif | |
1 | +#ifndef _RASTERIZER_SOFTWARE_CLASS_ | |
2 | +#define _RASTERIZER_SOFTWARE_CLASS_ | |
3 | +/* | |
4 | + | |
5 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + Rasterizer software impementation (plugs into scottish_textures files) | |
23 | + by Loren Petrich, | |
24 | + August 7, 2000 | |
25 | + | |
26 | +*/ | |
27 | + | |
28 | +#include "Rasterizer.h" | |
29 | + | |
30 | + | |
31 | +class Rasterizer_SW_Class: public RasterizerClass | |
32 | +{ | |
33 | +public: | |
34 | + | |
35 | + // Pointers to stuff used in scottish_textures: | |
36 | + view_data *view; | |
37 | + // Calling this one "screen" for scottish_textures convenience: | |
38 | + bitmap_definition *screen; | |
39 | + | |
40 | + // Sets the rasterizer's view data; | |
41 | + // be sure to call it before doing any rendering | |
42 | + void SetView(view_data& View) {view = &View;} | |
43 | + | |
44 | + // Rendering calls | |
45 | + // These are defined in scottish_textures.c (too great a name to change) | |
46 | + | |
47 | + void texture_horizontal_polygon(polygon_definition& textured_polygon); | |
48 | + | |
49 | + void texture_vertical_polygon(polygon_definition& textured_polygon); | |
50 | + | |
51 | + void texture_rectangle(rectangle_definition& textured_rectangle); | |
52 | +}; | |
53 | + | |
54 | + | |
55 | +#endif |
@@ -1,1328 +1,1328 @@ | ||
1 | -/* | |
2 | - | |
3 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | - Rendering Clipping/Rasterization Class | |
21 | - by Loren Petrich, | |
22 | - August 7, 2000 | |
23 | - | |
24 | - Contains the calculation of clipping and rasterization; from render.c | |
25 | - | |
26 | - Made [view_data *view] a member and removed it as an argument | |
27 | - Also removed [bitmap_definition *destination] as superfluous, | |
28 | - now that there is a special rasterizer object that can contain it. | |
29 | - | |
30 | -Sep 2, 2000 (Loren Petrich): | |
31 | - Added some idiot-proofing, since the shapes accessor now returns NULL for nonexistent bitmaps | |
32 | - | |
33 | - If a polygon has a "full_side" texture, and there is another polgyon on the other side, | |
34 | - then suppress the void for the boundary's texture. | |
35 | -*/ | |
36 | - | |
37 | -#include "cseries.h" | |
38 | - | |
39 | -#include "map.h" | |
40 | -#include "lightsource.h" | |
41 | -#include "media.h" | |
42 | -#include "RenderRasterize.h" | |
43 | -#include "AnimatedTextures.h" | |
44 | -#include "OGL_Setup.h" | |
45 | -#include "preferences.h" | |
46 | -#include "screen.h" | |
47 | - | |
48 | -#include <string.h> | |
49 | - | |
50 | - | |
51 | -/* maximum number of vertices a polygon can be world-clipped into (one per clip line) */ | |
52 | -#define MAXIMUM_VERTICES_PER_WORLD_POLYGON (MAXIMUM_VERTICES_PER_POLYGON+4) | |
53 | - | |
54 | - | |
55 | -RenderRasterizerClass::RenderRasterizerClass(): | |
56 | - view(NULL), // Idiot-proofing | |
57 | - RSPtr(NULL), | |
58 | - RasPtr(NULL) | |
59 | -{} | |
60 | - | |
61 | - | |
62 | -/* ---------- rendering the tree */ | |
63 | - | |
64 | -void RenderRasterizerClass::render_tree() | |
65 | -{ | |
66 | - render_tree(kDiffuse); | |
67 | -} | |
68 | - | |
69 | -void RenderRasterizerClass::render_tree(RenderStep renderStep) | |
70 | -{ | |
71 | - assert(view); // Idiot-proofing | |
72 | - assert(RSPtr); | |
73 | - assert(RasPtr); | |
74 | - vector<sorted_node_data>::iterator node; | |
75 | - // LP: reference to simplify the code | |
76 | - vector<sorted_node_data>& SortedNodes = RSPtr->SortedNodes; | |
77 | - | |
78 | - // LP change: added support for semitransparent liquids | |
79 | - bool SeeThruLiquids = get_screen_mode()->acceleration != _no_acceleration ? TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_LiqSeeThru) : graphics_preferences->software_alpha_blending != _sw_alpha_off; | |
80 | - | |
81 | - /* walls, ceilings, interior objects, floors, exterior objects for all nodes, back to front */ | |
82 | - for (node= SortedNodes.begin(); node != SortedNodes.end(); ++node) | |
83 | - render_node(&*node, SeeThruLiquids, renderStep); | |
84 | -} | |
85 | - | |
86 | -void RenderRasterizerClass::render_node( | |
87 | - sorted_node_data *node, | |
88 | - bool SeeThruLiquids, | |
89 | - RenderStep renderStep) | |
90 | -{ | |
91 | - polygon_data *polygon= get_polygon_data(node->polygon_index); | |
92 | - clipping_window_data *window; | |
93 | - render_object_data *object; | |
94 | - | |
95 | - // LP change: moved this stuff out here because it only has to be calculated | |
96 | - // once per polygon. | |
97 | - horizontal_surface_data floor_surface, ceiling_surface; | |
98 | - short i; | |
99 | - | |
100 | - ceiling_surface.origin= polygon->ceiling_origin; | |
101 | - ceiling_surface.height= polygon->ceiling_height; | |
102 | - ceiling_surface.texture= polygon->ceiling_texture; | |
103 | - ceiling_surface.lightsource_index= polygon->ceiling_lightsource_index; | |
104 | - ceiling_surface.transfer_mode= polygon->ceiling_transfer_mode; | |
105 | - ceiling_surface.transfer_mode_data= 0; | |
106 | - | |
107 | - floor_surface.origin= polygon->floor_origin; | |
108 | - floor_surface.height= polygon->floor_height; | |
109 | - floor_surface.texture= polygon->floor_texture; | |
110 | - floor_surface.lightsource_index= polygon->floor_lightsource_index; | |
111 | - floor_surface.transfer_mode= polygon->floor_transfer_mode; | |
112 | - floor_surface.transfer_mode_data= 0; | |
113 | - | |
114 | - // The "continue" conditions are OK to move out here, because a non-drawn polygon's | |
115 | - // inhabitants will be clipped away. | |
116 | - | |
117 | - // LP: get liquid data here for convenience; | |
118 | - // pointer to it being NULL means no liquid in the polygon | |
119 | - media_data *media = NULL; | |
120 | - if (polygon->media_index!=NONE) | |
121 | - media = get_media_data(polygon->media_index); | |
122 | - | |
123 | - /* if necessary, replace the ceiling or floor surfaces with the media surface */ | |
124 | - // LP change: don't do this step if liquids are semitransparent | |
125 | - if (media && !SeeThruLiquids) | |
126 | - { | |
127 | - // LP change: moved this get upwards | |
128 | - // struct media_data *media= get_media_data(polygon->media_index); | |
129 | - horizontal_surface_data *media_surface= NULL; | |
130 | - | |
131 | - if (view->under_media_boundary) | |
132 | - { | |
133 | - // LP change: skip if high and dry | |
134 | - if (media->height <= polygon->floor_height) | |
135 | - return; | |
136 | - | |
137 | - if (media->height<polygon->ceiling_height) media_surface= &ceiling_surface; | |
138 | - } | |
139 | - else | |
140 | - { | |
141 | - // LP change: skip if submerged | |
142 | - if (media->height >= polygon->ceiling_height) | |
143 | - return; | |
144 | - | |
145 | - if (media->height>polygon->floor_height) media_surface= &floor_surface; | |
146 | - } | |
147 | - | |
148 | - if (media_surface) | |
149 | - { | |
150 | - media_surface->origin= media->origin; | |
151 | - media_surface->height= media->height; | |
152 | - media_surface->texture= media->texture; | |
153 | - media_surface->lightsource_index= polygon->media_lightsource_index; | |
154 | - media_surface->transfer_mode= media->transfer_mode; | |
155 | - media_surface->transfer_mode_data= 0; | |
156 | - } | |
157 | - } | |
158 | - // LP change: always render liquids that are semitransparent | |
159 | - else if (!SeeThruLiquids) | |
160 | - { | |
161 | - // if weユre trying to draw a polygon without media from under a polygon with media, donユt | |
162 | - if (view->under_media_boundary) return; | |
163 | - } | |
164 | - | |
165 | - // LP: this loop renders the walls | |
166 | - for (window= node->clipping_windows; window; window= window->next_window) | |
167 | - { | |
168 | - if (ceiling_surface.height>floor_surface.height) | |
169 | - { | |
170 | - /* render ceiling if above viewer */ | |
171 | - if (ceiling_surface.height>view->origin.z) | |
172 | - { | |
173 | - // LP change: indicated that the void is on other side | |
174 | - render_node_floor_or_ceiling(window, polygon, &ceiling_surface, true, true, renderStep); | |
175 | - } | |
176 | - | |
177 | - /* render visible sides */ | |
178 | - for (i= 0; i<polygon->vertex_count; ++i) | |
179 | - { | |
180 | - short side_index= polygon->side_indexes[i]; | |
181 | - | |
182 | - if (side_index!=NONE && TEST_RENDER_FLAG(side_index, _side_is_visible)) | |
183 | - { | |
184 | - line_data *line= get_line_data(polygon->line_indexes[i]); | |
185 | - side_data *side= get_side_data(side_index); | |
186 | - vertical_surface_data surface; | |
187 | - | |
188 | - surface.length= line->length; | |
189 | - store_endpoint(get_endpoint_data(polygon->endpoint_indexes[i]), surface.p0); | |
190 | - store_endpoint(get_endpoint_data(polygon->endpoint_indexes[WRAP_HIGH(i, polygon->vertex_count-1)]), surface.p1); | |
191 | - surface.ambient_delta= side->ambient_delta; | |
192 | - | |
193 | - // LP change: indicate in all cases whether the void is on the other side; | |
194 | - // added a workaround for full-side textures with a polygon on the other side | |
195 | - bool void_present; | |
196 | - | |
197 | - switch (side->type) | |
198 | - { | |
199 | - case _full_side: | |
200 | - void_present = true; | |
201 | - // Suppress the void if there is a polygon on the other side. | |
202 | - if (polygon->adjacent_polygon_indexes[i] != NONE) void_present = false; | |
203 | - | |
204 | - surface.lightsource_index= side->primary_lightsource_index; | |
205 | - surface.h0= floor_surface.height - view->origin.z; | |
206 | - surface.hmax= ceiling_surface.height - view->origin.z; | |
207 | - surface.h1= polygon->ceiling_height - view->origin.z; | |
208 | - surface.texture_definition= &side->primary_texture; | |
209 | - surface.transfer_mode= side->primary_transfer_mode; | |
210 | - render_node_side(window, &surface, void_present, renderStep); | |
211 | - break; | |
212 | - case _split_side: /* render _low_side first */ | |
213 | - surface.lightsource_index= side->secondary_lightsource_index; | |
214 | - surface.h0= floor_surface.height - view->origin.z; | |
215 | - surface.h1= MAX(line->highest_adjacent_floor, floor_surface.height) - view->origin.z; | |
216 | - surface.hmax= ceiling_surface.height - view->origin.z; | |
217 | - surface.texture_definition= &side->secondary_texture; | |
218 | - surface.transfer_mode= side->secondary_transfer_mode; | |
219 | - render_node_side(window, &surface, true, renderStep); | |
220 | - /* fall through and render high side */ | |
221 | - case _high_side: | |
222 | - surface.lightsource_index= side->primary_lightsource_index; | |
223 | - surface.h0= MIN(line->lowest_adjacent_ceiling, ceiling_surface.height) - view->origin.z; | |
224 | - surface.hmax= ceiling_surface.height - view->origin.z; | |
225 | - surface.h1= polygon->ceiling_height - view->origin.z; | |
226 | - surface.texture_definition= &side->primary_texture; | |
227 | - surface.transfer_mode= side->primary_transfer_mode; | |
228 | - render_node_side(window, &surface, true, renderStep); | |
229 | - // render_node_side(view, destination, window, &surface); | |
230 | - break; | |
231 | - case _low_side: | |
232 | - surface.lightsource_index= side->primary_lightsource_index; | |
233 | - surface.h0= floor_surface.height - view->origin.z; | |
234 | - surface.h1= MAX(line->highest_adjacent_floor, floor_surface.height) - view->origin.z; | |
235 | - surface.hmax= ceiling_surface.height - view->origin.z; | |
236 | - surface.texture_definition= &side->primary_texture; | |
237 | - surface.transfer_mode= side->primary_transfer_mode; | |
238 | - render_node_side(window, &surface, true, renderStep); | |
239 | - // render_node_side(view, destination, window, &surface); | |
240 | - break; | |
241 | - | |
242 | - default: | |
243 | - assert(false); | |
244 | - break; | |
245 | - } | |
246 | - | |
247 | - if (side->transparent_texture.texture!=UNONE) | |
248 | - { | |
249 | - surface.lightsource_index= side->transparent_lightsource_index; | |
250 | - surface.h0= MAX(line->highest_adjacent_floor, floor_surface.height) - view->origin.z; | |
251 | - surface.h1= line->lowest_adjacent_ceiling - view->origin.z; | |
252 | - surface.hmax= ceiling_surface.height - view->origin.z; | |
253 | - surface.texture_definition= &side->transparent_texture; | |
254 | - surface.transfer_mode= side->transparent_transfer_mode; | |
255 | - render_node_side(window, &surface, false, renderStep); | |
256 | - } | |
257 | - } | |
258 | - } | |
259 | - | |
260 | - /* render floor if below viewer */ | |
261 | - if (floor_surface.height<view->origin.z) | |
262 | - { | |
263 | - // LP change: indicated that the void is on other side | |
264 | - render_node_floor_or_ceiling(window, polygon, &floor_surface, true, false, renderStep); | |
265 | - } | |
266 | - } | |
267 | - } | |
268 | - | |
269 | - // LP: this is for objects on the other side of the liquids; | |
270 | - // render them out here if one can see through the liquids | |
271 | - if (SeeThruLiquids) | |
272 | - { | |
273 | - /* render exterior objects (with their own clipping windows) */ | |
274 | - for (object= node->exterior_objects; object; object= object->next_object) | |
275 | - { | |
276 | - render_node_object(object, true, renderStep); | |
277 | - } | |
278 | - } | |
279 | - | |
280 | - // LP: render the liquid surface after the walls and the stuff behind it | |
281 | - // and before the stuff before it. | |
282 | - if (media && SeeThruLiquids) | |
283 | - { | |
284 | - | |
285 | - // Render only if between the floor and the ceiling: | |
286 | - if (media->height > polygon->floor_height && media->height < polygon->ceiling_height) | |
287 | - { | |
288 | - | |
289 | - // Render the liquids | |
290 | - bool ceil = (media->height > view->origin.z); | |
291 | - horizontal_surface_data LiquidSurface; | |
292 | - | |
293 | - LiquidSurface.origin= media->origin; | |
294 | - LiquidSurface.height= media->height; | |
295 | - LiquidSurface.texture= media->texture; | |
296 | - LiquidSurface.lightsource_index= polygon->media_lightsource_index; | |
297 | - LiquidSurface.transfer_mode= media->transfer_mode; | |
298 | - LiquidSurface.transfer_mode_data= 0; | |
299 | - | |
300 | - for (window= node->clipping_windows; window; window= window->next_window) | |
301 | - { | |
302 | - render_node_floor_or_ceiling(window, polygon, &LiquidSurface, false, ceil, renderStep); | |
303 | - } | |
304 | - } | |
305 | - } | |
306 | - | |
307 | - // LP: this is for objects on the view side of the liquids | |
308 | - /* render exterior objects (with their own clipping windows) */ | |
309 | - for (object= node->exterior_objects; object; object= object->next_object) | |
310 | - { | |
311 | - render_node_object(object, false, renderStep); | |
312 | - } | |
313 | -} | |
314 | - | |
315 | -void RenderRasterizerClass::store_endpoint( | |
316 | - endpoint_data *endpoint, | |
317 | - long_vector2d& p) | |
318 | -{ | |
319 | - overflow_short_to_long_2d(endpoint->transformed, endpoint->flags, p); | |
320 | -} | |
321 | - | |
322 | - | |
323 | -/* ---------- rendering ceilings and floors */ | |
324 | - | |
325 | -// LP change: added "void present on other side" flag | |
326 | -void RenderRasterizerClass::render_node_floor_or_ceiling( | |
327 | - clipping_window_data *window, | |
328 | - polygon_data *polygon, | |
329 | - horizontal_surface_data *surface, | |
330 | - bool void_present, | |
331 | - bool ceil, | |
332 | - RenderStep renderStep) | |
333 | -{ | |
334 | - // LP addition: animated-texture support | |
335 | - // Extra variable defined so as not to edit the original texture | |
336 | - shape_descriptor Texture = AnimTxtr_Translate(surface->texture); | |
337 | - if (Texture!=UNONE) | |
338 | - { | |
339 | - struct polygon_definition textured_polygon; | |
340 | - flagged_world_point2d vertices[MAXIMUM_VERTICES_PER_WORLD_POLYGON]; | |
341 | - world_distance adjusted_height= surface->height-view->origin.z; | |
342 | - int32 transformed_height= adjusted_height*view->world_to_screen_y; | |
343 | - short vertex_count; | |
344 | - short i; | |
345 | - | |
346 | - /* build transformed vertex list */ | |
347 | - vertex_count= polygon->vertex_count; | |
348 | - for (i=0;i<vertex_count;++i) | |
349 | - { | |
350 | - // LP change: expanded the transformed-endpoint access | |
351 | - long_vector2d temp_vertex; | |
352 | - endpoint_data *endpoint = get_endpoint_data(polygon->endpoint_indexes[i]); | |
353 | - overflow_short_to_long_2d(endpoint->transformed,endpoint->flags,temp_vertex); | |
354 | - vertices[i].x = temp_vertex.i; | |
355 | - vertices[i].y = temp_vertex.j; | |
356 | - vertices[i].flags= 0; | |
357 | - } | |
358 | - | |
359 | - /* reversing the order in which these two were clipped caused weird problems */ | |
360 | - | |
361 | - /* clip to left and right sides of the window */ | |
362 | - vertex_count= xy_clip_horizontal_polygon(vertices, vertex_count, &window->left, _clip_left); | |
363 | - vertex_count= xy_clip_horizontal_polygon(vertices, vertex_count, &window->right, _clip_right); | |
364 | - | |
365 | - /* clip to top and bottom sides of the window */ | |
366 | - vertex_count= z_clip_horizontal_polygon(vertices, vertex_count, &window->top, adjusted_height, _clip_up); | |
367 | - vertex_count= z_clip_horizontal_polygon(vertices, vertex_count, &window->bottom, adjusted_height, _clip_down); | |
368 | - | |
369 | - if (vertex_count) | |
370 | - { | |
371 | - /* transform the points we have into screen-space (backwards for ceiling polygons) */ | |
372 | - for (i=0;i<vertex_count;++i) | |
373 | - { | |
374 | - flagged_world_point2d *world= vertices + (adjusted_height>0 ? vertex_count-i-1 : i); | |
375 | - point2d *screen= textured_polygon.vertices + i; | |
376 | - | |
377 | - switch (world->flags&(_clip_left|_clip_right)) | |
378 | - { | |
379 | - case 0: | |
380 | - // LP change: making it long-distance friendly | |
381 | - { | |
382 | - int32 world_x = world->x ? world->x : 1; // fix for division by zero error | |
383 | - int32 screen_x= view->half_screen_width + (world->y*view->world_to_screen_x)/world_x; | |
384 | - screen->x= PIN(screen_x, 0, view->screen_width); | |
385 | - } | |
386 | - break; | |
387 | - case _clip_left: screen->x= window->x0; break; | |
388 | - case _clip_right: screen->x= window->x1; break; | |
389 | - default: | |
390 | - screen->x= window->x0; | |
391 | - break; | |
392 | - } | |
393 | - | |
394 | - switch (world->flags&(_clip_up|_clip_down)) | |
395 | - { | |
396 | - case 0: | |
397 | - // LP change: making it long-distance friendly | |
398 | - { | |
399 | - int32 world_x = world->x ? world->x : 1; // fix for division by zero error | |
400 | - int32 screen_y= view->half_screen_height - transformed_height/world_x + view->dtanpitch; | |
401 | - screen->y= PIN(screen_y, 0, view->screen_height); | |
402 | - } | |
403 | - break; | |
404 | - case _clip_up: screen->y= window->y0; break; | |
405 | - case _clip_down: screen->y= window->y1; break; | |
406 | - default: | |
407 | - screen->y= window->y0; | |
408 | - break; | |
409 | - } | |
410 | - // vassert(screen->y>=0&&screen->y<=view->screen_height, csprintf(temporary, "horizontal: flags==%x, window @ %p", world->flags, window)); | |
411 | - } | |
412 | - | |
413 | - /* setup the other parameters of the textured polygon */ | |
414 | - textured_polygon.flags= 0; | |
415 | - textured_polygon.origin.x= view->origin.x + surface->origin.x; | |
416 | - textured_polygon.origin.y= view->origin.y + surface->origin.y; | |
417 | - textured_polygon.origin.z= adjusted_height; | |
418 | - get_shape_bitmap_and_shading_table(Texture, &textured_polygon.texture, &textured_polygon.shading_tables, view->shading_mode); | |
419 | - // Bug out if bitmap is nonexistent | |
420 | - if (!textured_polygon.texture) return; | |
421 | - | |
422 | - textured_polygon.ShapeDesc = Texture; | |
423 | - textured_polygon.ambient_shade= get_light_intensity(surface->lightsource_index); | |
424 | - textured_polygon.vertex_count= vertex_count; | |
425 | - instantiate_polygon_transfer_mode(view, &textured_polygon, surface->transfer_mode, true); | |
426 | - if (view->shading_mode==_shading_infravision) textured_polygon.flags|= _SHADELESS_BIT; | |
427 | - | |
428 | - /* and, finally, map it */ | |
429 | - // LP: added OpenGL support; also presence of void on other side | |
430 | - textured_polygon.VoidPresent = void_present; | |
431 | - // LP: using rasterizer object | |
432 | - RasPtr->texture_horizontal_polygon(textured_polygon); | |
433 | - } | |
434 | - } | |
435 | -} | |
436 | - | |
437 | -/* ---------- rendering sides (walls) */ | |
438 | - | |
439 | -// LP change: added "void present on other side" flag | |
440 | -void RenderRasterizerClass::render_node_side( | |
441 | - clipping_window_data *window, | |
442 | - vertical_surface_data *surface, | |
443 | - bool void_present, | |
444 | - RenderStep renderStep) | |
445 | -{ | |
446 | - world_distance h= MIN(surface->h1, surface->hmax); | |
447 | - | |
448 | - // LP addition: animated-texture support | |
449 | - // Extra variable defined so as not to edit the original texture | |
450 | - shape_descriptor Texture = AnimTxtr_Translate(surface->texture_definition->texture); | |
451 | - if (h>surface->h0 && Texture!=UNONE) | |
452 | - { | |
453 | - struct polygon_definition textured_polygon; | |
454 | - flagged_world_point2d posts[2]; | |
455 | - flagged_world_point3d vertices[MAXIMUM_VERTICES_PER_WORLD_POLYGON]; | |
456 | - short vertex_count; | |
457 | - short i; | |
458 | - | |
459 | - /* initialize the two posts of our trapezoid */ | |
460 | - vertex_count= 2; | |
461 | - posts[0].x= surface->p0.i; posts[0].y= surface->p0.j; posts[0].flags= 0; | |
462 | - posts[1].x= surface->p1.i; posts[1].y= surface->p1.j; posts[1].flags= 0; | |
463 | - | |
464 | - /* clip to left and right sides of the cone (must happen in the same order as horizontal polygons) */ | |
465 | - vertex_count= xy_clip_line(posts, vertex_count, &window->left, _clip_left); | |
466 | - vertex_count= xy_clip_line(posts, vertex_count, &window->right, _clip_right); | |
467 | - | |
468 | - if (vertex_count) | |
469 | - { | |
470 | - /* build a polygon out of the two posts */ | |
471 | - vertex_count= 4; | |
472 | - vertices[0].z= vertices[1].z= h; | |
473 | - vertices[2].z= vertices[3].z= surface->h0; | |
474 | - vertices[0].x= vertices[3].x= posts[0].x, vertices[0].y= vertices[3].y= posts[0].y; | |
475 | - vertices[1].x= vertices[2].x= posts[1].x, vertices[1].y= vertices[2].y= posts[1].y; | |
476 | - vertices[0].flags= vertices[3].flags= posts[0].flags; | |
477 | - vertices[1].flags= vertices[2].flags= posts[1].flags; | |
478 | - | |
479 | - /* clip to top and bottom sides of the window; because xz_clip_vertical_polygon accepts | |
480 | - vertices in clockwise or counterclockwise order, we can set our vertex list up to be | |
481 | - clockwise on the screen and never worry about it after that */ | |
482 | - vertex_count= xz_clip_vertical_polygon(vertices, vertex_count, &window->top, _clip_up); | |
483 | - vertex_count= xz_clip_vertical_polygon(vertices, vertex_count, &window->bottom, _clip_down); | |
484 | - | |
485 | - if (vertex_count) | |
486 | - { | |
487 | - world_distance dx= surface->p1.i - surface->p0.i; | |
488 | - world_distance dy= surface->p1.j - surface->p0.j; | |
489 | - world_distance x0= WORLD_FRACTIONAL_PART(surface->texture_definition->x0); | |
490 | - world_distance y0= WORLD_FRACTIONAL_PART(surface->texture_definition->y0); | |
491 | - | |
492 | - /* calculate texture origin and direction */ | |
493 | - world_distance divisor = surface->length; | |
494 | - if (divisor == 0) | |
495 | - divisor = 1; | |
496 | - textured_polygon.vector.i= (WORLD_ONE*dx)/divisor; | |
497 | - textured_polygon.vector.j= (WORLD_ONE*dy)/divisor; | |
498 | - textured_polygon.vector.k= -WORLD_ONE; | |
499 | - textured_polygon.origin.x= surface->p0.i - (x0*dx)/divisor; | |
500 | - textured_polygon.origin.y= surface->p0.j - (x0*dy)/divisor; | |
501 | - textured_polygon.origin.z= surface->h1 + y0; | |
502 | - | |
503 | - /* transform the points we have into screen-space */ | |
504 | - for (i=0;i<vertex_count;++i) | |
505 | - { | |
506 | - flagged_world_point3d *world= vertices + i; | |
507 | - point2d *screen= textured_polygon.vertices + i; | |
508 | - | |
509 | - switch (world->flags&(_clip_left|_clip_right)) | |
510 | - { | |
511 | - case 0: | |
512 | - // LP change: making it long-distance friendly | |
513 | - { | |
514 | - int32 screen_x= view->half_screen_width + (world->x ? (world->y*view->world_to_screen_x)/world->x : 0); | |
515 | - screen->x= PIN(screen_x, 0, view->screen_width); | |
516 | - } | |
517 | - break; | |
518 | - case _clip_left: screen->x= window->x0; break; | |
519 | - case _clip_right: screen->x= window->x1; break; | |
520 | - default: | |
521 | - screen->x= window->x0; | |
522 | - break; | |
523 | - } | |
524 | - | |
525 | - switch (world->flags&(_clip_up|_clip_down)) | |
526 | - { | |
527 | - case 0: | |
528 | - // LP change: making it long-distance friendly | |
529 | - { | |
530 | - int32 screen_y= view->half_screen_height - (world->x ? (world->z*view->world_to_screen_y)/world->x : 0) + view->dtanpitch; | |
531 | - screen->y= PIN(screen_y, 0, view->screen_height); | |
532 | - } | |
533 | - break; | |
534 | - case _clip_up: screen->y= window->y0; break; | |
535 | - case _clip_down: screen->y= window->y1; break; | |
536 | - default: | |
537 | - screen->y= window->y0; | |
538 | - break; | |
539 | - } | |
540 | - // vassert(screen->y>=0&&screen->y<=view->screen_height, csprintf(temporary, "#%d!in[#0,#%d]: flags==%x, wind@%p #%d w@%p s@%p", screen->y, view->screen_height, world->flags, window, vertex_count, world, screen)); | |
541 | - } | |
542 | - | |
543 | - /* setup the other parameters of the textured polygon */ | |
544 | - textured_polygon.flags= 0; | |
545 | - get_shape_bitmap_and_shading_table(Texture, &textured_polygon.texture, &textured_polygon.shading_tables, view->shading_mode); | |
546 | - // Bug out if bitmap is nonexistent | |
547 | - if (!textured_polygon.texture) return; | |
548 | - | |
549 | - textured_polygon.ShapeDesc = Texture; | |
550 | - textured_polygon.ambient_shade= get_light_intensity(surface->lightsource_index) + surface->ambient_delta; | |
551 | - textured_polygon.ambient_shade= PIN(textured_polygon.ambient_shade, 0, FIXED_ONE); | |
552 | - textured_polygon.vertex_count= vertex_count; | |
553 | - instantiate_polygon_transfer_mode(view, &textured_polygon, surface->transfer_mode, false); | |
554 | - if (view->shading_mode==_shading_infravision) textured_polygon.flags|= _SHADELESS_BIT; | |
555 | - | |
556 | - /* and, finally, map it */ | |
557 | - // LP: added OpenGL support; also presence of void on other side | |
558 | - textured_polygon.VoidPresent = void_present; | |
559 | - // LP: using rasterizer object | |
560 | - RasPtr->texture_vertical_polygon(textured_polygon); | |
561 | - } | |
562 | - } | |
563 | - } | |
564 | -} | |
565 | - | |
566 | -/* ---------- rendering objects */ | |
567 | - | |
568 | -void RenderRasterizerClass::render_node_object( | |
569 | - render_object_data *object, | |
570 | - bool other_side_of_media, | |
571 | - RenderStep renderStep) | |
572 | -{ | |
573 | - struct clipping_window_data *window; | |
574 | - | |
575 | - for (window= object->clipping_windows; window; window= window->next_window) | |
576 | - { | |
577 | - object->rectangle.clip_left= window->x0; | |
578 | - object->rectangle.clip_right= window->x1; | |
579 | - object->rectangle.clip_top= window->y0; | |
580 | - object->rectangle.clip_bottom= window->y1; | |
581 | - | |
582 | - // Models will have their own liquid-surface clipping, | |
583 | - // so don't edit their clip rects | |
584 | - // This is bitwise XOR, but is presumably OK here | |
585 | - if (view->under_media_boundary ^ other_side_of_media) | |
586 | - { | |
587 | - // Clipping: below a liquid surface | |
588 | - if (object->rectangle.ModelPtr) | |
589 | - object->rectangle.BelowLiquid = true; | |
590 | - else | |
591 | - object->rectangle.clip_top= MAX(object->rectangle.clip_top, object->ymedia); | |
592 | - } | |
593 | - else | |
594 | - { | |
595 | - // Clipping: above a liquid surface | |
596 | - if (object->rectangle.ModelPtr) | |
597 | - object->rectangle.BelowLiquid = false; | |
598 | - else | |
599 | - object->rectangle.clip_bottom= MIN(object->rectangle.clip_bottom, object->ymedia); | |
600 | - } | |
601 | - | |
602 | - // LP: added OpenGL support | |
603 | - // LP: using rasterizer object | |
604 | - RasPtr->texture_rectangle(object->rectangle); | |
605 | - } | |
606 | -} | |
607 | - | |
608 | - | |
609 | -/* ---------- horizontal polygon clipping */ | |
610 | - | |
611 | -enum /* xy_clip_horizontal_polygon() states */ | |
612 | -{ | |
613 | - _testing_first_vertex, /* are we in or out? */ | |
614 | - _searching_cw_for_in_out_transition, | |
615 | - _searching_ccw_for_out_in_transition, | |
616 | - _searching_cw_for_out_in_transition | |
617 | -}; | |
618 | - | |
619 | -// LP change: make it better able to do long-distance views | |
620 | -short RenderRasterizerClass::xy_clip_horizontal_polygon( | |
621 | - flagged_world_point2d *vertices, | |
622 | - short vertex_count, | |
623 | - long_vector2d *line, | |
624 | - uint16 flag) | |
625 | -{ | |
626 | -#ifdef QUICKDRAW_DEBUG | |
627 | - debug_flagged_points(vertices, vertex_count); | |
628 | - debug_vector(line); | |
629 | -#endif | |
630 | -// dprintf("clipping %p (#%d vertices) to vector %x,%x (slope==%x)", vertices, vertex_count, line->i, line->j, slope); | |
631 | - | |
632 | - if (vertex_count) | |
633 | - { | |
634 | - short state= _testing_first_vertex; | |
635 | - short vertex_index= 0, vertex_delta= 1, first_vertex= 0; | |
636 | - short entrance_vertex= NONE, exit_vertex= NONE; /* exiting the clipped area and entering the clipped area */ | |
637 | - bool clipped_exit_vertex= true, clipped_entrance_vertex= true; /* will be false if these points lie directly on a vertex */ | |
638 | - | |
639 | - do | |
640 | - { | |
641 | - // LP change: | |
642 | - CROSSPROD_TYPE cross_product= CROSSPROD_TYPE(line->i)*vertices[vertex_index].y - CROSSPROD_TYPE(line->j)*vertices[vertex_index].x; | |
643 | - // int32 cross_product= line->i*vertices[vertex_index].y - line->j*vertices[vertex_index].x; | |
644 | - | |
645 | - switch (SGN(cross_product)) | |
646 | - { | |
647 | - case -1: /* inside (i.e., will be clipped) */ | |
648 | -// dprintf("vertex#%d is inside s==#%d", vertex_index, state); | |
649 | - switch (state) | |
650 | - { | |
651 | - case _testing_first_vertex: | |
652 | - first_vertex= vertex_index; | |
653 | - state= _searching_cw_for_in_out_transition; /* the exit point from the clip area */ | |
654 | - break; | |
655 | - | |
656 | - case _searching_ccw_for_out_in_transition: /* found exit point from clip area */ | |
657 | - state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
658 | - vertex_delta= 1; | |
659 | - if (exit_vertex==NONE) exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
660 | - vertex_index= first_vertex; /* skip vertices we already know are out */ | |
661 | - break; | |
662 | - | |
663 | - case _searching_cw_for_out_in_transition: /* found entrance point to clipped area */ | |
664 | - if (entrance_vertex==NONE) entrance_vertex= vertex_index; | |
665 | - state= NONE; | |
666 | - break; | |
667 | - } | |
668 | - break; | |
669 | - | |
670 | - case 0: /* clip line passed directly through a vertex */ | |
671 | -// dprintf("vertex#%d is on the clip line s==#%d", vertex_index, state); | |
672 | - switch (state) | |
673 | - { | |
674 | - /* if weユre testing the first vertex, this tells us nothing */ | |
675 | - | |
676 | - case _searching_cw_for_out_in_transition: | |
677 | - entrance_vertex= vertex_index; | |
678 | - clipped_entrance_vertex= false; /* remember if this passes through vertex */ | |
679 | - break; | |
680 | - | |
681 | - case _searching_cw_for_in_out_transition: | |
682 | - exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
683 | - clipped_exit_vertex= false; | |
684 | - break; | |
685 | - case _searching_ccw_for_out_in_transition: | |
686 | - exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
687 | - clipped_exit_vertex= false; /* remember if this passes through vertex */ | |
688 | - break; | |
689 | - } | |
690 | - break; | |
691 | - | |
692 | - case 1: /* outside (i.e., will not be clipped) */ | |
693 | -// dprintf("vertex#%d is outside s==#%d", vertex_index, state); | |
694 | - switch (state) | |
695 | - { | |
696 | - case _testing_first_vertex: | |
697 | - first_vertex= vertex_index; | |
698 | - state= _searching_ccw_for_out_in_transition; /* the exit point from the clipped area */ | |
699 | - vertex_delta= -1; | |
700 | - break; | |
701 | - | |
702 | - case _searching_cw_for_in_out_transition: /* found exit point from clipped area */ | |
703 | - state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
704 | - if (exit_vertex==NONE) exit_vertex= vertex_index; | |
705 | - break; | |
706 | - } | |
707 | - break; | |
708 | - } | |
709 | - | |
710 | - /* adjust vertex_index (clockwise or counterclockwise, depending on vertex_delta) | |
711 | - if weユve come back to the first vertex without finding an entrance point weユre | |
712 | - either all the way in or all the way out */ | |
713 | - vertex_index= (vertex_delta<0) ? WRAP_LOW(vertex_index, vertex_count-1) : | |
714 | - WRAP_HIGH(vertex_index, vertex_count-1); | |
715 | - if (vertex_index==first_vertex) /* we came full-circle without hitting anything */ | |
716 | - { | |
717 | - switch (state) | |
718 | - { | |
719 | - case _searching_cw_for_in_out_transition: /* never found a way out: clipped into nothing */ | |
720 | - vertex_count= 0; | |
721 | - case _testing_first_vertex: /* is this the right thing to do? */ | |
722 | - case _searching_ccw_for_out_in_transition: /* never found a way in: no clipping */ | |
723 | - exit_vertex= NONE; | |
724 | - state= NONE; | |
725 | - break; | |
726 | - } | |
727 | - } | |
728 | - } | |
729 | - while (state!=NONE); | |
730 | - | |
731 | - if (exit_vertex!=NONE) /* weユve got clipping to do */ | |
732 | - { | |
733 | - flagged_world_point2d new_entrance_point, new_exit_point; | |
734 | - | |
735 | -// dprintf("entrance_vertex==#%d (%s), exit_vertex==#%d (%s)", entrance_vertex, clipped_entrance_vertex ? "clipped" : "unclipped", exit_vertex, clipped_exit_vertex ? "clipped" : "unclipped"); | |
736 | - | |
737 | - /* clip the entrance to the clipped area */ | |
738 | - if (clipped_entrance_vertex) | |
739 | - { | |
740 | - xy_clip_flagged_world_points(vertices + WRAP_LOW(entrance_vertex, vertex_count-1), vertices + entrance_vertex, | |
741 | - &new_entrance_point, line); | |
742 | - } | |
743 | - else | |
744 | - { | |
745 | - new_entrance_point= vertices[entrance_vertex]; | |
746 | - } | |
747 | - new_entrance_point.flags|= flag; | |
748 | - | |
749 | - /* clip the exit from the clipped area */ | |
750 | - if (clipped_exit_vertex) | |
751 | - { | |
752 | - xy_clip_flagged_world_points(vertices + WRAP_LOW(exit_vertex, vertex_count-1), vertices + exit_vertex, | |
753 | - &new_exit_point, line); | |
754 | - } | |
755 | - else | |
756 | - { | |
757 | - new_exit_point= vertices[WRAP_LOW(exit_vertex, vertex_count-1)]; | |
758 | - } | |
759 | - new_exit_point.flags|= flag; | |
760 | - | |
761 | - /* adjust for the change in number of vertices */ | |
762 | - vertex_delta= entrance_vertex - exit_vertex; | |
763 | - if (vertex_delta<0) | |
764 | - { | |
765 | - if (vertex_delta!=-2) memmove(vertices+entrance_vertex+2, vertices+exit_vertex, (vertex_count-exit_vertex)*sizeof(flagged_world_point2d)); | |
766 | - vertex_delta= vertex_count+vertex_delta; | |
767 | - } | |
768 | - else | |
769 | - { | |
770 | - /* move down by exit_vertex, add new vertices to end */ | |
771 | - assert(vertex_delta); | |
772 | - if (exit_vertex) | |
773 | - { | |
774 | - memmove(vertices, vertices+exit_vertex, vertex_delta*sizeof(flagged_world_point2d)); | |
775 | - entrance_vertex-= exit_vertex; | |
776 | - } | |
777 | - } | |
778 | - | |
779 | - vertex_count= vertex_delta+2; | |
780 | - vwarn(vertex_count>=3 && vertex_count<=MAXIMUM_VERTICES_PER_WORLD_POLYGON, | |
781 | - csprintf(temporary, "vertex overflow or underflow (#%d);g;", vertex_count)); | |
782 | - | |
783 | - if (vertex_count<3 || vertex_count>MAXIMUM_VERTICES_PER_WORLD_POLYGON) | |
784 | - { | |
785 | - vertex_count= 0; | |
786 | - } | |
787 | - else | |
788 | - { | |
789 | - /* and, finally, add the new vertices */ | |
790 | - vertices[entrance_vertex]= new_entrance_point; | |
791 | - vertices[entrance_vertex+1]= new_exit_point; | |
792 | - } | |
793 | - } | |
794 | - } | |
795 | - | |
796 | -#ifdef QUICKDRAW_DEBUG | |
797 | - debug_flagged_points(vertices, vertex_count); | |
798 | - debug_vector(line); | |
799 | -#endif | |
800 | -// dprintf("result == %p (#%d vertices)", vertices, vertex_count); | |
801 | - | |
802 | - return vertex_count; | |
803 | -} | |
804 | - | |
805 | -/* sort points before clipping to assure consistency; there is a way to make this more accurate | |
806 | - but it requires the downshifting game, as played in SCOTTISH_TEXTURES.C. itユs tempting to | |
807 | - think that having a smaller scale for our world coordinates would help here (i.e., less bits | |
808 | - per distance) but then wouldnユt we be screwed when we tried to rotate? */ | |
809 | -// LP change: make it better able to do long-distance views | |
810 | -void RenderRasterizerClass::xy_clip_flagged_world_points( | |
811 | - flagged_world_point2d *p0, | |
812 | - flagged_world_point2d *p1, | |
813 | - flagged_world_point2d *clipped, | |
814 | - long_vector2d *line) | |
815 | -{ | |
816 | - bool swap= (p1->y>p0->y) ? false : ((p0->y==p1->y) ? (p1->x<p0->x) : true); | |
817 | - flagged_world_point2d *local_p0= swap ? p1 : p0; | |
818 | - flagged_world_point2d *local_p1= swap ? p0 : p1; | |
819 | - world_distance dx= local_p1->x - local_p0->x; | |
820 | - world_distance dy= local_p1->y - local_p0->y; | |
821 | - int32 numerator= line->j*local_p0->x - line->i*local_p0->y; | |
822 | - int32 denominator= line->i*dy - line->j*dx; | |
823 | - short shift_count= FIXED_FRACTIONAL_BITS; | |
824 | - _fixed t; | |
825 | - | |
826 | - /* give numerator 16 significant bits over denominator and then calculate t==n/d; MPWユs PPCC | |
827 | - didnユt seem to like (INT32_MIN>>1) and i had to substitute 0xc0000000 instead (hmmm) */ | |
828 | - while (numerator<=(int32)0x3fffffff && numerator>=(int32)0xc0000000 && shift_count--) numerator<<= 1; | |
829 | - if (shift_count>0) denominator>>= shift_count; | |
830 | - t= numerator; | |
831 | - if (denominator) | |
832 | - t /= denominator; | |
833 | - | |
834 | - /* calculate the clipped point */ | |
835 | - clipped->x= local_p0->x + FIXED_INTEGERAL_PART(t*dx); | |
836 | - clipped->y= local_p0->y + FIXED_INTEGERAL_PART(t*dy); | |
837 | - clipped->flags= local_p0->flags&local_p1->flags; | |
838 | -} | |
839 | - | |
840 | -/* almost wholly identical to xz_clip_vertical_polygon() except that this works off 2d points | |
841 | - in the xy-plane and a height */ | |
842 | -// LP change: make it better able to do long-distance views | |
843 | -short RenderRasterizerClass::z_clip_horizontal_polygon( | |
844 | - flagged_world_point2d *vertices, | |
845 | - short vertex_count, | |
846 | - long_vector2d *line, /* i==x, j==z */ | |
847 | - world_distance height, | |
848 | - uint16 flag) | |
849 | -{ | |
850 | - CROSSPROD_TYPE heighti= CROSSPROD_TYPE(line->i)*height; | |
851 | - | |
852 | -#ifdef QUICKDRAW_DEBUG | |
853 | - debug_flagged_points(vertices, vertex_count); | |
854 | - debug_x_line(line->j ? (line->i*height)/line->j : (height<0 ? INT32_MIN : INT32_MAX)); | |
855 | -#endif | |
856 | -// dprintf("clipping %p (#%d vertices) to vector %x,%x", vertices, vertex_count, line->i, line->j); | |
857 | - | |
858 | - if (vertex_count) | |
859 | - { | |
860 | - short state= _testing_first_vertex; | |
861 | - short vertex_index= 0, vertex_delta= 1, first_vertex= 0; | |
862 | - short entrance_vertex= NONE, exit_vertex= NONE; /* exiting the clipped area and entering the clipped area */ | |
863 | - bool clipped_exit_vertex= true, clipped_entrance_vertex= true; /* will be false if these points lie directly on a vertex */ | |
864 | - | |
865 | - do | |
866 | - { | |
867 | - CROSSPROD_TYPE cross_product= heighti - CROSSPROD_TYPE(line->j)*vertices[vertex_index].x; | |
868 | - | |
869 | - if (cross_product<0) /* inside (i.e., will be clipped) */ | |
870 | - { | |
871 | - switch (state) | |
872 | - { | |
873 | - case _testing_first_vertex: | |
874 | - first_vertex= vertex_index; | |
875 | - state= _searching_cw_for_in_out_transition; /* the exit point from the clip area */ | |
876 | - break; | |
877 | - | |
878 | - case _searching_ccw_for_out_in_transition: /* found exit point from clip area */ | |
879 | - state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
880 | - vertex_delta= 1; | |
881 | - if (exit_vertex==NONE) exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
882 | - vertex_index= first_vertex; /* skip vertices we already know are out */ | |
883 | - break; | |
884 | - | |
885 | - case _searching_cw_for_out_in_transition: /* found entrance point to clipped area */ | |
886 | - if (entrance_vertex==NONE) entrance_vertex= vertex_index; | |
887 | - state= NONE; | |
888 | - break; | |
889 | - } | |
890 | - } | |
891 | - else | |
892 | - { | |
893 | - if (cross_product>0) /* outside (i.e., will not be clipped) */ | |
894 | - { | |
895 | - switch (state) | |
896 | - { | |
897 | - case _testing_first_vertex: | |
898 | - first_vertex= vertex_index; | |
899 | - state= _searching_ccw_for_out_in_transition; /* the exit point from the clipped area */ | |
900 | - vertex_delta= -1; | |
901 | - break; | |
902 | - | |
903 | - case _searching_cw_for_in_out_transition: /* found exit point from clipped area */ | |
904 | - state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
905 | - if (exit_vertex==NONE) exit_vertex= vertex_index; | |
906 | - break; | |
907 | - } | |
908 | - } | |
909 | - else /* clip line passed directly through a vertex */ | |
910 | - { | |
911 | - switch (state) | |
912 | - { | |
913 | - /* if weユre testing the first vertex (_testing_first_vertex), this tells us nothing */ | |
914 | - | |
915 | - case _searching_cw_for_out_in_transition: | |
916 | - entrance_vertex= vertex_index; | |
917 | - clipped_entrance_vertex= false; /* remember if this passes through vertex */ | |
918 | - break; | |
919 | - | |
920 | - case _searching_cw_for_in_out_transition: | |
921 | - exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
922 | - clipped_exit_vertex= false; | |
923 | - break; | |
924 | - case _searching_ccw_for_out_in_transition: | |
925 | - exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
926 | - clipped_exit_vertex= false; /* remember if this passes through vertex */ | |
927 | - break; | |
928 | - } | |
929 | - } | |
930 | - } | |
931 | - | |
932 | - /* adjust vertex_index (clockwise or counterclockwise, depending on vertex_delta) | |
933 | - if weユve come back to the first vertex without finding an entrance point weユre | |
934 | - either all the way in or all the way out */ | |
935 | - vertex_index= (vertex_delta<0) ? WRAP_LOW(vertex_index, vertex_count-1) : | |
936 | - WRAP_HIGH(vertex_index, vertex_count-1); | |
937 | - if (vertex_index==first_vertex) /* we came full-circle without hitting anything */ | |
938 | - { | |
939 | - switch (state) | |
940 | - { | |
941 | - case _searching_cw_for_in_out_transition: /* never found a way out: clipped into nothing */ | |
942 | - vertex_count= 0; | |
943 | - case _testing_first_vertex: /* is this the right thing to do? */ | |
944 | - case _searching_ccw_for_out_in_transition: /* never found a way in: no clipping */ | |
945 | - exit_vertex= NONE; | |
946 | - state= NONE; | |
947 | - break; | |
948 | - } | |
949 | - } | |
950 | - } | |
951 | - while (state!=NONE); | |
952 | - | |
953 | - if (exit_vertex!=NONE) /* weユve got clipping to do */ | |
954 | - { | |
955 | - flagged_world_point2d new_entrance_point, new_exit_point; | |
956 | - | |
957 | -// dprintf("entrance_vertex==#%d (%s), exit_vertex==#%d (%s)", entrance_vertex, clipped_entrance_vertex ? "clipped" : "unclipped", exit_vertex, clipped_exit_vertex ? "clipped" : "unclipped"); | |
958 | - | |
959 | - /* clip the entrance to the clipped area */ | |
960 | - if (clipped_entrance_vertex) | |
961 | - { | |
962 | - z_clip_flagged_world_points(vertices + WRAP_LOW(entrance_vertex, vertex_count-1), vertices + entrance_vertex, | |
963 | - height, &new_entrance_point, line); | |
964 | - } | |
965 | - else | |
966 | - { | |
967 | - new_entrance_point= vertices[entrance_vertex]; | |
968 | - } | |
969 | - new_entrance_point.flags|= flag; | |
970 | - | |
971 | - /* clip the exit from the clipped area */ | |
972 | - if (clipped_exit_vertex) | |
973 | - { | |
974 | - z_clip_flagged_world_points(vertices + WRAP_LOW(exit_vertex, vertex_count-1), vertices + exit_vertex, | |
975 | - height, &new_exit_point, line); | |
976 | - } | |
977 | - else | |
978 | - { | |
979 | - new_exit_point= vertices[WRAP_LOW(exit_vertex, vertex_count-1)]; | |
980 | - } | |
981 | - new_exit_point.flags|= flag; | |
982 | - | |
983 | - /* adjust for the change in number of vertices */ | |
984 | - vertex_delta= entrance_vertex - exit_vertex; | |
985 | - if (vertex_delta<0) | |
986 | - { | |
987 | - if (vertex_delta!=-2) memmove(vertices+entrance_vertex+2, vertices+exit_vertex, (vertex_count-exit_vertex)*sizeof(flagged_world_point2d)); | |
988 | - vertex_delta= vertex_count+vertex_delta; | |
989 | - } | |
990 | - else | |
991 | - { | |
992 | - /* move down by exit_vertex, add new vertices to end */ | |
993 | - assert(vertex_delta); | |
994 | - if (exit_vertex) | |
995 | - { | |
996 | - memmove(vertices, vertices+exit_vertex, vertex_delta*sizeof(flagged_world_point2d)); | |
997 | - entrance_vertex-= exit_vertex; | |
998 | - } | |
999 | - } | |
1000 | - vertex_count= vertex_delta+2; | |
1001 | - | |
1002 | - vwarn(vertex_count>=3 && vertex_count<=MAXIMUM_VERTICES_PER_WORLD_POLYGON, | |
1003 | - csprintf(temporary, "vertex overflow or underflow (#%d);g;", vertex_count)); | |
1004 | - | |
1005 | - if (vertex_count<3 || vertex_count>MAXIMUM_VERTICES_PER_WORLD_POLYGON) | |
1006 | - { | |
1007 | - vertex_count= 0; | |
1008 | - } | |
1009 | - else | |
1010 | - { | |
1011 | - /* and, finally, add the new vertices */ | |
1012 | - vertices[entrance_vertex]= new_entrance_point; | |
1013 | - vertices[entrance_vertex+1]= new_exit_point; | |
1014 | - } | |
1015 | - } | |
1016 | - } | |
1017 | - | |
1018 | -#ifdef QUICKDRAW_DEBUG | |
1019 | - debug_flagged_points(vertices, vertex_count); | |
1020 | - debug_x_line(line->j ? (line->i*height)/line->j : (height<0 ? INT32_MIN : INT32_MAX)); | |
1021 | -#endif | |
1022 | -// dprintf("result == %p (#%d vertices)", vertices, vertex_count); | |
1023 | - | |
1024 | - return vertex_count; | |
1025 | -} | |
1026 | - | |
1027 | -/* sort points before clipping to assure consistency; this is almost identical to xz_clipノ() | |
1028 | - except that it clips 2d points in the xy-plane at the given height. */ | |
1029 | -// LP change: make it better able to do long-distance views | |
1030 | -void RenderRasterizerClass::z_clip_flagged_world_points( | |
1031 | - flagged_world_point2d *p0, | |
1032 | - flagged_world_point2d *p1, | |
1033 | - world_distance height, | |
1034 | - flagged_world_point2d *clipped, | |
1035 | - long_vector2d *line) | |
1036 | -{ | |
1037 | - bool swap= (p1->y>p0->y) ? false : ((p0->y==p1->y) ? (p1->x<p0->x) : true); | |
1038 | - flagged_world_point2d *local_p0= swap ? p1 : p0; | |
1039 | - flagged_world_point2d *local_p1= swap ? p0 : p1; | |
1040 | - world_distance dx= local_p1->x - local_p0->x; | |
1041 | - world_distance dy= local_p1->y - local_p0->y; | |
1042 | - int32 numerator= line->j*local_p0->x - line->i*height; | |
1043 | - int32 denominator= - line->j*dx; | |
1044 | - short shift_count= FIXED_FRACTIONAL_BITS; | |
1045 | - _fixed t; | |
1046 | - | |
1047 | - /* give numerator 16 significant bits over denominator and then calculate t==n/d; MPWユs PPCC | |
1048 | - didnユt seem to like (INT32_MIN>>1) and i had to substitute 0xc0000000 instead (hmmm) */ | |
1049 | - while (numerator<=(int32)0x3fffffff && numerator>=(int32)0xc0000000 && shift_count--) numerator<<= 1; | |
1050 | - if (shift_count>0) denominator>>= shift_count; | |
1051 | - t= numerator; | |
1052 | - if (denominator) | |
1053 | - t /= denominator; | |
1054 | - | |
1055 | - /* calculate the clipped point */ | |
1056 | - clipped->x= local_p0->x + FIXED_INTEGERAL_PART(t*dx); | |
1057 | - clipped->y= local_p0->y + FIXED_INTEGERAL_PART(t*dy); | |
1058 | - clipped->flags= local_p0->flags&local_p1->flags; | |
1059 | -} | |
1060 | - | |
1061 | -/* ---------- vertical polygon clipping */ | |
1062 | - | |
1063 | -// LP change: make it better able to do long-distance views | |
1064 | -short RenderRasterizerClass::xy_clip_line( | |
1065 | - flagged_world_point2d *posts, | |
1066 | - short vertex_count, | |
1067 | - long_vector2d *line, | |
1068 | - uint16 flag) | |
1069 | -{ | |
1070 | -#ifdef QUICKDRAW_DEBUG | |
1071 | -// debug_flagged_points(posts, vertex_count); | |
1072 | -// debug_vector(line); | |
1073 | -#endif | |
1074 | -// dprintf("clipping %p (#%d) to line (%d,%d)", posts, vertex_count, line->i, line->j); | |
1075 | - | |
1076 | - if (vertex_count) | |
1077 | - { | |
1078 | - CROSSPROD_TYPE cross_product0= CROSSPROD_TYPE(line->i)*posts[0].y - CROSSPROD_TYPE(line->j)*posts[0].x; | |
1079 | - CROSSPROD_TYPE cross_product1= CROSSPROD_TYPE(line->i)*posts[1].y - CROSSPROD_TYPE(line->j)*posts[1].x; | |
1080 | - | |
1081 | - if (cross_product0<0) | |
1082 | - { | |
1083 | - if (cross_product1<0) /* clipped out of existence */ | |
1084 | - { | |
1085 | - vertex_count= 0; | |
1086 | - } | |
1087 | - else | |
1088 | - { | |
1089 | - xy_clip_flagged_world_points(&posts[0], &posts[1], &posts[0], line); | |
1090 | - posts[0].flags|= flag; | |
1091 | - } | |
1092 | - } | |
1093 | - else | |
1094 | - { | |
1095 | - if (cross_product1<0) | |
1096 | - { | |
1097 | - xy_clip_flagged_world_points(&posts[0], &posts[1], &posts[1], line); | |
1098 | - posts[1].flags|= flag; | |
1099 | - } | |
1100 | - } | |
1101 | - } | |
1102 | - | |
1103 | -#ifdef QUICKDRAW_DEBUG | |
1104 | -// debug_flagged_points(posts, vertex_count); | |
1105 | -// debug_vector(line); | |
1106 | -#endif | |
1107 | -// dprintf("result #%d vertices", vertex_count); | |
1108 | - | |
1109 | - return vertex_count; | |
1110 | -} | |
1111 | - | |
1112 | -// LP change: make it better able to do long-distance views | |
1113 | -short RenderRasterizerClass::xz_clip_vertical_polygon( | |
1114 | - flagged_world_point3d *vertices, | |
1115 | - short vertex_count, | |
1116 | - long_vector2d *line, /* i==x, j==z */ | |
1117 | - uint16 flag) | |
1118 | -{ | |
1119 | -#ifdef QUICKDRAW_DEBUG | |
1120 | -// debug_flagged_points3d(vertices, vertex_count); | |
1121 | -// debug_vector(line); | |
1122 | -#endif | |
1123 | -// dprintf("clipping %p (#%d vertices) to vector %x,%x", vertices, vertex_count, line->i, line->j); | |
1124 | - | |
1125 | - if (vertex_count) | |
1126 | - { | |
1127 | - short state= _testing_first_vertex; | |
1128 | - short vertex_index= 0, vertex_delta= 1, first_vertex= 0; | |
1129 | - short entrance_vertex= NONE, exit_vertex= NONE; /* exiting the clipped area and entering the clipped area */ | |
1130 | - bool clipped_exit_vertex= true, clipped_entrance_vertex= true; /* will be false if these points lie directly on a vertex */ | |
1131 | - | |
1132 | - do | |
1133 | - { | |
1134 | - CROSSPROD_TYPE cross_product= CROSSPROD_TYPE(line->i)*vertices[vertex_index].z - CROSSPROD_TYPE(line->j)*vertices[vertex_index].x; | |
1135 | - | |
1136 | - switch (SGN(cross_product)) | |
1137 | - { | |
1138 | - case -1: /* inside (i.e., will be clipped) */ | |
1139 | -// dprintf("vertex#%d is inside s==#%d", vertex_index, state); | |
1140 | - switch (state) | |
1141 | - { | |
1142 | - case _testing_first_vertex: | |
1143 | - first_vertex= vertex_index; | |
1144 | - state= _searching_cw_for_in_out_transition; /* the exit point from the clip area */ | |
1145 | - break; | |
1146 | - | |
1147 | - case _searching_ccw_for_out_in_transition: /* found exit point from clip area */ | |
1148 | - state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
1149 | - vertex_delta= 1; | |
1150 | - if (exit_vertex==NONE) exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
1151 | - vertex_index= first_vertex; /* skip vertices we already know are out */ | |
1152 | - break; | |
1153 | - | |
1154 | - case _searching_cw_for_out_in_transition: /* found entrance point to clipped area */ | |
1155 | - if (entrance_vertex==NONE) entrance_vertex= vertex_index; | |
1156 | - state= NONE; | |
1157 | - break; | |
1158 | - } | |
1159 | - break; | |
1160 | - | |
1161 | - case 0: /* clip line passed directly through a vertex */ | |
1162 | -// dprintf("vertex#%d is on the clip line s==#%d", vertex_index, state); | |
1163 | - switch (state) | |
1164 | - { | |
1165 | - /* if weユre testing the first vertex, this tells us nothing */ | |
1166 | - | |
1167 | - case _searching_cw_for_out_in_transition: | |
1168 | - entrance_vertex= vertex_index; | |
1169 | - clipped_entrance_vertex= false; /* remember if this passes through vertex */ | |
1170 | - break; | |
1171 | - | |
1172 | - case _searching_cw_for_in_out_transition: | |
1173 | - exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
1174 | - clipped_exit_vertex= false; | |
1175 | - break; | |
1176 | - case _searching_ccw_for_out_in_transition: | |
1177 | - exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
1178 | - clipped_exit_vertex= false; /* remember if this passes through vertex */ | |
1179 | - break; | |
1180 | - } | |
1181 | - break; | |
1182 | - | |
1183 | - case 1: /* outside (i.e., will not be clipped) */ | |
1184 | -// dprintf("vertex#%d is outside s==#%d", vertex_index, state); | |
1185 | - switch (state) | |
1186 | - { | |
1187 | - case _testing_first_vertex: | |
1188 | - first_vertex= vertex_index; | |
1189 | - state= _searching_ccw_for_out_in_transition; /* the exit point from the clipped area */ | |
1190 | - vertex_delta= -1; | |
1191 | - break; | |
1192 | - | |
1193 | - case _searching_cw_for_in_out_transition: /* found exit point from clipped area */ | |
1194 | - state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
1195 | - if (exit_vertex==NONE) exit_vertex= vertex_index; | |
1196 | - break; | |
1197 | - } | |
1198 | - break; | |
1199 | - } | |
1200 | - | |
1201 | - /* adjust vertex_index (clockwise or counterclockwise, depending on vertex_delta) | |
1202 | - if weユve come back to the first vertex without finding an entrance point weユre | |
1203 | - either all the way in or all the way out */ | |
1204 | - vertex_index= (vertex_delta<0) ? WRAP_LOW(vertex_index, vertex_count-1) : | |
1205 | - WRAP_HIGH(vertex_index, vertex_count-1); | |
1206 | - if (vertex_index==first_vertex) /* we came full-circle without hitting anything */ | |
1207 | - { | |
1208 | - switch (state) | |
1209 | - { | |
1210 | - case _searching_cw_for_in_out_transition: /* never found a way out: clipped into nothing */ | |
1211 | - vertex_count= 0; | |
1212 | - case _testing_first_vertex: /* is this the right thing to do? */ | |
1213 | - case _searching_ccw_for_out_in_transition: /* never found a way in: no clipping */ | |
1214 | - exit_vertex= NONE; | |
1215 | - state= NONE; | |
1216 | - break; | |
1217 | - } | |
1218 | - } | |
1219 | - } | |
1220 | - while (state!=NONE); | |
1221 | - | |
1222 | - if (exit_vertex!=NONE) /* weユve got clipping to do */ | |
1223 | - { | |
1224 | - flagged_world_point3d new_entrance_point, new_exit_point; | |
1225 | - | |
1226 | -// dprintf("entrance_vertex==#%d (%s), exit_vertex==#%d (%s)", entrance_vertex, clipped_entrance_vertex ? "clipped" : "unclipped", exit_vertex, clipped_exit_vertex ? "clipped" : "unclipped"); | |
1227 | - | |
1228 | - /* clip the entrance to the clipped area */ | |
1229 | - if (clipped_entrance_vertex) | |
1230 | - { | |
1231 | - xz_clip_flagged_world_points(vertices + WRAP_LOW(entrance_vertex, vertex_count-1), vertices + entrance_vertex, | |
1232 | - &new_entrance_point, line); | |
1233 | - } | |
1234 | - else | |
1235 | - { | |
1236 | - new_entrance_point= vertices[entrance_vertex]; | |
1237 | - } | |
1238 | - new_entrance_point.flags|= flag; | |
1239 | - | |
1240 | - /* clip the exit from the clipped area */ | |
1241 | - if (clipped_exit_vertex) | |
1242 | - { | |
1243 | - xz_clip_flagged_world_points(vertices + WRAP_LOW(exit_vertex, vertex_count-1), vertices + exit_vertex, | |
1244 | - &new_exit_point, line); | |
1245 | - } | |
1246 | - else | |
1247 | - { | |
1248 | - new_exit_point= vertices[WRAP_LOW(exit_vertex, vertex_count-1)]; | |
1249 | - } | |
1250 | - new_exit_point.flags|= flag; | |
1251 | - | |
1252 | - /* adjust for the change in number of vertices */ | |
1253 | - vertex_delta= entrance_vertex - exit_vertex; | |
1254 | - if (vertex_delta<0) | |
1255 | - { | |
1256 | - if (vertex_delta!=-2) memmove(vertices+entrance_vertex+2, vertices+exit_vertex, (vertex_count-exit_vertex)*sizeof(flagged_world_point3d)); | |
1257 | - vertex_delta= vertex_count+vertex_delta; | |
1258 | - } | |
1259 | - else | |
1260 | - { | |
1261 | - /* move down by exit_vertex, add new vertices to end */ | |
1262 | - assert(vertex_delta); | |
1263 | - if (exit_vertex) | |
1264 | - { | |
1265 | - memmove(vertices, vertices+exit_vertex, vertex_delta*sizeof(flagged_world_point3d)); | |
1266 | - entrance_vertex-= exit_vertex; | |
1267 | - } | |
1268 | - } | |
1269 | - vertex_count= vertex_delta+2; | |
1270 | - | |
1271 | - vwarn(vertex_count>=3 && vertex_count<=MAXIMUM_VERTICES_PER_WORLD_POLYGON, | |
1272 | - csprintf(temporary, "vertex overflow or underflow (#%d);g;", vertex_count)); | |
1273 | - | |
1274 | - if (vertex_count<3 || vertex_count>MAXIMUM_VERTICES_PER_WORLD_POLYGON) | |
1275 | - { | |
1276 | - vertex_count= 0; | |
1277 | - } | |
1278 | - else | |
1279 | - { | |
1280 | - /* and, finally, add the new vertices */ | |
1281 | - vertices[entrance_vertex]= new_entrance_point; | |
1282 | - vertices[entrance_vertex+1]= new_exit_point; | |
1283 | - } | |
1284 | - } | |
1285 | - } | |
1286 | - | |
1287 | -#ifdef QUICKDRAW_DEBUG | |
1288 | -// debug_flagged_points3d(vertices, vertex_count); | |
1289 | -// debug_vector(line); | |
1290 | -#endif | |
1291 | -// dprintf("result == %p (#%d vertices)", vertices, vertex_count); | |
1292 | - | |
1293 | - return vertex_count; | |
1294 | -} | |
1295 | - | |
1296 | -/* sort points before clipping to assure consistency */ | |
1297 | -// LP change: make it better able to do long-distance views | |
1298 | -void RenderRasterizerClass::xz_clip_flagged_world_points( | |
1299 | - flagged_world_point3d *p0, | |
1300 | - flagged_world_point3d *p1, | |
1301 | - flagged_world_point3d *clipped, | |
1302 | - long_vector2d *line) | |
1303 | -{ | |
1304 | - bool swap= (p1->y>p0->y) ? false : ((p0->y==p1->y) ? (p1->x<p0->x) : true); | |
1305 | - flagged_world_point3d *local_p0= swap ? p1 : p0; | |
1306 | - flagged_world_point3d *local_p1= swap ? p0 : p1; | |
1307 | - world_distance dx= local_p1->x - local_p0->x; | |
1308 | - world_distance dy= local_p1->y - local_p0->y; | |
1309 | - world_distance dz= local_p1->z - local_p0->z; | |
1310 | - int32 numerator= line->j*local_p0->x - line->i*local_p0->z; | |
1311 | - int32 denominator= line->i*dz - line->j*dx; | |
1312 | - short shift_count= FIXED_FRACTIONAL_BITS; | |
1313 | - _fixed t; | |
1314 | - | |
1315 | - /* give numerator 16 significant bits over denominator and then calculate t==n/d; MPWユs PPCC | |
1316 | - didnユt seem to like (INT32_MIN>>1) and i had to substitute 0xc0000000 instead (hmmm) */ | |
1317 | - while (numerator<=(int32)0x3fffffff && numerator>=(int32)0xc0000000 && shift_count--) numerator<<= 1; | |
1318 | - if (shift_count>0) denominator>>= shift_count; | |
1319 | - t = numerator; | |
1320 | - if (denominator) | |
1321 | - t /= denominator; | |
1322 | - | |
1323 | - /* calculate the clipped point */ | |
1324 | - clipped->x= local_p0->x + FIXED_INTEGERAL_PART(t*dx); | |
1325 | - clipped->y= local_p0->y + FIXED_INTEGERAL_PART(t*dy); | |
1326 | - clipped->z= local_p0->z + FIXED_INTEGERAL_PART(t*dz); | |
1327 | - clipped->flags= local_p0->flags&local_p1->flags; | |
1328 | -} | |
1 | +/* | |
2 | + | |
3 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | + Rendering Clipping/Rasterization Class | |
21 | + by Loren Petrich, | |
22 | + August 7, 2000 | |
23 | + | |
24 | + Contains the calculation of clipping and rasterization; from render.c | |
25 | + | |
26 | + Made [view_data *view] a member and removed it as an argument | |
27 | + Also removed [bitmap_definition *destination] as superfluous, | |
28 | + now that there is a special rasterizer object that can contain it. | |
29 | + | |
30 | +Sep 2, 2000 (Loren Petrich): | |
31 | + Added some idiot-proofing, since the shapes accessor now returns NULL for nonexistent bitmaps | |
32 | + | |
33 | + If a polygon has a "full_side" texture, and there is another polgyon on the other side, | |
34 | + then suppress the void for the boundary's texture. | |
35 | +*/ | |
36 | + | |
37 | +#include "cseries.h" | |
38 | + | |
39 | +#include "map.h" | |
40 | +#include "lightsource.h" | |
41 | +#include "media.h" | |
42 | +#include "RenderRasterize.h" | |
43 | +#include "AnimatedTextures.h" | |
44 | +#include "OGL_Setup.h" | |
45 | +#include "preferences.h" | |
46 | +#include "screen.h" | |
47 | + | |
48 | +#include <string.h> | |
49 | + | |
50 | + | |
51 | +/* maximum number of vertices a polygon can be world-clipped into (one per clip line) */ | |
52 | +#define MAXIMUM_VERTICES_PER_WORLD_POLYGON (MAXIMUM_VERTICES_PER_POLYGON+4) | |
53 | + | |
54 | + | |
55 | +RenderRasterizerClass::RenderRasterizerClass(): | |
56 | + view(NULL), // Idiot-proofing | |
57 | + RSPtr(NULL), | |
58 | + RasPtr(NULL) | |
59 | +{} | |
60 | + | |
61 | + | |
62 | +/* ---------- rendering the tree */ | |
63 | + | |
64 | +void RenderRasterizerClass::render_tree() | |
65 | +{ | |
66 | + render_tree(kDiffuse); | |
67 | +} | |
68 | + | |
69 | +void RenderRasterizerClass::render_tree(RenderStep renderStep) | |
70 | +{ | |
71 | + assert(view); // Idiot-proofing | |
72 | + assert(RSPtr); | |
73 | + assert(RasPtr); | |
74 | + vector<sorted_node_data>::iterator node; | |
75 | + // LP: reference to simplify the code | |
76 | + vector<sorted_node_data>& SortedNodes = RSPtr->SortedNodes; | |
77 | + | |
78 | + // LP change: added support for semitransparent liquids | |
79 | + bool SeeThruLiquids = get_screen_mode()->acceleration != _no_acceleration ? TEST_FLAG(Get_OGL_ConfigureData().Flags,OGL_Flag_LiqSeeThru) : graphics_preferences->software_alpha_blending != _sw_alpha_off; | |
80 | + | |
81 | + /* walls, ceilings, interior objects, floors, exterior objects for all nodes, back to front */ | |
82 | + for (node= SortedNodes.begin(); node != SortedNodes.end(); ++node) | |
83 | + render_node(&*node, SeeThruLiquids, renderStep); | |
84 | +} | |
85 | + | |
86 | +void RenderRasterizerClass::render_node( | |
87 | + sorted_node_data *node, | |
88 | + bool SeeThruLiquids, | |
89 | + RenderStep renderStep) | |
90 | +{ | |
91 | + polygon_data *polygon= get_polygon_data(node->polygon_index); | |
92 | + clipping_window_data *window; | |
93 | + render_object_data *object; | |
94 | + | |
95 | + // LP change: moved this stuff out here because it only has to be calculated | |
96 | + // once per polygon. | |
97 | + horizontal_surface_data floor_surface, ceiling_surface; | |
98 | + short i; | |
99 | + | |
100 | + ceiling_surface.origin= polygon->ceiling_origin; | |
101 | + ceiling_surface.height= polygon->ceiling_height; | |
102 | + ceiling_surface.texture= polygon->ceiling_texture; | |
103 | + ceiling_surface.lightsource_index= polygon->ceiling_lightsource_index; | |
104 | + ceiling_surface.transfer_mode= polygon->ceiling_transfer_mode; | |
105 | + ceiling_surface.transfer_mode_data= 0; | |
106 | + | |
107 | + floor_surface.origin= polygon->floor_origin; | |
108 | + floor_surface.height= polygon->floor_height; | |
109 | + floor_surface.texture= polygon->floor_texture; | |
110 | + floor_surface.lightsource_index= polygon->floor_lightsource_index; | |
111 | + floor_surface.transfer_mode= polygon->floor_transfer_mode; | |
112 | + floor_surface.transfer_mode_data= 0; | |
113 | + | |
114 | + // The "continue" conditions are OK to move out here, because a non-drawn polygon's | |
115 | + // inhabitants will be clipped away. | |
116 | + | |
117 | + // LP: get liquid data here for convenience; | |
118 | + // pointer to it being NULL means no liquid in the polygon | |
119 | + media_data *media = NULL; | |
120 | + if (polygon->media_index!=NONE) | |
121 | + media = get_media_data(polygon->media_index); | |
122 | + | |
123 | + /* if necessary, replace the ceiling or floor surfaces with the media surface */ | |
124 | + // LP change: don't do this step if liquids are semitransparent | |
125 | + if (media && !SeeThruLiquids) | |
126 | + { | |
127 | + // LP change: moved this get upwards | |
128 | + // struct media_data *media= get_media_data(polygon->media_index); | |
129 | + horizontal_surface_data *media_surface= NULL; | |
130 | + | |
131 | + if (view->under_media_boundary) | |
132 | + { | |
133 | + // LP change: skip if high and dry | |
134 | + if (media->height <= polygon->floor_height) | |
135 | + return; | |
136 | + | |
137 | + if (media->height<polygon->ceiling_height) media_surface= &ceiling_surface; | |
138 | + } | |
139 | + else | |
140 | + { | |
141 | + // LP change: skip if submerged | |
142 | + if (media->height >= polygon->ceiling_height) | |
143 | + return; | |
144 | + | |
145 | + if (media->height>polygon->floor_height) media_surface= &floor_surface; | |
146 | + } | |
147 | + | |
148 | + if (media_surface) | |
149 | + { | |
150 | + media_surface->origin= media->origin; | |
151 | + media_surface->height= media->height; | |
152 | + media_surface->texture= media->texture; | |
153 | + media_surface->lightsource_index= polygon->media_lightsource_index; | |
154 | + media_surface->transfer_mode= media->transfer_mode; | |
155 | + media_surface->transfer_mode_data= 0; | |
156 | + } | |
157 | + } | |
158 | + // LP change: always render liquids that are semitransparent | |
159 | + else if (!SeeThruLiquids) | |
160 | + { | |
161 | + // if weユre trying to draw a polygon without media from under a polygon with media, donユt | |
162 | + if (view->under_media_boundary) return; | |
163 | + } | |
164 | + | |
165 | + // LP: this loop renders the walls | |
166 | + for (window= node->clipping_windows; window; window= window->next_window) | |
167 | + { | |
168 | + if (ceiling_surface.height>floor_surface.height) | |
169 | + { | |
170 | + /* render ceiling if above viewer */ | |
171 | + if (ceiling_surface.height>view->origin.z) | |
172 | + { | |
173 | + // LP change: indicated that the void is on other side | |
174 | + render_node_floor_or_ceiling(window, polygon, &ceiling_surface, true, true, renderStep); | |
175 | + } | |
176 | + | |
177 | + /* render visible sides */ | |
178 | + for (i= 0; i<polygon->vertex_count; ++i) | |
179 | + { | |
180 | + short side_index= polygon->side_indexes[i]; | |
181 | + | |
182 | + if (side_index!=NONE && TEST_RENDER_FLAG(side_index, _side_is_visible)) | |
183 | + { | |
184 | + line_data *line= get_line_data(polygon->line_indexes[i]); | |
185 | + side_data *side= get_side_data(side_index); | |
186 | + vertical_surface_data surface; | |
187 | + | |
188 | + surface.length= line->length; | |
189 | + store_endpoint(get_endpoint_data(polygon->endpoint_indexes[i]), surface.p0); | |
190 | + store_endpoint(get_endpoint_data(polygon->endpoint_indexes[WRAP_HIGH(i, polygon->vertex_count-1)]), surface.p1); | |
191 | + surface.ambient_delta= side->ambient_delta; | |
192 | + | |
193 | + // LP change: indicate in all cases whether the void is on the other side; | |
194 | + // added a workaround for full-side textures with a polygon on the other side | |
195 | + bool void_present; | |
196 | + | |
197 | + switch (side->type) | |
198 | + { | |
199 | + case _full_side: | |
200 | + void_present = true; | |
201 | + // Suppress the void if there is a polygon on the other side. | |
202 | + if (polygon->adjacent_polygon_indexes[i] != NONE) void_present = false; | |
203 | + | |
204 | + surface.lightsource_index= side->primary_lightsource_index; | |
205 | + surface.h0= floor_surface.height - view->origin.z; | |
206 | + surface.hmax= ceiling_surface.height - view->origin.z; | |
207 | + surface.h1= polygon->ceiling_height - view->origin.z; | |
208 | + surface.texture_definition= &side->primary_texture; | |
209 | + surface.transfer_mode= side->primary_transfer_mode; | |
210 | + render_node_side(window, &surface, void_present, renderStep); | |
211 | + break; | |
212 | + case _split_side: /* render _low_side first */ | |
213 | + surface.lightsource_index= side->secondary_lightsource_index; | |
214 | + surface.h0= floor_surface.height - view->origin.z; | |
215 | + surface.h1= MAX(line->highest_adjacent_floor, floor_surface.height) - view->origin.z; | |
216 | + surface.hmax= ceiling_surface.height - view->origin.z; | |
217 | + surface.texture_definition= &side->secondary_texture; | |
218 | + surface.transfer_mode= side->secondary_transfer_mode; | |
219 | + render_node_side(window, &surface, true, renderStep); | |
220 | + /* fall through and render high side */ | |
221 | + case _high_side: | |
222 | + surface.lightsource_index= side->primary_lightsource_index; | |
223 | + surface.h0= MIN(line->lowest_adjacent_ceiling, ceiling_surface.height) - view->origin.z; | |
224 | + surface.hmax= ceiling_surface.height - view->origin.z; | |
225 | + surface.h1= polygon->ceiling_height - view->origin.z; | |
226 | + surface.texture_definition= &side->primary_texture; | |
227 | + surface.transfer_mode= side->primary_transfer_mode; | |
228 | + render_node_side(window, &surface, true, renderStep); | |
229 | + // render_node_side(view, destination, window, &surface); | |
230 | + break; | |
231 | + case _low_side: | |
232 | + surface.lightsource_index= side->primary_lightsource_index; | |
233 | + surface.h0= floor_surface.height - view->origin.z; | |
234 | + surface.h1= MAX(line->highest_adjacent_floor, floor_surface.height) - view->origin.z; | |
235 | + surface.hmax= ceiling_surface.height - view->origin.z; | |
236 | + surface.texture_definition= &side->primary_texture; | |
237 | + surface.transfer_mode= side->primary_transfer_mode; | |
238 | + render_node_side(window, &surface, true, renderStep); | |
239 | + // render_node_side(view, destination, window, &surface); | |
240 | + break; | |
241 | + | |
242 | + default: | |
243 | + assert(false); | |
244 | + break; | |
245 | + } | |
246 | + | |
247 | + if (side->transparent_texture.texture!=UNONE) | |
248 | + { | |
249 | + surface.lightsource_index= side->transparent_lightsource_index; | |
250 | + surface.h0= MAX(line->highest_adjacent_floor, floor_surface.height) - view->origin.z; | |
251 | + surface.h1= line->lowest_adjacent_ceiling - view->origin.z; | |
252 | + surface.hmax= ceiling_surface.height - view->origin.z; | |
253 | + surface.texture_definition= &side->transparent_texture; | |
254 | + surface.transfer_mode= side->transparent_transfer_mode; | |
255 | + render_node_side(window, &surface, false, renderStep); | |
256 | + } | |
257 | + } | |
258 | + } | |
259 | + | |
260 | + /* render floor if below viewer */ | |
261 | + if (floor_surface.height<view->origin.z) | |
262 | + { | |
263 | + // LP change: indicated that the void is on other side | |
264 | + render_node_floor_or_ceiling(window, polygon, &floor_surface, true, false, renderStep); | |
265 | + } | |
266 | + } | |
267 | + } | |
268 | + | |
269 | + // LP: this is for objects on the other side of the liquids; | |
270 | + // render them out here if one can see through the liquids | |
271 | + if (SeeThruLiquids) | |
272 | + { | |
273 | + /* render exterior objects (with their own clipping windows) */ | |
274 | + for (object= node->exterior_objects; object; object= object->next_object) | |
275 | + { | |
276 | + render_node_object(object, true, renderStep); | |
277 | + } | |
278 | + } | |
279 | + | |
280 | + // LP: render the liquid surface after the walls and the stuff behind it | |
281 | + // and before the stuff before it. | |
282 | + if (media && SeeThruLiquids) | |
283 | + { | |
284 | + | |
285 | + // Render only if between the floor and the ceiling: | |
286 | + if (media->height > polygon->floor_height && media->height < polygon->ceiling_height) | |
287 | + { | |
288 | + | |
289 | + // Render the liquids | |
290 | + bool ceil = (media->height > view->origin.z); | |
291 | + horizontal_surface_data LiquidSurface; | |
292 | + | |
293 | + LiquidSurface.origin= media->origin; | |
294 | + LiquidSurface.height= media->height; | |
295 | + LiquidSurface.texture= media->texture; | |
296 | + LiquidSurface.lightsource_index= polygon->media_lightsource_index; | |
297 | + LiquidSurface.transfer_mode= media->transfer_mode; | |
298 | + LiquidSurface.transfer_mode_data= 0; | |
299 | + | |
300 | + for (window= node->clipping_windows; window; window= window->next_window) | |
301 | + { | |
302 | + render_node_floor_or_ceiling(window, polygon, &LiquidSurface, false, ceil, renderStep); | |
303 | + } | |
304 | + } | |
305 | + } | |
306 | + | |
307 | + // LP: this is for objects on the view side of the liquids | |
308 | + /* render exterior objects (with their own clipping windows) */ | |
309 | + for (object= node->exterior_objects; object; object= object->next_object) | |
310 | + { | |
311 | + render_node_object(object, false, renderStep); | |
312 | + } | |
313 | +} | |
314 | + | |
315 | +void RenderRasterizerClass::store_endpoint( | |
316 | + endpoint_data *endpoint, | |
317 | + long_vector2d& p) | |
318 | +{ | |
319 | + overflow_short_to_long_2d(endpoint->transformed, endpoint->flags, p); | |
320 | +} | |
321 | + | |
322 | + | |
323 | +/* ---------- rendering ceilings and floors */ | |
324 | + | |
325 | +// LP change: added "void present on other side" flag | |
326 | +void RenderRasterizerClass::render_node_floor_or_ceiling( | |
327 | + clipping_window_data *window, | |
328 | + polygon_data *polygon, | |
329 | + horizontal_surface_data *surface, | |
330 | + bool void_present, | |
331 | + bool ceil, | |
332 | + RenderStep renderStep) | |
333 | +{ | |
334 | + // LP addition: animated-texture support | |
335 | + // Extra variable defined so as not to edit the original texture | |
336 | + shape_descriptor Texture = AnimTxtr_Translate(surface->texture); | |
337 | + if (Texture!=UNONE) | |
338 | + { | |
339 | + struct polygon_definition textured_polygon; | |
340 | + flagged_world_point2d vertices[MAXIMUM_VERTICES_PER_WORLD_POLYGON]; | |
341 | + world_distance adjusted_height= surface->height-view->origin.z; | |
342 | + int32 transformed_height= adjusted_height*view->world_to_screen_y; | |
343 | + short vertex_count; | |
344 | + short i; | |
345 | + | |
346 | + /* build transformed vertex list */ | |
347 | + vertex_count= polygon->vertex_count; | |
348 | + for (i=0;i<vertex_count;++i) | |
349 | + { | |
350 | + // LP change: expanded the transformed-endpoint access | |
351 | + long_vector2d temp_vertex; | |
352 | + endpoint_data *endpoint = get_endpoint_data(polygon->endpoint_indexes[i]); | |
353 | + overflow_short_to_long_2d(endpoint->transformed,endpoint->flags,temp_vertex); | |
354 | + vertices[i].x = temp_vertex.i; | |
355 | + vertices[i].y = temp_vertex.j; | |
356 | + vertices[i].flags= 0; | |
357 | + } | |
358 | + | |
359 | + /* reversing the order in which these two were clipped caused weird problems */ | |
360 | + | |
361 | + /* clip to left and right sides of the window */ | |
362 | + vertex_count= xy_clip_horizontal_polygon(vertices, vertex_count, &window->left, _clip_left); | |
363 | + vertex_count= xy_clip_horizontal_polygon(vertices, vertex_count, &window->right, _clip_right); | |
364 | + | |
365 | + /* clip to top and bottom sides of the window */ | |
366 | + vertex_count= z_clip_horizontal_polygon(vertices, vertex_count, &window->top, adjusted_height, _clip_up); | |
367 | + vertex_count= z_clip_horizontal_polygon(vertices, vertex_count, &window->bottom, adjusted_height, _clip_down); | |
368 | + | |
369 | + if (vertex_count) | |
370 | + { | |
371 | + /* transform the points we have into screen-space (backwards for ceiling polygons) */ | |
372 | + for (i=0;i<vertex_count;++i) | |
373 | + { | |
374 | + flagged_world_point2d *world= vertices + (adjusted_height>0 ? vertex_count-i-1 : i); | |
375 | + point2d *screen= textured_polygon.vertices + i; | |
376 | + | |
377 | + switch (world->flags&(_clip_left|_clip_right)) | |
378 | + { | |
379 | + case 0: | |
380 | + // LP change: making it long-distance friendly | |
381 | + { | |
382 | + int32 world_x = world->x ? world->x : 1; // fix for division by zero error | |
383 | + int32 screen_x= view->half_screen_width + (world->y*view->world_to_screen_x)/world_x; | |
384 | + screen->x= PIN(screen_x, 0, view->screen_width); | |
385 | + } | |
386 | + break; | |
387 | + case _clip_left: screen->x= window->x0; break; | |
388 | + case _clip_right: screen->x= window->x1; break; | |
389 | + default: | |
390 | + screen->x= window->x0; | |
391 | + break; | |
392 | + } | |
393 | + | |
394 | + switch (world->flags&(_clip_up|_clip_down)) | |
395 | + { | |
396 | + case 0: | |
397 | + // LP change: making it long-distance friendly | |
398 | + { | |
399 | + int32 world_x = world->x ? world->x : 1; // fix for division by zero error | |
400 | + int32 screen_y= view->half_screen_height - transformed_height/world_x + view->dtanpitch; | |
401 | + screen->y= PIN(screen_y, 0, view->screen_height); | |
402 | + } | |
403 | + break; | |
404 | + case _clip_up: screen->y= window->y0; break; | |
405 | + case _clip_down: screen->y= window->y1; break; | |
406 | + default: | |
407 | + screen->y= window->y0; | |
408 | + break; | |
409 | + } | |
410 | + // vassert(screen->y>=0&&screen->y<=view->screen_height, csprintf(temporary, "horizontal: flags==%x, window @ %p", world->flags, window)); | |
411 | + } | |
412 | + | |
413 | + /* setup the other parameters of the textured polygon */ | |
414 | + textured_polygon.flags= 0; | |
415 | + textured_polygon.origin.x= view->origin.x + surface->origin.x; | |
416 | + textured_polygon.origin.y= view->origin.y + surface->origin.y; | |
417 | + textured_polygon.origin.z= adjusted_height; | |
418 | + get_shape_bitmap_and_shading_table(Texture, &textured_polygon.texture, &textured_polygon.shading_tables, view->shading_mode); | |
419 | + // Bug out if bitmap is nonexistent | |
420 | + if (!textured_polygon.texture) return; | |
421 | + | |
422 | + textured_polygon.ShapeDesc = Texture; | |
423 | + textured_polygon.ambient_shade= get_light_intensity(surface->lightsource_index); | |
424 | + textured_polygon.vertex_count= vertex_count; | |
425 | + instantiate_polygon_transfer_mode(view, &textured_polygon, surface->transfer_mode, true); | |
426 | + if (view->shading_mode==_shading_infravision) textured_polygon.flags|= _SHADELESS_BIT; | |
427 | + | |
428 | + /* and, finally, map it */ | |
429 | + // LP: added OpenGL support; also presence of void on other side | |
430 | + textured_polygon.VoidPresent = void_present; | |
431 | + // LP: using rasterizer object | |
432 | + RasPtr->texture_horizontal_polygon(textured_polygon); | |
433 | + } | |
434 | + } | |
435 | +} | |
436 | + | |
437 | +/* ---------- rendering sides (walls) */ | |
438 | + | |
439 | +// LP change: added "void present on other side" flag | |
440 | +void RenderRasterizerClass::render_node_side( | |
441 | + clipping_window_data *window, | |
442 | + vertical_surface_data *surface, | |
443 | + bool void_present, | |
444 | + RenderStep renderStep) | |
445 | +{ | |
446 | + world_distance h= MIN(surface->h1, surface->hmax); | |
447 | + | |
448 | + // LP addition: animated-texture support | |
449 | + // Extra variable defined so as not to edit the original texture | |
450 | + shape_descriptor Texture = AnimTxtr_Translate(surface->texture_definition->texture); | |
451 | + if (h>surface->h0 && Texture!=UNONE) | |
452 | + { | |
453 | + struct polygon_definition textured_polygon; | |
454 | + flagged_world_point2d posts[2]; | |
455 | + flagged_world_point3d vertices[MAXIMUM_VERTICES_PER_WORLD_POLYGON]; | |
456 | + short vertex_count; | |
457 | + short i; | |
458 | + | |
459 | + /* initialize the two posts of our trapezoid */ | |
460 | + vertex_count= 2; | |
461 | + posts[0].x= surface->p0.i; posts[0].y= surface->p0.j; posts[0].flags= 0; | |
462 | + posts[1].x= surface->p1.i; posts[1].y= surface->p1.j; posts[1].flags= 0; | |
463 | + | |
464 | + /* clip to left and right sides of the cone (must happen in the same order as horizontal polygons) */ | |
465 | + vertex_count= xy_clip_line(posts, vertex_count, &window->left, _clip_left); | |
466 | + vertex_count= xy_clip_line(posts, vertex_count, &window->right, _clip_right); | |
467 | + | |
468 | + if (vertex_count) | |
469 | + { | |
470 | + /* build a polygon out of the two posts */ | |
471 | + vertex_count= 4; | |
472 | + vertices[0].z= vertices[1].z= h; | |
473 | + vertices[2].z= vertices[3].z= surface->h0; | |
474 | + vertices[0].x= vertices[3].x= posts[0].x, vertices[0].y= vertices[3].y= posts[0].y; | |
475 | + vertices[1].x= vertices[2].x= posts[1].x, vertices[1].y= vertices[2].y= posts[1].y; | |
476 | + vertices[0].flags= vertices[3].flags= posts[0].flags; | |
477 | + vertices[1].flags= vertices[2].flags= posts[1].flags; | |
478 | + | |
479 | + /* clip to top and bottom sides of the window; because xz_clip_vertical_polygon accepts | |
480 | + vertices in clockwise or counterclockwise order, we can set our vertex list up to be | |
481 | + clockwise on the screen and never worry about it after that */ | |
482 | + vertex_count= xz_clip_vertical_polygon(vertices, vertex_count, &window->top, _clip_up); | |
483 | + vertex_count= xz_clip_vertical_polygon(vertices, vertex_count, &window->bottom, _clip_down); | |
484 | + | |
485 | + if (vertex_count) | |
486 | + { | |
487 | + world_distance dx= surface->p1.i - surface->p0.i; | |
488 | + world_distance dy= surface->p1.j - surface->p0.j; | |
489 | + world_distance x0= WORLD_FRACTIONAL_PART(surface->texture_definition->x0); | |
490 | + world_distance y0= WORLD_FRACTIONAL_PART(surface->texture_definition->y0); | |
491 | + | |
492 | + /* calculate texture origin and direction */ | |
493 | + world_distance divisor = surface->length; | |
494 | + if (divisor == 0) | |
495 | + divisor = 1; | |
496 | + textured_polygon.vector.i= (WORLD_ONE*dx)/divisor; | |
497 | + textured_polygon.vector.j= (WORLD_ONE*dy)/divisor; | |
498 | + textured_polygon.vector.k= -WORLD_ONE; | |
499 | + textured_polygon.origin.x= surface->p0.i - (x0*dx)/divisor; | |
500 | + textured_polygon.origin.y= surface->p0.j - (x0*dy)/divisor; | |
501 | + textured_polygon.origin.z= surface->h1 + y0; | |
502 | + | |
503 | + /* transform the points we have into screen-space */ | |
504 | + for (i=0;i<vertex_count;++i) | |
505 | + { | |
506 | + flagged_world_point3d *world= vertices + i; | |
507 | + point2d *screen= textured_polygon.vertices + i; | |
508 | + | |
509 | + switch (world->flags&(_clip_left|_clip_right)) | |
510 | + { | |
511 | + case 0: | |
512 | + // LP change: making it long-distance friendly | |
513 | + { | |
514 | + int32 screen_x= view->half_screen_width + (world->x ? (world->y*view->world_to_screen_x)/world->x : 0); | |
515 | + screen->x= PIN(screen_x, 0, view->screen_width); | |
516 | + } | |
517 | + break; | |
518 | + case _clip_left: screen->x= window->x0; break; | |
519 | + case _clip_right: screen->x= window->x1; break; | |
520 | + default: | |
521 | + screen->x= window->x0; | |
522 | + break; | |
523 | + } | |
524 | + | |
525 | + switch (world->flags&(_clip_up|_clip_down)) | |
526 | + { | |
527 | + case 0: | |
528 | + // LP change: making it long-distance friendly | |
529 | + { | |
530 | + int32 screen_y= view->half_screen_height - (world->x ? (world->z*view->world_to_screen_y)/world->x : 0) + view->dtanpitch; | |
531 | + screen->y= PIN(screen_y, 0, view->screen_height); | |
532 | + } | |
533 | + break; | |
534 | + case _clip_up: screen->y= window->y0; break; | |
535 | + case _clip_down: screen->y= window->y1; break; | |
536 | + default: | |
537 | + screen->y= window->y0; | |
538 | + break; | |
539 | + } | |
540 | + // vassert(screen->y>=0&&screen->y<=view->screen_height, csprintf(temporary, "#%d!in[#0,#%d]: flags==%x, wind@%p #%d w@%p s@%p", screen->y, view->screen_height, world->flags, window, vertex_count, world, screen)); | |
541 | + } | |
542 | + | |
543 | + /* setup the other parameters of the textured polygon */ | |
544 | + textured_polygon.flags= 0; | |
545 | + get_shape_bitmap_and_shading_table(Texture, &textured_polygon.texture, &textured_polygon.shading_tables, view->shading_mode); | |
546 | + // Bug out if bitmap is nonexistent | |
547 | + if (!textured_polygon.texture) return; | |
548 | + | |
549 | + textured_polygon.ShapeDesc = Texture; | |
550 | + textured_polygon.ambient_shade= get_light_intensity(surface->lightsource_index) + surface->ambient_delta; | |
551 | + textured_polygon.ambient_shade= PIN(textured_polygon.ambient_shade, 0, FIXED_ONE); | |
552 | + textured_polygon.vertex_count= vertex_count; | |
553 | + instantiate_polygon_transfer_mode(view, &textured_polygon, surface->transfer_mode, false); | |
554 | + if (view->shading_mode==_shading_infravision) textured_polygon.flags|= _SHADELESS_BIT; | |
555 | + | |
556 | + /* and, finally, map it */ | |
557 | + // LP: added OpenGL support; also presence of void on other side | |
558 | + textured_polygon.VoidPresent = void_present; | |
559 | + // LP: using rasterizer object | |
560 | + RasPtr->texture_vertical_polygon(textured_polygon); | |
561 | + } | |
562 | + } | |
563 | + } | |
564 | +} | |
565 | + | |
566 | +/* ---------- rendering objects */ | |
567 | + | |
568 | +void RenderRasterizerClass::render_node_object( | |
569 | + render_object_data *object, | |
570 | + bool other_side_of_media, | |
571 | + RenderStep renderStep) | |
572 | +{ | |
573 | + struct clipping_window_data *window; | |
574 | + | |
575 | + for (window= object->clipping_windows; window; window= window->next_window) | |
576 | + { | |
577 | + object->rectangle.clip_left= window->x0; | |
578 | + object->rectangle.clip_right= window->x1; | |
579 | + object->rectangle.clip_top= window->y0; | |
580 | + object->rectangle.clip_bottom= window->y1; | |
581 | + | |
582 | + // Models will have their own liquid-surface clipping, | |
583 | + // so don't edit their clip rects | |
584 | + // This is bitwise XOR, but is presumably OK here | |
585 | + if (view->under_media_boundary ^ other_side_of_media) | |
586 | + { | |
587 | + // Clipping: below a liquid surface | |
588 | + if (object->rectangle.ModelPtr) | |
589 | + object->rectangle.BelowLiquid = true; | |
590 | + else | |
591 | + object->rectangle.clip_top= MAX(object->rectangle.clip_top, object->ymedia); | |
592 | + } | |
593 | + else | |
594 | + { | |
595 | + // Clipping: above a liquid surface | |
596 | + if (object->rectangle.ModelPtr) | |
597 | + object->rectangle.BelowLiquid = false; | |
598 | + else | |
599 | + object->rectangle.clip_bottom= MIN(object->rectangle.clip_bottom, object->ymedia); | |
600 | + } | |
601 | + | |
602 | + // LP: added OpenGL support | |
603 | + // LP: using rasterizer object | |
604 | + RasPtr->texture_rectangle(object->rectangle); | |
605 | + } | |
606 | +} | |
607 | + | |
608 | + | |
609 | +/* ---------- horizontal polygon clipping */ | |
610 | + | |
611 | +enum /* xy_clip_horizontal_polygon() states */ | |
612 | +{ | |
613 | + _testing_first_vertex, /* are we in or out? */ | |
614 | + _searching_cw_for_in_out_transition, | |
615 | + _searching_ccw_for_out_in_transition, | |
616 | + _searching_cw_for_out_in_transition | |
617 | +}; | |
618 | + | |
619 | +// LP change: make it better able to do long-distance views | |
620 | +short RenderRasterizerClass::xy_clip_horizontal_polygon( | |
621 | + flagged_world_point2d *vertices, | |
622 | + short vertex_count, | |
623 | + long_vector2d *line, | |
624 | + uint16 flag) | |
625 | +{ | |
626 | +#ifdef QUICKDRAW_DEBUG | |
627 | + debug_flagged_points(vertices, vertex_count); | |
628 | + debug_vector(line); | |
629 | +#endif | |
630 | +// dprintf("clipping %p (#%d vertices) to vector %x,%x (slope==%x)", vertices, vertex_count, line->i, line->j, slope); | |
631 | + | |
632 | + if (vertex_count) | |
633 | + { | |
634 | + short state= _testing_first_vertex; | |
635 | + short vertex_index= 0, vertex_delta= 1, first_vertex= 0; | |
636 | + short entrance_vertex= NONE, exit_vertex= NONE; /* exiting the clipped area and entering the clipped area */ | |
637 | + bool clipped_exit_vertex= true, clipped_entrance_vertex= true; /* will be false if these points lie directly on a vertex */ | |
638 | + | |
639 | + do | |
640 | + { | |
641 | + // LP change: | |
642 | + CROSSPROD_TYPE cross_product= CROSSPROD_TYPE(line->i)*vertices[vertex_index].y - CROSSPROD_TYPE(line->j)*vertices[vertex_index].x; | |
643 | + // int32 cross_product= line->i*vertices[vertex_index].y - line->j*vertices[vertex_index].x; | |
644 | + | |
645 | + switch (SGN(cross_product)) | |
646 | + { | |
647 | + case -1: /* inside (i.e., will be clipped) */ | |
648 | +// dprintf("vertex#%d is inside s==#%d", vertex_index, state); | |
649 | + switch (state) | |
650 | + { | |
651 | + case _testing_first_vertex: | |
652 | + first_vertex= vertex_index; | |
653 | + state= _searching_cw_for_in_out_transition; /* the exit point from the clip area */ | |
654 | + break; | |
655 | + | |
656 | + case _searching_ccw_for_out_in_transition: /* found exit point from clip area */ | |
657 | + state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
658 | + vertex_delta= 1; | |
659 | + if (exit_vertex==NONE) exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
660 | + vertex_index= first_vertex; /* skip vertices we already know are out */ | |
661 | + break; | |
662 | + | |
663 | + case _searching_cw_for_out_in_transition: /* found entrance point to clipped area */ | |
664 | + if (entrance_vertex==NONE) entrance_vertex= vertex_index; | |
665 | + state= NONE; | |
666 | + break; | |
667 | + } | |
668 | + break; | |
669 | + | |
670 | + case 0: /* clip line passed directly through a vertex */ | |
671 | +// dprintf("vertex#%d is on the clip line s==#%d", vertex_index, state); | |
672 | + switch (state) | |
673 | + { | |
674 | + /* if weユre testing the first vertex, this tells us nothing */ | |
675 | + | |
676 | + case _searching_cw_for_out_in_transition: | |
677 | + entrance_vertex= vertex_index; | |
678 | + clipped_entrance_vertex= false; /* remember if this passes through vertex */ | |
679 | + break; | |
680 | + | |
681 | + case _searching_cw_for_in_out_transition: | |
682 | + exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
683 | + clipped_exit_vertex= false; | |
684 | + break; | |
685 | + case _searching_ccw_for_out_in_transition: | |
686 | + exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
687 | + clipped_exit_vertex= false; /* remember if this passes through vertex */ | |
688 | + break; | |
689 | + } | |
690 | + break; | |
691 | + | |
692 | + case 1: /* outside (i.e., will not be clipped) */ | |
693 | +// dprintf("vertex#%d is outside s==#%d", vertex_index, state); | |
694 | + switch (state) | |
695 | + { | |
696 | + case _testing_first_vertex: | |
697 | + first_vertex= vertex_index; | |
698 | + state= _searching_ccw_for_out_in_transition; /* the exit point from the clipped area */ | |
699 | + vertex_delta= -1; | |
700 | + break; | |
701 | + | |
702 | + case _searching_cw_for_in_out_transition: /* found exit point from clipped area */ | |
703 | + state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
704 | + if (exit_vertex==NONE) exit_vertex= vertex_index; | |
705 | + break; | |
706 | + } | |
707 | + break; | |
708 | + } | |
709 | + | |
710 | + /* adjust vertex_index (clockwise or counterclockwise, depending on vertex_delta) | |
711 | + if weユve come back to the first vertex without finding an entrance point weユre | |
712 | + either all the way in or all the way out */ | |
713 | + vertex_index= (vertex_delta<0) ? WRAP_LOW(vertex_index, vertex_count-1) : | |
714 | + WRAP_HIGH(vertex_index, vertex_count-1); | |
715 | + if (vertex_index==first_vertex) /* we came full-circle without hitting anything */ | |
716 | + { | |
717 | + switch (state) | |
718 | + { | |
719 | + case _searching_cw_for_in_out_transition: /* never found a way out: clipped into nothing */ | |
720 | + vertex_count= 0; | |
721 | + case _testing_first_vertex: /* is this the right thing to do? */ | |
722 | + case _searching_ccw_for_out_in_transition: /* never found a way in: no clipping */ | |
723 | + exit_vertex= NONE; | |
724 | + state= NONE; | |
725 | + break; | |
726 | + } | |
727 | + } | |
728 | + } | |
729 | + while (state!=NONE); | |
730 | + | |
731 | + if (exit_vertex!=NONE) /* weユve got clipping to do */ | |
732 | + { | |
733 | + flagged_world_point2d new_entrance_point, new_exit_point; | |
734 | + | |
735 | +// dprintf("entrance_vertex==#%d (%s), exit_vertex==#%d (%s)", entrance_vertex, clipped_entrance_vertex ? "clipped" : "unclipped", exit_vertex, clipped_exit_vertex ? "clipped" : "unclipped"); | |
736 | + | |
737 | + /* clip the entrance to the clipped area */ | |
738 | + if (clipped_entrance_vertex) | |
739 | + { | |
740 | + xy_clip_flagged_world_points(vertices + WRAP_LOW(entrance_vertex, vertex_count-1), vertices + entrance_vertex, | |
741 | + &new_entrance_point, line); | |
742 | + } | |
743 | + else | |
744 | + { | |
745 | + new_entrance_point= vertices[entrance_vertex]; | |
746 | + } | |
747 | + new_entrance_point.flags|= flag; | |
748 | + | |
749 | + /* clip the exit from the clipped area */ | |
750 | + if (clipped_exit_vertex) | |
751 | + { | |
752 | + xy_clip_flagged_world_points(vertices + WRAP_LOW(exit_vertex, vertex_count-1), vertices + exit_vertex, | |
753 | + &new_exit_point, line); | |
754 | + } | |
755 | + else | |
756 | + { | |
757 | + new_exit_point= vertices[WRAP_LOW(exit_vertex, vertex_count-1)]; | |
758 | + } | |
759 | + new_exit_point.flags|= flag; | |
760 | + | |
761 | + /* adjust for the change in number of vertices */ | |
762 | + vertex_delta= entrance_vertex - exit_vertex; | |
763 | + if (vertex_delta<0) | |
764 | + { | |
765 | + if (vertex_delta!=-2) memmove(vertices+entrance_vertex+2, vertices+exit_vertex, (vertex_count-exit_vertex)*sizeof(flagged_world_point2d)); | |
766 | + vertex_delta= vertex_count+vertex_delta; | |
767 | + } | |
768 | + else | |
769 | + { | |
770 | + /* move down by exit_vertex, add new vertices to end */ | |
771 | + assert(vertex_delta); | |
772 | + if (exit_vertex) | |
773 | + { | |
774 | + memmove(vertices, vertices+exit_vertex, vertex_delta*sizeof(flagged_world_point2d)); | |
775 | + entrance_vertex-= exit_vertex; | |
776 | + } | |
777 | + } | |
778 | + | |
779 | + vertex_count= vertex_delta+2; | |
780 | + vwarn(vertex_count>=3 && vertex_count<=MAXIMUM_VERTICES_PER_WORLD_POLYGON, | |
781 | + csprintf(temporary, "vertex overflow or underflow (#%d);g;", vertex_count)); | |
782 | + | |
783 | + if (vertex_count<3 || vertex_count>MAXIMUM_VERTICES_PER_WORLD_POLYGON) | |
784 | + { | |
785 | + vertex_count= 0; | |
786 | + } | |
787 | + else | |
788 | + { | |
789 | + /* and, finally, add the new vertices */ | |
790 | + vertices[entrance_vertex]= new_entrance_point; | |
791 | + vertices[entrance_vertex+1]= new_exit_point; | |
792 | + } | |
793 | + } | |
794 | + } | |
795 | + | |
796 | +#ifdef QUICKDRAW_DEBUG | |
797 | + debug_flagged_points(vertices, vertex_count); | |
798 | + debug_vector(line); | |
799 | +#endif | |
800 | +// dprintf("result == %p (#%d vertices)", vertices, vertex_count); | |
801 | + | |
802 | + return vertex_count; | |
803 | +} | |
804 | + | |
805 | +/* sort points before clipping to assure consistency; there is a way to make this more accurate | |
806 | + but it requires the downshifting game, as played in SCOTTISH_TEXTURES.C. itユs tempting to | |
807 | + think that having a smaller scale for our world coordinates would help here (i.e., less bits | |
808 | + per distance) but then wouldnユt we be screwed when we tried to rotate? */ | |
809 | +// LP change: make it better able to do long-distance views | |
810 | +void RenderRasterizerClass::xy_clip_flagged_world_points( | |
811 | + flagged_world_point2d *p0, | |
812 | + flagged_world_point2d *p1, | |
813 | + flagged_world_point2d *clipped, | |
814 | + long_vector2d *line) | |
815 | +{ | |
816 | + bool swap= (p1->y>p0->y) ? false : ((p0->y==p1->y) ? (p1->x<p0->x) : true); | |
817 | + flagged_world_point2d *local_p0= swap ? p1 : p0; | |
818 | + flagged_world_point2d *local_p1= swap ? p0 : p1; | |
819 | + world_distance dx= local_p1->x - local_p0->x; | |
820 | + world_distance dy= local_p1->y - local_p0->y; | |
821 | + int32 numerator= line->j*local_p0->x - line->i*local_p0->y; | |
822 | + int32 denominator= line->i*dy - line->j*dx; | |
823 | + short shift_count= FIXED_FRACTIONAL_BITS; | |
824 | + _fixed t; | |
825 | + | |
826 | + /* give numerator 16 significant bits over denominator and then calculate t==n/d; MPWユs PPCC | |
827 | + didnユt seem to like (INT32_MIN>>1) and i had to substitute 0xc0000000 instead (hmmm) */ | |
828 | + while (numerator<=(int32)0x3fffffff && numerator>=(int32)0xc0000000 && shift_count--) numerator<<= 1; | |
829 | + if (shift_count>0) denominator>>= shift_count; | |
830 | + t= numerator; | |
831 | + if (denominator) | |
832 | + t /= denominator; | |
833 | + | |
834 | + /* calculate the clipped point */ | |
835 | + clipped->x= local_p0->x + FIXED_INTEGERAL_PART(t*dx); | |
836 | + clipped->y= local_p0->y + FIXED_INTEGERAL_PART(t*dy); | |
837 | + clipped->flags= local_p0->flags&local_p1->flags; | |
838 | +} | |
839 | + | |
840 | +/* almost wholly identical to xz_clip_vertical_polygon() except that this works off 2d points | |
841 | + in the xy-plane and a height */ | |
842 | +// LP change: make it better able to do long-distance views | |
843 | +short RenderRasterizerClass::z_clip_horizontal_polygon( | |
844 | + flagged_world_point2d *vertices, | |
845 | + short vertex_count, | |
846 | + long_vector2d *line, /* i==x, j==z */ | |
847 | + world_distance height, | |
848 | + uint16 flag) | |
849 | +{ | |
850 | + CROSSPROD_TYPE heighti= CROSSPROD_TYPE(line->i)*height; | |
851 | + | |
852 | +#ifdef QUICKDRAW_DEBUG | |
853 | + debug_flagged_points(vertices, vertex_count); | |
854 | + debug_x_line(line->j ? (line->i*height)/line->j : (height<0 ? INT32_MIN : INT32_MAX)); | |
855 | +#endif | |
856 | +// dprintf("clipping %p (#%d vertices) to vector %x,%x", vertices, vertex_count, line->i, line->j); | |
857 | + | |
858 | + if (vertex_count) | |
859 | + { | |
860 | + short state= _testing_first_vertex; | |
861 | + short vertex_index= 0, vertex_delta= 1, first_vertex= 0; | |
862 | + short entrance_vertex= NONE, exit_vertex= NONE; /* exiting the clipped area and entering the clipped area */ | |
863 | + bool clipped_exit_vertex= true, clipped_entrance_vertex= true; /* will be false if these points lie directly on a vertex */ | |
864 | + | |
865 | + do | |
866 | + { | |
867 | + CROSSPROD_TYPE cross_product= heighti - CROSSPROD_TYPE(line->j)*vertices[vertex_index].x; | |
868 | + | |
869 | + if (cross_product<0) /* inside (i.e., will be clipped) */ | |
870 | + { | |
871 | + switch (state) | |
872 | + { | |
873 | + case _testing_first_vertex: | |
874 | + first_vertex= vertex_index; | |
875 | + state= _searching_cw_for_in_out_transition; /* the exit point from the clip area */ | |
876 | + break; | |
877 | + | |
878 | + case _searching_ccw_for_out_in_transition: /* found exit point from clip area */ | |
879 | + state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
880 | + vertex_delta= 1; | |
881 | + if (exit_vertex==NONE) exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
882 | + vertex_index= first_vertex; /* skip vertices we already know are out */ | |
883 | + break; | |
884 | + | |
885 | + case _searching_cw_for_out_in_transition: /* found entrance point to clipped area */ | |
886 | + if (entrance_vertex==NONE) entrance_vertex= vertex_index; | |
887 | + state= NONE; | |
888 | + break; | |
889 | + } | |
890 | + } | |
891 | + else | |
892 | + { | |
893 | + if (cross_product>0) /* outside (i.e., will not be clipped) */ | |
894 | + { | |
895 | + switch (state) | |
896 | + { | |
897 | + case _testing_first_vertex: | |
898 | + first_vertex= vertex_index; | |
899 | + state= _searching_ccw_for_out_in_transition; /* the exit point from the clipped area */ | |
900 | + vertex_delta= -1; | |
901 | + break; | |
902 | + | |
903 | + case _searching_cw_for_in_out_transition: /* found exit point from clipped area */ | |
904 | + state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
905 | + if (exit_vertex==NONE) exit_vertex= vertex_index; | |
906 | + break; | |
907 | + } | |
908 | + } | |
909 | + else /* clip line passed directly through a vertex */ | |
910 | + { | |
911 | + switch (state) | |
912 | + { | |
913 | + /* if weユre testing the first vertex (_testing_first_vertex), this tells us nothing */ | |
914 | + | |
915 | + case _searching_cw_for_out_in_transition: | |
916 | + entrance_vertex= vertex_index; | |
917 | + clipped_entrance_vertex= false; /* remember if this passes through vertex */ | |
918 | + break; | |
919 | + | |
920 | + case _searching_cw_for_in_out_transition: | |
921 | + exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
922 | + clipped_exit_vertex= false; | |
923 | + break; | |
924 | + case _searching_ccw_for_out_in_transition: | |
925 | + exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
926 | + clipped_exit_vertex= false; /* remember if this passes through vertex */ | |
927 | + break; | |
928 | + } | |
929 | + } | |
930 | + } | |
931 | + | |
932 | + /* adjust vertex_index (clockwise or counterclockwise, depending on vertex_delta) | |
933 | + if weユve come back to the first vertex without finding an entrance point weユre | |
934 | + either all the way in or all the way out */ | |
935 | + vertex_index= (vertex_delta<0) ? WRAP_LOW(vertex_index, vertex_count-1) : | |
936 | + WRAP_HIGH(vertex_index, vertex_count-1); | |
937 | + if (vertex_index==first_vertex) /* we came full-circle without hitting anything */ | |
938 | + { | |
939 | + switch (state) | |
940 | + { | |
941 | + case _searching_cw_for_in_out_transition: /* never found a way out: clipped into nothing */ | |
942 | + vertex_count= 0; | |
943 | + case _testing_first_vertex: /* is this the right thing to do? */ | |
944 | + case _searching_ccw_for_out_in_transition: /* never found a way in: no clipping */ | |
945 | + exit_vertex= NONE; | |
946 | + state= NONE; | |
947 | + break; | |
948 | + } | |
949 | + } | |
950 | + } | |
951 | + while (state!=NONE); | |
952 | + | |
953 | + if (exit_vertex!=NONE) /* weユve got clipping to do */ | |
954 | + { | |
955 | + flagged_world_point2d new_entrance_point, new_exit_point; | |
956 | + | |
957 | +// dprintf("entrance_vertex==#%d (%s), exit_vertex==#%d (%s)", entrance_vertex, clipped_entrance_vertex ? "clipped" : "unclipped", exit_vertex, clipped_exit_vertex ? "clipped" : "unclipped"); | |
958 | + | |
959 | + /* clip the entrance to the clipped area */ | |
960 | + if (clipped_entrance_vertex) | |
961 | + { | |
962 | + z_clip_flagged_world_points(vertices + WRAP_LOW(entrance_vertex, vertex_count-1), vertices + entrance_vertex, | |
963 | + height, &new_entrance_point, line); | |
964 | + } | |
965 | + else | |
966 | + { | |
967 | + new_entrance_point= vertices[entrance_vertex]; | |
968 | + } | |
969 | + new_entrance_point.flags|= flag; | |
970 | + | |
971 | + /* clip the exit from the clipped area */ | |
972 | + if (clipped_exit_vertex) | |
973 | + { | |
974 | + z_clip_flagged_world_points(vertices + WRAP_LOW(exit_vertex, vertex_count-1), vertices + exit_vertex, | |
975 | + height, &new_exit_point, line); | |
976 | + } | |
977 | + else | |
978 | + { | |
979 | + new_exit_point= vertices[WRAP_LOW(exit_vertex, vertex_count-1)]; | |
980 | + } | |
981 | + new_exit_point.flags|= flag; | |
982 | + | |
983 | + /* adjust for the change in number of vertices */ | |
984 | + vertex_delta= entrance_vertex - exit_vertex; | |
985 | + if (vertex_delta<0) | |
986 | + { | |
987 | + if (vertex_delta!=-2) memmove(vertices+entrance_vertex+2, vertices+exit_vertex, (vertex_count-exit_vertex)*sizeof(flagged_world_point2d)); | |
988 | + vertex_delta= vertex_count+vertex_delta; | |
989 | + } | |
990 | + else | |
991 | + { | |
992 | + /* move down by exit_vertex, add new vertices to end */ | |
993 | + assert(vertex_delta); | |
994 | + if (exit_vertex) | |
995 | + { | |
996 | + memmove(vertices, vertices+exit_vertex, vertex_delta*sizeof(flagged_world_point2d)); | |
997 | + entrance_vertex-= exit_vertex; | |
998 | + } | |
999 | + } | |
1000 | + vertex_count= vertex_delta+2; | |
1001 | + | |
1002 | + vwarn(vertex_count>=3 && vertex_count<=MAXIMUM_VERTICES_PER_WORLD_POLYGON, | |
1003 | + csprintf(temporary, "vertex overflow or underflow (#%d);g;", vertex_count)); | |
1004 | + | |
1005 | + if (vertex_count<3 || vertex_count>MAXIMUM_VERTICES_PER_WORLD_POLYGON) | |
1006 | + { | |
1007 | + vertex_count= 0; | |
1008 | + } | |
1009 | + else | |
1010 | + { | |
1011 | + /* and, finally, add the new vertices */ | |
1012 | + vertices[entrance_vertex]= new_entrance_point; | |
1013 | + vertices[entrance_vertex+1]= new_exit_point; | |
1014 | + } | |
1015 | + } | |
1016 | + } | |
1017 | + | |
1018 | +#ifdef QUICKDRAW_DEBUG | |
1019 | + debug_flagged_points(vertices, vertex_count); | |
1020 | + debug_x_line(line->j ? (line->i*height)/line->j : (height<0 ? INT32_MIN : INT32_MAX)); | |
1021 | +#endif | |
1022 | +// dprintf("result == %p (#%d vertices)", vertices, vertex_count); | |
1023 | + | |
1024 | + return vertex_count; | |
1025 | +} | |
1026 | + | |
1027 | +/* sort points before clipping to assure consistency; this is almost identical to xz_clipノ() | |
1028 | + except that it clips 2d points in the xy-plane at the given height. */ | |
1029 | +// LP change: make it better able to do long-distance views | |
1030 | +void RenderRasterizerClass::z_clip_flagged_world_points( | |
1031 | + flagged_world_point2d *p0, | |
1032 | + flagged_world_point2d *p1, | |
1033 | + world_distance height, | |
1034 | + flagged_world_point2d *clipped, | |
1035 | + long_vector2d *line) | |
1036 | +{ | |
1037 | + bool swap= (p1->y>p0->y) ? false : ((p0->y==p1->y) ? (p1->x<p0->x) : true); | |
1038 | + flagged_world_point2d *local_p0= swap ? p1 : p0; | |
1039 | + flagged_world_point2d *local_p1= swap ? p0 : p1; | |
1040 | + world_distance dx= local_p1->x - local_p0->x; | |
1041 | + world_distance dy= local_p1->y - local_p0->y; | |
1042 | + int32 numerator= line->j*local_p0->x - line->i*height; | |
1043 | + int32 denominator= - line->j*dx; | |
1044 | + short shift_count= FIXED_FRACTIONAL_BITS; | |
1045 | + _fixed t; | |
1046 | + | |
1047 | + /* give numerator 16 significant bits over denominator and then calculate t==n/d; MPWユs PPCC | |
1048 | + didnユt seem to like (INT32_MIN>>1) and i had to substitute 0xc0000000 instead (hmmm) */ | |
1049 | + while (numerator<=(int32)0x3fffffff && numerator>=(int32)0xc0000000 && shift_count--) numerator<<= 1; | |
1050 | + if (shift_count>0) denominator>>= shift_count; | |
1051 | + t= numerator; | |
1052 | + if (denominator) | |
1053 | + t /= denominator; | |
1054 | + | |
1055 | + /* calculate the clipped point */ | |
1056 | + clipped->x= local_p0->x + FIXED_INTEGERAL_PART(t*dx); | |
1057 | + clipped->y= local_p0->y + FIXED_INTEGERAL_PART(t*dy); | |
1058 | + clipped->flags= local_p0->flags&local_p1->flags; | |
1059 | +} | |
1060 | + | |
1061 | +/* ---------- vertical polygon clipping */ | |
1062 | + | |
1063 | +// LP change: make it better able to do long-distance views | |
1064 | +short RenderRasterizerClass::xy_clip_line( | |
1065 | + flagged_world_point2d *posts, | |
1066 | + short vertex_count, | |
1067 | + long_vector2d *line, | |
1068 | + uint16 flag) | |
1069 | +{ | |
1070 | +#ifdef QUICKDRAW_DEBUG | |
1071 | +// debug_flagged_points(posts, vertex_count); | |
1072 | +// debug_vector(line); | |
1073 | +#endif | |
1074 | +// dprintf("clipping %p (#%d) to line (%d,%d)", posts, vertex_count, line->i, line->j); | |
1075 | + | |
1076 | + if (vertex_count) | |
1077 | + { | |
1078 | + CROSSPROD_TYPE cross_product0= CROSSPROD_TYPE(line->i)*posts[0].y - CROSSPROD_TYPE(line->j)*posts[0].x; | |
1079 | + CROSSPROD_TYPE cross_product1= CROSSPROD_TYPE(line->i)*posts[1].y - CROSSPROD_TYPE(line->j)*posts[1].x; | |
1080 | + | |
1081 | + if (cross_product0<0) | |
1082 | + { | |
1083 | + if (cross_product1<0) /* clipped out of existence */ | |
1084 | + { | |
1085 | + vertex_count= 0; | |
1086 | + } | |
1087 | + else | |
1088 | + { | |
1089 | + xy_clip_flagged_world_points(&posts[0], &posts[1], &posts[0], line); | |
1090 | + posts[0].flags|= flag; | |
1091 | + } | |
1092 | + } | |
1093 | + else | |
1094 | + { | |
1095 | + if (cross_product1<0) | |
1096 | + { | |
1097 | + xy_clip_flagged_world_points(&posts[0], &posts[1], &posts[1], line); | |
1098 | + posts[1].flags|= flag; | |
1099 | + } | |
1100 | + } | |
1101 | + } | |
1102 | + | |
1103 | +#ifdef QUICKDRAW_DEBUG | |
1104 | +// debug_flagged_points(posts, vertex_count); | |
1105 | +// debug_vector(line); | |
1106 | +#endif | |
1107 | +// dprintf("result #%d vertices", vertex_count); | |
1108 | + | |
1109 | + return vertex_count; | |
1110 | +} | |
1111 | + | |
1112 | +// LP change: make it better able to do long-distance views | |
1113 | +short RenderRasterizerClass::xz_clip_vertical_polygon( | |
1114 | + flagged_world_point3d *vertices, | |
1115 | + short vertex_count, | |
1116 | + long_vector2d *line, /* i==x, j==z */ | |
1117 | + uint16 flag) | |
1118 | +{ | |
1119 | +#ifdef QUICKDRAW_DEBUG | |
1120 | +// debug_flagged_points3d(vertices, vertex_count); | |
1121 | +// debug_vector(line); | |
1122 | +#endif | |
1123 | +// dprintf("clipping %p (#%d vertices) to vector %x,%x", vertices, vertex_count, line->i, line->j); | |
1124 | + | |
1125 | + if (vertex_count) | |
1126 | + { | |
1127 | + short state= _testing_first_vertex; | |
1128 | + short vertex_index= 0, vertex_delta= 1, first_vertex= 0; | |
1129 | + short entrance_vertex= NONE, exit_vertex= NONE; /* exiting the clipped area and entering the clipped area */ | |
1130 | + bool clipped_exit_vertex= true, clipped_entrance_vertex= true; /* will be false if these points lie directly on a vertex */ | |
1131 | + | |
1132 | + do | |
1133 | + { | |
1134 | + CROSSPROD_TYPE cross_product= CROSSPROD_TYPE(line->i)*vertices[vertex_index].z - CROSSPROD_TYPE(line->j)*vertices[vertex_index].x; | |
1135 | + | |
1136 | + switch (SGN(cross_product)) | |
1137 | + { | |
1138 | + case -1: /* inside (i.e., will be clipped) */ | |
1139 | +// dprintf("vertex#%d is inside s==#%d", vertex_index, state); | |
1140 | + switch (state) | |
1141 | + { | |
1142 | + case _testing_first_vertex: | |
1143 | + first_vertex= vertex_index; | |
1144 | + state= _searching_cw_for_in_out_transition; /* the exit point from the clip area */ | |
1145 | + break; | |
1146 | + | |
1147 | + case _searching_ccw_for_out_in_transition: /* found exit point from clip area */ | |
1148 | + state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
1149 | + vertex_delta= 1; | |
1150 | + if (exit_vertex==NONE) exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
1151 | + vertex_index= first_vertex; /* skip vertices we already know are out */ | |
1152 | + break; | |
1153 | + | |
1154 | + case _searching_cw_for_out_in_transition: /* found entrance point to clipped area */ | |
1155 | + if (entrance_vertex==NONE) entrance_vertex= vertex_index; | |
1156 | + state= NONE; | |
1157 | + break; | |
1158 | + } | |
1159 | + break; | |
1160 | + | |
1161 | + case 0: /* clip line passed directly through a vertex */ | |
1162 | +// dprintf("vertex#%d is on the clip line s==#%d", vertex_index, state); | |
1163 | + switch (state) | |
1164 | + { | |
1165 | + /* if weユre testing the first vertex, this tells us nothing */ | |
1166 | + | |
1167 | + case _searching_cw_for_out_in_transition: | |
1168 | + entrance_vertex= vertex_index; | |
1169 | + clipped_entrance_vertex= false; /* remember if this passes through vertex */ | |
1170 | + break; | |
1171 | + | |
1172 | + case _searching_cw_for_in_out_transition: | |
1173 | + exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
1174 | + clipped_exit_vertex= false; | |
1175 | + break; | |
1176 | + case _searching_ccw_for_out_in_transition: | |
1177 | + exit_vertex= WRAP_HIGH(vertex_index, vertex_count-1); | |
1178 | + clipped_exit_vertex= false; /* remember if this passes through vertex */ | |
1179 | + break; | |
1180 | + } | |
1181 | + break; | |
1182 | + | |
1183 | + case 1: /* outside (i.e., will not be clipped) */ | |
1184 | +// dprintf("vertex#%d is outside s==#%d", vertex_index, state); | |
1185 | + switch (state) | |
1186 | + { | |
1187 | + case _testing_first_vertex: | |
1188 | + first_vertex= vertex_index; | |
1189 | + state= _searching_ccw_for_out_in_transition; /* the exit point from the clipped area */ | |
1190 | + vertex_delta= -1; | |
1191 | + break; | |
1192 | + | |
1193 | + case _searching_cw_for_in_out_transition: /* found exit point from clipped area */ | |
1194 | + state= _searching_cw_for_out_in_transition; /* the entrance point to the clipped area */ | |
1195 | + if (exit_vertex==NONE) exit_vertex= vertex_index; | |
1196 | + break; | |
1197 | + } | |
1198 | + break; | |
1199 | + } | |
1200 | + | |
1201 | + /* adjust vertex_index (clockwise or counterclockwise, depending on vertex_delta) | |
1202 | + if weユve come back to the first vertex without finding an entrance point weユre | |
1203 | + either all the way in or all the way out */ | |
1204 | + vertex_index= (vertex_delta<0) ? WRAP_LOW(vertex_index, vertex_count-1) : | |
1205 | + WRAP_HIGH(vertex_index, vertex_count-1); | |
1206 | + if (vertex_index==first_vertex) /* we came full-circle without hitting anything */ | |
1207 | + { | |
1208 | + switch (state) | |
1209 | + { | |
1210 | + case _searching_cw_for_in_out_transition: /* never found a way out: clipped into nothing */ | |
1211 | + vertex_count= 0; | |
1212 | + case _testing_first_vertex: /* is this the right thing to do? */ | |
1213 | + case _searching_ccw_for_out_in_transition: /* never found a way in: no clipping */ | |
1214 | + exit_vertex= NONE; | |
1215 | + state= NONE; | |
1216 | + break; | |
1217 | + } | |
1218 | + } | |
1219 | + } | |
1220 | + while (state!=NONE); | |
1221 | + | |
1222 | + if (exit_vertex!=NONE) /* weユve got clipping to do */ | |
1223 | + { | |
1224 | + flagged_world_point3d new_entrance_point, new_exit_point; | |
1225 | + | |
1226 | +// dprintf("entrance_vertex==#%d (%s), exit_vertex==#%d (%s)", entrance_vertex, clipped_entrance_vertex ? "clipped" : "unclipped", exit_vertex, clipped_exit_vertex ? "clipped" : "unclipped"); | |
1227 | + | |
1228 | + /* clip the entrance to the clipped area */ | |
1229 | + if (clipped_entrance_vertex) | |
1230 | + { | |
1231 | + xz_clip_flagged_world_points(vertices + WRAP_LOW(entrance_vertex, vertex_count-1), vertices + entrance_vertex, | |
1232 | + &new_entrance_point, line); | |
1233 | + } | |
1234 | + else | |
1235 | + { | |
1236 | + new_entrance_point= vertices[entrance_vertex]; | |
1237 | + } | |
1238 | + new_entrance_point.flags|= flag; | |
1239 | + | |
1240 | + /* clip the exit from the clipped area */ | |
1241 | + if (clipped_exit_vertex) | |
1242 | + { | |
1243 | + xz_clip_flagged_world_points(vertices + WRAP_LOW(exit_vertex, vertex_count-1), vertices + exit_vertex, | |
1244 | + &new_exit_point, line); | |
1245 | + } | |
1246 | + else | |
1247 | + { | |
1248 | + new_exit_point= vertices[WRAP_LOW(exit_vertex, vertex_count-1)]; | |
1249 | + } | |
1250 | + new_exit_point.flags|= flag; | |
1251 | + | |
1252 | + /* adjust for the change in number of vertices */ | |
1253 | + vertex_delta= entrance_vertex - exit_vertex; | |
1254 | + if (vertex_delta<0) | |
1255 | + { | |
1256 | + if (vertex_delta!=-2) memmove(vertices+entrance_vertex+2, vertices+exit_vertex, (vertex_count-exit_vertex)*sizeof(flagged_world_point3d)); | |
1257 | + vertex_delta= vertex_count+vertex_delta; | |
1258 | + } | |
1259 | + else | |
1260 | + { | |
1261 | + /* move down by exit_vertex, add new vertices to end */ | |
1262 | + assert(vertex_delta); | |
1263 | + if (exit_vertex) | |
1264 | + { | |
1265 | + memmove(vertices, vertices+exit_vertex, vertex_delta*sizeof(flagged_world_point3d)); | |
1266 | + entrance_vertex-= exit_vertex; | |
1267 | + } | |
1268 | + } | |
1269 | + vertex_count= vertex_delta+2; | |
1270 | + | |
1271 | + vwarn(vertex_count>=3 && vertex_count<=MAXIMUM_VERTICES_PER_WORLD_POLYGON, | |
1272 | + csprintf(temporary, "vertex overflow or underflow (#%d);g;", vertex_count)); | |
1273 | + | |
1274 | + if (vertex_count<3 || vertex_count>MAXIMUM_VERTICES_PER_WORLD_POLYGON) | |
1275 | + { | |
1276 | + vertex_count= 0; | |
1277 | + } | |
1278 | + else | |
1279 | + { | |
1280 | + /* and, finally, add the new vertices */ | |
1281 | + vertices[entrance_vertex]= new_entrance_point; | |
1282 | + vertices[entrance_vertex+1]= new_exit_point; | |
1283 | + } | |
1284 | + } | |
1285 | + } | |
1286 | + | |
1287 | +#ifdef QUICKDRAW_DEBUG | |
1288 | +// debug_flagged_points3d(vertices, vertex_count); | |
1289 | +// debug_vector(line); | |
1290 | +#endif | |
1291 | +// dprintf("result == %p (#%d vertices)", vertices, vertex_count); | |
1292 | + | |
1293 | + return vertex_count; | |
1294 | +} | |
1295 | + | |
1296 | +/* sort points before clipping to assure consistency */ | |
1297 | +// LP change: make it better able to do long-distance views | |
1298 | +void RenderRasterizerClass::xz_clip_flagged_world_points( | |
1299 | + flagged_world_point3d *p0, | |
1300 | + flagged_world_point3d *p1, | |
1301 | + flagged_world_point3d *clipped, | |
1302 | + long_vector2d *line) | |
1303 | +{ | |
1304 | + bool swap= (p1->y>p0->y) ? false : ((p0->y==p1->y) ? (p1->x<p0->x) : true); | |
1305 | + flagged_world_point3d *local_p0= swap ? p1 : p0; | |
1306 | + flagged_world_point3d *local_p1= swap ? p0 : p1; | |
1307 | + world_distance dx= local_p1->x - local_p0->x; | |
1308 | + world_distance dy= local_p1->y - local_p0->y; | |
1309 | + world_distance dz= local_p1->z - local_p0->z; | |
1310 | + int32 numerator= line->j*local_p0->x - line->i*local_p0->z; | |
1311 | + int32 denominator= line->i*dz - line->j*dx; | |
1312 | + short shift_count= FIXED_FRACTIONAL_BITS; | |
1313 | + _fixed t; | |
1314 | + | |
1315 | + /* give numerator 16 significant bits over denominator and then calculate t==n/d; MPWユs PPCC | |
1316 | + didnユt seem to like (INT32_MIN>>1) and i had to substitute 0xc0000000 instead (hmmm) */ | |
1317 | + while (numerator<=(int32)0x3fffffff && numerator>=(int32)0xc0000000 && shift_count--) numerator<<= 1; | |
1318 | + if (shift_count>0) denominator>>= shift_count; | |
1319 | + t = numerator; | |
1320 | + if (denominator) | |
1321 | + t /= denominator; | |
1322 | + | |
1323 | + /* calculate the clipped point */ | |
1324 | + clipped->x= local_p0->x + FIXED_INTEGERAL_PART(t*dx); | |
1325 | + clipped->y= local_p0->y + FIXED_INTEGERAL_PART(t*dy); | |
1326 | + clipped->z= local_p0->z + FIXED_INTEGERAL_PART(t*dz); | |
1327 | + clipped->flags= local_p0->flags&local_p1->flags; | |
1328 | +} |
@@ -1,170 +1,170 @@ | ||
1 | -#ifndef __COLLECTION_DEFINITION_H | |
2 | -#define __COLLECTION_DEFINITION_H | |
3 | - | |
4 | -/* | |
5 | -COLLECTION_DEFINITION.H | |
6 | - | |
7 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
8 | - and the "Aleph One" developers. | |
9 | - | |
10 | - This program is free software; you can redistribute it and/or modify | |
11 | - it under the terms of the GNU General Public License as published by | |
12 | - the Free Software Foundation; either version 3 of the License, or | |
13 | - (at your option) any later version. | |
14 | - | |
15 | - This program is distributed in the hope that it will be useful, | |
16 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | - GNU General Public License for more details. | |
19 | - | |
20 | - This license is contained in the file "COPYING", | |
21 | - which is included with this source code; it is available online at | |
22 | - http://www.gnu.org/licenses/gpl.html | |
23 | - | |
24 | -Friday, June 17, 1994 11:48:27 AM | |
25 | - | |
26 | -Friday, June 17, 1994 11:27:13 PM | |
27 | - added .minimum_light_intensity field to low-level shape. | |
28 | -Tuesday, June 21, 1994 2:59:16 PM | |
29 | - added collection version number, added unused bytes to all structures. | |
30 | -Wednesday, June 22, 1994 3:53:22 PM | |
31 | - scaling modifications. | |
32 | -Wednesday, June 22, 1994 10:07:38 PM | |
33 | - added _scenery_collection type, .size field to collection_definition structure, changed | |
34 | - ヤshape_indexesユ to ヤlow_level_shape_indexesユ in high_level_shape_definition structure | |
35 | -Saturday, July 9, 1994 3:36:05 PM | |
36 | - added NUMBER_OF_PRIVATE_COLORS constant. | |
37 | -*/ | |
38 | - | |
39 | -/* ---------- collection definition structure */ | |
40 | - | |
41 | -/* 2 added pixels_to_world to collection_definition structure */ | |
42 | -/* 3 added size to collection_definition structure */ | |
43 | -#define COLLECTION_VERSION 3 | |
44 | - | |
45 | -/* at the beginning of the clut, used by the extractor for various opaque reasons */ | |
46 | -#define NUMBER_OF_PRIVATE_COLORS 3 | |
47 | - | |
48 | -enum /* collection types */ | |
49 | -{ | |
50 | - _unused_collection= 0, /* raw */ | |
51 | - _wall_collection, /* raw */ | |
52 | - _object_collection, /* rle */ | |
53 | - _interface_collection, /* raw */ | |
54 | - _scenery_collection /* rle */ | |
55 | -}; | |
56 | - | |
57 | -struct high_level_shape_definition; | |
58 | -struct low_level_shape_definition; | |
59 | -struct bitmap_definition; | |
60 | -struct rgb_color_value; | |
61 | - | |
62 | -struct collection_definition | |
63 | -{ | |
64 | - int16 version; | |
65 | - | |
66 | - int16 type; /* used for get_shape_descriptors() */ | |
67 | - uint16 flags; /* [unused.16] */ | |
68 | - | |
69 | - int16 color_count, clut_count; | |
70 | - int32 color_table_offset; /* an array of clut_count arrays of color_count ColorSpec structures */ | |
71 | - | |
72 | - int16 high_level_shape_count; | |
73 | - int32 high_level_shape_offset_table_offset; | |
74 | - | |
75 | - int16 low_level_shape_count; | |
76 | - int32 low_level_shape_offset_table_offset; | |
77 | - | |
78 | - int16 bitmap_count; | |
79 | - int32 bitmap_offset_table_offset; | |
80 | - | |
81 | - int16 pixels_to_world; /* used to shift pixel values into world coordinates */ | |
82 | - | |
83 | - int32 size; /* used to assert offsets */ | |
84 | - | |
85 | - int16 unused[253]; | |
86 | - | |
87 | - std::vector<rgb_color_value> color_tables; | |
88 | - std::vector<std::vector<uint8> > high_level_shapes; | |
89 | - std::vector<low_level_shape_definition> low_level_shapes; | |
90 | - std::vector<std::vector<uint8> > bitmaps; | |
91 | -}; | |
92 | -const int SIZEOF_collection_definition = 544; | |
93 | - | |
94 | -/* ---------- high level shape definition */ | |
95 | - | |
96 | -#define HIGH_LEVEL_SHAPE_NAME_LENGTH 32 | |
97 | - | |
98 | -struct high_level_shape_definition // Starting with number_of_views, this is a shape_animation_data structure | |
99 | -{ | |
100 | - int16 type; /* ==0 */ | |
101 | - uint16 flags; /* [unused.16] */ | |
102 | - | |
103 | - char name[HIGH_LEVEL_SHAPE_NAME_LENGTH+2]; | |
104 | - | |
105 | - int16 number_of_views; | |
106 | - | |
107 | - int16 frames_per_view, ticks_per_frame; | |
108 | - int16 key_frame; | |
109 | - | |
110 | - int16 transfer_mode; | |
111 | - int16 transfer_mode_period; /* in ticks */ | |
112 | - | |
113 | - int16 first_frame_sound, key_frame_sound, last_frame_sound; | |
114 | - | |
115 | - int16 pixels_to_world; | |
116 | - | |
117 | - int16 loop_frame; | |
118 | - | |
119 | - int16 unused[14]; | |
120 | - | |
121 | - /* see the interface.h/shape_animation_data for a decription of how many | |
122 | - low-level indices follow (it's not simply number_of_view * frames_per_view) */ | |
123 | - int16 low_level_shape_indexes[1]; | |
124 | -}; | |
125 | -const int SIZEOF_high_level_shape_definition = 90; | |
126 | - | |
127 | -/* --------- low-level shape definition */ | |
128 | - | |
129 | -#define _X_MIRRORED_BIT 0x8000 | |
130 | -#define _Y_MIRRORED_BIT 0x4000 | |
131 | -#define _KEYPOINT_OBSCURED_BIT 0x2000 | |
132 | - | |
133 | -struct low_level_shape_definition | |
134 | -{ | |
135 | - uint16 flags; /* [x-mirror.1] [y-mirror.1] [keypoint_obscured.1] [unused.13] */ | |
136 | - | |
137 | - _fixed minimum_light_intensity; /* in [0,FIXED_ONE] */ | |
138 | - | |
139 | - int16 bitmap_index; | |
140 | - | |
141 | - /* (x,y) in pixel coordinates of origin */ | |
142 | - int16 origin_x, origin_y; | |
143 | - | |
144 | - /* (x,y) in pixel coordinates of key point */ | |
145 | - int16 key_x, key_y; | |
146 | - | |
147 | - int16 world_left, world_right, world_top, world_bottom; | |
148 | - int16 world_x0, world_y0; | |
149 | - | |
150 | - int16 unused[4]; | |
151 | -}; | |
152 | -const int SIZEOF_low_level_shape_definition = 36; | |
153 | - | |
154 | -/* ---------- colors */ | |
155 | - | |
156 | -enum | |
157 | -{ | |
158 | - SELF_LUMINESCENT_COLOR_FLAG= 0x80 | |
159 | -}; | |
160 | - | |
161 | -struct rgb_color_value | |
162 | -{ | |
163 | - uint8 flags; | |
164 | - uint8 value; | |
165 | - | |
166 | - uint16 red, green, blue; | |
167 | -}; | |
168 | -const int SIZEOF_rgb_color_value = 8; | |
169 | - | |
170 | -#endif | |
1 | +#ifndef __COLLECTION_DEFINITION_H | |
2 | +#define __COLLECTION_DEFINITION_H | |
3 | + | |
4 | +/* | |
5 | +COLLECTION_DEFINITION.H | |
6 | + | |
7 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
8 | + and the "Aleph One" developers. | |
9 | + | |
10 | + This program is free software; you can redistribute it and/or modify | |
11 | + it under the terms of the GNU General Public License as published by | |
12 | + the Free Software Foundation; either version 3 of the License, or | |
13 | + (at your option) any later version. | |
14 | + | |
15 | + This program is distributed in the hope that it will be useful, | |
16 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
18 | + GNU General Public License for more details. | |
19 | + | |
20 | + This license is contained in the file "COPYING", | |
21 | + which is included with this source code; it is available online at | |
22 | + http://www.gnu.org/licenses/gpl.html | |
23 | + | |
24 | +Friday, June 17, 1994 11:48:27 AM | |
25 | + | |
26 | +Friday, June 17, 1994 11:27:13 PM | |
27 | + added .minimum_light_intensity field to low-level shape. | |
28 | +Tuesday, June 21, 1994 2:59:16 PM | |
29 | + added collection version number, added unused bytes to all structures. | |
30 | +Wednesday, June 22, 1994 3:53:22 PM | |
31 | + scaling modifications. | |
32 | +Wednesday, June 22, 1994 10:07:38 PM | |
33 | + added _scenery_collection type, .size field to collection_definition structure, changed | |
34 | + ヤshape_indexesユ to ヤlow_level_shape_indexesユ in high_level_shape_definition structure | |
35 | +Saturday, July 9, 1994 3:36:05 PM | |
36 | + added NUMBER_OF_PRIVATE_COLORS constant. | |
37 | +*/ | |
38 | + | |
39 | +/* ---------- collection definition structure */ | |
40 | + | |
41 | +/* 2 added pixels_to_world to collection_definition structure */ | |
42 | +/* 3 added size to collection_definition structure */ | |
43 | +#define COLLECTION_VERSION 3 | |
44 | + | |
45 | +/* at the beginning of the clut, used by the extractor for various opaque reasons */ | |
46 | +#define NUMBER_OF_PRIVATE_COLORS 3 | |
47 | + | |
48 | +enum /* collection types */ | |
49 | +{ | |
50 | + _unused_collection= 0, /* raw */ | |
51 | + _wall_collection, /* raw */ | |
52 | + _object_collection, /* rle */ | |
53 | + _interface_collection, /* raw */ | |
54 | + _scenery_collection /* rle */ | |
55 | +}; | |
56 | + | |
57 | +struct high_level_shape_definition; | |
58 | +struct low_level_shape_definition; | |
59 | +struct bitmap_definition; | |
60 | +struct rgb_color_value; | |
61 | + | |
62 | +struct collection_definition | |
63 | +{ | |
64 | + int16 version; | |
65 | + | |
66 | + int16 type; /* used for get_shape_descriptors() */ | |
67 | + uint16 flags; /* [unused.16] */ | |
68 | + | |
69 | + int16 color_count, clut_count; | |
70 | + int32 color_table_offset; /* an array of clut_count arrays of color_count ColorSpec structures */ | |
71 | + | |
72 | + int16 high_level_shape_count; | |
73 | + int32 high_level_shape_offset_table_offset; | |
74 | + | |
75 | + int16 low_level_shape_count; | |
76 | + int32 low_level_shape_offset_table_offset; | |
77 | + | |
78 | + int16 bitmap_count; | |
79 | + int32 bitmap_offset_table_offset; | |
80 | + | |
81 | + int16 pixels_to_world; /* used to shift pixel values into world coordinates */ | |
82 | + | |
83 | + int32 size; /* used to assert offsets */ | |
84 | + | |
85 | + int16 unused[253]; | |
86 | + | |
87 | + std::vector<rgb_color_value> color_tables; | |
88 | + std::vector<std::vector<uint8> > high_level_shapes; | |
89 | + std::vector<low_level_shape_definition> low_level_shapes; | |
90 | + std::vector<std::vector<uint8> > bitmaps; | |
91 | +}; | |
92 | +const int SIZEOF_collection_definition = 544; | |
93 | + | |
94 | +/* ---------- high level shape definition */ | |
95 | + | |
96 | +#define HIGH_LEVEL_SHAPE_NAME_LENGTH 32 | |
97 | + | |
98 | +struct high_level_shape_definition // Starting with number_of_views, this is a shape_animation_data structure | |
99 | +{ | |
100 | + int16 type; /* ==0 */ | |
101 | + uint16 flags; /* [unused.16] */ | |
102 | + | |
103 | + char name[HIGH_LEVEL_SHAPE_NAME_LENGTH+2]; | |
104 | + | |
105 | + int16 number_of_views; | |
106 | + | |
107 | + int16 frames_per_view, ticks_per_frame; | |
108 | + int16 key_frame; | |
109 | + | |
110 | + int16 transfer_mode; | |
111 | + int16 transfer_mode_period; /* in ticks */ | |
112 | + | |
113 | + int16 first_frame_sound, key_frame_sound, last_frame_sound; | |
114 | + | |
115 | + int16 pixels_to_world; | |
116 | + | |
117 | + int16 loop_frame; | |
118 | + | |
119 | + int16 unused[14]; | |
120 | + | |
121 | + /* see the interface.h/shape_animation_data for a decription of how many | |
122 | + low-level indices follow (it's not simply number_of_view * frames_per_view) */ | |
123 | + int16 low_level_shape_indexes[1]; | |
124 | +}; | |
125 | +const int SIZEOF_high_level_shape_definition = 90; | |
126 | + | |
127 | +/* --------- low-level shape definition */ | |
128 | + | |
129 | +#define _X_MIRRORED_BIT 0x8000 | |
130 | +#define _Y_MIRRORED_BIT 0x4000 | |
131 | +#define _KEYPOINT_OBSCURED_BIT 0x2000 | |
132 | + | |
133 | +struct low_level_shape_definition | |
134 | +{ | |
135 | + uint16 flags; /* [x-mirror.1] [y-mirror.1] [keypoint_obscured.1] [unused.13] */ | |
136 | + | |
137 | + _fixed minimum_light_intensity; /* in [0,FIXED_ONE] */ | |
138 | + | |
139 | + int16 bitmap_index; | |
140 | + | |
141 | + /* (x,y) in pixel coordinates of origin */ | |
142 | + int16 origin_x, origin_y; | |
143 | + | |
144 | + /* (x,y) in pixel coordinates of key point */ | |
145 | + int16 key_x, key_y; | |
146 | + | |
147 | + int16 world_left, world_right, world_top, world_bottom; | |
148 | + int16 world_x0, world_y0; | |
149 | + | |
150 | + int16 unused[4]; | |
151 | +}; | |
152 | +const int SIZEOF_low_level_shape_definition = 36; | |
153 | + | |
154 | +/* ---------- colors */ | |
155 | + | |
156 | +enum | |
157 | +{ | |
158 | + SELF_LUMINESCENT_COLOR_FLAG= 0x80 | |
159 | +}; | |
160 | + | |
161 | +struct rgb_color_value | |
162 | +{ | |
163 | + uint8 flags; | |
164 | + uint8 value; | |
165 | + | |
166 | + uint16 red, green, blue; | |
167 | +}; | |
168 | +const int SIZEOF_rgb_color_value = 8; | |
169 | + | |
170 | +#endif |
@@ -1,57 +1,57 @@ | ||
1 | -#ifndef _OGL_FADERS_ | |
2 | -#define _OGL_FADERS_ | |
3 | -/* | |
4 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
5 | - and the "Aleph One" developers. | |
6 | - | |
7 | - This program is free software; you can redistribute it and/or modify | |
8 | - it under the terms of the GNU General Public License as published by | |
9 | - the Free Software Foundation; either version 3 of the License, or | |
10 | - (at your option) any later version. | |
11 | - | |
12 | - This program is distributed in the hope that it will be useful, | |
13 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | - GNU General Public License for more details. | |
16 | - | |
17 | - This license is contained in the file "COPYING", | |
18 | - which is included with this source code; it is available online at | |
19 | - http://www.gnu.org/licenses/gpl.html | |
20 | - | |
21 | - OpenGL Renderer, | |
22 | - by Loren Petrich, | |
23 | - May 30, 2000 | |
24 | - | |
25 | - This contains code for doing fader stuff. | |
26 | -*/ | |
27 | - | |
28 | - | |
29 | -// Indicates whether OpenGL-rendering faders will be used | |
30 | -bool OGL_FaderActive(); | |
31 | - | |
32 | -// Which kinds of faders in the fader queue? | |
33 | -enum | |
34 | -{ | |
35 | - FaderQueue_Liquid, | |
36 | - FaderQueue_Other, | |
37 | - NUMBER_OF_FADER_QUEUE_ENTRIES | |
38 | -}; | |
39 | - | |
40 | -// Fader data | |
41 | -struct OGL_Fader | |
42 | -{ | |
43 | - // Which type of fade to do | |
44 | - short Type; | |
45 | - // The three color channels and a transparency channel | |
46 | - float Color[4]; | |
47 | - | |
48 | - OGL_Fader(): Type(NONE) {} | |
49 | -}; | |
50 | - | |
51 | -// Fader=queue accessor | |
52 | -OGL_Fader *GetOGL_FaderQueueEntry(int Index); | |
53 | - | |
54 | -// Fader renderer; returns whether or not OpenGL faders were active. | |
55 | -bool OGL_DoFades(float Left, float Top, float Right, float Bottom); | |
56 | - | |
57 | -#endif | |
1 | +#ifndef _OGL_FADERS_ | |
2 | +#define _OGL_FADERS_ | |
3 | +/* | |
4 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
5 | + and the "Aleph One" developers. | |
6 | + | |
7 | + This program is free software; you can redistribute it and/or modify | |
8 | + it under the terms of the GNU General Public License as published by | |
9 | + the Free Software Foundation; either version 3 of the License, or | |
10 | + (at your option) any later version. | |
11 | + | |
12 | + This program is distributed in the hope that it will be useful, | |
13 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + GNU General Public License for more details. | |
16 | + | |
17 | + This license is contained in the file "COPYING", | |
18 | + which is included with this source code; it is available online at | |
19 | + http://www.gnu.org/licenses/gpl.html | |
20 | + | |
21 | + OpenGL Renderer, | |
22 | + by Loren Petrich, | |
23 | + May 30, 2000 | |
24 | + | |
25 | + This contains code for doing fader stuff. | |
26 | +*/ | |
27 | + | |
28 | + | |
29 | +// Indicates whether OpenGL-rendering faders will be used | |
30 | +bool OGL_FaderActive(); | |
31 | + | |
32 | +// Which kinds of faders in the fader queue? | |
33 | +enum | |
34 | +{ | |
35 | + FaderQueue_Liquid, | |
36 | + FaderQueue_Other, | |
37 | + NUMBER_OF_FADER_QUEUE_ENTRIES | |
38 | +}; | |
39 | + | |
40 | +// Fader data | |
41 | +struct OGL_Fader | |
42 | +{ | |
43 | + // Which type of fade to do | |
44 | + short Type; | |
45 | + // The three color channels and a transparency channel | |
46 | + float Color[4]; | |
47 | + | |
48 | + OGL_Fader(): Type(NONE) {} | |
49 | +}; | |
50 | + | |
51 | +// Fader=queue accessor | |
52 | +OGL_Fader *GetOGL_FaderQueueEntry(int Index); | |
53 | + | |
54 | +// Fader renderer; returns whether or not OpenGL faders were active. | |
55 | +bool OGL_DoFades(float Left, float Top, float Right, float Bottom); | |
56 | + | |
57 | +#endif |
@@ -1,142 +1,142 @@ | ||
1 | -/* | |
2 | - | |
3 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | -*/ | |
21 | - | |
22 | -/* | |
23 | - * ImageLoader_SDL.cpp - Image file loading, SDL implementation | |
24 | - * | |
25 | - * Written in 2001 by Christian Bauer | |
26 | - */ | |
27 | - | |
28 | -#include "ImageLoader.h" | |
29 | -#include "FileHandler.h" | |
30 | - | |
31 | -#ifdef HAVE_SDL_IMAGE_H | |
32 | -#include <SDL_image.h> | |
33 | -#endif | |
34 | - | |
35 | -#include <cmath> | |
36 | - | |
37 | -/* | |
38 | - * Load specified image file | |
39 | - */ | |
40 | - | |
41 | -bool ImageDescriptor::LoadFromFile(FileSpecifier& File, int ImgMode, int flags, int actual_width, int actual_height, int maxSize) | |
42 | -{ | |
43 | - if (flags & ImageLoader_ImageIsAlreadyPremultiplied) | |
44 | - PremultipliedAlpha = true; | |
45 | - | |
46 | - // Don't load opacity if there is no color component: | |
47 | - switch(ImgMode) { | |
48 | - case ImageLoader_Colors: | |
49 | - if (LoadDDSFromFile(File, flags, actual_width, actual_height, maxSize)) return true; | |
50 | - break; | |
51 | - | |
52 | - case ImageLoader_Opacity: | |
53 | - if (!IsPresent()) | |
54 | - return false; | |
55 | - break; | |
56 | - | |
57 | - default: | |
58 | - vassert(false, csprintf(temporary,"Bad image mode for loader: %d",ImgMode)); | |
59 | - } | |
60 | - | |
61 | - // Load image to surface | |
62 | - OpenedFile of; | |
63 | - if (!File.Open(of)) | |
64 | - { | |
65 | - return false; | |
66 | - } | |
67 | -#ifdef HAVE_SDL_IMAGE | |
68 | - SDL_Surface *s = IMG_Load_RW(of.GetRWops(), 0); | |
69 | -#else | |
70 | - SDL_Surface *s = SDL_LoadBMP_RW(of.GetRWops(), 0); | |
71 | -#endif | |
72 | - if (s == NULL) | |
73 | - return false; | |
74 | - | |
75 | - // Get image dimensions and set its size | |
76 | - int Width = s->w, Height = s->h; | |
77 | - int OriginalWidth = (actual_width) ? actual_width : Width; | |
78 | - int OriginalHeight = (actual_height) ? actual_height : Height; | |
79 | - if (flags & ImageLoader_ResizeToPowersOfTwo) { | |
80 | - Width = NextPowerOfTwo(Width); | |
81 | - Height = NextPowerOfTwo(Height); | |
82 | - } | |
83 | - switch (ImgMode) { | |
84 | - case ImageLoader_Colors: | |
85 | - Resize(Width, Height); | |
86 | - VScale = ((double) OriginalWidth / (double) Width); | |
87 | - UScale = ((double) OriginalHeight / (double) Height); | |
88 | - MipMapCount = 0; | |
89 | - break; | |
90 | - | |
91 | - case ImageLoader_Opacity: | |
92 | - // If the wrong size, then bug out | |
93 | - if (Width != this->Width || Height != this->Height || ((double) OriginalWidth / Width != VScale || ((double) OriginalHeight / Height != UScale))) { | |
94 | - SDL_FreeSurface(s); | |
95 | - return false; | |
96 | - } | |
97 | - break; | |
98 | - } | |
99 | - | |
100 | - // Convert to 32-bit OpenGL-friendly RGBA surface | |
101 | -#ifdef ALEPHONE_LITTLE_ENDIAN | |
102 | - SDL_Surface *rgba = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); | |
103 | -#else | |
104 | - SDL_Surface *rgba = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); | |
105 | -#endif | |
106 | - if (rgba == NULL) { | |
107 | - SDL_FreeSurface(s); | |
108 | - return false; | |
109 | - } | |
110 | - | |
111 | - SDL_SetAlpha(s, 0, 0xff); // disable SDL_SRCALPHA | |
112 | - SDL_BlitSurface(s, NULL, rgba, NULL); | |
113 | - SDL_FreeSurface(s); | |
114 | - | |
115 | - // Convert surface to RGBA texture | |
116 | - switch (ImgMode) { | |
117 | - case ImageLoader_Colors: | |
118 | - memcpy(GetPixelBasePtr(), rgba->pixels, Width * Height * 4); | |
119 | - break; | |
120 | - | |
121 | - case ImageLoader_Opacity: { | |
122 | - uint8 *p = (uint8 *)rgba->pixels; | |
123 | - uint8 *q = (uint8 *)GetPixelBasePtr(); | |
124 | - for (int h=0; h<Height; h++) { | |
125 | - for (int w=0; w<Width; w++) { | |
126 | - // RGB to greyscale value, and then to the opacity | |
127 | - float Red = float(*p++); | |
128 | - float Green = float(*p++); | |
129 | - float Blue = float(*p++); | |
130 | - p++; | |
131 | - float Opacity = (Red + Green + Blue) / 3.0F; | |
132 | - q[3] = PIN(int(Opacity + 0.5), 0, 255); | |
133 | - q += 4; | |
134 | - } | |
135 | - } | |
136 | - break; | |
137 | - } | |
138 | - } | |
139 | - | |
140 | - SDL_FreeSurface(rgba); | |
141 | - return true; | |
142 | -} | |
1 | +/* | |
2 | + | |
3 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | +*/ | |
21 | + | |
22 | +/* | |
23 | + * ImageLoader_SDL.cpp - Image file loading, SDL implementation | |
24 | + * | |
25 | + * Written in 2001 by Christian Bauer | |
26 | + */ | |
27 | + | |
28 | +#include "ImageLoader.h" | |
29 | +#include "FileHandler.h" | |
30 | + | |
31 | +#ifdef HAVE_SDL_IMAGE_H | |
32 | +#include <SDL_image.h> | |
33 | +#endif | |
34 | + | |
35 | +#include <cmath> | |
36 | + | |
37 | +/* | |
38 | + * Load specified image file | |
39 | + */ | |
40 | + | |
41 | +bool ImageDescriptor::LoadFromFile(FileSpecifier& File, int ImgMode, int flags, int actual_width, int actual_height, int maxSize) | |
42 | +{ | |
43 | + if (flags & ImageLoader_ImageIsAlreadyPremultiplied) | |
44 | + PremultipliedAlpha = true; | |
45 | + | |
46 | + // Don't load opacity if there is no color component: | |
47 | + switch(ImgMode) { | |
48 | + case ImageLoader_Colors: | |
49 | + if (LoadDDSFromFile(File, flags, actual_width, actual_height, maxSize)) return true; | |
50 | + break; | |
51 | + | |
52 | + case ImageLoader_Opacity: | |
53 | + if (!IsPresent()) | |
54 | + return false; | |
55 | + break; | |
56 | + | |
57 | + default: | |
58 | + vassert(false, csprintf(temporary,"Bad image mode for loader: %d",ImgMode)); | |
59 | + } | |
60 | + | |
61 | + // Load image to surface | |
62 | + OpenedFile of; | |
63 | + if (!File.Open(of)) | |
64 | + { | |
65 | + return false; | |
66 | + } | |
67 | +#ifdef HAVE_SDL_IMAGE | |
68 | + SDL_Surface *s = IMG_Load_RW(of.GetRWops(), 0); | |
69 | +#else | |
70 | + SDL_Surface *s = SDL_LoadBMP_RW(of.GetRWops(), 0); | |
71 | +#endif | |
72 | + if (s == NULL) | |
73 | + return false; | |
74 | + | |
75 | + // Get image dimensions and set its size | |
76 | + int Width = s->w, Height = s->h; | |
77 | + int OriginalWidth = (actual_width) ? actual_width : Width; | |
78 | + int OriginalHeight = (actual_height) ? actual_height : Height; | |
79 | + if (flags & ImageLoader_ResizeToPowersOfTwo) { | |
80 | + Width = NextPowerOfTwo(Width); | |
81 | + Height = NextPowerOfTwo(Height); | |
82 | + } | |
83 | + switch (ImgMode) { | |
84 | + case ImageLoader_Colors: | |
85 | + Resize(Width, Height); | |
86 | + VScale = ((double) OriginalWidth / (double) Width); | |
87 | + UScale = ((double) OriginalHeight / (double) Height); | |
88 | + MipMapCount = 0; | |
89 | + break; | |
90 | + | |
91 | + case ImageLoader_Opacity: | |
92 | + // If the wrong size, then bug out | |
93 | + if (Width != this->Width || Height != this->Height || ((double) OriginalWidth / Width != VScale || ((double) OriginalHeight / Height != UScale))) { | |
94 | + SDL_FreeSurface(s); | |
95 | + return false; | |
96 | + } | |
97 | + break; | |
98 | + } | |
99 | + | |
100 | + // Convert to 32-bit OpenGL-friendly RGBA surface | |
101 | +#ifdef ALEPHONE_LITTLE_ENDIAN | |
102 | + SDL_Surface *rgba = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000); | |
103 | +#else | |
104 | + SDL_Surface *rgba = SDL_CreateRGBSurface(SDL_SWSURFACE, Width, Height, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff); | |
105 | +#endif | |
106 | + if (rgba == NULL) { | |
107 | + SDL_FreeSurface(s); | |
108 | + return false; | |
109 | + } | |
110 | + | |
111 | + SDL_SetAlpha(s, 0, 0xff); // disable SDL_SRCALPHA | |
112 | + SDL_BlitSurface(s, NULL, rgba, NULL); | |
113 | + SDL_FreeSurface(s); | |
114 | + | |
115 | + // Convert surface to RGBA texture | |
116 | + switch (ImgMode) { | |
117 | + case ImageLoader_Colors: | |
118 | + memcpy(GetPixelBasePtr(), rgba->pixels, Width * Height * 4); | |
119 | + break; | |
120 | + | |
121 | + case ImageLoader_Opacity: { | |
122 | + uint8 *p = (uint8 *)rgba->pixels; | |
123 | + uint8 *q = (uint8 *)GetPixelBasePtr(); | |
124 | + for (int h=0; h<Height; h++) { | |
125 | + for (int w=0; w<Width; w++) { | |
126 | + // RGB to greyscale value, and then to the opacity | |
127 | + float Red = float(*p++); | |
128 | + float Green = float(*p++); | |
129 | + float Blue = float(*p++); | |
130 | + p++; | |
131 | + float Opacity = (Red + Green + Blue) / 3.0F; | |
132 | + q[3] = PIN(int(Opacity + 0.5), 0, 255); | |
133 | + q += 4; | |
134 | + } | |
135 | + } | |
136 | + break; | |
137 | + } | |
138 | + } | |
139 | + | |
140 | + SDL_FreeSurface(rgba); | |
141 | + return true; | |
142 | +} |
@@ -1,216 +1,216 @@ | ||
1 | -#ifndef _IMAGE_LOADER_ | |
2 | -#define _IMAGE_LOADER_ | |
3 | -/* | |
4 | - | |
5 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | - and the "Aleph One" developers. | |
7 | - | |
8 | - This program is free software; you can redistribute it and/or modify | |
9 | - it under the terms of the GNU General Public License as published by | |
10 | - the Free Software Foundation; either version 3 of the License, or | |
11 | - (at your option) any later version. | |
12 | - | |
13 | - This program is distributed in the hope that it will be useful, | |
14 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | - GNU General Public License for more details. | |
17 | - | |
18 | - This license is contained in the file "COPYING", | |
19 | - which is included with this source code; it is available online at | |
20 | - http://www.gnu.org/licenses/gpl.html | |
21 | - | |
22 | - Image-Loader Interface File, | |
23 | - by Loren Petrich, | |
24 | - October 21, 2000 | |
25 | - | |
26 | - This file contains an image-descriptor object and a function for loading the image | |
27 | - from a file. | |
28 | - | |
29 | -*/ | |
30 | - | |
31 | -#include "DDS.h" | |
32 | -#include <vector> | |
33 | -#include "cseries.h" | |
34 | -#include "FileHandler.h" | |
35 | - | |
36 | -using namespace std; | |
37 | - | |
38 | -// Need an object to hold the read-in image. | |
39 | -class ImageDescriptor | |
40 | -{ | |
41 | - int Width; // along scanlines | |
42 | - int Height; // scanline to scanline | |
43 | - | |
44 | - double VScale; | |
45 | - double UScale; | |
46 | - | |
47 | - uint32 *Pixels; | |
48 | - int Size; | |
49 | - | |
50 | - int MipMapCount; | |
51 | - | |
52 | -public: | |
53 | - | |
54 | - bool IsPresent() const {return (Pixels != NULL); } | |
55 | - bool IsPremultiplied() const { return (IsPresent() ? PremultipliedAlpha : false); } | |
56 | - | |
57 | - bool LoadFromFile(FileSpecifier& File, int ImgMode, int flags, int actual_width = 0, int actual_height = 0, int maxSize = 0); | |
58 | - | |
59 | - // Size of level 0 image | |
60 | - int GetWidth() const {return Width;} | |
61 | - int GetHeight() const {return Height;} | |
62 | - int GetNumPixels() const {return Width*Height;} | |
63 | - | |
64 | - int GetMipMapCount() const { return MipMapCount; } | |
65 | - int GetTotalBytes() const { return Size; } | |
66 | - int GetBufferSize() const { return Size; } | |
67 | - int GetFormat() const { return Format; } | |
68 | - | |
69 | - double GetVScale() const { return VScale; } | |
70 | - double GetUScale() const { return UScale; } | |
71 | - | |
72 | - // Pixel accessors | |
73 | - uint32& GetPixel(int Horiz, int Vert) {return Pixels[Width*(Vert%Height) + (Horiz%Width)];} | |
74 | - uint32 *GetPixelBasePtr() {return Pixels;} | |
75 | - const uint32 *GetBuffer() const { return Pixels; } | |
76 | - uint32 *GetBuffer() { return Pixels; } | |
77 | - | |
78 | - uint32 *GetMipMapPtr(int Level); | |
79 | - const uint32 *GetMipMapPtr(int Level) const; | |
80 | - int GetMipMapSize(int level) const; | |
81 | - | |
82 | - // Reallocation | |
83 | - void Resize(int _Width, int _Height); | |
84 | - | |
85 | - // mipmappy operations | |
86 | - void Resize(int _Width, int _Height, int _TotalBytes); | |
87 | - | |
88 | - bool Minify(); | |
89 | - | |
90 | - bool MakeRGBA(); | |
91 | - bool MakeDXTC3(); | |
92 | - | |
93 | - void PremultiplyAlpha(); | |
94 | - bool PremultipliedAlpha; // public so find silhouette version can unset | |
95 | - | |
96 | - // Clearing | |
97 | - void Clear() | |
98 | - {Width = Height = Size = 0; delete []Pixels; Pixels = NULL;} | |
99 | - | |
100 | - ImageDescriptor(const ImageDescriptor &CopyFrom); | |
101 | - | |
102 | -ImageDescriptor(): Width(0), Height(0), VScale(1.0), UScale(1.0), Pixels(NULL), Size(0), PremultipliedAlpha(false) {} | |
103 | - | |
104 | - // asumes RGBA8 | |
105 | - ImageDescriptor(int width, int height, uint32 *pixels); | |
106 | - | |
107 | - enum ImageFormat { | |
108 | - RGBA8, | |
109 | - DXTC1, | |
110 | - DXTC3, | |
111 | - DXTC5, | |
112 | - Unknown | |
113 | - }; | |
114 | - | |
115 | - ~ImageDescriptor() | |
116 | - { | |
117 | - delete []Pixels; | |
118 | - Pixels = NULL; | |
119 | - } | |
120 | - | |
121 | -private: | |
122 | - bool LoadDDSFromFile(FileSpecifier& File, int flags, int actual_width = 0, int actual_height = 0, int maxSize = 0); | |
123 | - bool LoadMipMapFromFile(OpenedFile &File, int flags, int level, DDSURFACEDESC2 &ddsd, int skip); | |
124 | - bool SkipMipMapFromFile(OpenedFile &File, int flags, int level, DDSURFACEDESC2 &ddsd); | |
125 | - | |
126 | - ImageFormat Format; | |
127 | -}; | |
128 | - | |
129 | -template <typename T> | |
130 | -class copy_on_edit | |
131 | -{ | |
132 | -public: | |
133 | - copy_on_edit() : _original(NULL), _copy(NULL) { }; | |
134 | - | |
135 | - void set(const T* original) { | |
136 | - if (_copy) { | |
137 | - delete _copy; | |
138 | - _copy = NULL; | |
139 | - } | |
140 | - _original = original; | |
141 | - } | |
142 | - | |
143 | - void set(T* original) { | |
144 | - if (_copy) { | |
145 | - delete _copy; | |
146 | - _copy= NULL; | |
147 | - } | |
148 | - _original = original; | |
149 | - } | |
150 | - | |
151 | - | |
152 | - const T* get() { | |
153 | - if (_copy) | |
154 | - return (const T*) _copy; | |
155 | - else | |
156 | - return (const T*) _original; | |
157 | - } | |
158 | - | |
159 | - T* edit() { | |
160 | - if (!_original) { | |
161 | - return _copy; | |
162 | - } else { | |
163 | - if (!_copy) { | |
164 | - _copy = new T(*_original); | |
165 | - } | |
166 | - return _copy; | |
167 | - } | |
168 | - } | |
169 | - | |
170 | - // takes possession of copy | |
171 | - T* edit(T* copy) { | |
172 | - if (_copy) { | |
173 | - delete _copy; | |
174 | - } | |
175 | - _original = NULL; | |
176 | - _copy = copy; | |
177 | - return _copy; | |
178 | - } | |
179 | - | |
180 | - ~copy_on_edit() { | |
181 | - if (_copy) { | |
182 | - delete _copy; | |
183 | - _copy = NULL; | |
184 | - } | |
185 | - } | |
186 | - | |
187 | -private: | |
188 | - T* _original; | |
189 | - T* _copy; | |
190 | -}; | |
191 | - | |
192 | -typedef copy_on_edit<ImageDescriptor> ImageDescriptorManager; | |
193 | - | |
194 | - | |
195 | -// What to load: image colors (must be loaded first) | |
196 | -// or image opacity (replaces the default, which is 100% opaque everywhere). | |
197 | -// The image-opacity image must have the same size as the color image; | |
198 | -// it is interpreted as a grayscale image. | |
199 | -enum { | |
200 | - ImageLoader_Colors, | |
201 | - ImageLoader_Opacity | |
202 | -}; | |
203 | - | |
204 | -enum { | |
205 | - ImageLoader_ResizeToPowersOfTwo = 0x1, | |
206 | - ImageLoader_CanUseDXTC = 0x2, | |
207 | - ImageLoader_LoadMipMaps = 0x4, | |
208 | - ImageLoader_LoadDXTC1AsDXTC3 = 0x8, | |
209 | - ImageLoader_ImageIsAlreadyPremultiplied = 0x10 | |
210 | -}; | |
211 | -// Returns whether or not the loading was successful | |
212 | -//bool LoadImageFromFile(ImageDescriptor& Img, FileSpecifier& File, int ImgMode, int flags, int maxSize = 0); | |
213 | - | |
214 | -uint32 *GetMipMapPtr(uint32 *pixels, int size, int level, int width, int height, int format); | |
215 | - | |
216 | -#endif | |
1 | +#ifndef _IMAGE_LOADER_ | |
2 | +#define _IMAGE_LOADER_ | |
3 | +/* | |
4 | + | |
5 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
6 | + and the "Aleph One" developers. | |
7 | + | |
8 | + This program is free software; you can redistribute it and/or modify | |
9 | + it under the terms of the GNU General Public License as published by | |
10 | + the Free Software Foundation; either version 3 of the License, or | |
11 | + (at your option) any later version. | |
12 | + | |
13 | + This program is distributed in the hope that it will be useful, | |
14 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | + GNU General Public License for more details. | |
17 | + | |
18 | + This license is contained in the file "COPYING", | |
19 | + which is included with this source code; it is available online at | |
20 | + http://www.gnu.org/licenses/gpl.html | |
21 | + | |
22 | + Image-Loader Interface File, | |
23 | + by Loren Petrich, | |
24 | + October 21, 2000 | |
25 | + | |
26 | + This file contains an image-descriptor object and a function for loading the image | |
27 | + from a file. | |
28 | + | |
29 | +*/ | |
30 | + | |
31 | +#include "DDS.h" | |
32 | +#include <vector> | |
33 | +#include "cseries.h" | |
34 | +#include "FileHandler.h" | |
35 | + | |
36 | +using namespace std; | |
37 | + | |
38 | +// Need an object to hold the read-in image. | |
39 | +class ImageDescriptor | |
40 | +{ | |
41 | + int Width; // along scanlines | |
42 | + int Height; // scanline to scanline | |
43 | + | |
44 | + double VScale; | |
45 | + double UScale; | |
46 | + | |
47 | + uint32 *Pixels; | |
48 | + int Size; | |
49 | + | |
50 | + int MipMapCount; | |
51 | + | |
52 | +public: | |
53 | + | |
54 | + bool IsPresent() const {return (Pixels != NULL); } | |
55 | + bool IsPremultiplied() const { return (IsPresent() ? PremultipliedAlpha : false); } | |
56 | + | |
57 | + bool LoadFromFile(FileSpecifier& File, int ImgMode, int flags, int actual_width = 0, int actual_height = 0, int maxSize = 0); | |
58 | + | |
59 | + // Size of level 0 image | |
60 | + int GetWidth() const {return Width;} | |
61 | + int GetHeight() const {return Height;} | |
62 | + int GetNumPixels() const {return Width*Height;} | |
63 | + | |
64 | + int GetMipMapCount() const { return MipMapCount; } | |
65 | + int GetTotalBytes() const { return Size; } | |
66 | + int GetBufferSize() const { return Size; } | |
67 | + int GetFormat() const { return Format; } | |
68 | + | |
69 | + double GetVScale() const { return VScale; } | |
70 | + double GetUScale() const { return UScale; } | |
71 | + | |
72 | + // Pixel accessors | |
73 | + uint32& GetPixel(int Horiz, int Vert) {return Pixels[Width*(Vert%Height) + (Horiz%Width)];} | |
74 | + uint32 *GetPixelBasePtr() {return Pixels;} | |
75 | + const uint32 *GetBuffer() const { return Pixels; } | |
76 | + uint32 *GetBuffer() { return Pixels; } | |
77 | + | |
78 | + uint32 *GetMipMapPtr(int Level); | |
79 | + const uint32 *GetMipMapPtr(int Level) const; | |
80 | + int GetMipMapSize(int level) const; | |
81 | + | |
82 | + // Reallocation | |
83 | + void Resize(int _Width, int _Height); | |
84 | + | |
85 | + // mipmappy operations | |
86 | + void Resize(int _Width, int _Height, int _TotalBytes); | |
87 | + | |
88 | + bool Minify(); | |
89 | + | |
90 | + bool MakeRGBA(); | |
91 | + bool MakeDXTC3(); | |
92 | + | |
93 | + void PremultiplyAlpha(); | |
94 | + bool PremultipliedAlpha; // public so find silhouette version can unset | |
95 | + | |
96 | + // Clearing | |
97 | + void Clear() | |
98 | + {Width = Height = Size = 0; delete []Pixels; Pixels = NULL;} | |
99 | + | |
100 | + ImageDescriptor(const ImageDescriptor &CopyFrom); | |
101 | + | |
102 | +ImageDescriptor(): Width(0), Height(0), VScale(1.0), UScale(1.0), Pixels(NULL), Size(0), PremultipliedAlpha(false) {} | |
103 | + | |
104 | + // asumes RGBA8 | |
105 | + ImageDescriptor(int width, int height, uint32 *pixels); | |
106 | + | |
107 | + enum ImageFormat { | |
108 | + RGBA8, | |
109 | + DXTC1, | |
110 | + DXTC3, | |
111 | + DXTC5, | |
112 | + Unknown | |
113 | + }; | |
114 | + | |
115 | + ~ImageDescriptor() | |
116 | + { | |
117 | + delete []Pixels; | |
118 | + Pixels = NULL; | |
119 | + } | |
120 | + | |
121 | +private: | |
122 | + bool LoadDDSFromFile(FileSpecifier& File, int flags, int actual_width = 0, int actual_height = 0, int maxSize = 0); | |
123 | + bool LoadMipMapFromFile(OpenedFile &File, int flags, int level, DDSURFACEDESC2 &ddsd, int skip); | |
124 | + bool SkipMipMapFromFile(OpenedFile &File, int flags, int level, DDSURFACEDESC2 &ddsd); | |
125 | + | |
126 | + ImageFormat Format; | |
127 | +}; | |
128 | + | |
129 | +template <typename T> | |
130 | +class copy_on_edit | |
131 | +{ | |
132 | +public: | |
133 | + copy_on_edit() : _original(NULL), _copy(NULL) { }; | |
134 | + | |
135 | + void set(const T* original) { | |
136 | + if (_copy) { | |
137 | + delete _copy; | |
138 | + _copy = NULL; | |
139 | + } | |
140 | + _original = original; | |
141 | + } | |
142 | + | |
143 | + void set(T* original) { | |
144 | + if (_copy) { | |
145 | + delete _copy; | |
146 | + _copy= NULL; | |
147 | + } | |
148 | + _original = original; | |
149 | + } | |
150 | + | |
151 | + | |
152 | + const T* get() { | |
153 | + if (_copy) | |
154 | + return (const T*) _copy; | |
155 | + else | |
156 | + return (const T*) _original; | |
157 | + } | |
158 | + | |
159 | + T* edit() { | |
160 | + if (!_original) { | |
161 | + return _copy; | |
162 | + } else { | |
163 | + if (!_copy) { | |
164 | + _copy = new T(*_original); | |
165 | + } | |
166 | + return _copy; | |
167 | + } | |
168 | + } | |
169 | + | |
170 | + // takes possession of copy | |
171 | + T* edit(T* copy) { | |
172 | + if (_copy) { | |
173 | + delete _copy; | |
174 | + } | |
175 | + _original = NULL; | |
176 | + _copy = copy; | |
177 | + return _copy; | |
178 | + } | |
179 | + | |
180 | + ~copy_on_edit() { | |
181 | + if (_copy) { | |
182 | + delete _copy; | |
183 | + _copy = NULL; | |
184 | + } | |
185 | + } | |
186 | + | |
187 | +private: | |
188 | + T* _original; | |
189 | + T* _copy; | |
190 | +}; | |
191 | + | |
192 | +typedef copy_on_edit<ImageDescriptor> ImageDescriptorManager; | |
193 | + | |
194 | + | |
195 | +// What to load: image colors (must be loaded first) | |
196 | +// or image opacity (replaces the default, which is 100% opaque everywhere). | |
197 | +// The image-opacity image must have the same size as the color image; | |
198 | +// it is interpreted as a grayscale image. | |
199 | +enum { | |
200 | + ImageLoader_Colors, | |
201 | + ImageLoader_Opacity | |
202 | +}; | |
203 | + | |
204 | +enum { | |
205 | + ImageLoader_ResizeToPowersOfTwo = 0x1, | |
206 | + ImageLoader_CanUseDXTC = 0x2, | |
207 | + ImageLoader_LoadMipMaps = 0x4, | |
208 | + ImageLoader_LoadDXTC1AsDXTC3 = 0x8, | |
209 | + ImageLoader_ImageIsAlreadyPremultiplied = 0x10 | |
210 | +}; | |
211 | +// Returns whether or not the loading was successful | |
212 | +//bool LoadImageFromFile(ImageDescriptor& Img, FileSpecifier& File, int ImgMode, int flags, int maxSize = 0); | |
213 | + | |
214 | +uint32 *GetMipMapPtr(uint32 *pixels, int size, int level, int width, int height, int format); | |
215 | + | |
216 | +#endif |
@@ -1,1119 +1,1119 @@ | ||
1 | -/* | |
2 | -RENDER.C | |
3 | - | |
4 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
5 | - and the "Aleph One" developers. | |
6 | - | |
7 | - This program is free software; you can redistribute it and/or modify | |
8 | - it under the terms of the GNU General Public License as published by | |
9 | - the Free Software Foundation; either version 3 of the License, or | |
10 | - (at your option) any later version. | |
11 | - | |
12 | - This program is distributed in the hope that it will be useful, | |
13 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | - GNU General Public License for more details. | |
16 | - | |
17 | - This license is contained in the file "COPYING", | |
18 | - which is included with this source code; it is available online at | |
19 | - http://www.gnu.org/licenses/gpl.html | |
20 | - | |
21 | -Thursday, September 8, 1994 1:58:20 PM (Jason') | |
22 | - | |
23 | -Friday, September 9, 1994 1:36:15 PM (Jason') | |
24 | - on the quads, in the sun. | |
25 | -Sunday, September 11, 1994 7:32:49 PM (Jason') | |
26 | - the clock on the 540 was wrong, yesterday was Saturday (not Friday). on quads again, but | |
27 | - back home now. something will draw before i go to bed tonight. dinner at the nile? | |
28 | -Tuesday, September 13, 1994 2:54:56 AM (Jason') | |
29 | - no fair!ムム itユs still monday, really. with the aid of some graphical debugging the clipping | |
30 | - all works now and iユm trying to have the entire floor/ceiling thing going tonight (the nile | |
31 | - was closed by the time i got around to taking a shower and heading out). | |
32 | -Friday, September 16, 1994 4:06:17 AM (Jason') | |
33 | - walls, floors and ceilings texture, wobble, etc. contemplating objects ... maybe this will | |
34 | - work after all. | |
35 | -Monday, September 19, 1994 11:03:49 AM (Jason') | |
36 | - unified xz_clip_vertical_polygon() and z_clip_horizontal_polygon() to get rid of the last | |
37 | - known whitespace problem. canユt wait to see what others i have. objects now respect the | |
38 | - clipping windows of all nodes they cross. | |
39 | -Monday, October 24, 1994 4:35:38 PM (Jason) | |
40 | - fixed render sorting problem with objects of equal depth (i.e., parasitic objects). | |
41 | -Tuesday, October 25, 1994 5:14:27 PM (Jason') | |
42 | - fixed object sort order with respect to nodes and height. | |
43 | -Wednesday, October 26, 1994 3:18:59 PM (Jason) | |
44 | - fixed half of the object sort order back so that it worked: in order to correctly handle | |
45 | - objects below the viewer projecting into higher polygons we need to sort objects inside | |
46 | - nodes (to be draw after their walls and ceilings but before their floors). | |
47 | -Wednesday, November 2, 1994 3:49:57 PM (Jason) | |
48 | - the bottom panel of short split sides sometimes takes on the ceiling lightsource. | |
49 | -Tuesday, November 8, 1994 5:29:12 PM (Jason') | |
50 | - implemented new transfer modes: _slide, _wander. _render_effect_earthquake doesnユt work | |
51 | - yet because the player can shake behind his own shape. | |
52 | -Thursday, December 15, 1994 12:15:55 AM (Jason) | |
53 | - the object depth sort order problem ocurrs with multiple objects in the same polygon, | |
54 | - is some function of relative depths and overlap, and does not seem to involve objects at | |
55 | - the same depth. also, it seems to sort many further objects in front of a single closer | |
56 | - one. | |
57 | -Monday, January 23, 1995 6:53:26 AM (Jason') | |
58 | - the way to fix the render object sorting problem is to ignore overlap and sort everything | |
59 | - by depth, regardless. imagine: two, far, non-overlapping objects; by the old algorithm their | |
60 | - drawing order is irrelevant. when a closer object which overlaps both of them is sorted, it | |
61 | - only attempts to lie in front of the closest of the two (leaving the farthest one in an | |
62 | - uncertain position). unfortunately this will cause us to do unnecessary promotion and might | |
63 | - look stupid. | |
64 | -Sunday, March 26, 1995 12:57:39 AM (Jason') | |
65 | - media modifications for marathon2; the object sort order problem still exists (the above | |
66 | - solution indeed looked stupid). | |
67 | -Thursday, March 30, 1995 11:23:35 PM (Jason') | |
68 | - tried to fix object sort problem by attempting to assure that objects are always drawn in | |
69 | - depth-order within a node. | |
70 | -Monday, June 5, 1995 8:37:42 AM (Jason) | |
71 | - blood and fire (baby). | |
72 | - | |
73 | -Jan 30, 2000 (Loren Petrich): | |
74 | - Added some typecasts | |
75 | - Increased MAXIMUM_NODE_ALIASES to 32 | |
76 | - Added an "assert" for when DEBUG is off in aliases-building section in sort_render_tree(). | |
77 | - | |
78 | -Feb 1, 2000 (Loren Petrich): | |
79 | - Added growable list of node aliases; replaced static-list node-alias code | |
80 | - | |
81 | -Feb 4, 2000 (Loren Petrich): | |
82 | - Changed halt() to assert(false) for better debugging | |
83 | - | |
84 | -Feb 5, 2000 (Loren Petrich): | |
85 | - Added growable lists of nodes and also clips for endpoints and lines. | |
86 | - | |
87 | -Feb 6, 2000 (Loren Petrich): | |
88 | - Doing initial allocations of the growable lists of various quantities as a defensive measure | |
89 | - against memory leaks that seem to occur. | |
90 | - | |
91 | -Feb 9, 2000 (Loren Petrich): | |
92 | - Suppressed ambiguous-clip-flag debugging statement; | |
93 | - it gets activated only for excessively distant objects | |
94 | - | |
95 | -Feb 10, 2000 (Loren Petrich): | |
96 | - Added dynamic-limits setting of MAXIMUM_RENDER_OBJECTS | |
97 | - | |
98 | -Feb 14, 2000 (Loren Petrich): | |
99 | - Added test for other-side polygon to LINE_IS_TRANSPARENT() check in next_polygon_along_line() | |
100 | - | |
101 | -Feb 16, 2000 (Loren Petrich): | |
102 | - Put in handling of overflow digits for the purpose of doing long distance correctly; | |
103 | - also turning several horizontal-coordinate short integers into long ones. | |
104 | - | |
105 | -Feb 17, 2000 (Loren Petrich): | |
106 | - Made the sprites long-distance-friendly; there is a bug where they flip around when they | |
107 | - go past half the world size, but that's a short-integer wraparound, and the relevant routine | |
108 | - is in map.c. | |
109 | - | |
110 | -Feb 18, 2000 (Loren Petrich): | |
111 | - Added support for conditional display of weapons-in-hand; so as to support third-person | |
112 | - as well as first-person view. | |
113 | - | |
114 | -Feb 21, 2000 (Loren Petrich): | |
115 | - Idiot-proofed next_polygon_along_line(), making it quit a loop if it searches a whole circle. | |
116 | - | |
117 | -Feb 24, 2000 (Loren Petrich): | |
118 | - Added animated-texture support | |
119 | - | |
120 | -Mar 3, 2000 (Loren Petrich): | |
121 | - Set view to normal in initialize_view_data(); | |
122 | - squashed persistent-extravision bug. | |
123 | - | |
124 | -Mar 5, 2000 (Loren Petrich): | |
125 | - Moved extravision-persistence bug fix out of this file. | |
126 | - | |
127 | -Mar 9, 2000 (Loren Petrich): | |
128 | - Sorted the nodes by polygon index in sort_render_tree() and used them to speed up searches | |
129 | - for nodes with the same polygon index; maps with slowed-down visibility calculations, | |
130 | - such as Desla, can become twice as fast. | |
131 | - | |
132 | -Mar 12, 2000 (Loren Petrich): | |
133 | - Added OpenGL support | |
134 | - | |
135 | -Mar 14, 2000 (Loren Petrich): | |
136 | - Modified data transmitted to OpenGL renderer; it's now collection/color/frame for | |
137 | - both walls and sprites. Also added transmission of view data. | |
138 | - | |
139 | -Mar 24, 2000 (Loren Petrich): | |
140 | - Added landscape_yaw calculation; this is the yaw of the landscapes' left edges | |
141 | - | |
142 | -Mar 30, 2000 (Loren Petrich): | |
143 | - Inspired by Rhys Hill's work, I've created a second tree to contain the visibility nodes; | |
144 | - in addition to their visibility tree, they have a polygon-sort tree. | |
145 | - This tree is implemented by setting up some additional members of node_data | |
146 | - for indicating its structure; there are members for polygon >, polygon <, | |
147 | - and the next member of a chain that shares polygon-index values. | |
148 | - As a result, the node-aliases list can now be abolished once and for all. | |
149 | - | |
150 | -Jun 11, 2000 (Loren Petrich): | |
151 | - Added support for see-through liquids; this requires several changes. | |
152 | - The rendering of each map polygon had to be changed so that there would be a | |
153 | - separate liquid surface; it would no longer replace the floor or the ceiling. | |
154 | - Next, the inhabitant objects had to be done in two passes, one other side, and one view side. | |
155 | - Also, whether there is void on the other side had to be indicated, so that | |
156 | - waterfalls and the like may look right. | |
157 | - | |
158 | -Jun 28, 2000 (Loren Petrich): | |
159 | - Fixed Aaron Davies bug; if a polygon is completely below a liquid, it will not be rendered | |
160 | - if the viewpoint is above the liquid; the bug was that it was not rendered if the viewpoint | |
161 | - was below the liquid. This only happened if semitransparent liquid surfaces was turned off. | |
162 | - | |
163 | -Jul 10, 2000 (Loren Petrich): | |
164 | - Fixed liquid visibility bug in render_tree() that happens when liquid surfaces are not semitransparent; | |
165 | - rendering is skipped if the viewpoint is under a liquid and the polygon is high and dry, | |
166 | - or else if the viewpoint is above a liquid and the polygon is submerged. | |
167 | - | |
168 | -Jul 17, 2000 (Loren Petrich): | |
169 | - Suppressed view-effect resetting in initialize_view_data(), | |
170 | - in order to make teleport view-stretching work correctly. | |
171 | - | |
172 | -Aug 9, 2000 (Loren Petrich): | |
173 | - Moved most of the rendering code here into separate files with these classes: | |
174 | - | |
175 | - RenderVisTreeClass -- | |
176 | - creates the visibility tree (which polygons can be seen from which other ones) | |
177 | - RenderSortPolyClass -- | |
178 | - uses that tree to sort the polygons into appropriate depth order | |
179 | - RenderPlaceObjsClass -- | |
180 | - finds which objects are visible and places them into appropriately sorted order | |
181 | - RenderRasterize -- | |
182 | - handles the clipping of each object and requests those objects' rasterization | |
183 | - | |
184 | - Added a rasterizer class; currently, it does everything from the base class, | |
185 | - though what it does will be moved into subclasses. | |
186 | - | |
187 | -Sep 2, 2000 (Loren Petrich): | |
188 | - Added some idiot-proofing, since the shapes accessor now returns NULL for nonexistent bitmaps | |
189 | - | |
190 | -Nov 12, 2000 (Loren Petrich): | |
191 | - Added automap reset before rendering | |
192 | - | |
193 | -Nov 29, 2000 (Loren Petrich): | |
194 | - Made teleport static/fold effect optional | |
195 | - | |
196 | -Jan 17, 2001 (Loren Petrich): | |
197 | - Added vertical flipping | |
198 | -*/ | |
199 | - | |
200 | - | |
201 | -#ifdef QUICKDRAW_DEBUG | |
202 | -#include "macintosh_cseries.h" | |
203 | -#else | |
204 | -#include "cseries.h" | |
205 | -#endif | |
206 | - | |
207 | -#include "map.h" | |
208 | -#include "render.h" | |
209 | -#include "interface.h" | |
210 | -#include "lightsource.h" | |
211 | -#include "media.h" | |
212 | -#include "weapons.h" | |
213 | - | |
214 | -// LP additions | |
215 | -#include "dynamic_limits.h" | |
216 | -#include "AnimatedTextures.h" | |
217 | -#ifdef HAVE_OPENGL | |
218 | -#include "OGL_Render.h" | |
219 | -#endif | |
220 | - | |
221 | -#ifdef QUICKDRAW_DEBUG | |
222 | -#include "shell.h" | |
223 | -extern WindowPtr screen_window; | |
224 | -#endif | |
225 | - | |
226 | -#include <math.h> | |
227 | -#include <string.h> | |
228 | -#include <stdlib.h> | |
229 | - | |
230 | -// LP additions for decomposition of this code: | |
231 | -#include "RenderVisTree.h" | |
232 | -#include "RenderSortPoly.h" | |
233 | -#include "RenderPlaceObjs.h" | |
234 | -#include "RenderRasterize.h" | |
235 | -#include "Rasterizer_SW.h" | |
236 | -#ifdef HAVE_OPENGL | |
237 | -#include "Rasterizer_OGL.h" | |
238 | -#include "RenderRasterize_Shader.h" | |
239 | -#include "Rasterizer_Shader.h" | |
240 | -#endif | |
241 | -#include "preferences.h" | |
242 | -#include "screen.h" | |
243 | - | |
244 | -#ifdef env68k | |
245 | -#pragma segment render | |
246 | -#endif | |
247 | - | |
248 | -/* use native alignment */ | |
249 | -#if defined (powerc) || defined (__powerc) | |
250 | -#pragma options align=power | |
251 | -#endif | |
252 | - | |
253 | -/* | |
254 | -//render transparent walls (if a bit is set or if the transparent texture is non-NULL?) | |
255 | -//use side lightsources instead of taking them from their polygons | |
256 | -//respect dark side bit (darken light intensity by k) | |
257 | -//fix solid/opaque endpoint confusion (solidity does not imply opacity) | |
258 | - | |
259 | -there exists a problem where an object can overlap into a polygon which is clipped by something | |
260 | - behind the object but that will clip the object because clip windows are subtractive; how | |
261 | - is this solved? | |
262 | -itユs still possible to get ambiguous clip flags, usually in very narrow (e.g., 1 pixel) windows | |
263 | -the renderer has a maximum range beyond which it shits bricks yet which it allows to be exceeded | |
264 | -itユs still possible, especially in high-res full-screen, for points to end up (slightly) off | |
265 | - the screen (usually discarding these has no noticable effect on the scene) | |
266 | -whitespace results when two adjacent polygons are clipped to different vertical windows. this | |
267 | - is not trivially solved with the current implementation, and may be acceptable (?) | |
268 | - | |
269 | -//build_base_polygon_index_list() should discard lower polygons for objects above the viewer and | |
270 | -// higher polygons for objects below the viewer because we certainly donユt sort objects | |
271 | -// correctly in these cases | |
272 | -//in strange cases, objects are sorted out of order. this seems to involve players in some way | |
273 | -// (i.e., parasitic objects). | |
274 | -*/ | |
275 | - | |
276 | -/* ---------- constants */ | |
277 | - | |
278 | -#define EXPLOSION_EFFECT_RANGE (WORLD_ONE/12) | |
279 | - | |
280 | -/* ---------- clip buffer */ | |
281 | -// Not used for anything | |
282 | -#define CLIP_INDEX_BUFFER_SIZE 4096 | |
283 | - | |
284 | -vector<uint16> RenderFlagList; | |
285 | - | |
286 | -// uint16 *render_flags; | |
287 | - | |
288 | -// LP additions: decomposition of the rendering code into various objects | |
289 | - | |
290 | -static RenderVisTreeClass RenderVisTree; // Visibility-tree object | |
291 | -static RenderSortPolyClass RenderSortPoly; // Polygon-sorting object | |
292 | -static RenderPlaceObjsClass RenderPlaceObjs; // Object-placement object | |
293 | -static RenderRasterizerClass Render_Classic; // Clipping and rasterization class | |
294 | - | |
295 | -static Rasterizer_SW_Class Rasterizer_SW; // Software rasterizer | |
296 | -#ifdef HAVE_OPENGL | |
297 | -static Rasterizer_OGL_Class Rasterizer_OGL; // OpenGL rasterizer | |
298 | -static Rasterizer_Shader_Class Rasterizer_Shader; // Shader rasterizer | |
299 | -static RenderRasterize_Shader Render_Shader; // Shader clipping and rasterization class | |
300 | -#endif | |
301 | - | |
302 | -void OGL_Rasterizer_Init() { | |
303 | - | |
304 | -#ifdef HAVE_OPENGL | |
305 | - if (graphics_preferences->screen_mode.acceleration == _shader_acceleration) | |
306 | - Render_Shader.setupGL(); | |
307 | -#endif | |
308 | -} | |
309 | - | |
310 | -/* ---------- private prototypes */ | |
311 | - | |
312 | -static void update_view_data(struct view_data *view); | |
313 | -static void update_render_effect(struct view_data *view); | |
314 | -static void shake_view_origin(struct view_data *view, world_distance delta); | |
315 | - | |
316 | -static void render_viewer_sprite_layer(view_data *view, RasterizerClass *RasPtr); | |
317 | -static void position_sprite_axis(short *x0, short *x1, short scale_width, short screen_width, | |
318 | - short positioning_mode, _fixed position, bool flip, world_distance world_left, world_distance world_right); | |
319 | - | |
320 | - | |
321 | -#ifdef QUICKDRAW_DEBUG | |
322 | -static void debug_flagged_points(flagged_world_point2d *points, short count); | |
323 | -static void debug_flagged_points3d(flagged_world_point3d *points, short count); | |
324 | -static void debug_vector(world_vector2d *v); | |
325 | -static void debug_x_line(world_distance x); | |
326 | -#endif | |
327 | - | |
328 | -/* ---------- code */ | |
329 | - | |
330 | -void allocate_render_memory( | |
331 | - void) | |
332 | -{ | |
333 | - assert(NUMBER_OF_RENDER_FLAGS<=16); | |
334 | - RenderFlagList.resize(RENDER_FLAGS_BUFFER_SIZE); | |
335 | - | |
336 | - // LP addition: check out pointer-arithmetic hack | |
337 | - assert(sizeof(void *) == sizeof(POINTER_DATA)); | |
338 | - | |
339 | - // LP change: do max allocation | |
340 | - RenderVisTree.Resize(MAXIMUM_ENDPOINTS_PER_MAP,MAXIMUM_LINES_PER_MAP); | |
341 | - RenderSortPoly.Resize(MAXIMUM_POLYGONS_PER_MAP); | |
342 | - | |
343 | - // LP change: set up pointers | |
344 | - RenderSortPoly.RVPtr = &RenderVisTree; | |
345 | - RenderPlaceObjs.RVPtr = &RenderVisTree; | |
346 | - RenderPlaceObjs.RSPtr = &RenderSortPoly; | |
347 | -#ifdef HAVE_OPENGL | |
348 | - Render_Classic.RSPtr = Render_Shader.RSPtr = &RenderSortPoly; | |
349 | -#else | |
350 | - Render_Classic.RSPtr = &RenderSortPoly; | |
351 | -#endif | |
352 | -} | |
353 | - | |
354 | -/* just in case anyone was wondering, standard_screen_width will usually be the same as | |
355 | - screen_width. the renderer assumes that the given field_of_view matches the standard | |
356 | - width provided (so if the actual width provided is larger, you'll be able to see more; | |
357 | - if it's smaller you'll be able to see less). this allows the destination bitmap to not | |
358 | - only grow and shrink while maintaining a constant aspect ratio, but to also change in | |
359 | - geometry without effecting the image being projected onto it. if you don't understand | |
360 | - this, pass standard_width==width */ | |
361 | -void initialize_view_data( | |
362 | - struct view_data *view) | |
363 | -{ | |
364 | - double two_pi= 8.0*atan(1.0); | |
365 | - double half_cone= view->field_of_view*(two_pi/360.0)/2; | |
366 | - /* half_cone needs to be extended for non oblique perspective projection (gluPerspective). | |
367 | - this is required because the viewing angle is different for about the same field of view */ | |
368 | - if (graphics_preferences->screen_mode.acceleration == _shader_acceleration) | |
369 | - half_cone= (view->field_of_view * 1.3)*(two_pi/360.0)/2; | |
370 | - | |
371 | - double adjusted_half_cone= View_FOV_FixHorizontalNotVertical() ? | |
372 | - half_cone : | |
373 | - atan(view->screen_width*tan(half_cone)/view->standard_screen_width); | |
374 | - double world_to_screen; | |
375 | - | |
376 | - view->half_screen_width= view->screen_width/2; | |
377 | - view->half_screen_height= view->screen_height/2; | |
378 | - | |
379 | - /* if thereユs a round-off error in half_cone, we want to make the cone too big (so when we clip | |
380 | - lines ヤto the edge of the screenユ theyユre actually off the screen, thus +1.0) */ | |
381 | - view->half_cone= (angle) (adjusted_half_cone*((double)NUMBER_OF_ANGLES)/two_pi+1.0); | |
382 | - | |
383 | - // LP change: find the adjusted yaw for the landscapes; | |
384 | - // this is the effective yaw value for the left edge. | |
385 | - // A landscape rotation can also be added if desired. | |
386 | - view->landscape_yaw = view->yaw - view->half_cone; | |
387 | - | |
388 | - /* calculate world_to_screen; we could calculate this with standard_screen_width/2 and | |
389 | - the old half_cone and get the same result */ | |
390 | - world_to_screen= view->half_screen_width/tan(adjusted_half_cone); | |
391 | - view->world_to_screen_x= view->real_world_to_screen_x= (short) ((world_to_screen/view->horizontal_scale)+0.5); | |
392 | - view->world_to_screen_y= view->real_world_to_screen_y= (short) ((world_to_screen/view->vertical_scale)+0.5); | |
393 | - | |
394 | - /* calculate the vertical cone angle; again, overflow instead of underflow when rounding */ | |
395 | - view->half_vertical_cone= (angle) (NUMBER_OF_ANGLES*atan(((double)view->half_screen_height*view->vertical_scale)/world_to_screen)/two_pi+1.0); | |
396 | - | |
397 | - /* calculate left edge vector */ | |
398 | - view->untransformed_left_edge.i= view->world_to_screen_x; | |
399 | - view->untransformed_left_edge.j= - view->half_screen_width; | |
400 | - | |
401 | - /* calculate right edge vector (negative, so it clips in the right direction) */ | |
402 | - view->untransformed_right_edge.i= - view->world_to_screen_x; | |
403 | - view->untransformed_right_edge.j= - view->half_screen_width; | |
404 | - | |
405 | - /* reset any active effects */ | |
406 | - // LP: this is now called in render_screen(), so we need to disable the initializing | |
407 | -} | |
408 | - | |
409 | -/* origin,origin_polygon_index,yaw,pitch,roll,etc. have probably changed since last call */ | |
410 | -void render_view( | |
411 | - struct view_data *view, | |
412 | - struct bitmap_definition *destination) | |
413 | -{ | |
414 | - update_view_data(view); | |
415 | - | |
416 | - /* clear the render flags */ | |
417 | - objlist_clear(render_flags, RENDER_FLAGS_BUFFER_SIZE); | |
418 | - | |
419 | - ResetOverheadMap(); | |
420 | -/* | |
421 | -#ifdef AUTOMAP_DEBUG | |
422 | - memset(automap_lines, 0, (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0)*sizeof(byte))); | |
423 | - memset(automap_polygons, 0, (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0)*sizeof(byte))); | |
424 | -#endif | |
425 | -*/ | |
426 | - | |
427 | - if(view->terminal_mode_active) | |
428 | - { | |
429 | - /* Render the computer interface. */ | |
430 | - render_computer_interface(view); | |
431 | - } | |
432 | - else | |
433 | - { | |
434 | - // LP: the render objects have a pointer to the current view in them, | |
435 | - // so that one can get rid of redundant references to it in them. | |
436 | - | |
437 | - // LP: now from the visibility-tree class | |
438 | - /* build the render tree, regardless of map mode, so the automap updates while active */ | |
439 | - RenderVisTree.view = view; | |
440 | - RenderVisTree.build_render_tree(); | |
441 | - | |
442 | - /* do something complicated and difficult to explain */ | |
443 | - if (!view->overhead_map_active || map_is_translucent()) | |
444 | - { | |
445 | - // LP: now from the polygon-sorter class | |
446 | - /* sort the render tree (so we have a depth-ordering of polygons) and accumulate | |
447 | - clipping information for each polygon */ | |
448 | - RenderSortPoly.view = view; | |
449 | - RenderSortPoly.sort_render_tree(); | |
450 | - | |
451 | - // LP: now from the object-placement class | |
452 | - /* build the render object list by looking at the sorted render tree */ | |
453 | - RenderPlaceObjs.view = view; | |
454 | - RenderPlaceObjs.build_render_object_list(); | |
455 | - | |
456 | - // LP addition: set the current rasterizer to whichever is appropriate here | |
457 | - RasterizerClass *RasPtr; | |
458 | -#ifdef HAVE_OPENGL | |
459 | - if (OGL_IsActive()) | |
460 | - RasPtr = (graphics_preferences->screen_mode.acceleration == _shader_acceleration) ? &Rasterizer_Shader : &Rasterizer_OGL; | |
461 | - else | |
462 | - { | |
463 | -#endif | |
464 | - // The software renderer needs this but the OpenGL one doesn't... | |
465 | - Rasterizer_SW.screen = destination; | |
466 | - RasPtr = &Rasterizer_SW; | |
467 | -#ifdef HAVE_OPENGL | |
468 | - } | |
469 | -#endif | |
470 | - | |
471 | - // Set its view: | |
472 | - RasPtr->SetView(*view); | |
473 | - | |
474 | - // Start rendering main view | |
475 | - RasPtr->Begin(); | |
476 | - | |
477 | - // LP: now from the clipping/rasterizer class | |
478 | -#ifdef HAVE_OPENGL | |
479 | - RenderRasterizerClass *RenPtr = (graphics_preferences->screen_mode.acceleration == _shader_acceleration) ? &Render_Shader : &Render_Classic; | |
480 | -#else | |
481 | - RenderRasterizerClass *RenPtr = &Render_Classic; | |
482 | -#endif | |
483 | - /* render the object list, back to front, doing clipping on each surface before passing | |
484 | - it to the texture-mapping code */ | |
485 | - RenPtr->view = view; | |
486 | - RenPtr->RasPtr = RasPtr; | |
487 | - RenPtr->render_tree(); | |
488 | - | |
489 | - // LP: won't put this into a separate class | |
490 | - /* render the playerユs weapons, etc. */ | |
491 | - render_viewer_sprite_layer(view, RasPtr); | |
492 | - | |
493 | - // Finish rendering main view | |
494 | - RasPtr->End(); | |
495 | - } | |
496 | - | |
497 | - if (view->overhead_map_active) | |
498 | - { | |
499 | - /* if the overhead map is active, render it */ | |
500 | - render_overhead_map(view); | |
501 | - } | |
502 | - } | |
503 | -} | |
504 | - | |
505 | -void start_render_effect( | |
506 | - struct view_data *view, | |
507 | - short effect) | |
508 | -{ | |
509 | - view->effect= effect; | |
510 | - view->effect_phase= NONE; | |
511 | -} | |
512 | - | |
513 | -/* ---------- private code */ | |
514 | - | |
515 | -static void update_view_data( | |
516 | - struct view_data *view) | |
517 | -{ | |
518 | - angle theta; | |
519 | - | |
520 | - // LP change: doing all the FOV changes here: | |
521 | - View_AdjustFOV(view->field_of_view,view->target_field_of_view); | |
522 | - | |
523 | - if (view->effect==NONE) | |
524 | - { | |
525 | - view->world_to_screen_x= view->real_world_to_screen_x; | |
526 | - view->world_to_screen_y= view->real_world_to_screen_y; | |
527 | - } | |
528 | - else | |
529 | - { | |
530 | - update_render_effect(view); | |
531 | - } | |
532 | - | |
533 | - view->untransformed_left_edge.i= view->world_to_screen_x; | |
534 | - view->untransformed_right_edge.i= - view->world_to_screen_x; | |
535 | - | |
536 | - /* calculate world_to_screen_y*tan(pitch) */ | |
537 | - view->dtanpitch= (view->world_to_screen_y*sine_table[view->pitch])/cosine_table[view->pitch]; | |
538 | - | |
539 | - /* calculate left cone vector */ | |
540 | - theta= NORMALIZE_ANGLE(view->yaw-view->half_cone); | |
541 | - view->left_edge.i= cosine_table[theta], view->left_edge.j= sine_table[theta]; | |
542 | - | |
543 | - /* calculate right cone vector */ | |
544 | - theta= NORMALIZE_ANGLE(view->yaw+view->half_cone); | |
545 | - view->right_edge.i= cosine_table[theta], view->right_edge.j= sine_table[theta]; | |
546 | - | |
547 | - /* calculate top cone vector (negative to clip the right direction) */ | |
548 | - view->top_edge.i= - view->world_to_screen_y; | |
549 | - view->top_edge.j= - (view->half_screen_height + view->dtanpitch); /* ==k */ | |
550 | - | |
551 | - /* calculate bottom cone vector */ | |
552 | - view->bottom_edge.i= view->world_to_screen_y; | |
553 | - view->bottom_edge.j= - view->half_screen_height + view->dtanpitch; /* ==k */ | |
554 | - | |
555 | - /* if weユre sitting on one of the endpoints in our origin polygon, move us back slightly (ア1) into | |
556 | - that polygon. when we split rays weユre assuming that weユll never pass through a given | |
557 | - vertex in different directions (because if we do the tree becomes a graph) but when | |
558 | - we start on a vertex this can happen. this is a destructive modification of the origin. */ | |
559 | - { | |
560 | - short i; | |
561 | - struct polygon_data *polygon= get_polygon_data(view->origin_polygon_index); | |
562 | - | |
563 | - for (i= 0;i<polygon->vertex_count;++i) | |
564 | - { | |
565 | - struct world_point2d *vertex= &get_endpoint_data(polygon->endpoint_indexes[i])->vertex; | |
566 | - | |
567 | - if (vertex->x==view->origin.x && vertex->y==view->origin.y) | |
568 | - { | |
569 | - world_point2d *ccw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_LOW(i, polygon->vertex_count-1)])->vertex; | |
570 | - world_point2d *cw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_HIGH(i, polygon->vertex_count-1)])->vertex; | |
571 | - world_vector2d inset_vector; | |
572 | - | |
573 | - inset_vector.i= (ccw_vertex->x-vertex->x) + (cw_vertex->x-vertex->x); | |
574 | - inset_vector.j= (ccw_vertex->y-vertex->y) + (cw_vertex->y-vertex->y); | |
575 | - view->origin.x+= SGN(inset_vector.i); | |
576 | - view->origin.y+= SGN(inset_vector.j); | |
577 | - | |
578 | - break; | |
579 | - } | |
580 | - } | |
581 | - | |
582 | - /* determine whether we are under or over the media boundary of our polygon; we will see all | |
583 | - other media boundaries from this orientation (above or below) or fail to draw them. */ | |
584 | - if (polygon->media_index==NONE) | |
585 | - { | |
586 | - view->under_media_boundary= false; | |
587 | - } | |
588 | - else | |
589 | - { | |
590 | - struct media_data *media= get_media_data(polygon->media_index); | |
591 | - | |
592 | - // LP change: idiot-proofing | |
593 | - if (media) | |
594 | - { | |
595 | - view->under_media_boundary= UNDER_MEDIA(media, view->origin.z); | |
596 | - view->under_media_index= polygon->media_index; | |
597 | - } else { | |
598 | - view->under_media_boundary= false; | |
599 | - } | |
600 | - } | |
601 | - } | |
602 | -} | |
603 | - | |
604 | -static void update_render_effect( | |
605 | - struct view_data *view) | |
606 | -{ | |
607 | - short effect= view->effect; | |
608 | - short phase= view->effect_phase==NONE ? 0 : (view->effect_phase+view->ticks_elapsed); | |
609 | - short period; | |
610 | - | |
611 | - view->effect_phase= phase; | |
612 | - | |
613 | - switch (effect) | |
614 | - { | |
615 | - // LP change: suppressed all the FOV changes | |
616 | - case _render_effect_fold_in: case _render_effect_fold_out: period= TICKS_PER_SECOND/2; break; | |
617 | - case _render_effect_explosion: period= TICKS_PER_SECOND; break; | |
618 | - default: | |
619 | - assert(false); | |
620 | - break; | |
621 | - } | |
622 | - | |
623 | - if (phase>period) | |
624 | - { | |
625 | - view->effect= NONE; | |
626 | - } | |
627 | - else | |
628 | - { | |
629 | - switch (effect) | |
630 | - { | |
631 | - case _render_effect_explosion: | |
632 | - shake_view_origin(view, EXPLOSION_EFFECT_RANGE - ((EXPLOSION_EFFECT_RANGE/2)*phase)/period); | |
633 | - break; | |
634 | - | |
635 | - case _render_effect_fold_in: | |
636 | - phase= period-phase; | |
637 | - case _render_effect_fold_out: | |
638 | - /* calculate world_to_screen based on phase */ | |
639 | - view->world_to_screen_x= view->real_world_to_screen_x + (4*view->real_world_to_screen_x*phase)/period; | |
640 | - view->world_to_screen_y= view->real_world_to_screen_y - (view->real_world_to_screen_y*phase)/(period+period/4); | |
641 | - break; | |
642 | - } | |
643 | - } | |
644 | -} | |
645 | - | |
646 | - | |
647 | -/* ---------- transfer modes */ | |
648 | - | |
649 | -/* given a transfer mode and phase, cause whatever changes it should cause to a rectangle_definition | |
650 | - structure */ | |
651 | -void instantiate_rectangle_transfer_mode( | |
652 | - view_data *view, | |
653 | - rectangle_definition *rectangle, | |
654 | - short transfer_mode, | |
655 | - _fixed transfer_phase) | |
656 | -{ | |
657 | - // For the 3D-model code | |
658 | - rectangle->HorizScale = 1; | |
659 | - | |
660 | - switch (transfer_mode) | |
661 | - { | |
662 | - case _xfer_invisibility: | |
663 | - case _xfer_subtle_invisibility: | |
664 | - if (view->shading_mode!=_shading_infravision) | |
665 | - { | |
666 | - rectangle->transfer_mode= _tinted_transfer; | |
667 | - rectangle->shading_tables= get_global_shading_table(); | |
668 | - rectangle->transfer_data= (transfer_mode==_xfer_invisibility) ? 0x000f : 0x0018; | |
669 | - break; | |
670 | - } | |
671 | - /* if we have infravision, fall through to _textured_transfer (i see you...) */ | |
672 | - case _xfer_normal: | |
673 | - rectangle->transfer_mode= _textured_transfer; | |
674 | - break; | |
675 | - | |
676 | - case _xfer_static: | |
677 | - case _xfer_50percent_static: | |
678 | - rectangle->transfer_mode= _static_transfer; | |
679 | - rectangle->transfer_data= (transfer_mode==_xfer_static) ? 0x0000 : 0x8000; | |
680 | - break; | |
681 | - | |
682 | - case _xfer_fade_out_static: | |
683 | - rectangle->transfer_mode= _static_transfer; | |
684 | - rectangle->transfer_data= transfer_phase; | |
685 | - break; | |
686 | - | |
687 | - case _xfer_pulsating_static: | |
688 | - rectangle->transfer_mode= _static_transfer; | |
689 | - rectangle->transfer_data= 0x8000+((0x6000*sine_table[FIXED_INTEGERAL_PART(transfer_phase*NUMBER_OF_ANGLES)])>>TRIG_SHIFT); | |
690 | - break; | |
691 | - | |
692 | - case _xfer_fold_in: | |
693 | - transfer_phase= FIXED_ONE-transfer_phase; /* do everything backwards */ | |
694 | - case _xfer_fold_out: | |
695 | - if (View_DoStaticEffect()) | |
696 | - { | |
697 | - // Corrected the teleport shrinkage so that the sprite/object | |
698 | - // shrinks to its object position and not to its sprite center | |
699 | - short delta0= FIXED_INTEGERAL_PART(((rectangle->xc-rectangle->x0)-1)*transfer_phase); | |
700 | - short delta1= FIXED_INTEGERAL_PART(((rectangle->x1-rectangle->xc)-1)*transfer_phase); | |
701 | - // short delta= FIXED_INTEGERAL_PART((((rectangle->x1-rectangle->x0)>>1)-1)*transfer_phase); | |
702 | - | |
703 | - rectangle->transfer_mode= _static_transfer; | |
704 | - rectangle->transfer_data= (transfer_phase>>1); | |
705 | - rectangle->x0+= delta0; | |
706 | - rectangle->x1-= delta1; | |
707 | - rectangle->HorizScale = 1 - float(transfer_phase)/float(FIXED_ONE); | |
708 | - } | |
709 | - else | |
710 | - rectangle->transfer_mode= _textured_transfer; | |
711 | - break; | |
712 | - | |
713 | -#if 0 | |
714 | - case _xfer_fade_out_to_black: | |
715 | - rectangle->shading_tables= get_global_shading_table(); | |
716 | - if (transfer_phase<FIXED_ONE_HALF) | |
717 | - { | |
718 | - /* fade to black */ | |
719 | - rectangle->ambient_shade= (rectangle->ambient_shade*(transfer_phase-FIXED_ONE_HALF))>>(FIXED_FRACTIONAL_BITS-1); | |
720 | - rectangle->transfer_mode= _textured_transfer; | |
721 | - } | |
722 | - else | |
723 | - { | |
724 | - /* vanish */ | |
725 | - rectangle->transfer_mode= _tinted_transfer; | |
726 | - rectangle->transfer_data= 0x1f - ((0x1f*(FIXED_ONE_HALF-transfer_phase))>>(FIXED_FRACTIONAL_BITS-1)); | |
727 | - } | |
728 | - break; | |
729 | -#endif | |
730 | - | |
731 | - // LP change: made an unrecognized mode act like normal | |
732 | - default: | |
733 | - rectangle->transfer_mode= _textured_transfer; | |
734 | - break; | |
735 | - } | |
736 | -} | |
737 | - | |
738 | -/* given a transfer mode and phase, cause whatever changes it should cause to a polygon_definition | |
739 | - structure (unfortunately we need to know whether this is a horizontal or vertical polygon) */ | |
740 | -void instantiate_polygon_transfer_mode( | |
741 | - struct view_data *view, | |
742 | - struct polygon_definition *polygon, | |
743 | - short transfer_mode, | |
744 | - bool horizontal) | |
745 | -{ | |
746 | - world_distance x0, y0; | |
747 | - world_distance vector_magnitude; | |
748 | - short alternate_transfer_phase; | |
749 | - short transfer_phase = view->tick_count; | |
750 | - | |
751 | - polygon->transfer_mode= _textured_transfer; | |
752 | - switch (transfer_mode) | |
753 | - { | |
754 | - case _xfer_fast_horizontal_slide: | |
755 | - case _xfer_horizontal_slide: | |
756 | - case _xfer_vertical_slide: | |
757 | - case _xfer_fast_vertical_slide: | |
758 | - case _xfer_wander: | |
759 | - case _xfer_fast_wander: | |
760 | - x0= y0= 0; | |
761 | - switch (transfer_mode) | |
762 | - { | |
763 | - case _xfer_fast_horizontal_slide: transfer_phase<<= 1; | |
764 | - case _xfer_horizontal_slide: x0= (transfer_phase<<2)&(WORLD_ONE-1); break; | |
765 | - | |
766 | - case _xfer_fast_vertical_slide: transfer_phase<<= 1; | |
767 | - case _xfer_vertical_slide: y0= (transfer_phase<<2)&(WORLD_ONE-1); break; | |
768 | - | |
769 | - case _xfer_fast_wander: transfer_phase<<= 1; | |
770 | - case _xfer_wander: | |
771 | - alternate_transfer_phase= transfer_phase%(10*FULL_CIRCLE); | |
772 | - transfer_phase= transfer_phase%(6*FULL_CIRCLE); | |
773 | - x0= (cosine_table[NORMALIZE_ANGLE(alternate_transfer_phase)] + | |
774 | - (cosine_table[NORMALIZE_ANGLE(2*alternate_transfer_phase)]>>1) + | |
775 | - (cosine_table[NORMALIZE_ANGLE(5*alternate_transfer_phase)]>>1))>>(WORLD_FRACTIONAL_BITS-TRIG_SHIFT+2); | |
776 | - y0= (sine_table[NORMALIZE_ANGLE(transfer_phase)] + | |
777 | - (sine_table[NORMALIZE_ANGLE(2*transfer_phase)]>>1) + | |
778 | - (sine_table[NORMALIZE_ANGLE(3*transfer_phase)]>>1))>>(WORLD_FRACTIONAL_BITS-TRIG_SHIFT+2); | |
779 | - break; | |
780 | - } | |
781 | - if (horizontal) | |
782 | - { | |
783 | - polygon->origin.x+= x0; | |
784 | - polygon->origin.y+= y0; | |
785 | - } | |
786 | - else | |
787 | - { | |
788 | - vector_magnitude= isqrt(polygon->vector.i*polygon->vector.i + polygon->vector.j*polygon->vector.j); | |
789 | - polygon->origin.x+= (polygon->vector.i*x0)/vector_magnitude; | |
790 | - polygon->origin.y+= (polygon->vector.j*x0)/vector_magnitude; | |
791 | - polygon->origin.z-= y0; | |
792 | - } | |
793 | - break; | |
794 | - | |
795 | - case _xfer_pulsate: | |
796 | - case _xfer_wobble: | |
797 | - case _xfer_fast_wobble: | |
798 | - if (transfer_mode==_xfer_fast_wobble) transfer_phase*= 15; | |
799 | - transfer_phase&= WORLD_ONE/16-1; | |
800 | - transfer_phase= (transfer_phase>=WORLD_ONE/32) ? (WORLD_ONE/32+WORLD_ONE/64 - transfer_phase) : (transfer_phase - WORLD_ONE/64); | |
801 | - if (horizontal) | |
802 | - { | |
803 | - polygon->origin.z+= transfer_phase; | |
804 | - } | |
805 | - else | |
806 | - { | |
807 | - if (transfer_mode==_xfer_pulsate) /* translate .origin perpendicular to .vector */ | |
808 | - { | |
809 | - world_vector2d offset; | |
810 | - world_distance vector_magnitude= isqrt(polygon->vector.i*polygon->vector.i + polygon->vector.j*polygon->vector.j); | |
811 | - | |
812 | - offset.i= (polygon->vector.j*transfer_phase)/vector_magnitude; | |
813 | - offset.j= (polygon->vector.i*transfer_phase)/vector_magnitude; | |
814 | - | |
815 | - polygon->origin.x+= offset.i; | |
816 | - polygon->origin.y+= offset.j; | |
817 | - } | |
818 | - else /* ==_xfer_wobble, wobble .vector */ | |
819 | - { | |
820 | - polygon->vector.i+= transfer_phase; | |
821 | - polygon->vector.j+= transfer_phase; | |
822 | - } | |
823 | - } | |
824 | - break; | |
825 | - | |
826 | - case _xfer_normal: | |
827 | - break; | |
828 | - | |
829 | - case _xfer_smear: | |
830 | - polygon->transfer_mode= _solid_transfer; | |
831 | - break; | |
832 | - | |
833 | - case _xfer_static: | |
834 | - polygon->transfer_mode= _static_transfer; | |
835 | - polygon->transfer_data= 0x0000; | |
836 | - break; | |
837 | - | |
838 | - case _xfer_landscape: | |
839 | - polygon->transfer_mode= _big_landscaped_transfer; | |
840 | - break; | |
841 | -// case _xfer_big_landscape: | |
842 | -// polygon->transfer_mode= _big_landscaped_transfer; | |
843 | -// break; | |
844 | - | |
845 | - default: | |
846 | - // LP change: made an unrecognized mode act like normal | |
847 | - break; | |
848 | - } | |
849 | -} | |
850 | - | |
851 | -/* ---------- viewer sprite layer (i.e., weapons) */ | |
852 | - | |
853 | -static void render_viewer_sprite_layer(view_data *view, RasterizerClass *RasPtr) | |
854 | -{ | |
855 | - rectangle_definition textured_rectangle; | |
856 | - weapon_display_information display_data; | |
857 | - shape_information_data *shape_information; | |
858 | - short count; | |
859 | - | |
860 | - // LP change: bug out if weapons-in-hand are not to be displayed | |
861 | - if (!view->show_weapons_in_hand) return; | |
862 | - | |
863 | - // Need to set this... | |
864 | - RasPtr->SetForeground(); | |
865 | - | |
866 | - // No models here, and completely opaque | |
867 | - textured_rectangle.ModelPtr = NULL; | |
868 | - textured_rectangle.Opacity = 1; | |
869 | - | |
870 | - /* get_weapon_display_information() returns true if there is a weapon to be drawn. it | |
871 | - should initially be passed a count of zero. it returns the weaponユs texture and | |
872 | - enough information to draw it correctly. */ | |
873 | - count= 0; | |
874 | - while (get_weapon_display_information(&count, &display_data)) | |
875 | - { | |
876 | - /* fetch relevant shape data */ | |
877 | - // LP: model-setup code is cribbed from | |
878 | - // RenderPlaceObjsClass::build_render_object() in RenderPlaceObjs.cpp | |
879 | -#ifdef HAVE_OPENGL | |
880 | - // Find which 3D model will take the place of this sprite, if any | |
881 | - short ModelSequence; | |
882 | - OGL_ModelData *ModelPtr = | |
883 | - OGL_GetModelData(GET_COLLECTION(display_data.collection),display_data.shape_index,ModelSequence); | |
884 | -#endif | |
885 | - shape_information= extended_get_shape_information(display_data.collection, display_data.low_level_shape_index); | |
886 | - // Nonexistent frame: skip | |
887 | - if (!shape_information) continue; | |
888 | - // No need for a fake sprite rectangle, since models are foreground objects | |
889 | - | |
890 | - // LP change: for the convenience of the OpenGL renderer | |
891 | - textured_rectangle.ShapeDesc = BUILD_DESCRIPTOR(display_data.collection,0); | |
892 | - textured_rectangle.LowLevelShape = display_data.low_level_shape_index; | |
893 | -#ifdef HAVE_OPENGL | |
894 | - textured_rectangle.ModelPtr = ModelPtr; | |
895 | - if (ModelPtr) | |
896 | - { | |
897 | - textured_rectangle.ModelSequence = ModelSequence; | |
898 | - textured_rectangle.ModelFrame = display_data.Frame; | |
899 | - textured_rectangle.NextModelFrame = display_data.NextFrame; | |
900 | - textured_rectangle.MixFrac = display_data.Ticks > 0 ? | |
901 | - float(display_data.Phase)/float(display_data.Ticks) : 0; | |
902 | - const world_point3d Zero = {0, 0, 0}; | |
903 | - textured_rectangle.Position = Zero; | |
904 | - textured_rectangle.Azimuth = 0; | |
905 | - textured_rectangle.Scale = 1; | |
906 | - textured_rectangle.LightDepth = 0; | |
907 | - const GLfloat LightDirection[3] = {0, 1, 0}; // y is forward | |
908 | - objlist_copy(textured_rectangle.LightDirection,LightDirection,3); | |
909 | - RasPtr->SetForegroundView(display_data.flip_horizontal); | |
910 | - } | |
911 | -#endif | |
912 | - | |
913 | - if (shape_information->flags&_X_MIRRORED_BIT) display_data.flip_horizontal= !display_data.flip_horizontal; | |
914 | - if (shape_information->flags&_Y_MIRRORED_BIT) display_data.flip_vertical= !display_data.flip_vertical; | |
915 | - | |
916 | - /* calculate shape rectangle */ | |
917 | - position_sprite_axis(&textured_rectangle.x0, &textured_rectangle.x1, view->screen_height, view->screen_width, display_data.horizontal_positioning_mode, | |
918 | - display_data.horizontal_position, display_data.flip_horizontal, shape_information->world_left, shape_information->world_right); | |
919 | - position_sprite_axis(&textured_rectangle.y0, &textured_rectangle.y1, view->screen_height, view->screen_height, display_data.vertical_positioning_mode, | |
920 | - display_data.vertical_position, display_data.flip_vertical, -shape_information->world_top, -shape_information->world_bottom); | |
921 | - | |
922 | - /* set rectangle bitmap and shading table */ | |
923 | - extended_get_shape_bitmap_and_shading_table(display_data.collection, display_data.low_level_shape_index, &textured_rectangle.texture, &textured_rectangle.shading_tables, view->shading_mode); | |
924 | - if (!textured_rectangle.texture) continue; | |
925 | - | |
926 | - textured_rectangle.flags= 0; | |
927 | - | |
928 | - /* initialize clipping window to full screen */ | |
929 | - textured_rectangle.clip_left= 0; | |
930 | - textured_rectangle.clip_right= view->screen_width; | |
931 | - textured_rectangle.clip_top= 0; | |
932 | - textured_rectangle.clip_bottom= view->screen_height; | |
933 | - | |
934 | - /* copy mirror flags */ | |
935 | - textured_rectangle.flip_horizontal= display_data.flip_horizontal; | |
936 | - textured_rectangle.flip_vertical= display_data.flip_vertical; | |
937 | - | |
938 | - /* lighting: depth of zero in the cameraユs polygon index */ | |
939 | - textured_rectangle.depth= 0; | |
940 | - textured_rectangle.ambient_shade= get_light_intensity(get_polygon_data(view->origin_polygon_index)->floor_lightsource_index); | |
941 | - textured_rectangle.ambient_shade= MAX(shape_information->minimum_light_intensity, textured_rectangle.ambient_shade); | |
942 | - if (view->shading_mode==_shading_infravision) textured_rectangle.flags|= _SHADELESS_BIT; | |
943 | - | |
944 | - // Calculate the object's horizontal position | |
945 | - // for the convenience of doing teleport-in/teleport-out | |
946 | - textured_rectangle.xc = (textured_rectangle.x0 + textured_rectangle.x1) >> 1; | |
947 | - | |
948 | - /* make the weapon reflect the ownerユs transfer mode */ | |
949 | - instantiate_rectangle_transfer_mode(view, &textured_rectangle, display_data.transfer_mode, display_data.transfer_phase); | |
950 | - | |
951 | - /* and draw it */ | |
952 | - // LP: added OpenGL support | |
953 | - RasPtr->texture_rectangle(textured_rectangle); | |
954 | - } | |
955 | -} | |
956 | - | |
957 | -static void position_sprite_axis( | |
958 | - short *x0, | |
959 | - short *x1, | |
960 | - short scale_width, | |
961 | - short screen_width, | |
962 | - short positioning_mode, | |
963 | - _fixed position, | |
964 | - bool flip, | |
965 | - world_distance world_left, | |
966 | - world_distance world_right) | |
967 | -{ | |
968 | - short origin; | |
969 | - | |
970 | - /* if this shape is mirrored, reverse the left/right world coordinates */ | |
971 | - if (flip) | |
972 | - { | |
973 | - world_distance swap= world_left; | |
974 | - world_left= -world_right, world_right= -swap; | |
975 | - } | |
976 | - | |
977 | - switch (positioning_mode) | |
978 | - { | |
979 | - case _position_center: | |
980 | - /* origin is the screen coordinate where the logical center of the shape will be drawn */ | |
981 | - origin= (screen_width*position)>>FIXED_FRACTIONAL_BITS; | |
982 | - break; | |
983 | - case _position_low: | |
984 | - case _position_high: | |
985 | - /* origin is in [0,WORLD_ONE] and represents the amount of the weapon visible off the side */ | |
986 | - origin= ((world_right-world_left)*position)>>FIXED_FRACTIONAL_BITS; | |
987 | - break; | |
988 | - | |
989 | - default: | |
990 | - assert(false); | |
991 | - break; | |
992 | - } | |
993 | - | |
994 | - switch (positioning_mode) | |
995 | - { | |
996 | - case _position_high: | |
997 | - *x0= screen_width - ((origin*scale_width)>>WORLD_FRACTIONAL_BITS); | |
998 | - *x1= *x0 + (((world_right-world_left)*scale_width)>>WORLD_FRACTIONAL_BITS); | |
999 | - break; | |
1000 | - case _position_low: | |
1001 | - *x1= ((origin*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1002 | - *x0= *x1 - (((world_right-world_left)*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1003 | - break; | |
1004 | - | |
1005 | - case _position_center: | |
1006 | - *x0= origin + ((world_left*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1007 | - *x1= origin + ((world_right*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1008 | - break; | |
1009 | - | |
1010 | - default: | |
1011 | - assert(false); | |
1012 | - break; | |
1013 | - } | |
1014 | -} | |
1015 | - | |
1016 | -static void shake_view_origin( | |
1017 | - struct view_data *view, | |
1018 | - world_distance delta) | |
1019 | -{ | |
1020 | - world_point3d new_origin= view->origin; | |
1021 | - short half_delta= delta>>1; | |
1022 | - | |
1023 | - new_origin.x+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE((view->tick_count&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT); | |
1024 | - new_origin.y+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE(((view->tick_count+5*TICKS_PER_SECOND)&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT); | |
1025 | - new_origin.z+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE(((view->tick_count+7*TICKS_PER_SECOND)&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT); | |
1026 | - | |
1027 | - /* only use the new origin if we didnユt cross a polygon boundary */ | |
1028 | - if (find_line_crossed_leaving_polygon(view->origin_polygon_index, (world_point2d *) &view->origin, | |
1029 | - (world_point2d *) &new_origin)==NONE) | |
1030 | - { | |
1031 | - view->origin= new_origin; | |
1032 | - } | |
1033 | -} | |
1034 | - | |
1035 | -// LP: begin no-compile | |
1036 | -#if 0 | |
1037 | - | |
1038 | -/* ---------- mac-specific debugging calls */ | |
1039 | - | |
1040 | -#ifdef QUICKDRAW_DEBUG | |
1041 | - | |
1042 | -#define SCALEF 5 | |
1043 | - | |
1044 | -static void debug_flagged_points( | |
1045 | - flagged_world_point2d *points, | |
1046 | - short count) | |
1047 | -{ | |
1048 | - short i; | |
1049 | - | |
1050 | - SetPort(screen_window); | |
1051 | - PenSize(1, 1); | |
1052 | - RGBForeColor(&rgb_black); | |
1053 | - RGBBackColor(&rgb_white); | |
1054 | - EraseRect(&screen_window->portRect); | |
1055 | - SetOrigin(-640/2, -480/2); | |
1056 | - MoveTo(-320, 0); LineTo(320, 0); | |
1057 | - MoveTo(0, 240); LineTo(0, -240); | |
1058 | - PenSize(2, 2); | |
1059 | - MoveTo(points[count-1].y>>SCALEF, - (points[count-1].x>>SCALEF)); | |
1060 | - for (i=0;i<count;++i) | |
1061 | - { | |
1062 | - LineTo(points[i].y>>SCALEF, - (points[i].x>>SCALEF)); | |
1063 | - psprintf(ptemporary, "%d", i); | |
1064 | - DrawString(temporary); | |
1065 | - MoveTo(points[i].y>>SCALEF, - (points[i].x>>SCALEF)); | |
1066 | - } | |
1067 | -} | |
1068 | - | |
1069 | -static void debug_flagged_points3d( | |
1070 | - flagged_world_point3d *points, | |
1071 | - short count) | |
1072 | -{ | |
1073 | - short i; | |
1074 | - | |
1075 | - SetPort(screen_window); | |
1076 | - PenSize(1, 1); | |
1077 | - RGBForeColor(&rgb_black); | |
1078 | - RGBBackColor(&rgb_white); | |
1079 | - EraseRect(&screen_window->portRect); | |
1080 | - SetOrigin(-640/2, -480/2); | |
1081 | - MoveTo(-320, 0); LineTo(320, 0); | |
1082 | - MoveTo(0, 240); LineTo(0, -240); | |
1083 | - PenSize(2, 2); | |
1084 | - MoveTo(points[count-1].z>>SCALEF, - (points[count-1].x>>SCALEF)); | |
1085 | - for (i=0;i<count;++i) | |
1086 | - { | |
1087 | - LineTo(points[i].z>>SCALEF, - (points[i].x>>SCALEF)); | |
1088 | - psprintf(ptemporary, "%d", i); | |
1089 | - DrawString(temporary); | |
1090 | - MoveTo(points[i].z>>SCALEF, - (points[i].x>>SCALEF)); | |
1091 | - } | |
1092 | -} | |
1093 | - | |
1094 | -static void debug_vector( | |
1095 | - world_vector2d *v) | |
1096 | -{ | |
1097 | - PenSize(1, 1); | |
1098 | - MoveTo(0, 0); | |
1099 | - LineTo(v->j, - v->i); | |
1100 | - MoveTo(0, 0); | |
1101 | - LineTo(- v->j, v->i); | |
1102 | - | |
1103 | - while (!Button()); while (Button()); | |
1104 | -} | |
1105 | - | |
1106 | -static void debug_x_line( | |
1107 | - world_distance x) | |
1108 | -{ | |
1109 | - PenSize(1, 1); | |
1110 | - MoveTo(-320, - x>>SCALEF); | |
1111 | - LineTo(320, - x>>SCALEF); | |
1112 | - | |
1113 | - while (!Button()); while (Button()); | |
1114 | -} | |
1115 | - | |
1116 | -#endif /* QUICKDRAW DEBUG */ | |
1117 | - | |
1118 | -// LP: end no-compile | |
1119 | -#endif | |
1 | +/* | |
2 | +RENDER.C | |
3 | + | |
4 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
5 | + and the "Aleph One" developers. | |
6 | + | |
7 | + This program is free software; you can redistribute it and/or modify | |
8 | + it under the terms of the GNU General Public License as published by | |
9 | + the Free Software Foundation; either version 3 of the License, or | |
10 | + (at your option) any later version. | |
11 | + | |
12 | + This program is distributed in the hope that it will be useful, | |
13 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | + GNU General Public License for more details. | |
16 | + | |
17 | + This license is contained in the file "COPYING", | |
18 | + which is included with this source code; it is available online at | |
19 | + http://www.gnu.org/licenses/gpl.html | |
20 | + | |
21 | +Thursday, September 8, 1994 1:58:20 PM (Jason') | |
22 | + | |
23 | +Friday, September 9, 1994 1:36:15 PM (Jason') | |
24 | + on the quads, in the sun. | |
25 | +Sunday, September 11, 1994 7:32:49 PM (Jason') | |
26 | + the clock on the 540 was wrong, yesterday was Saturday (not Friday). on quads again, but | |
27 | + back home now. something will draw before i go to bed tonight. dinner at the nile? | |
28 | +Tuesday, September 13, 1994 2:54:56 AM (Jason') | |
29 | + no fair!ムム itユs still monday, really. with the aid of some graphical debugging the clipping | |
30 | + all works now and iユm trying to have the entire floor/ceiling thing going tonight (the nile | |
31 | + was closed by the time i got around to taking a shower and heading out). | |
32 | +Friday, September 16, 1994 4:06:17 AM (Jason') | |
33 | + walls, floors and ceilings texture, wobble, etc. contemplating objects ... maybe this will | |
34 | + work after all. | |
35 | +Monday, September 19, 1994 11:03:49 AM (Jason') | |
36 | + unified xz_clip_vertical_polygon() and z_clip_horizontal_polygon() to get rid of the last | |
37 | + known whitespace problem. canユt wait to see what others i have. objects now respect the | |
38 | + clipping windows of all nodes they cross. | |
39 | +Monday, October 24, 1994 4:35:38 PM (Jason) | |
40 | + fixed render sorting problem with objects of equal depth (i.e., parasitic objects). | |
41 | +Tuesday, October 25, 1994 5:14:27 PM (Jason') | |
42 | + fixed object sort order with respect to nodes and height. | |
43 | +Wednesday, October 26, 1994 3:18:59 PM (Jason) | |
44 | + fixed half of the object sort order back so that it worked: in order to correctly handle | |
45 | + objects below the viewer projecting into higher polygons we need to sort objects inside | |
46 | + nodes (to be draw after their walls and ceilings but before their floors). | |
47 | +Wednesday, November 2, 1994 3:49:57 PM (Jason) | |
48 | + the bottom panel of short split sides sometimes takes on the ceiling lightsource. | |
49 | +Tuesday, November 8, 1994 5:29:12 PM (Jason') | |
50 | + implemented new transfer modes: _slide, _wander. _render_effect_earthquake doesnユt work | |
51 | + yet because the player can shake behind his own shape. | |
52 | +Thursday, December 15, 1994 12:15:55 AM (Jason) | |
53 | + the object depth sort order problem ocurrs with multiple objects in the same polygon, | |
54 | + is some function of relative depths and overlap, and does not seem to involve objects at | |
55 | + the same depth. also, it seems to sort many further objects in front of a single closer | |
56 | + one. | |
57 | +Monday, January 23, 1995 6:53:26 AM (Jason') | |
58 | + the way to fix the render object sorting problem is to ignore overlap and sort everything | |
59 | + by depth, regardless. imagine: two, far, non-overlapping objects; by the old algorithm their | |
60 | + drawing order is irrelevant. when a closer object which overlaps both of them is sorted, it | |
61 | + only attempts to lie in front of the closest of the two (leaving the farthest one in an | |
62 | + uncertain position). unfortunately this will cause us to do unnecessary promotion and might | |
63 | + look stupid. | |
64 | +Sunday, March 26, 1995 12:57:39 AM (Jason') | |
65 | + media modifications for marathon2; the object sort order problem still exists (the above | |
66 | + solution indeed looked stupid). | |
67 | +Thursday, March 30, 1995 11:23:35 PM (Jason') | |
68 | + tried to fix object sort problem by attempting to assure that objects are always drawn in | |
69 | + depth-order within a node. | |
70 | +Monday, June 5, 1995 8:37:42 AM (Jason) | |
71 | + blood and fire (baby). | |
72 | + | |
73 | +Jan 30, 2000 (Loren Petrich): | |
74 | + Added some typecasts | |
75 | + Increased MAXIMUM_NODE_ALIASES to 32 | |
76 | + Added an "assert" for when DEBUG is off in aliases-building section in sort_render_tree(). | |
77 | + | |
78 | +Feb 1, 2000 (Loren Petrich): | |
79 | + Added growable list of node aliases; replaced static-list node-alias code | |
80 | + | |
81 | +Feb 4, 2000 (Loren Petrich): | |
82 | + Changed halt() to assert(false) for better debugging | |
83 | + | |
84 | +Feb 5, 2000 (Loren Petrich): | |
85 | + Added growable lists of nodes and also clips for endpoints and lines. | |
86 | + | |
87 | +Feb 6, 2000 (Loren Petrich): | |
88 | + Doing initial allocations of the growable lists of various quantities as a defensive measure | |
89 | + against memory leaks that seem to occur. | |
90 | + | |
91 | +Feb 9, 2000 (Loren Petrich): | |
92 | + Suppressed ambiguous-clip-flag debugging statement; | |
93 | + it gets activated only for excessively distant objects | |
94 | + | |
95 | +Feb 10, 2000 (Loren Petrich): | |
96 | + Added dynamic-limits setting of MAXIMUM_RENDER_OBJECTS | |
97 | + | |
98 | +Feb 14, 2000 (Loren Petrich): | |
99 | + Added test for other-side polygon to LINE_IS_TRANSPARENT() check in next_polygon_along_line() | |
100 | + | |
101 | +Feb 16, 2000 (Loren Petrich): | |
102 | + Put in handling of overflow digits for the purpose of doing long distance correctly; | |
103 | + also turning several horizontal-coordinate short integers into long ones. | |
104 | + | |
105 | +Feb 17, 2000 (Loren Petrich): | |
106 | + Made the sprites long-distance-friendly; there is a bug where they flip around when they | |
107 | + go past half the world size, but that's a short-integer wraparound, and the relevant routine | |
108 | + is in map.c. | |
109 | + | |
110 | +Feb 18, 2000 (Loren Petrich): | |
111 | + Added support for conditional display of weapons-in-hand; so as to support third-person | |
112 | + as well as first-person view. | |
113 | + | |
114 | +Feb 21, 2000 (Loren Petrich): | |
115 | + Idiot-proofed next_polygon_along_line(), making it quit a loop if it searches a whole circle. | |
116 | + | |
117 | +Feb 24, 2000 (Loren Petrich): | |
118 | + Added animated-texture support | |
119 | + | |
120 | +Mar 3, 2000 (Loren Petrich): | |
121 | + Set view to normal in initialize_view_data(); | |
122 | + squashed persistent-extravision bug. | |
123 | + | |
124 | +Mar 5, 2000 (Loren Petrich): | |
125 | + Moved extravision-persistence bug fix out of this file. | |
126 | + | |
127 | +Mar 9, 2000 (Loren Petrich): | |
128 | + Sorted the nodes by polygon index in sort_render_tree() and used them to speed up searches | |
129 | + for nodes with the same polygon index; maps with slowed-down visibility calculations, | |
130 | + such as Desla, can become twice as fast. | |
131 | + | |
132 | +Mar 12, 2000 (Loren Petrich): | |
133 | + Added OpenGL support | |
134 | + | |
135 | +Mar 14, 2000 (Loren Petrich): | |
136 | + Modified data transmitted to OpenGL renderer; it's now collection/color/frame for | |
137 | + both walls and sprites. Also added transmission of view data. | |
138 | + | |
139 | +Mar 24, 2000 (Loren Petrich): | |
140 | + Added landscape_yaw calculation; this is the yaw of the landscapes' left edges | |
141 | + | |
142 | +Mar 30, 2000 (Loren Petrich): | |
143 | + Inspired by Rhys Hill's work, I've created a second tree to contain the visibility nodes; | |
144 | + in addition to their visibility tree, they have a polygon-sort tree. | |
145 | + This tree is implemented by setting up some additional members of node_data | |
146 | + for indicating its structure; there are members for polygon >, polygon <, | |
147 | + and the next member of a chain that shares polygon-index values. | |
148 | + As a result, the node-aliases list can now be abolished once and for all. | |
149 | + | |
150 | +Jun 11, 2000 (Loren Petrich): | |
151 | + Added support for see-through liquids; this requires several changes. | |
152 | + The rendering of each map polygon had to be changed so that there would be a | |
153 | + separate liquid surface; it would no longer replace the floor or the ceiling. | |
154 | + Next, the inhabitant objects had to be done in two passes, one other side, and one view side. | |
155 | + Also, whether there is void on the other side had to be indicated, so that | |
156 | + waterfalls and the like may look right. | |
157 | + | |
158 | +Jun 28, 2000 (Loren Petrich): | |
159 | + Fixed Aaron Davies bug; if a polygon is completely below a liquid, it will not be rendered | |
160 | + if the viewpoint is above the liquid; the bug was that it was not rendered if the viewpoint | |
161 | + was below the liquid. This only happened if semitransparent liquid surfaces was turned off. | |
162 | + | |
163 | +Jul 10, 2000 (Loren Petrich): | |
164 | + Fixed liquid visibility bug in render_tree() that happens when liquid surfaces are not semitransparent; | |
165 | + rendering is skipped if the viewpoint is under a liquid and the polygon is high and dry, | |
166 | + or else if the viewpoint is above a liquid and the polygon is submerged. | |
167 | + | |
168 | +Jul 17, 2000 (Loren Petrich): | |
169 | + Suppressed view-effect resetting in initialize_view_data(), | |
170 | + in order to make teleport view-stretching work correctly. | |
171 | + | |
172 | +Aug 9, 2000 (Loren Petrich): | |
173 | + Moved most of the rendering code here into separate files with these classes: | |
174 | + | |
175 | + RenderVisTreeClass -- | |
176 | + creates the visibility tree (which polygons can be seen from which other ones) | |
177 | + RenderSortPolyClass -- | |
178 | + uses that tree to sort the polygons into appropriate depth order | |
179 | + RenderPlaceObjsClass -- | |
180 | + finds which objects are visible and places them into appropriately sorted order | |
181 | + RenderRasterize -- | |
182 | + handles the clipping of each object and requests those objects' rasterization | |
183 | + | |
184 | + Added a rasterizer class; currently, it does everything from the base class, | |
185 | + though what it does will be moved into subclasses. | |
186 | + | |
187 | +Sep 2, 2000 (Loren Petrich): | |
188 | + Added some idiot-proofing, since the shapes accessor now returns NULL for nonexistent bitmaps | |
189 | + | |
190 | +Nov 12, 2000 (Loren Petrich): | |
191 | + Added automap reset before rendering | |
192 | + | |
193 | +Nov 29, 2000 (Loren Petrich): | |
194 | + Made teleport static/fold effect optional | |
195 | + | |
196 | +Jan 17, 2001 (Loren Petrich): | |
197 | + Added vertical flipping | |
198 | +*/ | |
199 | + | |
200 | + | |
201 | +#ifdef QUICKDRAW_DEBUG | |
202 | +#include "macintosh_cseries.h" | |
203 | +#else | |
204 | +#include "cseries.h" | |
205 | +#endif | |
206 | + | |
207 | +#include "map.h" | |
208 | +#include "render.h" | |
209 | +#include "interface.h" | |
210 | +#include "lightsource.h" | |
211 | +#include "media.h" | |
212 | +#include "weapons.h" | |
213 | + | |
214 | +// LP additions | |
215 | +#include "dynamic_limits.h" | |
216 | +#include "AnimatedTextures.h" | |
217 | +#ifdef HAVE_OPENGL | |
218 | +#include "OGL_Render.h" | |
219 | +#endif | |
220 | + | |
221 | +#ifdef QUICKDRAW_DEBUG | |
222 | +#include "shell.h" | |
223 | +extern WindowPtr screen_window; | |
224 | +#endif | |
225 | + | |
226 | +#include <math.h> | |
227 | +#include <string.h> | |
228 | +#include <stdlib.h> | |
229 | + | |
230 | +// LP additions for decomposition of this code: | |
231 | +#include "RenderVisTree.h" | |
232 | +#include "RenderSortPoly.h" | |
233 | +#include "RenderPlaceObjs.h" | |
234 | +#include "RenderRasterize.h" | |
235 | +#include "Rasterizer_SW.h" | |
236 | +#ifdef HAVE_OPENGL | |
237 | +#include "Rasterizer_OGL.h" | |
238 | +#include "RenderRasterize_Shader.h" | |
239 | +#include "Rasterizer_Shader.h" | |
240 | +#endif | |
241 | +#include "preferences.h" | |
242 | +#include "screen.h" | |
243 | + | |
244 | +#ifdef env68k | |
245 | +#pragma segment render | |
246 | +#endif | |
247 | + | |
248 | +/* use native alignment */ | |
249 | +#if defined (powerc) || defined (__powerc) | |
250 | +#pragma options align=power | |
251 | +#endif | |
252 | + | |
253 | +/* | |
254 | +//render transparent walls (if a bit is set or if the transparent texture is non-NULL?) | |
255 | +//use side lightsources instead of taking them from their polygons | |
256 | +//respect dark side bit (darken light intensity by k) | |
257 | +//fix solid/opaque endpoint confusion (solidity does not imply opacity) | |
258 | + | |
259 | +there exists a problem where an object can overlap into a polygon which is clipped by something | |
260 | + behind the object but that will clip the object because clip windows are subtractive; how | |
261 | + is this solved? | |
262 | +itユs still possible to get ambiguous clip flags, usually in very narrow (e.g., 1 pixel) windows | |
263 | +the renderer has a maximum range beyond which it shits bricks yet which it allows to be exceeded | |
264 | +itユs still possible, especially in high-res full-screen, for points to end up (slightly) off | |
265 | + the screen (usually discarding these has no noticable effect on the scene) | |
266 | +whitespace results when two adjacent polygons are clipped to different vertical windows. this | |
267 | + is not trivially solved with the current implementation, and may be acceptable (?) | |
268 | + | |
269 | +//build_base_polygon_index_list() should discard lower polygons for objects above the viewer and | |
270 | +// higher polygons for objects below the viewer because we certainly donユt sort objects | |
271 | +// correctly in these cases | |
272 | +//in strange cases, objects are sorted out of order. this seems to involve players in some way | |
273 | +// (i.e., parasitic objects). | |
274 | +*/ | |
275 | + | |
276 | +/* ---------- constants */ | |
277 | + | |
278 | +#define EXPLOSION_EFFECT_RANGE (WORLD_ONE/12) | |
279 | + | |
280 | +/* ---------- clip buffer */ | |
281 | +// Not used for anything | |
282 | +#define CLIP_INDEX_BUFFER_SIZE 4096 | |
283 | + | |
284 | +vector<uint16> RenderFlagList; | |
285 | + | |
286 | +// uint16 *render_flags; | |
287 | + | |
288 | +// LP additions: decomposition of the rendering code into various objects | |
289 | + | |
290 | +static RenderVisTreeClass RenderVisTree; // Visibility-tree object | |
291 | +static RenderSortPolyClass RenderSortPoly; // Polygon-sorting object | |
292 | +static RenderPlaceObjsClass RenderPlaceObjs; // Object-placement object | |
293 | +static RenderRasterizerClass Render_Classic; // Clipping and rasterization class | |
294 | + | |
295 | +static Rasterizer_SW_Class Rasterizer_SW; // Software rasterizer | |
296 | +#ifdef HAVE_OPENGL | |
297 | +static Rasterizer_OGL_Class Rasterizer_OGL; // OpenGL rasterizer | |
298 | +static Rasterizer_Shader_Class Rasterizer_Shader; // Shader rasterizer | |
299 | +static RenderRasterize_Shader Render_Shader; // Shader clipping and rasterization class | |
300 | +#endif | |
301 | + | |
302 | +void OGL_Rasterizer_Init() { | |
303 | + | |
304 | +#ifdef HAVE_OPENGL | |
305 | + if (graphics_preferences->screen_mode.acceleration == _shader_acceleration) | |
306 | + Render_Shader.setupGL(); | |
307 | +#endif | |
308 | +} | |
309 | + | |
310 | +/* ---------- private prototypes */ | |
311 | + | |
312 | +static void update_view_data(struct view_data *view); | |
313 | +static void update_render_effect(struct view_data *view); | |
314 | +static void shake_view_origin(struct view_data *view, world_distance delta); | |
315 | + | |
316 | +static void render_viewer_sprite_layer(view_data *view, RasterizerClass *RasPtr); | |
317 | +static void position_sprite_axis(short *x0, short *x1, short scale_width, short screen_width, | |
318 | + short positioning_mode, _fixed position, bool flip, world_distance world_left, world_distance world_right); | |
319 | + | |
320 | + | |
321 | +#ifdef QUICKDRAW_DEBUG | |
322 | +static void debug_flagged_points(flagged_world_point2d *points, short count); | |
323 | +static void debug_flagged_points3d(flagged_world_point3d *points, short count); | |
324 | +static void debug_vector(world_vector2d *v); | |
325 | +static void debug_x_line(world_distance x); | |
326 | +#endif | |
327 | + | |
328 | +/* ---------- code */ | |
329 | + | |
330 | +void allocate_render_memory( | |
331 | + void) | |
332 | +{ | |
333 | + assert(NUMBER_OF_RENDER_FLAGS<=16); | |
334 | + RenderFlagList.resize(RENDER_FLAGS_BUFFER_SIZE); | |
335 | + | |
336 | + // LP addition: check out pointer-arithmetic hack | |
337 | + assert(sizeof(void *) == sizeof(POINTER_DATA)); | |
338 | + | |
339 | + // LP change: do max allocation | |
340 | + RenderVisTree.Resize(MAXIMUM_ENDPOINTS_PER_MAP,MAXIMUM_LINES_PER_MAP); | |
341 | + RenderSortPoly.Resize(MAXIMUM_POLYGONS_PER_MAP); | |
342 | + | |
343 | + // LP change: set up pointers | |
344 | + RenderSortPoly.RVPtr = &RenderVisTree; | |
345 | + RenderPlaceObjs.RVPtr = &RenderVisTree; | |
346 | + RenderPlaceObjs.RSPtr = &RenderSortPoly; | |
347 | +#ifdef HAVE_OPENGL | |
348 | + Render_Classic.RSPtr = Render_Shader.RSPtr = &RenderSortPoly; | |
349 | +#else | |
350 | + Render_Classic.RSPtr = &RenderSortPoly; | |
351 | +#endif | |
352 | +} | |
353 | + | |
354 | +/* just in case anyone was wondering, standard_screen_width will usually be the same as | |
355 | + screen_width. the renderer assumes that the given field_of_view matches the standard | |
356 | + width provided (so if the actual width provided is larger, you'll be able to see more; | |
357 | + if it's smaller you'll be able to see less). this allows the destination bitmap to not | |
358 | + only grow and shrink while maintaining a constant aspect ratio, but to also change in | |
359 | + geometry without effecting the image being projected onto it. if you don't understand | |
360 | + this, pass standard_width==width */ | |
361 | +void initialize_view_data( | |
362 | + struct view_data *view) | |
363 | +{ | |
364 | + double two_pi= 8.0*atan(1.0); | |
365 | + double half_cone= view->field_of_view*(two_pi/360.0)/2; | |
366 | + /* half_cone needs to be extended for non oblique perspective projection (gluPerspective). | |
367 | + this is required because the viewing angle is different for about the same field of view */ | |
368 | + if (graphics_preferences->screen_mode.acceleration == _shader_acceleration) | |
369 | + half_cone= (view->field_of_view * 1.3)*(two_pi/360.0)/2; | |
370 | + | |
371 | + double adjusted_half_cone= View_FOV_FixHorizontalNotVertical() ? | |
372 | + half_cone : | |
373 | + atan(view->screen_width*tan(half_cone)/view->standard_screen_width); | |
374 | + double world_to_screen; | |
375 | + | |
376 | + view->half_screen_width= view->screen_width/2; | |
377 | + view->half_screen_height= view->screen_height/2; | |
378 | + | |
379 | + /* if thereユs a round-off error in half_cone, we want to make the cone too big (so when we clip | |
380 | + lines ヤto the edge of the screenユ theyユre actually off the screen, thus +1.0) */ | |
381 | + view->half_cone= (angle) (adjusted_half_cone*((double)NUMBER_OF_ANGLES)/two_pi+1.0); | |
382 | + | |
383 | + // LP change: find the adjusted yaw for the landscapes; | |
384 | + // this is the effective yaw value for the left edge. | |
385 | + // A landscape rotation can also be added if desired. | |
386 | + view->landscape_yaw = view->yaw - view->half_cone; | |
387 | + | |
388 | + /* calculate world_to_screen; we could calculate this with standard_screen_width/2 and | |
389 | + the old half_cone and get the same result */ | |
390 | + world_to_screen= view->half_screen_width/tan(adjusted_half_cone); | |
391 | + view->world_to_screen_x= view->real_world_to_screen_x= (short) ((world_to_screen/view->horizontal_scale)+0.5); | |
392 | + view->world_to_screen_y= view->real_world_to_screen_y= (short) ((world_to_screen/view->vertical_scale)+0.5); | |
393 | + | |
394 | + /* calculate the vertical cone angle; again, overflow instead of underflow when rounding */ | |
395 | + view->half_vertical_cone= (angle) (NUMBER_OF_ANGLES*atan(((double)view->half_screen_height*view->vertical_scale)/world_to_screen)/two_pi+1.0); | |
396 | + | |
397 | + /* calculate left edge vector */ | |
398 | + view->untransformed_left_edge.i= view->world_to_screen_x; | |
399 | + view->untransformed_left_edge.j= - view->half_screen_width; | |
400 | + | |
401 | + /* calculate right edge vector (negative, so it clips in the right direction) */ | |
402 | + view->untransformed_right_edge.i= - view->world_to_screen_x; | |
403 | + view->untransformed_right_edge.j= - view->half_screen_width; | |
404 | + | |
405 | + /* reset any active effects */ | |
406 | + // LP: this is now called in render_screen(), so we need to disable the initializing | |
407 | +} | |
408 | + | |
409 | +/* origin,origin_polygon_index,yaw,pitch,roll,etc. have probably changed since last call */ | |
410 | +void render_view( | |
411 | + struct view_data *view, | |
412 | + struct bitmap_definition *destination) | |
413 | +{ | |
414 | + update_view_data(view); | |
415 | + | |
416 | + /* clear the render flags */ | |
417 | + objlist_clear(render_flags, RENDER_FLAGS_BUFFER_SIZE); | |
418 | + | |
419 | + ResetOverheadMap(); | |
420 | +/* | |
421 | +#ifdef AUTOMAP_DEBUG | |
422 | + memset(automap_lines, 0, (dynamic_world->line_count/8+((dynamic_world->line_count%8)?1:0)*sizeof(byte))); | |
423 | + memset(automap_polygons, 0, (dynamic_world->polygon_count/8+((dynamic_world->polygon_count%8)?1:0)*sizeof(byte))); | |
424 | +#endif | |
425 | +*/ | |
426 | + | |
427 | + if(view->terminal_mode_active) | |
428 | + { | |
429 | + /* Render the computer interface. */ | |
430 | + render_computer_interface(view); | |
431 | + } | |
432 | + else | |
433 | + { | |
434 | + // LP: the render objects have a pointer to the current view in them, | |
435 | + // so that one can get rid of redundant references to it in them. | |
436 | + | |
437 | + // LP: now from the visibility-tree class | |
438 | + /* build the render tree, regardless of map mode, so the automap updates while active */ | |
439 | + RenderVisTree.view = view; | |
440 | + RenderVisTree.build_render_tree(); | |
441 | + | |
442 | + /* do something complicated and difficult to explain */ | |
443 | + if (!view->overhead_map_active || map_is_translucent()) | |
444 | + { | |
445 | + // LP: now from the polygon-sorter class | |
446 | + /* sort the render tree (so we have a depth-ordering of polygons) and accumulate | |
447 | + clipping information for each polygon */ | |
448 | + RenderSortPoly.view = view; | |
449 | + RenderSortPoly.sort_render_tree(); | |
450 | + | |
451 | + // LP: now from the object-placement class | |
452 | + /* build the render object list by looking at the sorted render tree */ | |
453 | + RenderPlaceObjs.view = view; | |
454 | + RenderPlaceObjs.build_render_object_list(); | |
455 | + | |
456 | + // LP addition: set the current rasterizer to whichever is appropriate here | |
457 | + RasterizerClass *RasPtr; | |
458 | +#ifdef HAVE_OPENGL | |
459 | + if (OGL_IsActive()) | |
460 | + RasPtr = (graphics_preferences->screen_mode.acceleration == _shader_acceleration) ? &Rasterizer_Shader : &Rasterizer_OGL; | |
461 | + else | |
462 | + { | |
463 | +#endif | |
464 | + // The software renderer needs this but the OpenGL one doesn't... | |
465 | + Rasterizer_SW.screen = destination; | |
466 | + RasPtr = &Rasterizer_SW; | |
467 | +#ifdef HAVE_OPENGL | |
468 | + } | |
469 | +#endif | |
470 | + | |
471 | + // Set its view: | |
472 | + RasPtr->SetView(*view); | |
473 | + | |
474 | + // Start rendering main view | |
475 | + RasPtr->Begin(); | |
476 | + | |
477 | + // LP: now from the clipping/rasterizer class | |
478 | +#ifdef HAVE_OPENGL | |
479 | + RenderRasterizerClass *RenPtr = (graphics_preferences->screen_mode.acceleration == _shader_acceleration) ? &Render_Shader : &Render_Classic; | |
480 | +#else | |
481 | + RenderRasterizerClass *RenPtr = &Render_Classic; | |
482 | +#endif | |
483 | + /* render the object list, back to front, doing clipping on each surface before passing | |
484 | + it to the texture-mapping code */ | |
485 | + RenPtr->view = view; | |
486 | + RenPtr->RasPtr = RasPtr; | |
487 | + RenPtr->render_tree(); | |
488 | + | |
489 | + // LP: won't put this into a separate class | |
490 | + /* render the playerユs weapons, etc. */ | |
491 | + render_viewer_sprite_layer(view, RasPtr); | |
492 | + | |
493 | + // Finish rendering main view | |
494 | + RasPtr->End(); | |
495 | + } | |
496 | + | |
497 | + if (view->overhead_map_active) | |
498 | + { | |
499 | + /* if the overhead map is active, render it */ | |
500 | + render_overhead_map(view); | |
501 | + } | |
502 | + } | |
503 | +} | |
504 | + | |
505 | +void start_render_effect( | |
506 | + struct view_data *view, | |
507 | + short effect) | |
508 | +{ | |
509 | + view->effect= effect; | |
510 | + view->effect_phase= NONE; | |
511 | +} | |
512 | + | |
513 | +/* ---------- private code */ | |
514 | + | |
515 | +static void update_view_data( | |
516 | + struct view_data *view) | |
517 | +{ | |
518 | + angle theta; | |
519 | + | |
520 | + // LP change: doing all the FOV changes here: | |
521 | + View_AdjustFOV(view->field_of_view,view->target_field_of_view); | |
522 | + | |
523 | + if (view->effect==NONE) | |
524 | + { | |
525 | + view->world_to_screen_x= view->real_world_to_screen_x; | |
526 | + view->world_to_screen_y= view->real_world_to_screen_y; | |
527 | + } | |
528 | + else | |
529 | + { | |
530 | + update_render_effect(view); | |
531 | + } | |
532 | + | |
533 | + view->untransformed_left_edge.i= view->world_to_screen_x; | |
534 | + view->untransformed_right_edge.i= - view->world_to_screen_x; | |
535 | + | |
536 | + /* calculate world_to_screen_y*tan(pitch) */ | |
537 | + view->dtanpitch= (view->world_to_screen_y*sine_table[view->pitch])/cosine_table[view->pitch]; | |
538 | + | |
539 | + /* calculate left cone vector */ | |
540 | + theta= NORMALIZE_ANGLE(view->yaw-view->half_cone); | |
541 | + view->left_edge.i= cosine_table[theta], view->left_edge.j= sine_table[theta]; | |
542 | + | |
543 | + /* calculate right cone vector */ | |
544 | + theta= NORMALIZE_ANGLE(view->yaw+view->half_cone); | |
545 | + view->right_edge.i= cosine_table[theta], view->right_edge.j= sine_table[theta]; | |
546 | + | |
547 | + /* calculate top cone vector (negative to clip the right direction) */ | |
548 | + view->top_edge.i= - view->world_to_screen_y; | |
549 | + view->top_edge.j= - (view->half_screen_height + view->dtanpitch); /* ==k */ | |
550 | + | |
551 | + /* calculate bottom cone vector */ | |
552 | + view->bottom_edge.i= view->world_to_screen_y; | |
553 | + view->bottom_edge.j= - view->half_screen_height + view->dtanpitch; /* ==k */ | |
554 | + | |
555 | + /* if weユre sitting on one of the endpoints in our origin polygon, move us back slightly (ア1) into | |
556 | + that polygon. when we split rays weユre assuming that weユll never pass through a given | |
557 | + vertex in different directions (because if we do the tree becomes a graph) but when | |
558 | + we start on a vertex this can happen. this is a destructive modification of the origin. */ | |
559 | + { | |
560 | + short i; | |
561 | + struct polygon_data *polygon= get_polygon_data(view->origin_polygon_index); | |
562 | + | |
563 | + for (i= 0;i<polygon->vertex_count;++i) | |
564 | + { | |
565 | + struct world_point2d *vertex= &get_endpoint_data(polygon->endpoint_indexes[i])->vertex; | |
566 | + | |
567 | + if (vertex->x==view->origin.x && vertex->y==view->origin.y) | |
568 | + { | |
569 | + world_point2d *ccw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_LOW(i, polygon->vertex_count-1)])->vertex; | |
570 | + world_point2d *cw_vertex= &get_endpoint_data(polygon->endpoint_indexes[WRAP_HIGH(i, polygon->vertex_count-1)])->vertex; | |
571 | + world_vector2d inset_vector; | |
572 | + | |
573 | + inset_vector.i= (ccw_vertex->x-vertex->x) + (cw_vertex->x-vertex->x); | |
574 | + inset_vector.j= (ccw_vertex->y-vertex->y) + (cw_vertex->y-vertex->y); | |
575 | + view->origin.x+= SGN(inset_vector.i); | |
576 | + view->origin.y+= SGN(inset_vector.j); | |
577 | + | |
578 | + break; | |
579 | + } | |
580 | + } | |
581 | + | |
582 | + /* determine whether we are under or over the media boundary of our polygon; we will see all | |
583 | + other media boundaries from this orientation (above or below) or fail to draw them. */ | |
584 | + if (polygon->media_index==NONE) | |
585 | + { | |
586 | + view->under_media_boundary= false; | |
587 | + } | |
588 | + else | |
589 | + { | |
590 | + struct media_data *media= get_media_data(polygon->media_index); | |
591 | + | |
592 | + // LP change: idiot-proofing | |
593 | + if (media) | |
594 | + { | |
595 | + view->under_media_boundary= UNDER_MEDIA(media, view->origin.z); | |
596 | + view->under_media_index= polygon->media_index; | |
597 | + } else { | |
598 | + view->under_media_boundary= false; | |
599 | + } | |
600 | + } | |
601 | + } | |
602 | +} | |
603 | + | |
604 | +static void update_render_effect( | |
605 | + struct view_data *view) | |
606 | +{ | |
607 | + short effect= view->effect; | |
608 | + short phase= view->effect_phase==NONE ? 0 : (view->effect_phase+view->ticks_elapsed); | |
609 | + short period; | |
610 | + | |
611 | + view->effect_phase= phase; | |
612 | + | |
613 | + switch (effect) | |
614 | + { | |
615 | + // LP change: suppressed all the FOV changes | |
616 | + case _render_effect_fold_in: case _render_effect_fold_out: period= TICKS_PER_SECOND/2; break; | |
617 | + case _render_effect_explosion: period= TICKS_PER_SECOND; break; | |
618 | + default: | |
619 | + assert(false); | |
620 | + break; | |
621 | + } | |
622 | + | |
623 | + if (phase>period) | |
624 | + { | |
625 | + view->effect= NONE; | |
626 | + } | |
627 | + else | |
628 | + { | |
629 | + switch (effect) | |
630 | + { | |
631 | + case _render_effect_explosion: | |
632 | + shake_view_origin(view, EXPLOSION_EFFECT_RANGE - ((EXPLOSION_EFFECT_RANGE/2)*phase)/period); | |
633 | + break; | |
634 | + | |
635 | + case _render_effect_fold_in: | |
636 | + phase= period-phase; | |
637 | + case _render_effect_fold_out: | |
638 | + /* calculate world_to_screen based on phase */ | |
639 | + view->world_to_screen_x= view->real_world_to_screen_x + (4*view->real_world_to_screen_x*phase)/period; | |
640 | + view->world_to_screen_y= view->real_world_to_screen_y - (view->real_world_to_screen_y*phase)/(period+period/4); | |
641 | + break; | |
642 | + } | |
643 | + } | |
644 | +} | |
645 | + | |
646 | + | |
647 | +/* ---------- transfer modes */ | |
648 | + | |
649 | +/* given a transfer mode and phase, cause whatever changes it should cause to a rectangle_definition | |
650 | + structure */ | |
651 | +void instantiate_rectangle_transfer_mode( | |
652 | + view_data *view, | |
653 | + rectangle_definition *rectangle, | |
654 | + short transfer_mode, | |
655 | + _fixed transfer_phase) | |
656 | +{ | |
657 | + // For the 3D-model code | |
658 | + rectangle->HorizScale = 1; | |
659 | + | |
660 | + switch (transfer_mode) | |
661 | + { | |
662 | + case _xfer_invisibility: | |
663 | + case _xfer_subtle_invisibility: | |
664 | + if (view->shading_mode!=_shading_infravision) | |
665 | + { | |
666 | + rectangle->transfer_mode= _tinted_transfer; | |
667 | + rectangle->shading_tables= get_global_shading_table(); | |
668 | + rectangle->transfer_data= (transfer_mode==_xfer_invisibility) ? 0x000f : 0x0018; | |
669 | + break; | |
670 | + } | |
671 | + /* if we have infravision, fall through to _textured_transfer (i see you...) */ | |
672 | + case _xfer_normal: | |
673 | + rectangle->transfer_mode= _textured_transfer; | |
674 | + break; | |
675 | + | |
676 | + case _xfer_static: | |
677 | + case _xfer_50percent_static: | |
678 | + rectangle->transfer_mode= _static_transfer; | |
679 | + rectangle->transfer_data= (transfer_mode==_xfer_static) ? 0x0000 : 0x8000; | |
680 | + break; | |
681 | + | |
682 | + case _xfer_fade_out_static: | |
683 | + rectangle->transfer_mode= _static_transfer; | |
684 | + rectangle->transfer_data= transfer_phase; | |
685 | + break; | |
686 | + | |
687 | + case _xfer_pulsating_static: | |
688 | + rectangle->transfer_mode= _static_transfer; | |
689 | + rectangle->transfer_data= 0x8000+((0x6000*sine_table[FIXED_INTEGERAL_PART(transfer_phase*NUMBER_OF_ANGLES)])>>TRIG_SHIFT); | |
690 | + break; | |
691 | + | |
692 | + case _xfer_fold_in: | |
693 | + transfer_phase= FIXED_ONE-transfer_phase; /* do everything backwards */ | |
694 | + case _xfer_fold_out: | |
695 | + if (View_DoStaticEffect()) | |
696 | + { | |
697 | + // Corrected the teleport shrinkage so that the sprite/object | |
698 | + // shrinks to its object position and not to its sprite center | |
699 | + short delta0= FIXED_INTEGERAL_PART(((rectangle->xc-rectangle->x0)-1)*transfer_phase); | |
700 | + short delta1= FIXED_INTEGERAL_PART(((rectangle->x1-rectangle->xc)-1)*transfer_phase); | |
701 | + // short delta= FIXED_INTEGERAL_PART((((rectangle->x1-rectangle->x0)>>1)-1)*transfer_phase); | |
702 | + | |
703 | + rectangle->transfer_mode= _static_transfer; | |
704 | + rectangle->transfer_data= (transfer_phase>>1); | |
705 | + rectangle->x0+= delta0; | |
706 | + rectangle->x1-= delta1; | |
707 | + rectangle->HorizScale = 1 - float(transfer_phase)/float(FIXED_ONE); | |
708 | + } | |
709 | + else | |
710 | + rectangle->transfer_mode= _textured_transfer; | |
711 | + break; | |
712 | + | |
713 | +#if 0 | |
714 | + case _xfer_fade_out_to_black: | |
715 | + rectangle->shading_tables= get_global_shading_table(); | |
716 | + if (transfer_phase<FIXED_ONE_HALF) | |
717 | + { | |
718 | + /* fade to black */ | |
719 | + rectangle->ambient_shade= (rectangle->ambient_shade*(transfer_phase-FIXED_ONE_HALF))>>(FIXED_FRACTIONAL_BITS-1); | |
720 | + rectangle->transfer_mode= _textured_transfer; | |
721 | + } | |
722 | + else | |
723 | + { | |
724 | + /* vanish */ | |
725 | + rectangle->transfer_mode= _tinted_transfer; | |
726 | + rectangle->transfer_data= 0x1f - ((0x1f*(FIXED_ONE_HALF-transfer_phase))>>(FIXED_FRACTIONAL_BITS-1)); | |
727 | + } | |
728 | + break; | |
729 | +#endif | |
730 | + | |
731 | + // LP change: made an unrecognized mode act like normal | |
732 | + default: | |
733 | + rectangle->transfer_mode= _textured_transfer; | |
734 | + break; | |
735 | + } | |
736 | +} | |
737 | + | |
738 | +/* given a transfer mode and phase, cause whatever changes it should cause to a polygon_definition | |
739 | + structure (unfortunately we need to know whether this is a horizontal or vertical polygon) */ | |
740 | +void instantiate_polygon_transfer_mode( | |
741 | + struct view_data *view, | |
742 | + struct polygon_definition *polygon, | |
743 | + short transfer_mode, | |
744 | + bool horizontal) | |
745 | +{ | |
746 | + world_distance x0, y0; | |
747 | + world_distance vector_magnitude; | |
748 | + short alternate_transfer_phase; | |
749 | + short transfer_phase = view->tick_count; | |
750 | + | |
751 | + polygon->transfer_mode= _textured_transfer; | |
752 | + switch (transfer_mode) | |
753 | + { | |
754 | + case _xfer_fast_horizontal_slide: | |
755 | + case _xfer_horizontal_slide: | |
756 | + case _xfer_vertical_slide: | |
757 | + case _xfer_fast_vertical_slide: | |
758 | + case _xfer_wander: | |
759 | + case _xfer_fast_wander: | |
760 | + x0= y0= 0; | |
761 | + switch (transfer_mode) | |
762 | + { | |
763 | + case _xfer_fast_horizontal_slide: transfer_phase<<= 1; | |
764 | + case _xfer_horizontal_slide: x0= (transfer_phase<<2)&(WORLD_ONE-1); break; | |
765 | + | |
766 | + case _xfer_fast_vertical_slide: transfer_phase<<= 1; | |
767 | + case _xfer_vertical_slide: y0= (transfer_phase<<2)&(WORLD_ONE-1); break; | |
768 | + | |
769 | + case _xfer_fast_wander: transfer_phase<<= 1; | |
770 | + case _xfer_wander: | |
771 | + alternate_transfer_phase= transfer_phase%(10*FULL_CIRCLE); | |
772 | + transfer_phase= transfer_phase%(6*FULL_CIRCLE); | |
773 | + x0= (cosine_table[NORMALIZE_ANGLE(alternate_transfer_phase)] + | |
774 | + (cosine_table[NORMALIZE_ANGLE(2*alternate_transfer_phase)]>>1) + | |
775 | + (cosine_table[NORMALIZE_ANGLE(5*alternate_transfer_phase)]>>1))>>(WORLD_FRACTIONAL_BITS-TRIG_SHIFT+2); | |
776 | + y0= (sine_table[NORMALIZE_ANGLE(transfer_phase)] + | |
777 | + (sine_table[NORMALIZE_ANGLE(2*transfer_phase)]>>1) + | |
778 | + (sine_table[NORMALIZE_ANGLE(3*transfer_phase)]>>1))>>(WORLD_FRACTIONAL_BITS-TRIG_SHIFT+2); | |
779 | + break; | |
780 | + } | |
781 | + if (horizontal) | |
782 | + { | |
783 | + polygon->origin.x+= x0; | |
784 | + polygon->origin.y+= y0; | |
785 | + } | |
786 | + else | |
787 | + { | |
788 | + vector_magnitude= isqrt(polygon->vector.i*polygon->vector.i + polygon->vector.j*polygon->vector.j); | |
789 | + polygon->origin.x+= (polygon->vector.i*x0)/vector_magnitude; | |
790 | + polygon->origin.y+= (polygon->vector.j*x0)/vector_magnitude; | |
791 | + polygon->origin.z-= y0; | |
792 | + } | |
793 | + break; | |
794 | + | |
795 | + case _xfer_pulsate: | |
796 | + case _xfer_wobble: | |
797 | + case _xfer_fast_wobble: | |
798 | + if (transfer_mode==_xfer_fast_wobble) transfer_phase*= 15; | |
799 | + transfer_phase&= WORLD_ONE/16-1; | |
800 | + transfer_phase= (transfer_phase>=WORLD_ONE/32) ? (WORLD_ONE/32+WORLD_ONE/64 - transfer_phase) : (transfer_phase - WORLD_ONE/64); | |
801 | + if (horizontal) | |
802 | + { | |
803 | + polygon->origin.z+= transfer_phase; | |
804 | + } | |
805 | + else | |
806 | + { | |
807 | + if (transfer_mode==_xfer_pulsate) /* translate .origin perpendicular to .vector */ | |
808 | + { | |
809 | + world_vector2d offset; | |
810 | + world_distance vector_magnitude= isqrt(polygon->vector.i*polygon->vector.i + polygon->vector.j*polygon->vector.j); | |
811 | + | |
812 | + offset.i= (polygon->vector.j*transfer_phase)/vector_magnitude; | |
813 | + offset.j= (polygon->vector.i*transfer_phase)/vector_magnitude; | |
814 | + | |
815 | + polygon->origin.x+= offset.i; | |
816 | + polygon->origin.y+= offset.j; | |
817 | + } | |
818 | + else /* ==_xfer_wobble, wobble .vector */ | |
819 | + { | |
820 | + polygon->vector.i+= transfer_phase; | |
821 | + polygon->vector.j+= transfer_phase; | |
822 | + } | |
823 | + } | |
824 | + break; | |
825 | + | |
826 | + case _xfer_normal: | |
827 | + break; | |
828 | + | |
829 | + case _xfer_smear: | |
830 | + polygon->transfer_mode= _solid_transfer; | |
831 | + break; | |
832 | + | |
833 | + case _xfer_static: | |
834 | + polygon->transfer_mode= _static_transfer; | |
835 | + polygon->transfer_data= 0x0000; | |
836 | + break; | |
837 | + | |
838 | + case _xfer_landscape: | |
839 | + polygon->transfer_mode= _big_landscaped_transfer; | |
840 | + break; | |
841 | +// case _xfer_big_landscape: | |
842 | +// polygon->transfer_mode= _big_landscaped_transfer; | |
843 | +// break; | |
844 | + | |
845 | + default: | |
846 | + // LP change: made an unrecognized mode act like normal | |
847 | + break; | |
848 | + } | |
849 | +} | |
850 | + | |
851 | +/* ---------- viewer sprite layer (i.e., weapons) */ | |
852 | + | |
853 | +static void render_viewer_sprite_layer(view_data *view, RasterizerClass *RasPtr) | |
854 | +{ | |
855 | + rectangle_definition textured_rectangle; | |
856 | + weapon_display_information display_data; | |
857 | + shape_information_data *shape_information; | |
858 | + short count; | |
859 | + | |
860 | + // LP change: bug out if weapons-in-hand are not to be displayed | |
861 | + if (!view->show_weapons_in_hand) return; | |
862 | + | |
863 | + // Need to set this... | |
864 | + RasPtr->SetForeground(); | |
865 | + | |
866 | + // No models here, and completely opaque | |
867 | + textured_rectangle.ModelPtr = NULL; | |
868 | + textured_rectangle.Opacity = 1; | |
869 | + | |
870 | + /* get_weapon_display_information() returns true if there is a weapon to be drawn. it | |
871 | + should initially be passed a count of zero. it returns the weaponユs texture and | |
872 | + enough information to draw it correctly. */ | |
873 | + count= 0; | |
874 | + while (get_weapon_display_information(&count, &display_data)) | |
875 | + { | |
876 | + /* fetch relevant shape data */ | |
877 | + // LP: model-setup code is cribbed from | |
878 | + // RenderPlaceObjsClass::build_render_object() in RenderPlaceObjs.cpp | |
879 | +#ifdef HAVE_OPENGL | |
880 | + // Find which 3D model will take the place of this sprite, if any | |
881 | + short ModelSequence; | |
882 | + OGL_ModelData *ModelPtr = | |
883 | + OGL_GetModelData(GET_COLLECTION(display_data.collection),display_data.shape_index,ModelSequence); | |
884 | +#endif | |
885 | + shape_information= extended_get_shape_information(display_data.collection, display_data.low_level_shape_index); | |
886 | + // Nonexistent frame: skip | |
887 | + if (!shape_information) continue; | |
888 | + // No need for a fake sprite rectangle, since models are foreground objects | |
889 | + | |
890 | + // LP change: for the convenience of the OpenGL renderer | |
891 | + textured_rectangle.ShapeDesc = BUILD_DESCRIPTOR(display_data.collection,0); | |
892 | + textured_rectangle.LowLevelShape = display_data.low_level_shape_index; | |
893 | +#ifdef HAVE_OPENGL | |
894 | + textured_rectangle.ModelPtr = ModelPtr; | |
895 | + if (ModelPtr) | |
896 | + { | |
897 | + textured_rectangle.ModelSequence = ModelSequence; | |
898 | + textured_rectangle.ModelFrame = display_data.Frame; | |
899 | + textured_rectangle.NextModelFrame = display_data.NextFrame; | |
900 | + textured_rectangle.MixFrac = display_data.Ticks > 0 ? | |
901 | + float(display_data.Phase)/float(display_data.Ticks) : 0; | |
902 | + const world_point3d Zero = {0, 0, 0}; | |
903 | + textured_rectangle.Position = Zero; | |
904 | + textured_rectangle.Azimuth = 0; | |
905 | + textured_rectangle.Scale = 1; | |
906 | + textured_rectangle.LightDepth = 0; | |
907 | + const GLfloat LightDirection[3] = {0, 1, 0}; // y is forward | |
908 | + objlist_copy(textured_rectangle.LightDirection,LightDirection,3); | |
909 | + RasPtr->SetForegroundView(display_data.flip_horizontal); | |
910 | + } | |
911 | +#endif | |
912 | + | |
913 | + if (shape_information->flags&_X_MIRRORED_BIT) display_data.flip_horizontal= !display_data.flip_horizontal; | |
914 | + if (shape_information->flags&_Y_MIRRORED_BIT) display_data.flip_vertical= !display_data.flip_vertical; | |
915 | + | |
916 | + /* calculate shape rectangle */ | |
917 | + position_sprite_axis(&textured_rectangle.x0, &textured_rectangle.x1, view->screen_height, view->screen_width, display_data.horizontal_positioning_mode, | |
918 | + display_data.horizontal_position, display_data.flip_horizontal, shape_information->world_left, shape_information->world_right); | |
919 | + position_sprite_axis(&textured_rectangle.y0, &textured_rectangle.y1, view->screen_height, view->screen_height, display_data.vertical_positioning_mode, | |
920 | + display_data.vertical_position, display_data.flip_vertical, -shape_information->world_top, -shape_information->world_bottom); | |
921 | + | |
922 | + /* set rectangle bitmap and shading table */ | |
923 | + extended_get_shape_bitmap_and_shading_table(display_data.collection, display_data.low_level_shape_index, &textured_rectangle.texture, &textured_rectangle.shading_tables, view->shading_mode); | |
924 | + if (!textured_rectangle.texture) continue; | |
925 | + | |
926 | + textured_rectangle.flags= 0; | |
927 | + | |
928 | + /* initialize clipping window to full screen */ | |
929 | + textured_rectangle.clip_left= 0; | |
930 | + textured_rectangle.clip_right= view->screen_width; | |
931 | + textured_rectangle.clip_top= 0; | |
932 | + textured_rectangle.clip_bottom= view->screen_height; | |
933 | + | |
934 | + /* copy mirror flags */ | |
935 | + textured_rectangle.flip_horizontal= display_data.flip_horizontal; | |
936 | + textured_rectangle.flip_vertical= display_data.flip_vertical; | |
937 | + | |
938 | + /* lighting: depth of zero in the cameraユs polygon index */ | |
939 | + textured_rectangle.depth= 0; | |
940 | + textured_rectangle.ambient_shade= get_light_intensity(get_polygon_data(view->origin_polygon_index)->floor_lightsource_index); | |
941 | + textured_rectangle.ambient_shade= MAX(shape_information->minimum_light_intensity, textured_rectangle.ambient_shade); | |
942 | + if (view->shading_mode==_shading_infravision) textured_rectangle.flags|= _SHADELESS_BIT; | |
943 | + | |
944 | + // Calculate the object's horizontal position | |
945 | + // for the convenience of doing teleport-in/teleport-out | |
946 | + textured_rectangle.xc = (textured_rectangle.x0 + textured_rectangle.x1) >> 1; | |
947 | + | |
948 | + /* make the weapon reflect the ownerユs transfer mode */ | |
949 | + instantiate_rectangle_transfer_mode(view, &textured_rectangle, display_data.transfer_mode, display_data.transfer_phase); | |
950 | + | |
951 | + /* and draw it */ | |
952 | + // LP: added OpenGL support | |
953 | + RasPtr->texture_rectangle(textured_rectangle); | |
954 | + } | |
955 | +} | |
956 | + | |
957 | +static void position_sprite_axis( | |
958 | + short *x0, | |
959 | + short *x1, | |
960 | + short scale_width, | |
961 | + short screen_width, | |
962 | + short positioning_mode, | |
963 | + _fixed position, | |
964 | + bool flip, | |
965 | + world_distance world_left, | |
966 | + world_distance world_right) | |
967 | +{ | |
968 | + short origin; | |
969 | + | |
970 | + /* if this shape is mirrored, reverse the left/right world coordinates */ | |
971 | + if (flip) | |
972 | + { | |
973 | + world_distance swap= world_left; | |
974 | + world_left= -world_right, world_right= -swap; | |
975 | + } | |
976 | + | |
977 | + switch (positioning_mode) | |
978 | + { | |
979 | + case _position_center: | |
980 | + /* origin is the screen coordinate where the logical center of the shape will be drawn */ | |
981 | + origin= (screen_width*position)>>FIXED_FRACTIONAL_BITS; | |
982 | + break; | |
983 | + case _position_low: | |
984 | + case _position_high: | |
985 | + /* origin is in [0,WORLD_ONE] and represents the amount of the weapon visible off the side */ | |
986 | + origin= ((world_right-world_left)*position)>>FIXED_FRACTIONAL_BITS; | |
987 | + break; | |
988 | + | |
989 | + default: | |
990 | + assert(false); | |
991 | + break; | |
992 | + } | |
993 | + | |
994 | + switch (positioning_mode) | |
995 | + { | |
996 | + case _position_high: | |
997 | + *x0= screen_width - ((origin*scale_width)>>WORLD_FRACTIONAL_BITS); | |
998 | + *x1= *x0 + (((world_right-world_left)*scale_width)>>WORLD_FRACTIONAL_BITS); | |
999 | + break; | |
1000 | + case _position_low: | |
1001 | + *x1= ((origin*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1002 | + *x0= *x1 - (((world_right-world_left)*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1003 | + break; | |
1004 | + | |
1005 | + case _position_center: | |
1006 | + *x0= origin + ((world_left*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1007 | + *x1= origin + ((world_right*scale_width)>>WORLD_FRACTIONAL_BITS); | |
1008 | + break; | |
1009 | + | |
1010 | + default: | |
1011 | + assert(false); | |
1012 | + break; | |
1013 | + } | |
1014 | +} | |
1015 | + | |
1016 | +static void shake_view_origin( | |
1017 | + struct view_data *view, | |
1018 | + world_distance delta) | |
1019 | +{ | |
1020 | + world_point3d new_origin= view->origin; | |
1021 | + short half_delta= delta>>1; | |
1022 | + | |
1023 | + new_origin.x+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE((view->tick_count&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT); | |
1024 | + new_origin.y+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE(((view->tick_count+5*TICKS_PER_SECOND)&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT); | |
1025 | + new_origin.z+= half_delta - ((delta*sine_table[NORMALIZE_ANGLE(((view->tick_count+7*TICKS_PER_SECOND)&~3)*(7*FULL_CIRCLE))])>>TRIG_SHIFT); | |
1026 | + | |
1027 | + /* only use the new origin if we didnユt cross a polygon boundary */ | |
1028 | + if (find_line_crossed_leaving_polygon(view->origin_polygon_index, (world_point2d *) &view->origin, | |
1029 | + (world_point2d *) &new_origin)==NONE) | |
1030 | + { | |
1031 | + view->origin= new_origin; | |
1032 | + } | |
1033 | +} | |
1034 | + | |
1035 | +// LP: begin no-compile | |
1036 | +#if 0 | |
1037 | + | |
1038 | +/* ---------- mac-specific debugging calls */ | |
1039 | + | |
1040 | +#ifdef QUICKDRAW_DEBUG | |
1041 | + | |
1042 | +#define SCALEF 5 | |
1043 | + | |
1044 | +static void debug_flagged_points( | |
1045 | + flagged_world_point2d *points, | |
1046 | + short count) | |
1047 | +{ | |
1048 | + short i; | |
1049 | + | |
1050 | + SetPort(screen_window); | |
1051 | + PenSize(1, 1); | |
1052 | + RGBForeColor(&rgb_black); | |
1053 | + RGBBackColor(&rgb_white); | |
1054 | + EraseRect(&screen_window->portRect); | |
1055 | + SetOrigin(-640/2, -480/2); | |
1056 | + MoveTo(-320, 0); LineTo(320, 0); | |
1057 | + MoveTo(0, 240); LineTo(0, -240); | |
1058 | + PenSize(2, 2); | |
1059 | + MoveTo(points[count-1].y>>SCALEF, - (points[count-1].x>>SCALEF)); | |
1060 | + for (i=0;i<count;++i) | |
1061 | + { | |
1062 | + LineTo(points[i].y>>SCALEF, - (points[i].x>>SCALEF)); | |
1063 | + psprintf(ptemporary, "%d", i); | |
1064 | + DrawString(temporary); | |
1065 | + MoveTo(points[i].y>>SCALEF, - (points[i].x>>SCALEF)); | |
1066 | + } | |
1067 | +} | |
1068 | + | |
1069 | +static void debug_flagged_points3d( | |
1070 | + flagged_world_point3d *points, | |
1071 | + short count) | |
1072 | +{ | |
1073 | + short i; | |
1074 | + | |
1075 | + SetPort(screen_window); | |
1076 | + PenSize(1, 1); | |
1077 | + RGBForeColor(&rgb_black); | |
1078 | + RGBBackColor(&rgb_white); | |
1079 | + EraseRect(&screen_window->portRect); | |
1080 | + SetOrigin(-640/2, -480/2); | |
1081 | + MoveTo(-320, 0); LineTo(320, 0); | |
1082 | + MoveTo(0, 240); LineTo(0, -240); | |
1083 | + PenSize(2, 2); | |
1084 | + MoveTo(points[count-1].z>>SCALEF, - (points[count-1].x>>SCALEF)); | |
1085 | + for (i=0;i<count;++i) | |
1086 | + { | |
1087 | + LineTo(points[i].z>>SCALEF, - (points[i].x>>SCALEF)); | |
1088 | + psprintf(ptemporary, "%d", i); | |
1089 | + DrawString(temporary); | |
1090 | + MoveTo(points[i].z>>SCALEF, - (points[i].x>>SCALEF)); | |
1091 | + } | |
1092 | +} | |
1093 | + | |
1094 | +static void debug_vector( | |
1095 | + world_vector2d *v) | |
1096 | +{ | |
1097 | + PenSize(1, 1); | |
1098 | + MoveTo(0, 0); | |
1099 | + LineTo(v->j, - v->i); | |
1100 | + MoveTo(0, 0); | |
1101 | + LineTo(- v->j, v->i); | |
1102 | + | |
1103 | + while (!Button()); while (Button()); | |
1104 | +} | |
1105 | + | |
1106 | +static void debug_x_line( | |
1107 | + world_distance x) | |
1108 | +{ | |
1109 | + PenSize(1, 1); | |
1110 | + MoveTo(-320, - x>>SCALEF); | |
1111 | + LineTo(320, - x>>SCALEF); | |
1112 | + | |
1113 | + while (!Button()); while (Button()); | |
1114 | +} | |
1115 | + | |
1116 | +#endif /* QUICKDRAW DEBUG */ | |
1117 | + | |
1118 | +// LP: end no-compile | |
1119 | +#endif |
@@ -1,1032 +1,1032 @@ | ||
1 | -/* | |
2 | - | |
3 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | - OpenGL Model-Definition File | |
21 | - by Loren Petrich, | |
22 | - May 11, 2003 | |
23 | - | |
24 | - This contains the definitions of all the OpenGL models and skins | |
25 | -*/ | |
26 | - | |
27 | -#include "cseries.h" | |
28 | -#include "OGL_Model_Def.h" | |
29 | -#include "OGL_Setup.h" | |
30 | - | |
31 | -#ifdef HAVE_OPENGL | |
32 | - | |
33 | -#include <cmath> | |
34 | - | |
35 | -#include "Dim3_Loader.h" | |
36 | -#include "StudioLoader.h" | |
37 | -#include "WavefrontLoader.h" | |
38 | -#include "QD3D_Loader.h" | |
39 | - | |
40 | - | |
41 | -// Model-data stuff; | |
42 | -// defaults for whatever might need them | |
43 | -// Including skin stuff for convenience here | |
44 | -static OGL_ModelData DefaultModelData; | |
45 | -static OGL_SkinData DefaultSkinData; | |
46 | - | |
47 | - | |
48 | -// For mapping Marathon-physics sequences onto model sequences | |
49 | -struct SequenceMapEntry | |
50 | -{ | |
51 | - int16 Sequence; | |
52 | - int16 ModelSequence; | |
53 | -}; | |
54 | - | |
55 | - | |
56 | -// Store model-data stuff in a set of STL vectors | |
57 | -struct ModelDataEntry | |
58 | -{ | |
59 | - // Which Marathon-engine sequence gets translated into this model, | |
60 | - // if static, or the neutral sequence, if dynamic | |
61 | - short Sequence; | |
62 | - | |
63 | - vector<SequenceMapEntry> SequenceMap; | |
64 | - | |
65 | - // Make a member for more convenient access | |
66 | - OGL_ModelData ModelData; | |
67 | - | |
68 | - ModelDataEntry(): Sequence(NONE) {} | |
69 | -}; | |
70 | - | |
71 | - | |
72 | -// Separate model-data sequence lists for each collection ID, | |
73 | -// to speed up searching | |
74 | -static vector<ModelDataEntry> MdlList[NUMBER_OF_COLLECTIONS]; | |
75 | - | |
76 | -// Will look up both the model index and its sequence | |
77 | -struct ModelHashEntry | |
78 | -{ | |
79 | - int16 ModelIndex; | |
80 | - int16 ModelSeqTabIndex; | |
81 | -}; | |
82 | - | |
83 | -// Model-data hash table for extra-fast searching: | |
84 | -static vector<ModelHashEntry> MdlHash[NUMBER_OF_COLLECTIONS]; | |
85 | - | |
86 | -// Hash-table size and function | |
87 | -const int MdlHashSize = 1 << 8; | |
88 | -const int MdlHashMask = MdlHashSize - 1; | |
89 | -inline uint8 MdlHashFunc(short Sequence) | |
90 | -{ | |
91 | - // E-Z | |
92 | - return (uint8)(Sequence & MdlHashMask); | |
93 | -} | |
94 | - | |
95 | - | |
96 | -// Deletes a collection's model-data sequences | |
97 | -static void MdlDelete(short Collection) | |
98 | -{ | |
99 | - int c = Collection; | |
100 | - MdlList[c].clear(); | |
101 | - MdlHash[c].clear(); | |
102 | -} | |
103 | - | |
104 | -// Deletes all of them | |
105 | -static void MdlDeleteAll() | |
106 | -{ | |
107 | - for (int c=0; c<NUMBER_OF_COLLECTIONS; c++) MdlDelete(c); | |
108 | -} | |
109 | - | |
110 | - | |
111 | -OGL_ModelData *OGL_GetModelData(short Collection, short Sequence, short& ModelSequence) | |
112 | -{ | |
113 | - // Model is neutral unless specified otherwise | |
114 | - ModelSequence = NONE; | |
115 | - | |
116 | - // Initialize the hash table if necessary | |
117 | - if (MdlHash[Collection].empty()) | |
118 | - { | |
119 | - MdlHash[Collection].resize(MdlHashSize); | |
120 | - objlist_set(&MdlHash[Collection][0],NONE,MdlHashSize); | |
121 | - } | |
122 | - | |
123 | - // Set up a *reference* to the appropriate hashtable entry; | |
124 | - // this makes setting this entry a bit more convenient | |
125 | - ModelHashEntry& HashVal = MdlHash[Collection][MdlHashFunc(Sequence)]; | |
126 | - | |
127 | - // Check to see if the model-data entry is correct; | |
128 | - // if it is, then we're done. | |
129 | - if (HashVal.ModelIndex != NONE) | |
130 | - { | |
131 | - // First, check in the sequence-map table | |
132 | - vector<ModelDataEntry>::iterator MdlIter = MdlList[Collection].begin() + HashVal.ModelIndex; | |
133 | - size_t MSTIndex = static_cast<size_t>(HashVal.ModelSeqTabIndex); // Cast only safe b/c of following check | |
134 | - if (MSTIndex < MdlIter->SequenceMap.size()) | |
135 | - { | |
136 | - vector<SequenceMapEntry>::iterator SMIter = MdlIter->SequenceMap.begin() + MSTIndex; | |
137 | - if (SMIter->Sequence == Sequence) | |
138 | - { | |
139 | - ModelSequence = SMIter->ModelSequence; | |
140 | - return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
141 | - } | |
142 | - } | |
143 | - | |
144 | - // Now check the neutral sequence | |
145 | - if (MdlIter->Sequence == Sequence) | |
146 | - { | |
147 | - return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
148 | - } | |
149 | - } | |
150 | - | |
151 | - // Fallback for the case of a hashtable miss; | |
152 | - // do a linear search and then update the hash entry appropriately. | |
153 | - vector<ModelDataEntry>& ML = MdlList[Collection]; | |
154 | - int16 Indx = 0; | |
155 | - for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++, Indx++) | |
156 | - { | |
157 | - // First, search the sequence-map table | |
158 | - int16 SMIndx = 0; | |
159 | - vector<SequenceMapEntry>& SM = MdlIter->SequenceMap; | |
160 | - for (vector<SequenceMapEntry>::iterator SMIter = SM.begin(); SMIter < SM.end(); SMIter++, SMIndx++) | |
161 | - { | |
162 | - if (SMIter->Sequence == Sequence) | |
163 | - { | |
164 | - HashVal.ModelIndex = Indx; | |
165 | - HashVal.ModelSeqTabIndex = SMIndx; | |
166 | - ModelSequence = SMIter->ModelSequence; | |
167 | - return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
168 | - } | |
169 | - } | |
170 | - | |
171 | - // Now check the neutral sequence | |
172 | - if (MdlIter->Sequence == Sequence) | |
173 | - { | |
174 | - HashVal.ModelIndex = Indx; | |
175 | - HashVal.ModelSeqTabIndex = NONE; | |
176 | - return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
177 | - } | |
178 | - } | |
179 | - | |
180 | - // None found! | |
181 | - return NULL; | |
182 | -} | |
183 | - | |
184 | -int OGL_SkinData::GetMaxSize() | |
185 | -{ | |
186 | - return Get_OGL_ConfigureData().ModelConfig.MaxSize; | |
187 | -} | |
188 | - | |
189 | - | |
190 | -// Any easy STL ways of doing this mapping of functions onto members of arrays? | |
191 | - | |
192 | -void OGL_SkinManager::Load() | |
193 | -{ | |
194 | - for (vector<OGL_SkinData>::iterator SkinIter = SkinData.begin(); SkinIter < SkinData.end(); SkinIter++) | |
195 | - SkinIter->Load(); | |
196 | -} | |
197 | - | |
198 | - | |
199 | -void OGL_SkinManager::Unload() | |
200 | -{ | |
201 | - for (vector<OGL_SkinData>::iterator SkinIter = SkinData.begin(); SkinIter < SkinData.end(); SkinIter++) | |
202 | - SkinIter->Unload(); | |
203 | -} | |
204 | - | |
205 | - | |
206 | -void OGL_SkinManager::Reset(bool Clear_OGL_Txtrs) | |
207 | -{ | |
208 | - if (Clear_OGL_Txtrs) | |
209 | - { | |
210 | - for (int k=0; k<NUMBER_OF_OPENGL_BITMAP_SETS; k++) | |
211 | - for (int l=0; l<NUMBER_OF_TEXTURES; l++) | |
212 | - { | |
213 | - if (IDsInUse[k][l]) | |
214 | - glDeleteTextures(1,&IDs[k][l]); | |
215 | - } | |
216 | - } | |
217 | - | |
218 | - // Mass clearing | |
219 | - objlist_clear(IDsInUse[0],NUMBER_OF_OPENGL_BITMAP_SETS*NUMBER_OF_TEXTURES); | |
220 | -} | |
221 | - | |
222 | - | |
223 | -OGL_SkinData *OGL_SkinManager::GetSkin(short CLUT) | |
224 | -{ | |
225 | - for (unsigned k=0; k<SkinData.size(); k++) | |
226 | - { | |
227 | - OGL_SkinData& Skin = SkinData[k]; | |
228 | - if (Skin.CLUT == CLUT || Skin.CLUT == ALL_CLUTS) | |
229 | - return &Skin; | |
230 | - } | |
231 | - | |
232 | - return NULL; | |
233 | -} | |
234 | - | |
235 | - | |
236 | -bool OGL_SkinManager::Use(short CLUT, short Which) | |
237 | -{ | |
238 | - // References so they can be written into | |
239 | - GLuint& TxtrID = IDs[CLUT][Which]; | |
240 | - bool& InUse = IDsInUse[CLUT][Which]; | |
241 | - bool LoadSkin = false; | |
242 | - if (!InUse) | |
243 | - { | |
244 | - glGenTextures(1,&TxtrID); | |
245 | - InUse = true; | |
246 | - LoadSkin = true; | |
247 | - } | |
248 | - glBindTexture(GL_TEXTURE_2D,TxtrID); | |
249 | - return LoadSkin; | |
250 | -} | |
251 | - | |
252 | - | |
253 | -// Circle constants | |
254 | -const double TWO_PI = 8*atan(1.0); | |
255 | -const double Degree2Radian = TWO_PI/360; // A circle is 2*pi radians | |
256 | - | |
257 | -// Matrix manipulation (can't trust OpenGL to be active, so won't be using OpenGL matrix stuff) | |
258 | - | |
259 | -// Src -> Dest | |
260 | -static void MatCopy(const GLfloat SrcMat[3][3], GLfloat DestMat[3][3]) | |
261 | -{ | |
262 | - objlist_copy(DestMat[0],SrcMat[0],9); | |
263 | -} | |
264 | - | |
265 | -// Sets the arg to that matrix | |
266 | -static void MatIdentity(GLfloat Mat[3][3]) | |
267 | -{ | |
268 | - const GLfloat IMat[3][3] = {{1,0,0},{0,1,0},{0,0,1}}; | |
269 | - MatCopy(IMat,Mat); | |
270 | -} | |
271 | - | |
272 | -// Src1, Src2 -> Dest (cannot be one of the sources) | |
273 | -static void MatMult(const GLfloat Mat1[3][3], const GLfloat Mat2[3][3], GLfloat DestMat[3][3]) | |
274 | -{ | |
275 | - for (int k=0; k<3; k++) | |
276 | - for (int l=0; l<3; l++) | |
277 | - { | |
278 | - GLfloat Sum = 0; | |
279 | - for (int m=0; m<3; m++) | |
280 | - Sum += Mat1[k][m]*Mat2[m][l]; | |
281 | - DestMat[k][l] = Sum; | |
282 | - } | |
283 | -} | |
284 | - | |
285 | -// Alters the matrix in place | |
286 | -static void MatScalMult(GLfloat Mat[3][3], const GLfloat Scale) | |
287 | -{ | |
288 | - for (int k=0; k<3; k++) | |
289 | - { | |
290 | - GLfloat *MatRow = Mat[k]; | |
291 | - for (int l=0; l<3; l++) | |
292 | - MatRow[l] *= Scale; | |
293 | - } | |
294 | -} | |
295 | - | |
296 | -// Src -> Dest vector (cannot be the same location) | |
297 | -static void MatVecMult(const GLfloat Mat[3][3], const GLfloat *SrcVec, GLfloat *DestVec) | |
298 | -{ | |
299 | - for (int k=0; k<3; k++) | |
300 | - { | |
301 | - const GLfloat *MatRow = Mat[k]; | |
302 | - GLfloat Sum = 0; | |
303 | - for (int l=0; l<3; l++) | |
304 | - Sum += MatRow[l]*SrcVec[l]; | |
305 | - DestVec[k] = Sum; | |
306 | - } | |
307 | -} | |
308 | - | |
309 | - | |
310 | -inline bool StringPresent(vector<char>& String) | |
311 | -{ | |
312 | - return (String.size() > 1); | |
313 | -} | |
314 | - | |
315 | - | |
316 | -void OGL_ModelData::Load() | |
317 | -{ | |
318 | - // Already loaded? | |
319 | - if (ModelPresent()) return; | |
320 | - | |
321 | - // Load the model | |
322 | - Model.Clear(); | |
323 | - | |
324 | - if (ModelFile == FileSpecifier()) return; | |
325 | - if (!ModelFile.Exists()) return; | |
326 | - | |
327 | - bool Success = false; | |
328 | - | |
329 | - char *Type = &ModelType[0]; | |
330 | - if (StringsEqual(Type,"wave",4)) | |
331 | - { | |
332 | - // Alias|Wavefront, backward compatible version | |
333 | - Success = LoadModel_Wavefront(ModelFile, Model); | |
334 | - } | |
335 | - else if (StringsEqual(Type,"obj",3)) | |
336 | - { | |
337 | - // Alias|Wavefront, but with coordinate system conversion. | |
338 | - Success = LoadModel_Wavefront_RightHand(ModelFile, Model); | |
339 | - } | |
340 | - else if (StringsEqual(Type,"3ds",3)) | |
341 | - { | |
342 | - // 3D Studio Max, backward compatible version | |
343 | - Success = LoadModel_Studio(ModelFile, Model); | |
344 | - } | |
345 | - else if (StringsEqual(Type,"max",3)) | |
346 | - { | |
347 | - // 3D Studio Max, but with coordinate system conversion. | |
348 | - Success = LoadModel_Studio_RightHand(ModelFile, Model); | |
349 | - } | |
350 | - else if (StringsEqual(Type,"dim3",4)) | |
351 | - { | |
352 | - // Brian Barnes's "Dim3" model format (first pass: model geometry) | |
353 | - Success = LoadModel_Dim3(ModelFile, Model, LoadModelDim3_First); | |
354 | - | |
355 | - // Second and third passes: frames and sequences | |
356 | - try | |
357 | - { | |
358 | - if (ModelFile1 == FileSpecifier()) throw 0; | |
359 | - if (!ModelFile1.Exists()) throw 0; | |
360 | - if (!LoadModel_Dim3(ModelFile1, Model, LoadModelDim3_Rest)) throw 0; | |
361 | - } | |
362 | - catch(...) | |
363 | - {} | |
364 | - // | |
365 | - try | |
366 | - { | |
367 | - if (ModelFile2 == FileSpecifier()) throw 0; | |
368 | - if (!ModelFile2.Exists()) throw 0; | |
369 | - if (!LoadModel_Dim3(ModelFile2, Model, LoadModelDim3_Rest)) throw 0; | |
370 | - } | |
371 | - catch(...) | |
372 | - {} | |
373 | - } | |
374 | -#if HAVE_QUESA | |
375 | - else if (StringsEqual(Type,"qd3d") || StringsEqual(Type,"3dmf") || StringsEqual(Type,"quesa")) | |
376 | - { | |
377 | - // QuickDraw 3D / Quesa | |
378 | - Success = LoadModel_QD3D(ModelFile, Model); | |
379 | - } | |
380 | -#endif | |
381 | - | |
382 | - if (!Success) | |
383 | - { | |
384 | - Model.Clear(); | |
385 | - return; | |
386 | - } | |
387 | - | |
388 | - // Calculate transformation matrix | |
389 | - GLfloat Angle, Cosine, Sine; | |
390 | - GLfloat RotMatrix[3][3], NewRotMatrix[3][3], IndivRotMatrix[3][3]; | |
391 | - MatIdentity(RotMatrix); | |
392 | - | |
393 | - MatIdentity(IndivRotMatrix); | |
394 | - Angle = (float)Degree2Radian*XRot; | |
395 | - Cosine = (float)cos(Angle); | |
396 | - Sine = (float)sin(Angle); | |
397 | - IndivRotMatrix[1][1] = Cosine; | |
398 | - IndivRotMatrix[1][2] = - Sine; | |
399 | - IndivRotMatrix[2][1] = Sine; | |
400 | - IndivRotMatrix[2][2] = Cosine; | |
401 | - MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); | |
402 | - MatCopy(NewRotMatrix,RotMatrix); | |
403 | - | |
404 | - MatIdentity(IndivRotMatrix); | |
405 | - Angle = (float)Degree2Radian*YRot; | |
406 | - Cosine = (float)cos(Angle); | |
407 | - Sine = (float)sin(Angle); | |
408 | - IndivRotMatrix[2][2] = Cosine; | |
409 | - IndivRotMatrix[2][0] = - Sine; | |
410 | - IndivRotMatrix[0][2] = Sine; | |
411 | - IndivRotMatrix[0][0] = Cosine; | |
412 | - MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); | |
413 | - MatCopy(NewRotMatrix,RotMatrix); | |
414 | - | |
415 | - MatIdentity(IndivRotMatrix); | |
416 | - Angle = (float)Degree2Radian*ZRot; | |
417 | - Cosine = (float)cos(Angle); | |
418 | - Sine = (float)sin(Angle); | |
419 | - IndivRotMatrix[0][0] = Cosine; | |
420 | - IndivRotMatrix[0][1] = - Sine; | |
421 | - IndivRotMatrix[1][0] = Sine; | |
422 | - IndivRotMatrix[1][1] = Cosine; | |
423 | - MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); | |
424 | - MatCopy(NewRotMatrix,RotMatrix); | |
425 | - | |
426 | - MatScalMult(NewRotMatrix,Scale); // For the position vertices | |
427 | - if (Scale < 0) MatScalMult(RotMatrix,-1); // For the normals | |
428 | - | |
429 | - // Is model animated or static? | |
430 | - // Test by trying to find neutral positions (useful for working with the normals later on) | |
431 | - if (Model.FindPositions_Neutral(false)) | |
432 | - { | |
433 | - // Copy over the vector and normal transformation matrices: | |
434 | - for (int k=0; k<3; k++) | |
435 | - for (int l=0; l<3; l++) | |
436 | - { | |
437 | - Model.TransformPos.M[k][l] = NewRotMatrix[k][l]; | |
438 | - Model.TransformNorm.M[k][l] = RotMatrix[k][l]; | |
439 | - } | |
440 | - | |
441 | - Model.TransformPos.M[0][3] = XShift; | |
442 | - Model.TransformPos.M[1][3] = YShift; | |
443 | - Model.TransformPos.M[2][3] = ZShift; | |
444 | - | |
445 | - // Find the transformed bounding box: | |
446 | - bool RestOfCorners = false; | |
447 | - GLfloat NewBoundingBox[2][3]; | |
448 | - // The indices i1, i2, and i3 are for selecting which of the box's two principal corners | |
449 | - // to get coordinates from | |
450 | - for (int i1=0; i1<2; i1++) | |
451 | - { | |
452 | - GLfloat X = Model.BoundingBox[i1][0]; | |
453 | - for (int i2=0; i2<2; i2++) | |
454 | - { | |
455 | - GLfloat Y = Model.BoundingBox[i2][0]; | |
456 | - for (int i3=0; i3<2; i3++) | |
457 | - { | |
458 | - GLfloat Z = Model.BoundingBox[i3][0]; | |
459 | - | |
460 | - GLfloat Corner[3]; | |
461 | - for (int ic=0; ic<3; ic++) | |
462 | - { | |
463 | - GLfloat *Row = Model.TransformPos.M[ic]; | |
464 | - Corner[ic] = Row[0]*X + Row[1]*Y + Row[2]*Z + Row[3]; | |
465 | - } | |
466 | - | |
467 | - if (RestOfCorners) | |
468 | - { | |
469 | - // Find minimum and maximum for each coordinate | |
470 | - for (int ic=0; ic<3; ic++) | |
471 | - { | |
472 | - NewBoundingBox[0][ic] = min(NewBoundingBox[0][ic],Corner[ic]); | |
473 | - NewBoundingBox[1][ic] = max(NewBoundingBox[1][ic],Corner[ic]); | |
474 | - } | |
475 | - } | |
476 | - else | |
477 | - { | |
478 | - // Simply copy it in: | |
479 | - for (int ic=0; ic<3; ic++) | |
480 | - NewBoundingBox[0][ic] = NewBoundingBox[1][ic] = Corner[ic]; | |
481 | - RestOfCorners = true; | |
482 | - } | |
483 | - } | |
484 | - } | |
485 | - } | |
486 | - | |
487 | - for (int ic=0; ic<2; ic++) | |
488 | - objlist_copy(Model.BoundingBox[ic],NewBoundingBox[ic],3); | |
489 | - } | |
490 | - else | |
491 | - { | |
492 | - // Static model | |
493 | - size_t NumVerts = Model.Positions.size()/3; | |
494 | - | |
495 | - for (size_t k=0; k<NumVerts; k++) | |
496 | - { | |
497 | - GLfloat *Pos = Model.PosBase() + 3*k; | |
498 | - GLfloat NewPos[3]; | |
499 | - MatVecMult(NewRotMatrix,Pos,NewPos); // Has the scaling | |
500 | - Pos[0] = NewPos[0] + XShift; | |
501 | - Pos[1] = NewPos[1] + YShift; | |
502 | - Pos[2] = NewPos[2] + ZShift; | |
503 | - } | |
504 | - | |
505 | - size_t NumNorms = Model.Normals.size()/3; | |
506 | - for (size_t k=0; k<NumNorms; k++) | |
507 | - { | |
508 | - GLfloat *Norms = Model.NormBase() + 3*k; | |
509 | - GLfloat NewNorms[3]; | |
510 | - MatVecMult(RotMatrix,Norms,NewNorms); // Not scaled | |
511 | - objlist_copy(Norms,NewNorms,3); | |
512 | - } | |
513 | - | |
514 | - // So as to be consistent with the new points | |
515 | - Model.FindBoundingBox(); | |
516 | - } | |
517 | - | |
518 | - Model.AdjustNormals(NormalType,NormalSplit); | |
519 | - Model.CalculateTangents(); | |
520 | - | |
521 | - // Don't forget the skins | |
522 | - OGL_SkinManager::Load(); | |
523 | -} | |
524 | - | |
525 | - | |
526 | -void OGL_ModelData::Unload() | |
527 | -{ | |
528 | - Model.Clear(); | |
529 | - OGL_ResetForceSpriteDepth(); | |
530 | - | |
531 | - // Don't forget the skins | |
532 | - OGL_SkinManager::Unload(); | |
533 | -} | |
534 | - | |
535 | -int OGL_CountModels(short Collection) | |
536 | -{ | |
537 | - return MdlList[Collection].size(); | |
538 | -} | |
539 | - | |
540 | -extern void OGL_ProgressCallback(int); | |
541 | - | |
542 | -static bool ForcingSpriteDepth = false; | |
543 | -void OGL_ResetForceSpriteDepth() { ForcingSpriteDepth = false; } | |
544 | -bool OGL_ForceSpriteDepth() { return ForcingSpriteDepth; } | |
545 | - | |
546 | -// for managing the model and image loading and unloading | |
547 | -void OGL_LoadModels(short Collection) | |
548 | -{ | |
549 | - vector<ModelDataEntry>& ML = MdlList[Collection]; | |
550 | - for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
551 | - { | |
552 | - MdlIter->ModelData.Load(); | |
553 | - if (MdlIter->ModelData.ForceSpriteDepth) | |
554 | - { | |
555 | - ForcingSpriteDepth = true; | |
556 | - } | |
557 | - OGL_ProgressCallback(1); | |
558 | - } | |
559 | -} | |
560 | - | |
561 | -void OGL_UnloadModels(short Collection) | |
562 | -{ | |
563 | - vector<ModelDataEntry>& ML = MdlList[Collection]; | |
564 | - for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
565 | - { | |
566 | - MdlIter->ModelData.Unload(); | |
567 | - } | |
568 | -} | |
569 | - | |
570 | - | |
571 | -// Reset model skins; used in OGL_ResetTextures() in OGL_Textures.cpp | |
572 | -void OGL_ResetModelSkins(bool Clear_OGL_Txtrs) | |
573 | -{ | |
574 | - for (int ic=0; ic<MAXIMUM_COLLECTIONS; ic++) | |
575 | - { | |
576 | - vector<ModelDataEntry>& ML = MdlList[ic]; | |
577 | - for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
578 | - { | |
579 | - MdlIter->ModelData.Reset(Clear_OGL_Txtrs); | |
580 | - } | |
581 | - } | |
582 | -} | |
583 | - | |
584 | -class XML_SkinDataParser: public XML_ElementParser | |
585 | -{ | |
586 | - short CLUT; | |
587 | - | |
588 | - OGL_SkinData Data; | |
589 | -public: | |
590 | - bool Start(); | |
591 | - bool HandleAttribute(const char *Tag, const char *Value); | |
592 | - bool AttributesDone(); | |
593 | - | |
594 | - vector<OGL_SkinData> *SkinDataPtr; | |
595 | - | |
596 | - XML_SkinDataParser(): XML_ElementParser("skin"), SkinDataPtr(NULL) {} | |
597 | -}; | |
598 | - | |
599 | -bool XML_SkinDataParser::Start() | |
600 | -{ | |
601 | - Data = DefaultSkinData; | |
602 | - | |
603 | - return true; | |
604 | -} | |
605 | - | |
606 | -bool XML_SkinDataParser::HandleAttribute(const char *Tag, const char *Value) | |
607 | -{ | |
608 | - if (StringsEqual(Tag,"clut")) | |
609 | - { | |
610 | - return ReadBoundedInt16Value(Value,Data.CLUT,short(ALL_CLUTS),short(SILHOUETTE_BITMAP_SET)); | |
611 | - } | |
612 | - else if (StringsEqual(Tag,"opac_type")) | |
613 | - { | |
614 | - return ReadBoundedInt16Value(Value,Data.OpacityType,0,OGL_NUMBER_OF_OPACITY_TYPES-1); | |
615 | - } | |
616 | - else if (StringsEqual(Tag,"opac_scale")) | |
617 | - { | |
618 | - return ReadFloatValue(Value,Data.OpacityScale); | |
619 | - } | |
620 | - else if (StringsEqual(Tag,"opac_shift")) | |
621 | - { | |
622 | - return ReadFloatValue(Value,Data.OpacityShift); | |
623 | - } | |
624 | - else if (StringsEqual(Tag,"normal_image")) | |
625 | - { | |
626 | - Data.NormalColors.SetNameWithPath(Value); | |
627 | - return true; | |
628 | - } | |
629 | - else if (StringsEqual(Tag,"offset_image")) | |
630 | - { | |
631 | - Data.OffsetMap.SetNameWithPath(Value); | |
632 | - return true; | |
633 | - } | |
634 | - else if (StringsEqual(Tag,"normal_mask")) | |
635 | - { | |
636 | - Data.NormalMask.SetNameWithPath(Value); | |
637 | - return true; | |
638 | - } | |
639 | - else if (StringsEqual(Tag,"glow_image")) | |
640 | - { | |
641 | - Data.GlowColors.SetNameWithPath(Value); | |
642 | - return true; | |
643 | - } | |
644 | - else if (StringsEqual(Tag,"glow_mask")) | |
645 | - { | |
646 | - Data.GlowMask.SetNameWithPath(Value); | |
647 | - return true; | |
648 | - } | |
649 | - else if (StringsEqual(Tag,"normal_blend")) | |
650 | - { | |
651 | - return ReadBoundedInt16Value(Value,Data.NormalBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
652 | - } | |
653 | - else if (StringsEqual(Tag,"glow_blend")) | |
654 | - { | |
655 | - return ReadBoundedInt16Value(Value,Data.GlowBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
656 | - } | |
657 | - else if (StringsEqual(Tag,"normal_bloom_scale")) | |
658 | - { | |
659 | - return ReadFloatValue(Value,Data.BloomScale); | |
660 | - } | |
661 | - else if (StringsEqual(Tag,"normal_bloom_shift")) | |
662 | - { | |
663 | - return ReadFloatValue(Value,Data.BloomShift); | |
664 | - } | |
665 | - else if (StringsEqual(Tag,"glow_bloom_scale")) | |
666 | - { | |
667 | - return ReadFloatValue(Value,Data.GlowBloomScale); | |
668 | - } | |
669 | - else if (StringsEqual(Tag,"glow_bloom_shift")) | |
670 | - { | |
671 | - return ReadFloatValue(Value,Data.GlowBloomShift); | |
672 | - } | |
673 | - else if (StringsEqual(Tag,"minimum_glow_intensity")) | |
674 | - { | |
675 | - return ReadFloatValue(Value,Data.MinGlowIntensity); | |
676 | - } | |
677 | - UnrecognizedTag(); | |
678 | - return false; | |
679 | -} | |
680 | - | |
681 | -bool XML_SkinDataParser::AttributesDone() | |
682 | -{ | |
683 | - // Check to see if a frame is already accounted for | |
684 | - assert(SkinDataPtr); | |
685 | - vector<OGL_SkinData>& SkinData = *SkinDataPtr; | |
686 | - for (vector<OGL_SkinData>::iterator SDIter = SkinData.begin(); SDIter < SkinData.end(); SDIter++) | |
687 | - { | |
688 | - if (SDIter->CLUT == Data.CLUT) | |
689 | - { | |
690 | - // Replace the data | |
691 | - *SDIter = Data; | |
692 | - return true; | |
693 | - } | |
694 | - } | |
695 | - | |
696 | - // If not, then add a new frame entry | |
697 | - SkinData.push_back(Data); | |
698 | - | |
699 | - return true; | |
700 | -} | |
701 | - | |
702 | -static XML_SkinDataParser SkinDataParser; | |
703 | - | |
704 | - | |
705 | -// For mapping Marathon-engine sequences onto model sequences | |
706 | -class XML_SequenceMapParser: public XML_ElementParser | |
707 | -{ | |
708 | - bool SeqIsPresent, ModelSeqIsPresent; | |
709 | - SequenceMapEntry Data; | |
710 | - | |
711 | -public: | |
712 | - bool Start(); | |
713 | - bool HandleAttribute(const char *Tag, const char *Value); | |
714 | - bool AttributesDone(); | |
715 | - | |
716 | - vector<SequenceMapEntry> *SeqMapPtr; | |
717 | - | |
718 | - XML_SequenceMapParser(): XML_ElementParser("seq_map") {} | |
719 | -}; | |
720 | - | |
721 | -bool XML_SequenceMapParser::Start() | |
722 | -{ | |
723 | - Data.Sequence = Data.ModelSequence = NONE; | |
724 | - SeqIsPresent = ModelSeqIsPresent = false; | |
725 | - return true; | |
726 | -} | |
727 | - | |
728 | -bool XML_SequenceMapParser::HandleAttribute(const char *Tag, const char *Value) | |
729 | -{ | |
730 | - if (StringsEqual(Tag,"seq")) | |
731 | - { | |
732 | - if (ReadBoundedInt16Value(Value,Data.Sequence,0,MAXIMUM_SHAPES_PER_COLLECTION-1)) | |
733 | - { | |
734 | - SeqIsPresent = true; | |
735 | - return true; | |
736 | - } | |
737 | - else return false; | |
738 | - } | |
739 | - else if (StringsEqual(Tag,"model_seq")) | |
740 | - { | |
741 | - if (ReadBoundedInt16Value(Value,Data.ModelSequence,NONE,MAXIMUM_SHAPES_PER_COLLECTION-1)) | |
742 | - { | |
743 | - ModelSeqIsPresent = true; | |
744 | - return true; | |
745 | - } | |
746 | - else return false; | |
747 | - } | |
748 | - UnrecognizedTag(); | |
749 | - return false; | |
750 | -} | |
751 | - | |
752 | -bool XML_SequenceMapParser::AttributesDone() | |
753 | -{ | |
754 | - // Verify... | |
755 | - if (!SeqIsPresent || !ModelSeqIsPresent) | |
756 | - { | |
757 | - AttribsMissing(); | |
758 | - return false; | |
759 | - } | |
760 | - | |
761 | - // Add the entry | |
762 | - vector<SequenceMapEntry>& SeqMap = *SeqMapPtr; | |
763 | - SeqMap.push_back(Data); | |
764 | - return true; | |
765 | -} | |
766 | - | |
767 | -static XML_SequenceMapParser SequenceMapParser; | |
768 | - | |
769 | - | |
770 | -class XML_MdlClearParser: public XML_ElementParser | |
771 | -{ | |
772 | - bool IsPresent; | |
773 | - short Collection; | |
774 | - | |
775 | -public: | |
776 | - bool Start(); | |
777 | - bool HandleAttribute(const char *Tag, const char *Value); | |
778 | - bool AttributesDone(); | |
779 | - | |
780 | - XML_MdlClearParser(): XML_ElementParser("model_clear") {} | |
781 | -}; | |
782 | - | |
783 | -bool XML_MdlClearParser::Start() | |
784 | -{ | |
785 | - IsPresent = false; | |
786 | - return true; | |
787 | -} | |
788 | - | |
789 | -bool XML_MdlClearParser::HandleAttribute(const char *Tag, const char *Value) | |
790 | -{ | |
791 | - if (StringsEqual(Tag,"coll")) | |
792 | - { | |
793 | - if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
794 | - { | |
795 | - IsPresent = true; | |
796 | - return true; | |
797 | - } | |
798 | - else return false; | |
799 | - } | |
800 | - UnrecognizedTag(); | |
801 | - return false; | |
802 | -} | |
803 | - | |
804 | -bool XML_MdlClearParser::AttributesDone() | |
805 | -{ | |
806 | - if (IsPresent) | |
807 | - MdlDelete(Collection); | |
808 | - else | |
809 | - MdlDeleteAll(); | |
810 | - | |
811 | - return true; | |
812 | -} | |
813 | - | |
814 | -static XML_MdlClearParser Mdl_ClearParser; | |
815 | - | |
816 | - | |
817 | -class XML_ModelDataParser: public XML_ElementParser | |
818 | -{ | |
819 | - bool CollIsPresent; | |
820 | - short Collection, Sequence; | |
821 | - vector<SequenceMapEntry> SequenceMap; | |
822 | - | |
823 | - OGL_ModelData Data; | |
824 | - | |
825 | -public: | |
826 | - bool Start(); | |
827 | - bool ReadSignValue(const char *String, int16& Value); | |
828 | - bool HandleAttribute(const char *Tag, const char *Value); | |
829 | - bool AttributesDone(); | |
830 | - bool ResetValues(); | |
831 | - bool End(); | |
832 | - | |
833 | - XML_ModelDataParser(): XML_ElementParser("model") {} | |
834 | -}; | |
835 | - | |
836 | -bool XML_ModelDataParser::Start() | |
837 | -{ | |
838 | - Data = DefaultModelData; | |
839 | - CollIsPresent = false; | |
840 | - Sequence = NONE; | |
841 | - SequenceMap.clear(); | |
842 | - | |
843 | - // For doing the model skins | |
844 | - SkinDataParser.SkinDataPtr = &Data.SkinData; | |
845 | - | |
846 | - // For doing the sequence mapping | |
847 | - SequenceMapParser.SeqMapPtr = &SequenceMap; | |
848 | - | |
849 | - return true; | |
850 | -} | |
851 | - | |
852 | -bool XML_ModelDataParser::ReadSignValue(const char *String, int16& Value) | |
853 | -{ | |
854 | - if (StringsEqual(String, "+")) | |
855 | - { | |
856 | - Value = 1; | |
857 | - return true; | |
858 | - } | |
859 | - else if (StringsEqual(String, "-")) | |
860 | - { | |
861 | - Value = -1; | |
862 | - return true; | |
863 | - } | |
864 | - return ReadInt16Value(String, Value); | |
865 | -} | |
866 | - | |
867 | -bool XML_ModelDataParser::HandleAttribute(const char *Tag, const char *Value) | |
868 | -{ | |
869 | - if (StringsEqual(Tag,"coll")) | |
870 | - { | |
871 | - if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
872 | - { | |
873 | - CollIsPresent = true; | |
874 | - return true; | |
875 | - } | |
876 | - else return false; | |
877 | - } | |
878 | - else if (StringsEqual(Tag,"seq")) | |
879 | - { | |
880 | - return (ReadBoundedInt16Value(Value,Sequence,0,MAXIMUM_SHAPES_PER_COLLECTION-1)); | |
881 | - } | |
882 | - else if (StringsEqual(Tag,"scale")) | |
883 | - { | |
884 | - return ReadFloatValue(Value,Data.Scale); | |
885 | - } | |
886 | - else if (StringsEqual(Tag,"x_rot")) | |
887 | - { | |
888 | - return ReadFloatValue(Value,Data.XRot); | |
889 | - } | |
890 | - else if (StringsEqual(Tag,"y_rot")) | |
891 | - { | |
892 | - return ReadFloatValue(Value,Data.YRot); | |
893 | - } | |
894 | - else if (StringsEqual(Tag,"z_rot")) | |
895 | - { | |
896 | - return ReadFloatValue(Value,Data.ZRot); | |
897 | - } | |
898 | - else if (StringsEqual(Tag,"x_shift")) | |
899 | - { | |
900 | - return ReadFloatValue(Value,Data.XShift); | |
901 | - } | |
902 | - else if (StringsEqual(Tag,"y_shift")) | |
903 | - { | |
904 | - return ReadFloatValue(Value,Data.YShift); | |
905 | - } | |
906 | - else if (StringsEqual(Tag,"z_shift")) | |
907 | - { | |
908 | - return ReadFloatValue(Value,Data.ZShift); | |
909 | - } | |
910 | - else if (StringsEqual(Tag,"side")) | |
911 | - { | |
912 | - return ReadSignValue(Value,Data.Sidedness); | |
913 | - } | |
914 | - else if (StringsEqual(Tag,"norm_type")) | |
915 | - { | |
916 | - return ReadBoundedInt16Value(Value,Data.NormalType,0,Model3D::NUMBER_OF_NORMAL_TYPES-1); | |
917 | - } | |
918 | - else if (StringsEqual(Tag,"norm_split")) | |
919 | - { | |
920 | - return ReadFloatValue(Value,Data.NormalSplit); | |
921 | - } | |
922 | - else if (StringsEqual(Tag,"light_type")) | |
923 | - { | |
924 | - return ReadBoundedInt16Value(Value,Data.LightType,0,NUMBER_OF_MODEL_LIGHT_TYPES-1); | |
925 | - } | |
926 | - else if (StringsEqual(Tag,"depth_type")) | |
927 | - { | |
928 | - return ReadSignValue(Value,Data.DepthType); | |
929 | - } | |
930 | - else if (StringsEqual(Tag,"force_sprite_depth")) | |
931 | - { | |
932 | - return ReadBooleanValue(Value,Data.ForceSpriteDepth); | |
933 | - } | |
934 | - else if (StringsEqual(Tag,"file")) | |
935 | - { | |
936 | - Data.ModelFile.SetNameWithPath(Value); | |
937 | - return true; | |
938 | - } | |
939 | - else if (StringsEqual(Tag,"file1")) | |
940 | - { | |
941 | - Data.ModelFile1.SetNameWithPath(Value); | |
942 | - return true; | |
943 | - } | |
944 | - else if (StringsEqual(Tag,"file2")) | |
945 | - { | |
946 | - Data.ModelFile2.SetNameWithPath(Value); | |
947 | - return true; | |
948 | - } | |
949 | - else if (StringsEqual(Tag,"type")) | |
950 | - { | |
951 | - size_t nchars = strlen(Value)+1; | |
952 | - Data.ModelType.resize(nchars); | |
953 | - memcpy(&Data.ModelType[0],Value,nchars); | |
954 | - return true; | |
955 | - } | |
956 | - UnrecognizedTag(); | |
957 | - return false; | |
958 | -} | |
959 | - | |
960 | -bool XML_ModelDataParser::AttributesDone() | |
961 | -{ | |
962 | - // Verify... | |
963 | - if (!CollIsPresent) | |
964 | - { | |
965 | - AttribsMissing(); | |
966 | - return false; | |
967 | - } | |
968 | - return true; | |
969 | -} | |
970 | - | |
971 | -bool XML_ModelDataParser::ResetValues() | |
972 | -{ | |
973 | - MdlDeleteAll(); | |
974 | - return true; | |
975 | -} | |
976 | - | |
977 | -bool XML_ModelDataParser::End() | |
978 | -{ | |
979 | - // Do this at the end because the model data will then include the skin data | |
980 | - | |
981 | - // Check to see if a frame is already accounted for | |
982 | - vector<ModelDataEntry>& ML = MdlList[Collection]; | |
983 | - for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
984 | - { | |
985 | - // Run a gauntlet of equality tests | |
986 | - if (MdlIter->Sequence != Sequence) continue; | |
987 | - | |
988 | - if (MdlIter->SequenceMap.size() != SequenceMap.size()) continue; | |
989 | - | |
990 | - // Ought to sort, then compare, for correct results | |
991 | - bool AllEqual = true; | |
992 | - for (size_t q=0; q<SequenceMap.size(); q++) | |
993 | - { | |
994 | - SequenceMapEntry& MS = MdlIter->SequenceMap[q]; | |
995 | - SequenceMapEntry& S = SequenceMap[q]; | |
996 | - if (MS.Sequence != S.Sequence || MS.ModelSequence != S.ModelSequence) | |
997 | - { | |
998 | - AllEqual = false; | |
999 | - break; | |
1000 | - } | |
1001 | - } | |
1002 | - if (!AllEqual) continue; | |
1003 | - | |
1004 | - // Replace the data; it passed the tests | |
1005 | - MdlIter->ModelData = Data; | |
1006 | - return true; | |
1007 | - } | |
1008 | - | |
1009 | - // If not, then add a new frame entry | |
1010 | - ModelDataEntry DataEntry; | |
1011 | - DataEntry.Sequence = Sequence; | |
1012 | - DataEntry.SequenceMap.swap(SequenceMap); // Quick transfer of contents in desired direction | |
1013 | - DataEntry.ModelData = Data; | |
1014 | - ML.push_back(DataEntry); | |
1015 | - | |
1016 | - return true; | |
1017 | -} | |
1018 | - | |
1019 | -static XML_ModelDataParser ModelDataParser; | |
1020 | - | |
1021 | - | |
1022 | -// XML-parser support: | |
1023 | -XML_ElementParser *ModelData_GetParser() | |
1024 | -{ | |
1025 | - ModelDataParser.AddChild(&SkinDataParser); | |
1026 | - ModelDataParser.AddChild(&SequenceMapParser); | |
1027 | - | |
1028 | - return &ModelDataParser; | |
1029 | -} | |
1030 | -XML_ElementParser *Mdl_Clear_GetParser() {return &Mdl_ClearParser;} | |
1031 | - | |
1032 | -#endif | |
1 | +/* | |
2 | + | |
3 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | + OpenGL Model-Definition File | |
21 | + by Loren Petrich, | |
22 | + May 11, 2003 | |
23 | + | |
24 | + This contains the definitions of all the OpenGL models and skins | |
25 | +*/ | |
26 | + | |
27 | +#include "cseries.h" | |
28 | +#include "OGL_Model_Def.h" | |
29 | +#include "OGL_Setup.h" | |
30 | + | |
31 | +#ifdef HAVE_OPENGL | |
32 | + | |
33 | +#include <cmath> | |
34 | + | |
35 | +#include "Dim3_Loader.h" | |
36 | +#include "StudioLoader.h" | |
37 | +#include "WavefrontLoader.h" | |
38 | +#include "QD3D_Loader.h" | |
39 | + | |
40 | + | |
41 | +// Model-data stuff; | |
42 | +// defaults for whatever might need them | |
43 | +// Including skin stuff for convenience here | |
44 | +static OGL_ModelData DefaultModelData; | |
45 | +static OGL_SkinData DefaultSkinData; | |
46 | + | |
47 | + | |
48 | +// For mapping Marathon-physics sequences onto model sequences | |
49 | +struct SequenceMapEntry | |
50 | +{ | |
51 | + int16 Sequence; | |
52 | + int16 ModelSequence; | |
53 | +}; | |
54 | + | |
55 | + | |
56 | +// Store model-data stuff in a set of STL vectors | |
57 | +struct ModelDataEntry | |
58 | +{ | |
59 | + // Which Marathon-engine sequence gets translated into this model, | |
60 | + // if static, or the neutral sequence, if dynamic | |
61 | + short Sequence; | |
62 | + | |
63 | + vector<SequenceMapEntry> SequenceMap; | |
64 | + | |
65 | + // Make a member for more convenient access | |
66 | + OGL_ModelData ModelData; | |
67 | + | |
68 | + ModelDataEntry(): Sequence(NONE) {} | |
69 | +}; | |
70 | + | |
71 | + | |
72 | +// Separate model-data sequence lists for each collection ID, | |
73 | +// to speed up searching | |
74 | +static vector<ModelDataEntry> MdlList[NUMBER_OF_COLLECTIONS]; | |
75 | + | |
76 | +// Will look up both the model index and its sequence | |
77 | +struct ModelHashEntry | |
78 | +{ | |
79 | + int16 ModelIndex; | |
80 | + int16 ModelSeqTabIndex; | |
81 | +}; | |
82 | + | |
83 | +// Model-data hash table for extra-fast searching: | |
84 | +static vector<ModelHashEntry> MdlHash[NUMBER_OF_COLLECTIONS]; | |
85 | + | |
86 | +// Hash-table size and function | |
87 | +const int MdlHashSize = 1 << 8; | |
88 | +const int MdlHashMask = MdlHashSize - 1; | |
89 | +inline uint8 MdlHashFunc(short Sequence) | |
90 | +{ | |
91 | + // E-Z | |
92 | + return (uint8)(Sequence & MdlHashMask); | |
93 | +} | |
94 | + | |
95 | + | |
96 | +// Deletes a collection's model-data sequences | |
97 | +static void MdlDelete(short Collection) | |
98 | +{ | |
99 | + int c = Collection; | |
100 | + MdlList[c].clear(); | |
101 | + MdlHash[c].clear(); | |
102 | +} | |
103 | + | |
104 | +// Deletes all of them | |
105 | +static void MdlDeleteAll() | |
106 | +{ | |
107 | + for (int c=0; c<NUMBER_OF_COLLECTIONS; c++) MdlDelete(c); | |
108 | +} | |
109 | + | |
110 | + | |
111 | +OGL_ModelData *OGL_GetModelData(short Collection, short Sequence, short& ModelSequence) | |
112 | +{ | |
113 | + // Model is neutral unless specified otherwise | |
114 | + ModelSequence = NONE; | |
115 | + | |
116 | + // Initialize the hash table if necessary | |
117 | + if (MdlHash[Collection].empty()) | |
118 | + { | |
119 | + MdlHash[Collection].resize(MdlHashSize); | |
120 | + objlist_set(&MdlHash[Collection][0],NONE,MdlHashSize); | |
121 | + } | |
122 | + | |
123 | + // Set up a *reference* to the appropriate hashtable entry; | |
124 | + // this makes setting this entry a bit more convenient | |
125 | + ModelHashEntry& HashVal = MdlHash[Collection][MdlHashFunc(Sequence)]; | |
126 | + | |
127 | + // Check to see if the model-data entry is correct; | |
128 | + // if it is, then we're done. | |
129 | + if (HashVal.ModelIndex != NONE) | |
130 | + { | |
131 | + // First, check in the sequence-map table | |
132 | + vector<ModelDataEntry>::iterator MdlIter = MdlList[Collection].begin() + HashVal.ModelIndex; | |
133 | + size_t MSTIndex = static_cast<size_t>(HashVal.ModelSeqTabIndex); // Cast only safe b/c of following check | |
134 | + if (MSTIndex < MdlIter->SequenceMap.size()) | |
135 | + { | |
136 | + vector<SequenceMapEntry>::iterator SMIter = MdlIter->SequenceMap.begin() + MSTIndex; | |
137 | + if (SMIter->Sequence == Sequence) | |
138 | + { | |
139 | + ModelSequence = SMIter->ModelSequence; | |
140 | + return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
141 | + } | |
142 | + } | |
143 | + | |
144 | + // Now check the neutral sequence | |
145 | + if (MdlIter->Sequence == Sequence) | |
146 | + { | |
147 | + return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
148 | + } | |
149 | + } | |
150 | + | |
151 | + // Fallback for the case of a hashtable miss; | |
152 | + // do a linear search and then update the hash entry appropriately. | |
153 | + vector<ModelDataEntry>& ML = MdlList[Collection]; | |
154 | + int16 Indx = 0; | |
155 | + for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++, Indx++) | |
156 | + { | |
157 | + // First, search the sequence-map table | |
158 | + int16 SMIndx = 0; | |
159 | + vector<SequenceMapEntry>& SM = MdlIter->SequenceMap; | |
160 | + for (vector<SequenceMapEntry>::iterator SMIter = SM.begin(); SMIter < SM.end(); SMIter++, SMIndx++) | |
161 | + { | |
162 | + if (SMIter->Sequence == Sequence) | |
163 | + { | |
164 | + HashVal.ModelIndex = Indx; | |
165 | + HashVal.ModelSeqTabIndex = SMIndx; | |
166 | + ModelSequence = SMIter->ModelSequence; | |
167 | + return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
168 | + } | |
169 | + } | |
170 | + | |
171 | + // Now check the neutral sequence | |
172 | + if (MdlIter->Sequence == Sequence) | |
173 | + { | |
174 | + HashVal.ModelIndex = Indx; | |
175 | + HashVal.ModelSeqTabIndex = NONE; | |
176 | + return MdlIter->ModelData.ModelPresent() ? &MdlIter->ModelData : NULL; | |
177 | + } | |
178 | + } | |
179 | + | |
180 | + // None found! | |
181 | + return NULL; | |
182 | +} | |
183 | + | |
184 | +int OGL_SkinData::GetMaxSize() | |
185 | +{ | |
186 | + return Get_OGL_ConfigureData().ModelConfig.MaxSize; | |
187 | +} | |
188 | + | |
189 | + | |
190 | +// Any easy STL ways of doing this mapping of functions onto members of arrays? | |
191 | + | |
192 | +void OGL_SkinManager::Load() | |
193 | +{ | |
194 | + for (vector<OGL_SkinData>::iterator SkinIter = SkinData.begin(); SkinIter < SkinData.end(); SkinIter++) | |
195 | + SkinIter->Load(); | |
196 | +} | |
197 | + | |
198 | + | |
199 | +void OGL_SkinManager::Unload() | |
200 | +{ | |
201 | + for (vector<OGL_SkinData>::iterator SkinIter = SkinData.begin(); SkinIter < SkinData.end(); SkinIter++) | |
202 | + SkinIter->Unload(); | |
203 | +} | |
204 | + | |
205 | + | |
206 | +void OGL_SkinManager::Reset(bool Clear_OGL_Txtrs) | |
207 | +{ | |
208 | + if (Clear_OGL_Txtrs) | |
209 | + { | |
210 | + for (int k=0; k<NUMBER_OF_OPENGL_BITMAP_SETS; k++) | |
211 | + for (int l=0; l<NUMBER_OF_TEXTURES; l++) | |
212 | + { | |
213 | + if (IDsInUse[k][l]) | |
214 | + glDeleteTextures(1,&IDs[k][l]); | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + // Mass clearing | |
219 | + objlist_clear(IDsInUse[0],NUMBER_OF_OPENGL_BITMAP_SETS*NUMBER_OF_TEXTURES); | |
220 | +} | |
221 | + | |
222 | + | |
223 | +OGL_SkinData *OGL_SkinManager::GetSkin(short CLUT) | |
224 | +{ | |
225 | + for (unsigned k=0; k<SkinData.size(); k++) | |
226 | + { | |
227 | + OGL_SkinData& Skin = SkinData[k]; | |
228 | + if (Skin.CLUT == CLUT || Skin.CLUT == ALL_CLUTS) | |
229 | + return &Skin; | |
230 | + } | |
231 | + | |
232 | + return NULL; | |
233 | +} | |
234 | + | |
235 | + | |
236 | +bool OGL_SkinManager::Use(short CLUT, short Which) | |
237 | +{ | |
238 | + // References so they can be written into | |
239 | + GLuint& TxtrID = IDs[CLUT][Which]; | |
240 | + bool& InUse = IDsInUse[CLUT][Which]; | |
241 | + bool LoadSkin = false; | |
242 | + if (!InUse) | |
243 | + { | |
244 | + glGenTextures(1,&TxtrID); | |
245 | + InUse = true; | |
246 | + LoadSkin = true; | |
247 | + } | |
248 | + glBindTexture(GL_TEXTURE_2D,TxtrID); | |
249 | + return LoadSkin; | |
250 | +} | |
251 | + | |
252 | + | |
253 | +// Circle constants | |
254 | +const double TWO_PI = 8*atan(1.0); | |
255 | +const double Degree2Radian = TWO_PI/360; // A circle is 2*pi radians | |
256 | + | |
257 | +// Matrix manipulation (can't trust OpenGL to be active, so won't be using OpenGL matrix stuff) | |
258 | + | |
259 | +// Src -> Dest | |
260 | +static void MatCopy(const GLfloat SrcMat[3][3], GLfloat DestMat[3][3]) | |
261 | +{ | |
262 | + objlist_copy(DestMat[0],SrcMat[0],9); | |
263 | +} | |
264 | + | |
265 | +// Sets the arg to that matrix | |
266 | +static void MatIdentity(GLfloat Mat[3][3]) | |
267 | +{ | |
268 | + const GLfloat IMat[3][3] = {{1,0,0},{0,1,0},{0,0,1}}; | |
269 | + MatCopy(IMat,Mat); | |
270 | +} | |
271 | + | |
272 | +// Src1, Src2 -> Dest (cannot be one of the sources) | |
273 | +static void MatMult(const GLfloat Mat1[3][3], const GLfloat Mat2[3][3], GLfloat DestMat[3][3]) | |
274 | +{ | |
275 | + for (int k=0; k<3; k++) | |
276 | + for (int l=0; l<3; l++) | |
277 | + { | |
278 | + GLfloat Sum = 0; | |
279 | + for (int m=0; m<3; m++) | |
280 | + Sum += Mat1[k][m]*Mat2[m][l]; | |
281 | + DestMat[k][l] = Sum; | |
282 | + } | |
283 | +} | |
284 | + | |
285 | +// Alters the matrix in place | |
286 | +static void MatScalMult(GLfloat Mat[3][3], const GLfloat Scale) | |
287 | +{ | |
288 | + for (int k=0; k<3; k++) | |
289 | + { | |
290 | + GLfloat *MatRow = Mat[k]; | |
291 | + for (int l=0; l<3; l++) | |
292 | + MatRow[l] *= Scale; | |
293 | + } | |
294 | +} | |
295 | + | |
296 | +// Src -> Dest vector (cannot be the same location) | |
297 | +static void MatVecMult(const GLfloat Mat[3][3], const GLfloat *SrcVec, GLfloat *DestVec) | |
298 | +{ | |
299 | + for (int k=0; k<3; k++) | |
300 | + { | |
301 | + const GLfloat *MatRow = Mat[k]; | |
302 | + GLfloat Sum = 0; | |
303 | + for (int l=0; l<3; l++) | |
304 | + Sum += MatRow[l]*SrcVec[l]; | |
305 | + DestVec[k] = Sum; | |
306 | + } | |
307 | +} | |
308 | + | |
309 | + | |
310 | +inline bool StringPresent(vector<char>& String) | |
311 | +{ | |
312 | + return (String.size() > 1); | |
313 | +} | |
314 | + | |
315 | + | |
316 | +void OGL_ModelData::Load() | |
317 | +{ | |
318 | + // Already loaded? | |
319 | + if (ModelPresent()) return; | |
320 | + | |
321 | + // Load the model | |
322 | + Model.Clear(); | |
323 | + | |
324 | + if (ModelFile == FileSpecifier()) return; | |
325 | + if (!ModelFile.Exists()) return; | |
326 | + | |
327 | + bool Success = false; | |
328 | + | |
329 | + char *Type = &ModelType[0]; | |
330 | + if (StringsEqual(Type,"wave",4)) | |
331 | + { | |
332 | + // Alias|Wavefront, backward compatible version | |
333 | + Success = LoadModel_Wavefront(ModelFile, Model); | |
334 | + } | |
335 | + else if (StringsEqual(Type,"obj",3)) | |
336 | + { | |
337 | + // Alias|Wavefront, but with coordinate system conversion. | |
338 | + Success = LoadModel_Wavefront_RightHand(ModelFile, Model); | |
339 | + } | |
340 | + else if (StringsEqual(Type,"3ds",3)) | |
341 | + { | |
342 | + // 3D Studio Max, backward compatible version | |
343 | + Success = LoadModel_Studio(ModelFile, Model); | |
344 | + } | |
345 | + else if (StringsEqual(Type,"max",3)) | |
346 | + { | |
347 | + // 3D Studio Max, but with coordinate system conversion. | |
348 | + Success = LoadModel_Studio_RightHand(ModelFile, Model); | |
349 | + } | |
350 | + else if (StringsEqual(Type,"dim3",4)) | |
351 | + { | |
352 | + // Brian Barnes's "Dim3" model format (first pass: model geometry) | |
353 | + Success = LoadModel_Dim3(ModelFile, Model, LoadModelDim3_First); | |
354 | + | |
355 | + // Second and third passes: frames and sequences | |
356 | + try | |
357 | + { | |
358 | + if (ModelFile1 == FileSpecifier()) throw 0; | |
359 | + if (!ModelFile1.Exists()) throw 0; | |
360 | + if (!LoadModel_Dim3(ModelFile1, Model, LoadModelDim3_Rest)) throw 0; | |
361 | + } | |
362 | + catch(...) | |
363 | + {} | |
364 | + // | |
365 | + try | |
366 | + { | |
367 | + if (ModelFile2 == FileSpecifier()) throw 0; | |
368 | + if (!ModelFile2.Exists()) throw 0; | |
369 | + if (!LoadModel_Dim3(ModelFile2, Model, LoadModelDim3_Rest)) throw 0; | |
370 | + } | |
371 | + catch(...) | |
372 | + {} | |
373 | + } | |
374 | +#if HAVE_QUESA | |
375 | + else if (StringsEqual(Type,"qd3d") || StringsEqual(Type,"3dmf") || StringsEqual(Type,"quesa")) | |
376 | + { | |
377 | + // QuickDraw 3D / Quesa | |
378 | + Success = LoadModel_QD3D(ModelFile, Model); | |
379 | + } | |
380 | +#endif | |
381 | + | |
382 | + if (!Success) | |
383 | + { | |
384 | + Model.Clear(); | |
385 | + return; | |
386 | + } | |
387 | + | |
388 | + // Calculate transformation matrix | |
389 | + GLfloat Angle, Cosine, Sine; | |
390 | + GLfloat RotMatrix[3][3], NewRotMatrix[3][3], IndivRotMatrix[3][3]; | |
391 | + MatIdentity(RotMatrix); | |
392 | + | |
393 | + MatIdentity(IndivRotMatrix); | |
394 | + Angle = (float)Degree2Radian*XRot; | |
395 | + Cosine = (float)cos(Angle); | |
396 | + Sine = (float)sin(Angle); | |
397 | + IndivRotMatrix[1][1] = Cosine; | |
398 | + IndivRotMatrix[1][2] = - Sine; | |
399 | + IndivRotMatrix[2][1] = Sine; | |
400 | + IndivRotMatrix[2][2] = Cosine; | |
401 | + MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); | |
402 | + MatCopy(NewRotMatrix,RotMatrix); | |
403 | + | |
404 | + MatIdentity(IndivRotMatrix); | |
405 | + Angle = (float)Degree2Radian*YRot; | |
406 | + Cosine = (float)cos(Angle); | |
407 | + Sine = (float)sin(Angle); | |
408 | + IndivRotMatrix[2][2] = Cosine; | |
409 | + IndivRotMatrix[2][0] = - Sine; | |
410 | + IndivRotMatrix[0][2] = Sine; | |
411 | + IndivRotMatrix[0][0] = Cosine; | |
412 | + MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); | |
413 | + MatCopy(NewRotMatrix,RotMatrix); | |
414 | + | |
415 | + MatIdentity(IndivRotMatrix); | |
416 | + Angle = (float)Degree2Radian*ZRot; | |
417 | + Cosine = (float)cos(Angle); | |
418 | + Sine = (float)sin(Angle); | |
419 | + IndivRotMatrix[0][0] = Cosine; | |
420 | + IndivRotMatrix[0][1] = - Sine; | |
421 | + IndivRotMatrix[1][0] = Sine; | |
422 | + IndivRotMatrix[1][1] = Cosine; | |
423 | + MatMult(IndivRotMatrix,RotMatrix,NewRotMatrix); | |
424 | + MatCopy(NewRotMatrix,RotMatrix); | |
425 | + | |
426 | + MatScalMult(NewRotMatrix,Scale); // For the position vertices | |
427 | + if (Scale < 0) MatScalMult(RotMatrix,-1); // For the normals | |
428 | + | |
429 | + // Is model animated or static? | |
430 | + // Test by trying to find neutral positions (useful for working with the normals later on) | |
431 | + if (Model.FindPositions_Neutral(false)) | |
432 | + { | |
433 | + // Copy over the vector and normal transformation matrices: | |
434 | + for (int k=0; k<3; k++) | |
435 | + for (int l=0; l<3; l++) | |
436 | + { | |
437 | + Model.TransformPos.M[k][l] = NewRotMatrix[k][l]; | |
438 | + Model.TransformNorm.M[k][l] = RotMatrix[k][l]; | |
439 | + } | |
440 | + | |
441 | + Model.TransformPos.M[0][3] = XShift; | |
442 | + Model.TransformPos.M[1][3] = YShift; | |
443 | + Model.TransformPos.M[2][3] = ZShift; | |
444 | + | |
445 | + // Find the transformed bounding box: | |
446 | + bool RestOfCorners = false; | |
447 | + GLfloat NewBoundingBox[2][3]; | |
448 | + // The indices i1, i2, and i3 are for selecting which of the box's two principal corners | |
449 | + // to get coordinates from | |
450 | + for (int i1=0; i1<2; i1++) | |
451 | + { | |
452 | + GLfloat X = Model.BoundingBox[i1][0]; | |
453 | + for (int i2=0; i2<2; i2++) | |
454 | + { | |
455 | + GLfloat Y = Model.BoundingBox[i2][0]; | |
456 | + for (int i3=0; i3<2; i3++) | |
457 | + { | |
458 | + GLfloat Z = Model.BoundingBox[i3][0]; | |
459 | + | |
460 | + GLfloat Corner[3]; | |
461 | + for (int ic=0; ic<3; ic++) | |
462 | + { | |
463 | + GLfloat *Row = Model.TransformPos.M[ic]; | |
464 | + Corner[ic] = Row[0]*X + Row[1]*Y + Row[2]*Z + Row[3]; | |
465 | + } | |
466 | + | |
467 | + if (RestOfCorners) | |
468 | + { | |
469 | + // Find minimum and maximum for each coordinate | |
470 | + for (int ic=0; ic<3; ic++) | |
471 | + { | |
472 | + NewBoundingBox[0][ic] = min(NewBoundingBox[0][ic],Corner[ic]); | |
473 | + NewBoundingBox[1][ic] = max(NewBoundingBox[1][ic],Corner[ic]); | |
474 | + } | |
475 | + } | |
476 | + else | |
477 | + { | |
478 | + // Simply copy it in: | |
479 | + for (int ic=0; ic<3; ic++) | |
480 | + NewBoundingBox[0][ic] = NewBoundingBox[1][ic] = Corner[ic]; | |
481 | + RestOfCorners = true; | |
482 | + } | |
483 | + } | |
484 | + } | |
485 | + } | |
486 | + | |
487 | + for (int ic=0; ic<2; ic++) | |
488 | + objlist_copy(Model.BoundingBox[ic],NewBoundingBox[ic],3); | |
489 | + } | |
490 | + else | |
491 | + { | |
492 | + // Static model | |
493 | + size_t NumVerts = Model.Positions.size()/3; | |
494 | + | |
495 | + for (size_t k=0; k<NumVerts; k++) | |
496 | + { | |
497 | + GLfloat *Pos = Model.PosBase() + 3*k; | |
498 | + GLfloat NewPos[3]; | |
499 | + MatVecMult(NewRotMatrix,Pos,NewPos); // Has the scaling | |
500 | + Pos[0] = NewPos[0] + XShift; | |
501 | + Pos[1] = NewPos[1] + YShift; | |
502 | + Pos[2] = NewPos[2] + ZShift; | |
503 | + } | |
504 | + | |
505 | + size_t NumNorms = Model.Normals.size()/3; | |
506 | + for (size_t k=0; k<NumNorms; k++) | |
507 | + { | |
508 | + GLfloat *Norms = Model.NormBase() + 3*k; | |
509 | + GLfloat NewNorms[3]; | |
510 | + MatVecMult(RotMatrix,Norms,NewNorms); // Not scaled | |
511 | + objlist_copy(Norms,NewNorms,3); | |
512 | + } | |
513 | + | |
514 | + // So as to be consistent with the new points | |
515 | + Model.FindBoundingBox(); | |
516 | + } | |
517 | + | |
518 | + Model.AdjustNormals(NormalType,NormalSplit); | |
519 | + Model.CalculateTangents(); | |
520 | + | |
521 | + // Don't forget the skins | |
522 | + OGL_SkinManager::Load(); | |
523 | +} | |
524 | + | |
525 | + | |
526 | +void OGL_ModelData::Unload() | |
527 | +{ | |
528 | + Model.Clear(); | |
529 | + OGL_ResetForceSpriteDepth(); | |
530 | + | |
531 | + // Don't forget the skins | |
532 | + OGL_SkinManager::Unload(); | |
533 | +} | |
534 | + | |
535 | +int OGL_CountModels(short Collection) | |
536 | +{ | |
537 | + return MdlList[Collection].size(); | |
538 | +} | |
539 | + | |
540 | +extern void OGL_ProgressCallback(int); | |
541 | + | |
542 | +static bool ForcingSpriteDepth = false; | |
543 | +void OGL_ResetForceSpriteDepth() { ForcingSpriteDepth = false; } | |
544 | +bool OGL_ForceSpriteDepth() { return ForcingSpriteDepth; } | |
545 | + | |
546 | +// for managing the model and image loading and unloading | |
547 | +void OGL_LoadModels(short Collection) | |
548 | +{ | |
549 | + vector<ModelDataEntry>& ML = MdlList[Collection]; | |
550 | + for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
551 | + { | |
552 | + MdlIter->ModelData.Load(); | |
553 | + if (MdlIter->ModelData.ForceSpriteDepth) | |
554 | + { | |
555 | + ForcingSpriteDepth = true; | |
556 | + } | |
557 | + OGL_ProgressCallback(1); | |
558 | + } | |
559 | +} | |
560 | + | |
561 | +void OGL_UnloadModels(short Collection) | |
562 | +{ | |
563 | + vector<ModelDataEntry>& ML = MdlList[Collection]; | |
564 | + for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
565 | + { | |
566 | + MdlIter->ModelData.Unload(); | |
567 | + } | |
568 | +} | |
569 | + | |
570 | + | |
571 | +// Reset model skins; used in OGL_ResetTextures() in OGL_Textures.cpp | |
572 | +void OGL_ResetModelSkins(bool Clear_OGL_Txtrs) | |
573 | +{ | |
574 | + for (int ic=0; ic<MAXIMUM_COLLECTIONS; ic++) | |
575 | + { | |
576 | + vector<ModelDataEntry>& ML = MdlList[ic]; | |
577 | + for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
578 | + { | |
579 | + MdlIter->ModelData.Reset(Clear_OGL_Txtrs); | |
580 | + } | |
581 | + } | |
582 | +} | |
583 | + | |
584 | +class XML_SkinDataParser: public XML_ElementParser | |
585 | +{ | |
586 | + short CLUT; | |
587 | + | |
588 | + OGL_SkinData Data; | |
589 | +public: | |
590 | + bool Start(); | |
591 | + bool HandleAttribute(const char *Tag, const char *Value); | |
592 | + bool AttributesDone(); | |
593 | + | |
594 | + vector<OGL_SkinData> *SkinDataPtr; | |
595 | + | |
596 | + XML_SkinDataParser(): XML_ElementParser("skin"), SkinDataPtr(NULL) {} | |
597 | +}; | |
598 | + | |
599 | +bool XML_SkinDataParser::Start() | |
600 | +{ | |
601 | + Data = DefaultSkinData; | |
602 | + | |
603 | + return true; | |
604 | +} | |
605 | + | |
606 | +bool XML_SkinDataParser::HandleAttribute(const char *Tag, const char *Value) | |
607 | +{ | |
608 | + if (StringsEqual(Tag,"clut")) | |
609 | + { | |
610 | + return ReadBoundedInt16Value(Value,Data.CLUT,short(ALL_CLUTS),short(SILHOUETTE_BITMAP_SET)); | |
611 | + } | |
612 | + else if (StringsEqual(Tag,"opac_type")) | |
613 | + { | |
614 | + return ReadBoundedInt16Value(Value,Data.OpacityType,0,OGL_NUMBER_OF_OPACITY_TYPES-1); | |
615 | + } | |
616 | + else if (StringsEqual(Tag,"opac_scale")) | |
617 | + { | |
618 | + return ReadFloatValue(Value,Data.OpacityScale); | |
619 | + } | |
620 | + else if (StringsEqual(Tag,"opac_shift")) | |
621 | + { | |
622 | + return ReadFloatValue(Value,Data.OpacityShift); | |
623 | + } | |
624 | + else if (StringsEqual(Tag,"normal_image")) | |
625 | + { | |
626 | + Data.NormalColors.SetNameWithPath(Value); | |
627 | + return true; | |
628 | + } | |
629 | + else if (StringsEqual(Tag,"offset_image")) | |
630 | + { | |
631 | + Data.OffsetMap.SetNameWithPath(Value); | |
632 | + return true; | |
633 | + } | |
634 | + else if (StringsEqual(Tag,"normal_mask")) | |
635 | + { | |
636 | + Data.NormalMask.SetNameWithPath(Value); | |
637 | + return true; | |
638 | + } | |
639 | + else if (StringsEqual(Tag,"glow_image")) | |
640 | + { | |
641 | + Data.GlowColors.SetNameWithPath(Value); | |
642 | + return true; | |
643 | + } | |
644 | + else if (StringsEqual(Tag,"glow_mask")) | |
645 | + { | |
646 | + Data.GlowMask.SetNameWithPath(Value); | |
647 | + return true; | |
648 | + } | |
649 | + else if (StringsEqual(Tag,"normal_blend")) | |
650 | + { | |
651 | + return ReadBoundedInt16Value(Value,Data.NormalBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
652 | + } | |
653 | + else if (StringsEqual(Tag,"glow_blend")) | |
654 | + { | |
655 | + return ReadBoundedInt16Value(Value,Data.GlowBlend,0,OGL_NUMBER_OF_BLEND_TYPES-1); | |
656 | + } | |
657 | + else if (StringsEqual(Tag,"normal_bloom_scale")) | |
658 | + { | |
659 | + return ReadFloatValue(Value,Data.BloomScale); | |
660 | + } | |
661 | + else if (StringsEqual(Tag,"normal_bloom_shift")) | |
662 | + { | |
663 | + return ReadFloatValue(Value,Data.BloomShift); | |
664 | + } | |
665 | + else if (StringsEqual(Tag,"glow_bloom_scale")) | |
666 | + { | |
667 | + return ReadFloatValue(Value,Data.GlowBloomScale); | |
668 | + } | |
669 | + else if (StringsEqual(Tag,"glow_bloom_shift")) | |
670 | + { | |
671 | + return ReadFloatValue(Value,Data.GlowBloomShift); | |
672 | + } | |
673 | + else if (StringsEqual(Tag,"minimum_glow_intensity")) | |
674 | + { | |
675 | + return ReadFloatValue(Value,Data.MinGlowIntensity); | |
676 | + } | |
677 | + UnrecognizedTag(); | |
678 | + return false; | |
679 | +} | |
680 | + | |
681 | +bool XML_SkinDataParser::AttributesDone() | |
682 | +{ | |
683 | + // Check to see if a frame is already accounted for | |
684 | + assert(SkinDataPtr); | |
685 | + vector<OGL_SkinData>& SkinData = *SkinDataPtr; | |
686 | + for (vector<OGL_SkinData>::iterator SDIter = SkinData.begin(); SDIter < SkinData.end(); SDIter++) | |
687 | + { | |
688 | + if (SDIter->CLUT == Data.CLUT) | |
689 | + { | |
690 | + // Replace the data | |
691 | + *SDIter = Data; | |
692 | + return true; | |
693 | + } | |
694 | + } | |
695 | + | |
696 | + // If not, then add a new frame entry | |
697 | + SkinData.push_back(Data); | |
698 | + | |
699 | + return true; | |
700 | +} | |
701 | + | |
702 | +static XML_SkinDataParser SkinDataParser; | |
703 | + | |
704 | + | |
705 | +// For mapping Marathon-engine sequences onto model sequences | |
706 | +class XML_SequenceMapParser: public XML_ElementParser | |
707 | +{ | |
708 | + bool SeqIsPresent, ModelSeqIsPresent; | |
709 | + SequenceMapEntry Data; | |
710 | + | |
711 | +public: | |
712 | + bool Start(); | |
713 | + bool HandleAttribute(const char *Tag, const char *Value); | |
714 | + bool AttributesDone(); | |
715 | + | |
716 | + vector<SequenceMapEntry> *SeqMapPtr; | |
717 | + | |
718 | + XML_SequenceMapParser(): XML_ElementParser("seq_map") {} | |
719 | +}; | |
720 | + | |
721 | +bool XML_SequenceMapParser::Start() | |
722 | +{ | |
723 | + Data.Sequence = Data.ModelSequence = NONE; | |
724 | + SeqIsPresent = ModelSeqIsPresent = false; | |
725 | + return true; | |
726 | +} | |
727 | + | |
728 | +bool XML_SequenceMapParser::HandleAttribute(const char *Tag, const char *Value) | |
729 | +{ | |
730 | + if (StringsEqual(Tag,"seq")) | |
731 | + { | |
732 | + if (ReadBoundedInt16Value(Value,Data.Sequence,0,MAXIMUM_SHAPES_PER_COLLECTION-1)) | |
733 | + { | |
734 | + SeqIsPresent = true; | |
735 | + return true; | |
736 | + } | |
737 | + else return false; | |
738 | + } | |
739 | + else if (StringsEqual(Tag,"model_seq")) | |
740 | + { | |
741 | + if (ReadBoundedInt16Value(Value,Data.ModelSequence,NONE,MAXIMUM_SHAPES_PER_COLLECTION-1)) | |
742 | + { | |
743 | + ModelSeqIsPresent = true; | |
744 | + return true; | |
745 | + } | |
746 | + else return false; | |
747 | + } | |
748 | + UnrecognizedTag(); | |
749 | + return false; | |
750 | +} | |
751 | + | |
752 | +bool XML_SequenceMapParser::AttributesDone() | |
753 | +{ | |
754 | + // Verify... | |
755 | + if (!SeqIsPresent || !ModelSeqIsPresent) | |
756 | + { | |
757 | + AttribsMissing(); | |
758 | + return false; | |
759 | + } | |
760 | + | |
761 | + // Add the entry | |
762 | + vector<SequenceMapEntry>& SeqMap = *SeqMapPtr; | |
763 | + SeqMap.push_back(Data); | |
764 | + return true; | |
765 | +} | |
766 | + | |
767 | +static XML_SequenceMapParser SequenceMapParser; | |
768 | + | |
769 | + | |
770 | +class XML_MdlClearParser: public XML_ElementParser | |
771 | +{ | |
772 | + bool IsPresent; | |
773 | + short Collection; | |
774 | + | |
775 | +public: | |
776 | + bool Start(); | |
777 | + bool HandleAttribute(const char *Tag, const char *Value); | |
778 | + bool AttributesDone(); | |
779 | + | |
780 | + XML_MdlClearParser(): XML_ElementParser("model_clear") {} | |
781 | +}; | |
782 | + | |
783 | +bool XML_MdlClearParser::Start() | |
784 | +{ | |
785 | + IsPresent = false; | |
786 | + return true; | |
787 | +} | |
788 | + | |
789 | +bool XML_MdlClearParser::HandleAttribute(const char *Tag, const char *Value) | |
790 | +{ | |
791 | + if (StringsEqual(Tag,"coll")) | |
792 | + { | |
793 | + if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
794 | + { | |
795 | + IsPresent = true; | |
796 | + return true; | |
797 | + } | |
798 | + else return false; | |
799 | + } | |
800 | + UnrecognizedTag(); | |
801 | + return false; | |
802 | +} | |
803 | + | |
804 | +bool XML_MdlClearParser::AttributesDone() | |
805 | +{ | |
806 | + if (IsPresent) | |
807 | + MdlDelete(Collection); | |
808 | + else | |
809 | + MdlDeleteAll(); | |
810 | + | |
811 | + return true; | |
812 | +} | |
813 | + | |
814 | +static XML_MdlClearParser Mdl_ClearParser; | |
815 | + | |
816 | + | |
817 | +class XML_ModelDataParser: public XML_ElementParser | |
818 | +{ | |
819 | + bool CollIsPresent; | |
820 | + short Collection, Sequence; | |
821 | + vector<SequenceMapEntry> SequenceMap; | |
822 | + | |
823 | + OGL_ModelData Data; | |
824 | + | |
825 | +public: | |
826 | + bool Start(); | |
827 | + bool ReadSignValue(const char *String, int16& Value); | |
828 | + bool HandleAttribute(const char *Tag, const char *Value); | |
829 | + bool AttributesDone(); | |
830 | + bool ResetValues(); | |
831 | + bool End(); | |
832 | + | |
833 | + XML_ModelDataParser(): XML_ElementParser("model") {} | |
834 | +}; | |
835 | + | |
836 | +bool XML_ModelDataParser::Start() | |
837 | +{ | |
838 | + Data = DefaultModelData; | |
839 | + CollIsPresent = false; | |
840 | + Sequence = NONE; | |
841 | + SequenceMap.clear(); | |
842 | + | |
843 | + // For doing the model skins | |
844 | + SkinDataParser.SkinDataPtr = &Data.SkinData; | |
845 | + | |
846 | + // For doing the sequence mapping | |
847 | + SequenceMapParser.SeqMapPtr = &SequenceMap; | |
848 | + | |
849 | + return true; | |
850 | +} | |
851 | + | |
852 | +bool XML_ModelDataParser::ReadSignValue(const char *String, int16& Value) | |
853 | +{ | |
854 | + if (StringsEqual(String, "+")) | |
855 | + { | |
856 | + Value = 1; | |
857 | + return true; | |
858 | + } | |
859 | + else if (StringsEqual(String, "-")) | |
860 | + { | |
861 | + Value = -1; | |
862 | + return true; | |
863 | + } | |
864 | + return ReadInt16Value(String, Value); | |
865 | +} | |
866 | + | |
867 | +bool XML_ModelDataParser::HandleAttribute(const char *Tag, const char *Value) | |
868 | +{ | |
869 | + if (StringsEqual(Tag,"coll")) | |
870 | + { | |
871 | + if (ReadBoundedInt16Value(Value,Collection,0,NUMBER_OF_COLLECTIONS-1)) | |
872 | + { | |
873 | + CollIsPresent = true; | |
874 | + return true; | |
875 | + } | |
876 | + else return false; | |
877 | + } | |
878 | + else if (StringsEqual(Tag,"seq")) | |
879 | + { | |
880 | + return (ReadBoundedInt16Value(Value,Sequence,0,MAXIMUM_SHAPES_PER_COLLECTION-1)); | |
881 | + } | |
882 | + else if (StringsEqual(Tag,"scale")) | |
883 | + { | |
884 | + return ReadFloatValue(Value,Data.Scale); | |
885 | + } | |
886 | + else if (StringsEqual(Tag,"x_rot")) | |
887 | + { | |
888 | + return ReadFloatValue(Value,Data.XRot); | |
889 | + } | |
890 | + else if (StringsEqual(Tag,"y_rot")) | |
891 | + { | |
892 | + return ReadFloatValue(Value,Data.YRot); | |
893 | + } | |
894 | + else if (StringsEqual(Tag,"z_rot")) | |
895 | + { | |
896 | + return ReadFloatValue(Value,Data.ZRot); | |
897 | + } | |
898 | + else if (StringsEqual(Tag,"x_shift")) | |
899 | + { | |
900 | + return ReadFloatValue(Value,Data.XShift); | |
901 | + } | |
902 | + else if (StringsEqual(Tag,"y_shift")) | |
903 | + { | |
904 | + return ReadFloatValue(Value,Data.YShift); | |
905 | + } | |
906 | + else if (StringsEqual(Tag,"z_shift")) | |
907 | + { | |
908 | + return ReadFloatValue(Value,Data.ZShift); | |
909 | + } | |
910 | + else if (StringsEqual(Tag,"side")) | |
911 | + { | |
912 | + return ReadSignValue(Value,Data.Sidedness); | |
913 | + } | |
914 | + else if (StringsEqual(Tag,"norm_type")) | |
915 | + { | |
916 | + return ReadBoundedInt16Value(Value,Data.NormalType,0,Model3D::NUMBER_OF_NORMAL_TYPES-1); | |
917 | + } | |
918 | + else if (StringsEqual(Tag,"norm_split")) | |
919 | + { | |
920 | + return ReadFloatValue(Value,Data.NormalSplit); | |
921 | + } | |
922 | + else if (StringsEqual(Tag,"light_type")) | |
923 | + { | |
924 | + return ReadBoundedInt16Value(Value,Data.LightType,0,NUMBER_OF_MODEL_LIGHT_TYPES-1); | |
925 | + } | |
926 | + else if (StringsEqual(Tag,"depth_type")) | |
927 | + { | |
928 | + return ReadSignValue(Value,Data.DepthType); | |
929 | + } | |
930 | + else if (StringsEqual(Tag,"force_sprite_depth")) | |
931 | + { | |
932 | + return ReadBooleanValue(Value,Data.ForceSpriteDepth); | |
933 | + } | |
934 | + else if (StringsEqual(Tag,"file")) | |
935 | + { | |
936 | + Data.ModelFile.SetNameWithPath(Value); | |
937 | + return true; | |
938 | + } | |
939 | + else if (StringsEqual(Tag,"file1")) | |
940 | + { | |
941 | + Data.ModelFile1.SetNameWithPath(Value); | |
942 | + return true; | |
943 | + } | |
944 | + else if (StringsEqual(Tag,"file2")) | |
945 | + { | |
946 | + Data.ModelFile2.SetNameWithPath(Value); | |
947 | + return true; | |
948 | + } | |
949 | + else if (StringsEqual(Tag,"type")) | |
950 | + { | |
951 | + size_t nchars = strlen(Value)+1; | |
952 | + Data.ModelType.resize(nchars); | |
953 | + memcpy(&Data.ModelType[0],Value,nchars); | |
954 | + return true; | |
955 | + } | |
956 | + UnrecognizedTag(); | |
957 | + return false; | |
958 | +} | |
959 | + | |
960 | +bool XML_ModelDataParser::AttributesDone() | |
961 | +{ | |
962 | + // Verify... | |
963 | + if (!CollIsPresent) | |
964 | + { | |
965 | + AttribsMissing(); | |
966 | + return false; | |
967 | + } | |
968 | + return true; | |
969 | +} | |
970 | + | |
971 | +bool XML_ModelDataParser::ResetValues() | |
972 | +{ | |
973 | + MdlDeleteAll(); | |
974 | + return true; | |
975 | +} | |
976 | + | |
977 | +bool XML_ModelDataParser::End() | |
978 | +{ | |
979 | + // Do this at the end because the model data will then include the skin data | |
980 | + | |
981 | + // Check to see if a frame is already accounted for | |
982 | + vector<ModelDataEntry>& ML = MdlList[Collection]; | |
983 | + for (vector<ModelDataEntry>::iterator MdlIter = ML.begin(); MdlIter < ML.end(); MdlIter++) | |
984 | + { | |
985 | + // Run a gauntlet of equality tests | |
986 | + if (MdlIter->Sequence != Sequence) continue; | |
987 | + | |
988 | + if (MdlIter->SequenceMap.size() != SequenceMap.size()) continue; | |
989 | + | |
990 | + // Ought to sort, then compare, for correct results | |
991 | + bool AllEqual = true; | |
992 | + for (size_t q=0; q<SequenceMap.size(); q++) | |
993 | + { | |
994 | + SequenceMapEntry& MS = MdlIter->SequenceMap[q]; | |
995 | + SequenceMapEntry& S = SequenceMap[q]; | |
996 | + if (MS.Sequence != S.Sequence || MS.ModelSequence != S.ModelSequence) | |
997 | + { | |
998 | + AllEqual = false; | |
999 | + break; | |
1000 | + } | |
1001 | + } | |
1002 | + if (!AllEqual) continue; | |
1003 | + | |
1004 | + // Replace the data; it passed the tests | |
1005 | + MdlIter->ModelData = Data; | |
1006 | + return true; | |
1007 | + } | |
1008 | + | |
1009 | + // If not, then add a new frame entry | |
1010 | + ModelDataEntry DataEntry; | |
1011 | + DataEntry.Sequence = Sequence; | |
1012 | + DataEntry.SequenceMap.swap(SequenceMap); // Quick transfer of contents in desired direction | |
1013 | + DataEntry.ModelData = Data; | |
1014 | + ML.push_back(DataEntry); | |
1015 | + | |
1016 | + return true; | |
1017 | +} | |
1018 | + | |
1019 | +static XML_ModelDataParser ModelDataParser; | |
1020 | + | |
1021 | + | |
1022 | +// XML-parser support: | |
1023 | +XML_ElementParser *ModelData_GetParser() | |
1024 | +{ | |
1025 | + ModelDataParser.AddChild(&SkinDataParser); | |
1026 | + ModelDataParser.AddChild(&SequenceMapParser); | |
1027 | + | |
1028 | + return &ModelDataParser; | |
1029 | +} | |
1030 | +XML_ElementParser *Mdl_Clear_GetParser() {return &Mdl_ClearParser;} | |
1031 | + | |
1032 | +#endif |
@@ -1,185 +1,185 @@ | ||
1 | -/* | |
2 | - | |
3 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | - and the "Aleph One" developers. | |
5 | - | |
6 | - This program is free software; you can redistribute it and/or modify | |
7 | - it under the terms of the GNU General Public License as published by | |
8 | - the Free Software Foundation; either version 3 of the License, or | |
9 | - (at your option) any later version. | |
10 | - | |
11 | - This program is distributed in the hope that it will be useful, | |
12 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | - GNU General Public License for more details. | |
15 | - | |
16 | - This license is contained in the file "COPYING", | |
17 | - which is included with this source code; it is available online at | |
18 | - http://www.gnu.org/licenses/gpl.html | |
19 | - | |
20 | - February 21, 2000 (Loren Petrich) | |
21 | - | |
22 | - Crosshairs-implementation file; contains Quickdraw crosshairs code. | |
23 | - | |
24 | -Jan 25, 2002 (Br'fin (Jeremy Parsons)): | |
25 | - Added accessors for datafields now opaque in Carbon | |
26 | - | |
27 | -Jun 26, 2002 (Loren Petrich): | |
28 | - Added support for crosshairs being circular and/or partially transparent | |
29 | -*/ | |
30 | - | |
31 | -#include "cseries.h" | |
32 | -#include "Crosshairs.h" | |
33 | - | |
34 | - | |
35 | -static bool _Crosshairs_IsActive = false; | |
36 | - | |
37 | -bool Crosshairs_IsActive() {return _Crosshairs_IsActive;} | |
38 | -bool Crosshairs_SetActive(bool NewState) {return (_Crosshairs_IsActive = (NewState != 0));} | |
39 | - | |
40 | - | |
41 | -// Will draw in center of view rectangle in the current QD graphics context; | |
42 | -// modeled after fps display. | |
43 | -// It won't try to clip the ends of the lines. | |
44 | -bool Crosshairs_Render(Rect &ViewRect) | |
45 | -{ | |
46 | - if (!_Crosshairs_IsActive) return _Crosshairs_IsActive; | |
47 | - | |
48 | - // Get the crosshair data | |
49 | - CrosshairData &Crosshairs = GetCrosshairData(); | |
50 | - | |
51 | - // Push previous state | |
52 | - PenState OldPen; | |
53 | - RGBColor OldBackColor, OldForeColor; | |
54 | - | |
55 | - GetPenState(&OldPen); | |
56 | - GetBackColor(&OldBackColor); | |
57 | - GetForeColor(&OldForeColor); | |
58 | - | |
59 | - // Get ready to draw the crosshairs | |
60 | - PenNormal(); | |
61 | - | |
62 | - // Drawing color | |
63 | - RGBForeColor(&Crosshairs.Color); | |
64 | - | |
65 | - // Get the center location from the view rectangle | |
66 | - // Shift it down one, so that even line widths can come out right. | |
67 | - short XCen = ((ViewRect.left + ViewRect.right) >> 1) - 1; | |
68 | - short YCen = ((ViewRect.top + ViewRect.bottom) >> 1) - 1; | |
69 | - | |
70 | - // Separate for X and Y; the points are three on top/left and three on top/right: | |
71 | - // Line-split position | |
72 | - // Octagon vertex (half-way) | |
73 | - // Farthest extent | |
74 | - short OctaPoints[2][6]; | |
75 | - short Len; | |
76 | - | |
77 | - switch(Crosshairs.Shape) | |
78 | - { | |
79 | - case CHShape_RealCrosshairs: | |
80 | - | |
81 | - PenSize(1,Crosshairs.Thickness); | |
82 | - | |
83 | - // Left | |
84 | - MoveTo(XCen-Crosshairs.FromCenter+Crosshairs.Thickness-1,YCen); | |
85 | - Line(-Crosshairs.Length,0); | |
86 | - | |
87 | - // Right | |
88 | - MoveTo(XCen+Crosshairs.FromCenter,YCen); | |
89 | - Line(Crosshairs.Length,0); | |
90 | - | |
91 | - PenSize(Crosshairs.Thickness,1); | |
92 | - | |
93 | - // Top | |
94 | - MoveTo(XCen,YCen-Crosshairs.FromCenter+Crosshairs.Thickness-1); | |
95 | - Line(0,-Crosshairs.Length); | |
96 | - | |
97 | - // Bottom | |
98 | - MoveTo(XCen,YCen+Crosshairs.FromCenter); | |
99 | - Line(0,Crosshairs.Length); | |
100 | - | |
101 | - break; | |
102 | - | |
103 | - case CHShape_Circle: | |
104 | - // This will really be an octagon, for OpenGL-rendering convenience | |
105 | - | |
106 | - // Precalculate the line endpoints, for convenience | |
107 | - | |
108 | - Len = Crosshairs.Length; | |
109 | - OctaPoints[0][0] = XCen - Len; | |
110 | - OctaPoints[0][5] = XCen + Len; | |
111 | - OctaPoints[1][0] = YCen - Len; | |
112 | - OctaPoints[1][5] = YCen + Len; | |
113 | - | |
114 | - Len = Len/2; | |
115 | - OctaPoints[0][1] = XCen - Len; | |
116 | - OctaPoints[0][4] = XCen + Len; | |
117 | - OctaPoints[1][1] = YCen - Len; | |
118 | - OctaPoints[1][4] = YCen + Len; | |
119 | - | |
120 | - Len = MIN(Len,Crosshairs.FromCenter); | |
121 | - OctaPoints[0][2] = XCen - Len; | |
122 | - OctaPoints[0][3] = XCen + Len; | |
123 | - OctaPoints[1][2] = YCen - Len; | |
124 | - OctaPoints[1][3] = YCen + Len; | |
125 | - | |
126 | - // We need to do 12 line segments, so we do them in 2*2*3 fashion | |
127 | - | |
128 | - for (int ix=0; ix<2; ix++) | |
129 | - { | |
130 | - int ixi = (ix > 0) ? 5 : 0; | |
131 | - int ixid = (ix > 0) ? -1 : 1; | |
132 | - for (int iy=0; iy<2; iy++) | |
133 | - { | |
134 | - int iyi = (iy > 0) ? 5 : 0; | |
135 | - int iyid = (iy > 0) ? -1 : 1; | |
136 | - | |
137 | - // Vertical | |
138 | - PenSize(Crosshairs.Thickness,1); | |
139 | - MoveTo(OctaPoints[0][ixi],OctaPoints[1][iyi+2*iyid]); | |
140 | - LineTo(OctaPoints[0][ixi],OctaPoints[1][iyi+iyid]); | |
141 | - | |
142 | - // Diagonal | |
143 | - PenSize(Crosshairs.Thickness,Crosshairs.Thickness); | |
144 | - LineTo(OctaPoints[0][ixi+ixid],OctaPoints[1][iyi]); | |
145 | - | |
146 | - // Horizontal | |
147 | - PenSize(1,Crosshairs.Thickness); | |
148 | - LineTo(OctaPoints[0][ixi+2*ixid],OctaPoints[1][iyi]); | |
149 | - } | |
150 | - } | |
151 | - | |
152 | - break; | |
153 | - } | |
154 | - | |
155 | - // Pop previous state | |
156 | - SetPenState(&OldPen); | |
157 | - RGBBackColor(&OldBackColor); | |
158 | - RGBForeColor(&OldForeColor); | |
159 | - | |
160 | - return _Crosshairs_IsActive; | |
161 | -} | |
162 | - | |
163 | -// If a graphics context is also specified | |
164 | -bool Crosshairs_Render(GrafPtr Context, Rect &ViewRect) | |
165 | -{ | |
166 | - // Push context | |
167 | - GrafPtr OldContext; | |
168 | - GetPort(&OldContext); | |
169 | - SetPort(Context); | |
170 | - | |
171 | - bool RetCode = Crosshairs_Render(ViewRect); | |
172 | - | |
173 | - // Pop context | |
174 | - SetPort(OldContext); | |
175 | - | |
176 | - return RetCode; | |
177 | -} | |
178 | - | |
179 | -// If no view rectangle is explicitly specified | |
180 | -bool Crosshairs_Render(GrafPtr Context) | |
181 | -{ | |
182 | - Rect portRect; | |
183 | - GetPortBounds(Context, &portRect); | |
184 | - return Crosshairs_Render(Context, portRect); | |
185 | -} | |
1 | +/* | |
2 | + | |
3 | + Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
4 | + and the "Aleph One" developers. | |
5 | + | |
6 | + This program is free software; you can redistribute it and/or modify | |
7 | + it under the terms of the GNU General Public License as published by | |
8 | + the Free Software Foundation; either version 3 of the License, or | |
9 | + (at your option) any later version. | |
10 | + | |
11 | + This program is distributed in the hope that it will be useful, | |
12 | + but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | + GNU General Public License for more details. | |
15 | + | |
16 | + This license is contained in the file "COPYING", | |
17 | + which is included with this source code; it is available online at | |
18 | + http://www.gnu.org/licenses/gpl.html | |
19 | + | |
20 | + February 21, 2000 (Loren Petrich) | |
21 | + | |
22 | + Crosshairs-implementation file; contains Quickdraw crosshairs code. | |
23 | + | |
24 | +Jan 25, 2002 (Br'fin (Jeremy Parsons)): | |
25 | + Added accessors for datafields now opaque in Carbon | |
26 | + | |
27 | +Jun 26, 2002 (Loren Petrich): | |
28 | + Added support for crosshairs being circular and/or partially transparent | |
29 | +*/ | |
30 | + | |
31 | +#include "cseries.h" | |
32 | +#include "Crosshairs.h" | |
33 | + | |
34 | + | |
35 | +static bool _Crosshairs_IsActive = false; | |
36 | + | |
37 | +bool Crosshairs_IsActive() {return _Crosshairs_IsActive;} | |
38 | +bool Crosshairs_SetActive(bool NewState) {return (_Crosshairs_IsActive = (NewState != 0));} | |
39 | + | |
40 | + | |
41 | +// Will draw in center of view rectangle in the current QD graphics context; | |
42 | +// modeled after fps display. | |
43 | +// It won't try to clip the ends of the lines. | |
44 | +bool Crosshairs_Render(Rect &ViewRect) | |
45 | +{ | |
46 | + if (!_Crosshairs_IsActive) return _Crosshairs_IsActive; | |
47 | + | |
48 | + // Get the crosshair data | |
49 | + CrosshairData &Crosshairs = GetCrosshairData(); | |
50 | + | |
51 | + // Push previous state | |
52 | + PenState OldPen; | |
53 | + RGBColor OldBackColor, OldForeColor; | |
54 | + | |
55 | + GetPenState(&OldPen); | |
56 | + GetBackColor(&OldBackColor); | |
57 | + GetForeColor(&OldForeColor); | |
58 | + | |
59 | + // Get ready to draw the crosshairs | |
60 | + PenNormal(); | |
61 | + | |
62 | + // Drawing color | |
63 | + RGBForeColor(&Crosshairs.Color); | |
64 | + | |
65 | + // Get the center location from the view rectangle | |
66 | + // Shift it down one, so that even line widths can come out right. | |
67 | + short XCen = ((ViewRect.left + ViewRect.right) >> 1) - 1; | |
68 | + short YCen = ((ViewRect.top + ViewRect.bottom) >> 1) - 1; | |
69 | + | |
70 | + // Separate for X and Y; the points are three on top/left and three on top/right: | |
71 | + // Line-split position | |
72 | + // Octagon vertex (half-way) | |
73 | + // Farthest extent | |
74 | + short OctaPoints[2][6]; | |
75 | + short Len; | |
76 | + | |
77 | + switch(Crosshairs.Shape) | |
78 | + { | |
79 | + case CHShape_RealCrosshairs: | |
80 | + | |
81 | + PenSize(1,Crosshairs.Thickness); | |
82 | + | |
83 | + // Left | |
84 | + MoveTo(XCen-Crosshairs.FromCenter+Crosshairs.Thickness-1,YCen); | |
85 | + Line(-Crosshairs.Length,0); | |
86 | + | |
87 | + // Right | |
88 | + MoveTo(XCen+Crosshairs.FromCenter,YCen); | |
89 | + Line(Crosshairs.Length,0); | |
90 | + | |
91 | + PenSize(Crosshairs.Thickness,1); | |
92 | + | |
93 | + // Top | |
94 | + MoveTo(XCen,YCen-Crosshairs.FromCenter+Crosshairs.Thickness-1); | |
95 | + Line(0,-Crosshairs.Length); | |
96 | + | |
97 | + // Bottom | |
98 | + MoveTo(XCen,YCen+Crosshairs.FromCenter); | |
99 | + Line(0,Crosshairs.Length); | |
100 | + | |
101 | + break; | |
102 | + | |
103 | + case CHShape_Circle: | |
104 | + // This will really be an octagon, for OpenGL-rendering convenience | |
105 | + | |
106 | + // Precalculate the line endpoints, for convenience | |
107 | + | |
108 | + Len = Crosshairs.Length; | |
109 | + OctaPoints[0][0] = XCen - Len; | |
110 | + OctaPoints[0][5] = XCen + Len; | |
111 | + OctaPoints[1][0] = YCen - Len; | |
112 | + OctaPoints[1][5] = YCen + Len; | |
113 | + | |
114 | + Len = Len/2; | |
115 | + OctaPoints[0][1] = XCen - Len; | |
116 | + OctaPoints[0][4] = XCen + Len; | |
117 | + OctaPoints[1][1] = YCen - Len; | |
118 | + OctaPoints[1][4] = YCen + Len; | |
119 | + | |
120 | + Len = MIN(Len,Crosshairs.FromCenter); | |
121 | + OctaPoints[0][2] = XCen - Len; | |
122 | + OctaPoints[0][3] = XCen + Len; | |
123 | + OctaPoints[1][2] = YCen - Len; | |
124 | + OctaPoints[1][3] = YCen + Len; | |
125 | + | |
126 | + // We need to do 12 line segments, so we do them in 2*2*3 fashion | |
127 | + | |
128 | + for (int ix=0; ix<2; ix++) | |
129 | + { | |
130 | + int ixi = (ix > 0) ? 5 : 0; | |
131 | + int ixid = (ix > 0) ? -1 : 1; | |
132 | + for (int iy=0; iy<2; iy++) | |
133 | + { | |
134 | + int iyi = (iy > 0) ? 5 : 0; | |
135 | + int iyid = (iy > 0) ? -1 : 1; | |
136 | + | |
137 | + // Vertical | |
138 | + PenSize(Crosshairs.Thickness,1); | |
139 | + MoveTo(OctaPoints[0][ixi],OctaPoints[1][iyi+2*iyid]); | |
140 | + LineTo(OctaPoints[0][ixi],OctaPoints[1][iyi+iyid]); | |
141 | + | |
142 | + // Diagonal | |
143 | + PenSize(Crosshairs.Thickness,Crosshairs.Thickness); | |
144 | + LineTo(OctaPoints[0][ixi+ixid],OctaPoints[1][iyi]); | |
145 | + | |
146 | + // Horizontal | |
147 | + PenSize(1,Crosshairs.Thickness); | |
148 | + LineTo(OctaPoints[0][ixi+2*ixid],OctaPoints[1][iyi]); | |
149 | + } | |
150 | + } | |
151 | + | |
152 | + break; | |
153 | + } | |
154 | + | |
155 | + // Pop previous state | |
156 | + SetPenState(&OldPen); | |
157 | + RGBBackColor(&OldBackColor); | |
158 | + RGBForeColor(&OldForeColor); | |
159 | + | |
160 | + return _Crosshairs_IsActive; | |
161 | +} | |
162 | + | |
163 | +// If a graphics context is also specified | |
164 | +bool Crosshairs_Render(GrafPtr Context, Rect &ViewRect) | |
165 | +{ | |
166 | + // Push context | |
167 | + GrafPtr OldContext; | |
168 | + GetPort(&OldContext); | |
169 | + SetPort(Context); | |
170 | + | |
171 | + bool RetCode = Crosshairs_Render(ViewRect); | |
172 | + | |
173 | + // Pop context | |
174 | + SetPort(OldContext); | |
175 | + | |
176 | + return RetCode; | |
177 | +} | |
178 | + | |
179 | +// If no view rectangle is explicitly specified | |
180 | +bool Crosshairs_Render(GrafPtr Context) | |
181 | +{ | |
182 | + Rect portRect; | |
183 | + GetPortBounds(Context, &portRect); | |
184 | + return Crosshairs_Render(Context, portRect); | |
185 | +} |
@@ -1 +1 @@ | ||
1 | -ゥ | |
1 | +ゥ |
@@ -1,2987 +1,2898 @@ | ||
1 | -/* | |
2 | -SHAPES.C | |
3 | - | |
4 | - Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc. | |
5 | - and the "Aleph One" developers. | |
6 | - | |
7 | - This program is free software; you can redistribute it and/or modify | |
8 | - it under the terms of the GNU General Public License as published by | |
9 | - the Free Software Foundation; either version 3 of the License, or | |
10 | - (at your option) any later version. | |
11 | - | |
12 | - This program is distributed in the hope that it will be useful, | |
13 | - but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | - GNU General Public License for more details. | |
16 | - | |
17 | - This license is contained in the file "COPYING", | |
18 | - which is included with this source code; it is available online at | |
19 | - http://www.gnu.org/licenses/gpl.html | |
20 | - | |
21 | -Saturday, September 4, 1993 9:26:41 AM | |
22 | - | |
23 | -Thursday, May 19, 1994 9:06:28 AM | |
24 | - unification of wall and object shapes complete, new shading table builder. | |
25 | -Wednesday, June 22, 1994 11:55:07 PM | |
26 | - we now read data from alainユs shape extractor. | |
27 | -Saturday, July 9, 1994 3:22:11 PM | |
28 | - lightening_table removed; we now build darkening tables on a collection-by-collection basis | |
29 | - (one 8k darkening table per clut permutation of the given collection) | |
30 | -Monday, October 3, 1994 4:17:15 PM (Jason) | |
31 | - compressed or uncompressed collection resources | |
32 | -Friday, June 16, 1995 11:34:08 AM (Jason) | |
33 | - self-luminescent colors | |
34 | - | |
35 | -Jan 30, 2000 (Loren Petrich): | |
36 | - Changed "new" to "_new" to make data structures more C++-friendly | |
37 | - Did some typecasts | |
38 | - | |
39 | -Feb 3, 2000 (Loren Petrich): | |
40 | - Changed _collection_madd to _collection_vacbob (later changed all "vacbob"'s to "civilian_fusion"'s) | |
41 | - | |
42 | -Feb 4, 2000 (Loren Petrich): | |
43 | - Changed halt() to assert(false) for better debugging | |
44 | - | |
45 | -Feb 12, 2000 (Loren Petrich): | |
46 | - Set up a fallback strategy for the colors; | |
47 | - when there are more colors in all the tables than there are in the general color table, | |
48 | - then look for the nearest one. | |
49 | - | |
50 | -Feb 24, 2000 (Loren Petrich): | |
51 | - Added get_number_of_collection_frames(), so as to assist in wall-texture error checking | |
52 | - | |
53 | -Mar 14, 2000 (Loren Petrich): | |
54 | - Added accessors for number of bitmaps and which bitmap index for a frame index; | |
55 | - these will be useful for OpenGL rendering | |
56 | - | |
57 | -Mar 23, 2000 (Loren Petrich): | |
58 | - Made infravision tinting more generic and reassignable | |
59 | - | |
60 | -Aug 12, 2000 (Loren Petrich): | |
61 | - Using object-oriented file handler | |
62 | - | |
63 | -Aug 14, 2000 (Loren Petrich): | |
64 | - Turned collection and shading-table handles into pointers, | |
65 | - because handles are needlessly MacOS-specific, | |
66 | - and because these are variable-format objects. | |
67 | - | |
68 | -Aug 26, 2000 (Loren Petrich): | |
69 | - Moved get_default_shapes_spec() to preprocess_map_mac.c | |
70 | - | |
71 | -Sept 2, 2000 (Loren Petrich): | |
72 | - Added shapes-file unpacking. | |
73 | - | |
74 | -Jan 17, 2001 (Loren Petrich): | |
75 | - Added support for offsets for OpenGL-rendered substitute textures | |
76 | -*/ | |
77 | - | |
78 | -/* | |
79 | -//gracefully handle out-of-memory conditions when loading shapes. it will happen. | |
80 | -//get_shape_descriptors() needs to look at high-level instead of low-level shapes when fetching scenery instead of walls/ceilings/floors | |
81 | -//get_shape_information() is called often, and is quite slow | |
82 | -//it is possible to have more than 255 low-level shapes in a collection, which means the existing shape_descriptor is too small | |
83 | -//must build different shading tables for each collection (even in 8-bit, for alternate color tables) | |
84 | -*/ | |
85 | - | |
86 | -#include "cseries.h" | |
87 | - | |
88 | -#include <stdio.h> | |
89 | -#include <stdlib.h> | |
90 | -#include <string.h> | |
91 | - | |
92 | -#include "shell.h" | |
93 | -#include "render.h" | |
94 | -#include "interface.h" | |
95 | -#include "collection_definition.h" | |
96 | -#include "screen.h" | |
97 | -#include "game_errors.h" | |
98 | -#include "FileHandler.h" | |
99 | -#include "progress.h" | |
100 | - | |
101 | -#include "map.h" | |
102 | - | |
103 | -// LP addition: OpenGL support | |
104 | -#include "OGL_Render.h" | |
105 | -#include "OGL_LoadScreen.h" | |
106 | - | |
107 | -// LP addition: infravision XML setup needs colors | |
108 | -#include "ColorParser.h" | |
109 | - | |
110 | -#include "Packing.h" | |
111 | -#include "SW_Texture_Extras.h" | |
112 | - | |
113 | -#include <SDL_rwops.h> | |
114 | -#include <memory> | |
115 | - | |
116 | -#include <boost/shared_ptr.hpp> | |
117 | - | |
118 | -#include "Plugins.h" | |
119 | - | |
120 | -#ifdef env68k | |
121 | -#pragma segment shell | |
122 | -#endif | |
123 | - | |
124 | -/* ---------- constants */ | |
125 | - | |
126 | -#define iWHITE 1 | |
127 | -#ifdef SCREAMING_METAL | |
128 | -#define iBLACK 255 | |
129 | -#else | |
130 | -#define iBLACK 18 | |
131 | -#endif | |
132 | - | |
133 | -/* each collection has a tint table which (fully) tints the clut of that collection to whatever it | |
134 | - looks like through the light enhancement goggles */ | |
135 | -#define NUMBER_OF_TINT_TABLES 1 | |
136 | - | |
137 | -// Moved from shapes_macintosh.c: | |
138 | - | |
139 | -// Possibly of historical interest: | |
140 | -// #define COLLECTIONS_RESOURCE_BASE 128 | |
141 | -// #define COLLECTIONS_RESOURCE_BASE16 1128 | |
142 | - | |
143 | -enum /* collection status */ | |
144 | -{ | |
145 | - markNONE, | |
146 | - markLOAD= 1, | |
147 | - markUNLOAD= 2, | |
148 | - markSTRIP= 4 /* we donユt want bitmaps, just high/low-level shape data */, | |
149 | - markPATCHED = 8 /* force re-load */ | |
150 | -}; | |
151 | - | |
152 | -enum /* flags */ | |
153 | -{ | |
154 | - _collection_is_stripped= 0x0001 | |
155 | -}; | |
156 | - | |
157 | -/* ---------- macros */ | |
158 | - | |
159 | -// LP: fake portable-files stuff | |
160 | -#ifdef mac | |
161 | -inline short memory_error() {return MemError();} | |
162 | -#else | |
163 | -inline short memory_error() {return 0;} | |
164 | -#endif | |
165 | - | |
166 | -/* ---------- structures */ | |
167 | - | |
168 | -/* ---------- globals */ | |
169 | - | |
170 | -extern SDL_Surface* world_pixels; | |
171 | - | |
172 | -#include "shape_definitions.h" | |
173 | - | |
174 | -static pixel16 *global_shading_table16= (pixel16 *) NULL; | |
175 | -static pixel32 *global_shading_table32= (pixel32 *) NULL; | |
176 | - | |
177 | -short number_of_shading_tables, shading_table_fractional_bits, shading_table_size; | |
178 | - | |
179 | -// LP addition: opened-shapes-file object | |
180 | -static OpenedFile ShapesFile; | |
181 | -static OpenedResourceFile M1ShapesFile; | |
182 | -static bool m1_shapes; | |
183 | - | |
184 | -/* ---------- private prototypes */ | |
185 | - | |
186 | -static void update_color_environment(bool is_opengl); | |
187 | -static short find_or_add_color(struct rgb_color_value *color, struct rgb_color_value *colors, short *color_count, bool update_flags); | |
188 | -static void _change_clut(void (*change_clut_proc)(struct color_table *color_table), struct rgb_color_value *colors, short color_count); | |
189 | - | |
190 | -static void build_shading_tables8(struct rgb_color_value *colors, short color_count, pixel8 *shading_tables); | |
191 | -static void build_shading_tables16(struct rgb_color_value *colors, short color_count, pixel16 *shading_tables, byte *remapping_table, bool is_opengl); | |
192 | -static void build_shading_tables32(struct rgb_color_value *colors, short color_count, pixel32 *shading_tables, byte *remapping_table, bool is_opengl); | |
193 | -static void build_global_shading_table16(void); | |
194 | -static void build_global_shading_table32(void); | |
195 | - | |
196 | -static bool get_next_color_run(struct rgb_color_value *colors, short color_count, short *start, short *count); | |
197 | -static bool new_color_run(struct rgb_color_value *_new, struct rgb_color_value *last); | |
198 | - | |
199 | -static int32 get_shading_table_size(short collection_code); | |
200 | - | |
201 | -static void build_collection_tinting_table(struct rgb_color_value *colors, short color_count, short collection_index, bool is_opengl); | |
202 | -static void build_tinting_table8(struct rgb_color_value *colors, short color_count, pixel8 *tint_table, short tint_start, short tint_count); | |
203 | -static void build_tinting_table16(struct rgb_color_value *colors, short color_count, pixel16 *tint_table, struct rgb_color *tint_color); | |
204 | -static void build_tinting_table32(struct rgb_color_value *colors, short color_count, pixel32 *tint_table, struct rgb_color *tint_color, bool is_opengl); | |
205 | - | |
206 | -static void precalculate_bit_depth_constants(void); | |
207 | - | |
208 | -static bool collection_loaded(struct collection_header *header); | |
209 | -static void unload_collection(struct collection_header *header); | |
210 | -static void unlock_collection(struct collection_header *header); | |
211 | -static void lock_collection(struct collection_header *header); | |
212 | -static bool load_collection(short collection_index, bool strip); | |
213 | -#ifdef mac | |
214 | -static byte *unpack_collection(byte *collection, int32 length, bool strip); | |
215 | -#endif | |
216 | - | |
217 | -static void shutdown_shape_handler(void); | |
218 | -static void close_shapes_file(void); | |
219 | - | |
220 | -#ifdef mac | |
221 | -static byte *read_object_from_file(OpenedFile& OFile, int32 offset, int32 length); | |
222 | -#endif | |
223 | - | |
224 | -// static byte *make_stripped_collection(byte *collection); | |
225 | - | |
226 | -/* --------- collection accessor prototypes */ | |
227 | - | |
228 | -// Modified to return NULL for unloaded collections and out-of-range indices for collection contents. | |
229 | -// This is to allow for more graceful degradation. | |
230 | - | |
231 | -static struct collection_header *get_collection_header(short collection_index); | |
232 | -/*static*/ struct collection_definition *get_collection_definition(short collection_index); | |
233 | -static void *get_collection_shading_tables(short collection_index, short clut_index); | |
234 | -static void *get_collection_tint_tables(short collection_index, short tint_index); | |
235 | -static struct rgb_color_value *get_collection_colors(short collection_index, short clut_number); | |
236 | -static struct high_level_shape_definition *get_high_level_shape_definition(short collection_index, short high_level_shape_index); | |
237 | -static struct bitmap_definition *get_bitmap_definition(short collection_index, short bitmap_index); | |
238 | - | |
239 | - | |
240 | -#include <SDL_endian.h> | |
241 | -#include "byte_swapping.h" | |
242 | - | |
243 | -/* | |
244 | - * Initialize shapes handling | |
245 | - */ | |
246 | - | |
247 | -static void initialize_pixmap_handler() | |
248 | -{ | |
249 | - // nothing to do | |
250 | -} | |
251 | - | |
252 | - | |
253 | -/* | |
254 | - * Convert shape to surface | |
255 | - */ | |
256 | - | |
257 | -// ZZZ extension: pass out (if non-NULL) a pointer to a block of pixel data - | |
258 | -// caller should free() that storage after freeing the returned surface. | |
259 | -// Only needed for RLE-encoded shapes. | |
260 | -// Note that default arguments are used to make this function | |
261 | -// source-code compatible with existing usage. | |
262 | -// Note also that inShrinkImage currently only applies to RLE shapes. | |
263 | -SDL_Surface *get_shape_surface(int shape, int inCollection, byte** outPointerToPixelData, float inIllumination, bool inShrinkImage) | |
264 | -{ | |
265 | - // Get shape information | |
266 | - int collection_index = GET_COLLECTION(GET_DESCRIPTOR_COLLECTION(shape)); | |
267 | - int clut_index = GET_COLLECTION_CLUT(GET_DESCRIPTOR_COLLECTION(shape)); | |
268 | - int low_level_shape_index = GET_DESCRIPTOR_SHAPE(shape); | |
269 | - | |
270 | - if(inCollection != NONE) { | |
271 | - collection_index = GET_COLLECTION(inCollection); | |
272 | - clut_index = GET_COLLECTION_CLUT(inCollection); | |
273 | - low_level_shape_index = shape; | |
274 | - } | |
275 | - | |
276 | - struct collection_definition *collection = get_collection_definition(collection_index); | |
277 | - struct low_level_shape_definition *low_level_shape = get_low_level_shape_definition(collection_index, low_level_shape_index); | |
278 | - if (!low_level_shape) return NULL; | |
279 | - struct bitmap_definition *bitmap; | |
280 | - SDL_Color colors[256]; | |
281 | - | |
282 | - if(inIllumination >= 0) { | |
283 | - assert(inIllumination <= 1.0f); | |
284 | - | |
285 | - // ZZZ: get shading tables to use instead of CLUT, if requested | |
286 | - void* shading_tables_as_void; | |
287 | - extended_get_shape_bitmap_and_shading_table(BUILD_COLLECTION(collection_index, clut_index), low_level_shape_index, | |
288 | - &bitmap, &shading_tables_as_void, _shading_normal); | |
289 | - if (!bitmap) return NULL; | |
290 | - | |
291 | - switch(bit_depth) { | |
292 | - case 16: | |
293 | - { | |
294 | - uint16* shading_tables = (uint16*) shading_tables_as_void; | |
295 | - shading_tables += 256 * (int)(inIllumination * (number_of_shading_tables - 1)); | |
296 | - | |
297 | - // Extract color table - ZZZ change to use shading table rather than CLUT. Hope it works. | |
298 | - | |
299 | - SDL_PixelFormat *fmt = &pixel_format_16; | |
300 | - for (int i = 0; i < 256; i++) { | |
301 | - SDL_GetRGB(shading_tables[i], fmt, &colors[i].r, &colors[i].g, &colors[i].b); | |
302 | - } | |
303 | - } | |
304 | - break; | |
305 | - | |
306 | - case 32: | |
307 | - { | |
308 | - uint32* shading_tables = (uint32*) shading_tables_as_void; | |
309 | - shading_tables += 256 * (int)(inIllumination * (number_of_shading_tables - 1)); | |
310 | - | |
311 | - // Extract color table - ZZZ change to use shading table rather than CLUT. Hope it works. | |
312 | - for(int i = 0; i < 256; i++) { | |
313 | - colors[i].r = RED32(shading_tables[i]); | |
314 | - colors[i].g = GREEN32(shading_tables[i]); | |
315 | - colors[i].b = BLUE32(shading_tables[i]); | |
316 | - } | |
317 | - } | |
318 | - break; | |
319 | - | |
320 | - default: | |
321 | - vhalt("oops, bit_depth not supported for get_shape_surface with illumination\n"); | |
322 | - break; | |
323 | - } | |
324 | - | |
325 | - } // inIllumination >= 0 | |
326 | - else { // inIllumination < 0 | |
327 | - bitmap = get_bitmap_definition(collection_index, low_level_shape->bitmap_index); | |
328 | - if(!bitmap) return NULL; | |
329 | - | |
330 | - // Extract color table | |
331 | - int num_colors = collection->color_count - NUMBER_OF_PRIVATE_COLORS; | |
332 | - rgb_color_value *src_colors = get_collection_colors(collection_index, clut_index) + NUMBER_OF_PRIVATE_COLORS; | |
333 | - for (int i=0; i<num_colors; i++) { | |
334 | - int idx = src_colors[i].value; | |
335 | - colors[idx].r = src_colors[i].red >> 8; | |
336 | - colors[idx].g = src_colors[i].green >> 8; | |
337 | - colors[idx].b = src_colors[i].blue >> 8; | |
338 | - } | |
339 | - } // inIllumination < 0 | |
340 | - | |
341 | - | |
342 | - SDL_Surface *s = NULL; | |
343 | - if (bitmap->bytes_per_row == NONE) { | |
344 | - | |
345 | - // ZZZ: process RLE-encoded shape | |
346 | - | |
347 | - // Allocate storage for un-RLE'd pixels | |
348 | - uint32 theNumberOfStorageBytes = bitmap->width * bitmap->height * sizeof(byte); | |
349 | - byte* pixel_storage = (byte*) malloc(theNumberOfStorageBytes); | |
350 | - memset(pixel_storage, 0, theNumberOfStorageBytes); | |
351 | - | |
352 | - // Here, a "run" is a row or column. An "element" is a single pixel's data. | |
353 | - // We always go forward through the source data. Thus, the offsets for where the next run | |
354 | - // or element goes into the destination data area change depending on the circumstances. | |
355 | - int16 theNumRuns; | |
356 | - int16 theNumElementsPerRun; | |
357 | - int16 theDestDataNextRunOffset; | |
358 | - int16 theDestDataNextElementOffset; | |
359 | - | |
360 | - // Is this row-major or column-major? | |
361 | - if(bitmap->flags & _COLUMN_ORDER_BIT) { | |
362 | - theNumRuns = bitmap->width; | |
363 | - theNumElementsPerRun = bitmap->height; | |
364 | - theDestDataNextRunOffset = (low_level_shape->flags & _X_MIRRORED_BIT) ? -1 : 1; | |
365 | - theDestDataNextElementOffset = (low_level_shape->flags & _Y_MIRRORED_BIT) ? -bitmap->width : bitmap->width; | |
366 | - } | |
367 | - else { | |
368 | - theNumRuns = bitmap->height; | |
369 | - theNumElementsPerRun = bitmap->width; | |
370 | - theDestDataNextElementOffset = (low_level_shape->flags & _X_MIRRORED_BIT) ? -1 : 1; | |
371 | - theDestDataNextRunOffset = (low_level_shape->flags & _Y_MIRRORED_BIT) ? -bitmap->width : bitmap->width; | |
372 | - } | |
373 | - | |
374 | - // Figure out where our first byte will be written | |
375 | - byte* theDestDataStartAddress = pixel_storage; | |
376 | - | |
377 | - if(low_level_shape->flags & _X_MIRRORED_BIT) | |
378 | - theDestDataStartAddress += bitmap->width - 1; | |
379 | - | |
380 | - if(low_level_shape->flags & _Y_MIRRORED_BIT) | |
381 | - theDestDataStartAddress += bitmap->width * (bitmap->height - 1); | |
382 | - | |
383 | - // Walk through runs, un-RLE'ing as we go | |
384 | - for(int run = 0; run < theNumRuns; run++) { | |
385 | - uint16* theLengthData = (uint16*) bitmap->row_addresses[run]; | |
386 | - uint16 theFirstOpaquePixelElement = SDL_SwapBE16(theLengthData[0]); | |
387 | - uint16 theFirstTransparentAfterOpaquePixelElement = SDL_SwapBE16(theLengthData[1]); | |
388 | - uint16 theNumberOfOpaquePixels = theFirstTransparentAfterOpaquePixelElement - theFirstOpaquePixelElement; | |
389 | - | |
390 | - byte* theOriginalPixelData = (byte*) &theLengthData[2]; | |
391 | - byte* theUnpackedPixelData; | |
392 | - | |
393 | - theUnpackedPixelData = theDestDataStartAddress + run * theDestDataNextRunOffset | |
394 | - + theFirstOpaquePixelElement * theDestDataNextElementOffset; | |
395 | - | |
396 | - for(int i = 0; i < theNumberOfOpaquePixels; i++) { | |
397 | - assert(theUnpackedPixelData >= pixel_storage); | |
398 | - assert(theUnpackedPixelData < (pixel_storage + theNumberOfStorageBytes)); | |
399 | - *theUnpackedPixelData = *theOriginalPixelData; | |
400 | - theUnpackedPixelData += theDestDataNextElementOffset; | |
401 | - theOriginalPixelData++; | |
402 | - } | |
403 | - } | |
404 | - | |
405 | - // Let's shrink the image if the user wants us to. | |
406 | - // We do this here by discarding every other pixel in each direction. | |
407 | - // Really, I guess there's probably a library out there that would do nice smoothing | |
408 | - // for us etc. that we should use here. I just want to hack something out now and run with it. | |
409 | - int image_width = bitmap->width; | |
410 | - int image_height = bitmap->height; | |
411 | - | |
412 | - if(inShrinkImage) { | |
413 | - int theLargerWidth = bitmap->width; | |
414 | - int theLargerHeight = bitmap->height; | |
415 | - byte* theLargerPixelStorage = pixel_storage; | |
416 | - int theSmallerWidth = theLargerWidth / 2 + theLargerWidth % 2; | |
417 | - int theSmallerHeight = theLargerHeight / 2 + theLargerHeight % 2; | |
418 | - byte* theSmallerPixelStorage = (byte*) malloc(theSmallerWidth * theSmallerHeight); | |
419 | - | |
420 | - for(int y = 0; y < theSmallerHeight; y++) { | |
421 | - for(int x = 0; x < theSmallerWidth; x++) { | |
422 | - theSmallerPixelStorage[y * theSmallerWidth + x] = | |
423 | - theLargerPixelStorage[(y * theLargerWidth + x) * 2]; | |
424 | - } | |
425 | - } | |
426 | - | |
427 | - free(pixel_storage); | |
428 | - | |
429 | - pixel_storage = theSmallerPixelStorage; | |
430 | - image_width = theSmallerWidth; | |
431 | - image_height = theSmallerHeight; | |
432 | - } | |
433 | - | |
434 | - // Now we can create a surface from this new storage | |
435 | - s = SDL_CreateRGBSurfaceFrom(pixel_storage, image_width, image_height, 8, image_width, 0xff, 0xff, 0xff, 0xff); | |
436 | - | |
437 | - if(s != NULL) { | |
438 | - // If caller is not prepared to take this data, it's a coding error. | |
439 | - assert(outPointerToPixelData != NULL); | |
440 | - *outPointerToPixelData = pixel_storage; | |
441 | - | |
442 | - // Set color table | |
443 | - SDL_SetColors(s, colors, 0, 256); | |
444 | - | |
445 | - // Set transparent pixel (color #0) | |
446 | - SDL_SetColorKey(s, SDL_SRCCOLORKEY, 0); | |
447 | - } | |
448 | - | |
449 | - } else { | |
450 | - // Row-order shape, we can directly create a surface from it | |
451 | - if (collection->type == _wall_collection) | |
452 | - { | |
453 | - s = SDL_CreateRGBSurfaceFrom(bitmap->row_addresses[0], bitmap->height, bitmap->width, 8, bitmap->bytes_per_row, 0xff, 0xff, 0xff, 0xff); | |
454 | - } | |
455 | - else | |
456 | - { | |
457 | - s = SDL_CreateRGBSurfaceFrom(bitmap->row_addresses[0], bitmap->width, bitmap->height, 8, bitmap->bytes_per_row, 0xff, 0xff, 0xff, 0xff); | |
458 | - } | |
459 | - // ZZZ: caller should not dispose of any additional data - just free the surface. | |
460 | - if(outPointerToPixelData != NULL) | |
461 | - *outPointerToPixelData = NULL; | |
462 | - | |
463 | - if(s != NULL) { | |
464 | - // Set color table | |
465 | - SDL_SetColors(s, colors, 0, 256); | |
466 | - } | |
467 | - } | |
468 | - | |
469 | - return s; | |
470 | -} | |
471 | - | |
472 | -static void load_collection_definition(collection_definition* cd, SDL_RWops *p) | |
473 | -{ | |
474 | - cd->version = SDL_ReadBE16(p); | |
475 | - cd->type = SDL_ReadBE16(p); | |
476 | - cd->flags = SDL_ReadBE16(p); | |
477 | - cd->color_count = SDL_ReadBE16(p); | |
478 | - cd->clut_count = SDL_ReadBE16(p); | |
479 | - cd->color_table_offset = SDL_ReadBE32(p); | |
480 | - cd->high_level_shape_count = SDL_ReadBE16(p); | |
481 | - cd->high_level_shape_offset_table_offset = SDL_ReadBE32(p); | |
482 | - cd->low_level_shape_count = SDL_ReadBE16(p); | |
483 | - cd->low_level_shape_offset_table_offset = SDL_ReadBE32(p); | |
484 | - cd->bitmap_count = SDL_ReadBE16(p); | |
485 | - cd->bitmap_offset_table_offset = SDL_ReadBE32(p); | |
486 | - cd->pixels_to_world = SDL_ReadBE16(p); | |
487 | - SDL_ReadBE32(p); // skip size | |
488 | - SDL_RWseek(p, 253 * sizeof(int16), SEEK_CUR); // unused | |
489 | - | |
490 | - // resize members | |
491 | - cd->color_tables.resize(cd->clut_count * cd->color_count); | |
492 | - cd->high_level_shapes.resize(cd->high_level_shape_count); | |
493 | - cd->low_level_shapes.resize(cd->low_level_shape_count); | |
494 | - cd->bitmaps.resize(cd->bitmap_count); | |
495 | - | |
496 | -} | |
497 | - | |
498 | -static void load_clut(rgb_color_value *r, int count, SDL_RWops *p) | |
499 | -{ | |
500 | - for (int i = 0; i < count; i++, r++) | |
501 | - { | |
502 | - SDL_RWread(p, r, 1, 2); | |
503 | - r->red = SDL_ReadBE16(p); | |
504 | - r->green = SDL_ReadBE16(p); | |
505 | - r->blue = SDL_ReadBE16(p); | |
506 | - } | |
507 | -} | |
508 | - | |
509 | -static void load_high_level_shape(std::vector<uint8>& shape, SDL_RWops *p) | |
510 | -{ | |
511 | - int16 type = SDL_ReadBE16(p); | |
512 | - int16 flags = SDL_ReadBE16(p); | |
513 | - char name[HIGH_LEVEL_SHAPE_NAME_LENGTH + 2]; | |
514 | - SDL_RWread(p, name, 1, HIGH_LEVEL_SHAPE_NAME_LENGTH + 2); | |
515 | - int16 number_of_views = SDL_ReadBE16(p); | |
516 | - int16 frames_per_view = SDL_ReadBE16(p); | |
517 | - | |
518 | - // Convert low-level shape index list | |
519 | - int num_views; | |
520 | - switch (number_of_views) { | |
521 | - case _unanimated: | |
522 | - case _animated1: | |
523 | - num_views = 1; | |
524 | - break; | |
525 | - case _animated3to4: | |
526 | - case _animated4: | |
527 | - num_views = 4; | |
528 | - break; | |
529 | - case _animated3to5: | |
530 | - case _animated5: | |
531 | - num_views = 5; | |
532 | - break; | |
533 | - case _animated2to8: | |
534 | - case _animated5to8: | |
535 | - case _animated8: | |
536 | - num_views = 8; | |
537 | - break; | |
538 | - default: | |
539 | - num_views = number_of_views; | |
540 | - break; | |
541 | - } | |
542 | - | |
543 | - shape.resize(sizeof(high_level_shape_definition) + num_views * frames_per_view * sizeof(int16)); | |
544 | - | |
545 | - high_level_shape_definition *d = (high_level_shape_definition *) &shape[0]; | |
546 | - | |
547 | - d->type = type; | |
548 | - d->flags = flags; | |
549 | - memcpy(d->name, name, HIGH_LEVEL_SHAPE_NAME_LENGTH + 2); | |
550 | - d->number_of_views = number_of_views; | |
551 | - d->frames_per_view = frames_per_view; | |
552 | - d->ticks_per_frame = SDL_ReadBE16(p); | |
553 | - d->key_frame = SDL_ReadBE16(p); | |
554 | - d->transfer_mode = SDL_ReadBE16(p); | |
555 | - d->transfer_mode_period = SDL_ReadBE16(p); | |
556 | - d->first_frame_sound = SDL_ReadBE16(p); | |
557 | - d->key_frame_sound = SDL_ReadBE16(p); | |
558 | - d->last_frame_sound = SDL_ReadBE16(p); | |
559 | - d->pixels_to_world = SDL_ReadBE16(p); | |
560 | - d->loop_frame = SDL_ReadBE16(p); | |
561 | - SDL_RWseek(p, 14 * sizeof(int16), SEEK_CUR); | |
562 | - | |
563 | - // Convert low-level shape index list | |
564 | - for (int j = 0; j < num_views * d->frames_per_view; j++) { | |
565 | - d->low_level_shape_indexes[j] = SDL_ReadBE16(p); | |
566 | - } | |
567 | -} | |
568 | - | |
569 | -static void load_low_level_shape(low_level_shape_definition *d, SDL_RWops *p) | |
570 | -{ | |
571 | - d->flags = SDL_ReadBE16(p); | |
572 | - d->minimum_light_intensity = SDL_ReadBE32(p); | |
573 | - d->bitmap_index = SDL_ReadBE16(p); | |
574 | - d->origin_x = SDL_ReadBE16(p); | |
575 | - d->origin_y = SDL_ReadBE16(p); | |
576 | - d->key_x = SDL_ReadBE16(p); | |
577 | - d->key_y = SDL_ReadBE16(p); | |
578 | - d->world_left = SDL_ReadBE16(p); | |
579 | - d->world_right = SDL_ReadBE16(p); | |
580 | - d->world_top = SDL_ReadBE16(p); | |
581 | - d->world_bottom = SDL_ReadBE16(p); | |
582 | - d->world_x0 = SDL_ReadBE16(p); | |
583 | - d->world_y0 = SDL_ReadBE16(p); | |
584 | - SDL_RWseek(p, 4 * sizeof(int16), SEEK_CUR); | |
585 | -} | |
586 | - | |
587 | -static void convert_m1_rle(std::vector<uint8>& bitmap, int scanlines, int scanline_length, SDL_RWops* p) | |
588 | -{ | |
589 | -// std::vector<uint8> bitmap; | |
590 | - for (int scanline = 0; scanline < scanlines; ++scanline) | |
591 | - { | |
592 | - std::vector<uint8> scanline_data(scanline_length); | |
593 | - uint8* dst = &scanline_data[0]; | |
594 | - uint8* sentry = &scanline_data[scanline_length]; | |
595 | - | |
596 | - while (true) | |
597 | - { | |
598 | - int16 opcode = SDL_ReadBE16(p); | |
599 | - if (opcode > 0) | |
600 | - { | |
601 | - assert(dst + opcode <= sentry); | |
602 | - SDL_RWread(p, dst, opcode, 1); | |
603 | - dst += opcode; | |
604 | - } | |
605 | - else if (opcode < 0) | |
606 | - { | |
607 | - assert(dst - opcode <= sentry); | |
608 | - dst -= opcode; | |
609 | - } | |
610 | - else | |
611 | - break; | |
612 | - } | |
613 | - | |
614 | - assert (dst == sentry); | |
615 | - | |
616 | - // Find M2/oo-format RLE compression; | |
617 | - // it needs the first nonblank pixel and the last nonblank one + 1 | |
618 | - int16 first = 0; | |
619 | - int16 last = 0; | |
620 | - for (int i = 0; i < scanline_length; ++i) | |
621 | - { | |
622 | - if (scanline_data[i] != 0) | |
623 | - { | |
624 | - first = i; | |
625 | - break; | |
626 | - } | |
627 | - } | |
628 | - | |
629 | - for (int i = scanline_length - 1; i >= 0; --i) | |
630 | - { | |
631 | - if (scanline_data[i] != 0) | |
632 | - { | |
633 | - last = i + 1; | |
634 | - break; | |
635 | - } | |
636 | - } | |
637 | - | |
638 | - if (last < first) last = first; | |
639 | - | |
640 | - bitmap.push_back(first >> 8); | |
641 | - bitmap.push_back(first & 0xff); | |
642 | - bitmap.push_back(last >> 8); | |
643 | - bitmap.push_back(last & 0xff); | |
644 | - bitmap.insert(bitmap.end(), &scanline_data[first], &scanline_data[last]); | |
645 | - } | |
646 | -} | |
647 | - | |
648 | -static void load_bitmap(std::vector<uint8>& bitmap, SDL_RWops *p) | |
649 | -{ | |
650 | - bitmap_definition b; | |
651 | - | |
652 | - // Convert bitmap definition | |
653 | - b.width = SDL_ReadBE16(p); | |
654 | - b.height = SDL_ReadBE16(p); | |
655 | - b.bytes_per_row = SDL_ReadBE16(p); | |
656 | - b.flags = SDL_ReadBE16(p); | |
657 | - b.bit_depth = SDL_ReadBE16(p); | |
658 | - | |
659 | - // guess how big to make it | |
660 | - int rows = (b.flags & _COLUMN_ORDER_BIT) ? b.width : b.height; | |
661 | - int row_len = (b.flags & _COLUMN_ORDER_BIT) ? b.height : b.width; | |
662 | - | |
663 | - SDL_RWseek(p, 16, SEEK_CUR); | |
664 | - | |
665 | - // Skip row address pointers | |
666 | - SDL_RWseek(p, (rows + 1) * sizeof(uint32), SEEK_CUR); | |
667 | - | |
668 | - if (b.bytes_per_row == NONE) | |
669 | - { | |
670 | - if (m1_shapes) | |
671 | - { | |
672 | - // make enough room for the definition, then append as we convert RLE | |
673 | - bitmap.resize(sizeof(bitmap_definition) + rows * sizeof(pixel8*)); | |
674 | - } | |
675 | - else | |
676 | - { | |
677 | - // ugly--figure out how big it's going to be | |
678 | - | |
679 | - int32 size = 0; | |
680 | - for (int j = 0; j < rows; j++) { | |
681 | - int16 first = SDL_ReadBE16(p); | |
682 | - int16 last = SDL_ReadBE16(p); | |
683 | - size += 4; | |
684 | - SDL_RWseek(p, last - first, SEEK_CUR); | |
685 | - size += last - first; | |
686 | - } | |
687 | - | |
688 | - bitmap.resize(sizeof(bitmap_definition) + rows * sizeof(pixel8*) + size); | |
689 | - | |
690 | - // Now, seek back | |
691 | - SDL_RWseek(p, -size, SEEK_CUR); | |
692 | - } | |
693 | - } | |
694 | - else | |
695 | - { | |
696 | - bitmap.resize(sizeof(bitmap_definition) + rows * sizeof(pixel8*) + rows * b.bytes_per_row); | |
697 | - } | |
698 | - | |
699 | - | |
700 | - uint8* c = &bitmap[0]; | |
701 | - bitmap_definition *d = (bitmap_definition *) &bitmap[0]; | |
702 | - d->width = b.width; | |
703 | - d->height = b.height; | |
704 | - d->bytes_per_row = b.bytes_per_row; | |
705 | - d->flags = b.flags; | |
706 | - d->flags &= ~_PATCHED_BIT; // Anvil sets unused flags :( we'll set it later | |
707 | - d->bit_depth = b.bit_depth; | |
708 | - c += sizeof(bitmap_definition); | |
709 | - | |
710 | - // Skip row address pointers | |
711 | - c += rows * sizeof(pixel8 *); | |
712 | - | |
713 | - // Copy bitmap data | |
714 | - if (d->bytes_per_row == NONE) | |
715 | - { | |
716 | - // RLE format | |
717 | - | |
718 | - if (m1_shapes) | |
719 | - { | |
720 | - convert_m1_rle(bitmap, rows, row_len, p); | |
721 | - } | |
722 | - else | |
723 | - { | |
724 | - for (int j = 0; j < rows; j++) { | |
725 | - int16 first = SDL_ReadBE16(p); | |
726 | - int16 last = SDL_ReadBE16(p); | |
727 | - *(c++) = (uint8)(first >> 8); | |
728 | - *(c++) = (uint8)(first); | |
729 | - *(c++) = (uint8)(last >> 8); | |
730 | - *(c++) = (uint8)(last); | |
731 | - SDL_RWread(p, c, 1, last - first); | |
732 | - c += last - first; | |
733 | - } | |
734 | - } | |
735 | - } else { | |
736 | - SDL_RWread(p, c, d->bytes_per_row, rows); | |
737 | - c += rows * d->bytes_per_row; | |
738 | - } | |
739 | - | |
740 | -} | |
741 | - | |
742 | -static void allocate_shading_tables(short collection_index, bool strip) | |
743 | -{ | |
744 | - collection_header *header = get_collection_header(collection_index); | |
745 | - // Allocate enough space for this collection's shading tables | |
746 | - if (strip) | |
747 | - header->shading_tables = NULL; | |
748 | - else { | |
749 | - collection_definition *definition = get_collection_definition(collection_index); | |
750 | - header->shading_tables = (byte *)malloc(get_shading_table_size(collection_index) * definition->clut_count + shading_table_size * NUMBER_OF_TINT_TABLES); | |
751 | - } | |
752 | -} | |
753 | - | |
754 | -/* | |
755 | - * Load collection | |
756 | - */ | |
757 | - | |
758 | -static bool load_collection(short collection_index, bool strip) | |
759 | -{ | |
760 | - SDL_RWops* p; | |
761 | - boost::shared_ptr<SDL_RWops> m1_p; // automatic deallocation | |
762 | - LoadedResource r; | |
763 | - int32 src_offset; | |
764 | - | |
765 | - collection_header *header = get_collection_header(collection_index); | |
766 | - | |
767 | - if (m1_shapes) | |
768 | - { | |
769 | - // Collections are stored in .256 resources | |
770 | - if (!M1ShapesFile.Get('.', '2', '5', '6', 128 + collection_index, r)) | |
771 | - { | |
772 | - return false; | |
773 | - } | |
774 | - | |
775 | - m1_p.reset(SDL_RWFromConstMem(r.GetPointer(), r.GetLength()), SDL_FreeRW); | |
776 | - p = m1_p.get(); | |
777 | - src_offset = 0; | |
778 | - } | |
779 | - else | |
780 | - { | |
781 | - // Get offset and length of data in source file from header | |
782 | - | |
783 | - if (bit_depth == 8 || header->offset16 == -1) { | |
784 | - if (header->offset == -1) | |
785 | - { | |
786 | - return false; | |
787 | - } | |
788 | - src_offset = header->offset; | |
789 | - } else { | |
790 | - src_offset = header->offset16; | |
791 | - } | |
792 | - | |
793 | - p = ShapesFile.GetRWops(); | |
794 | - ShapesFile.SetPosition(0); | |
795 | - src_offset += SDL_RWtell(p); | |
796 | - } | |
797 | - | |
798 | - // Read collection definition | |
799 | - std::auto_ptr<collection_definition> cd(new collection_definition); | |
800 | - SDL_RWseek(p, src_offset, RW_SEEK_SET); | |
801 | - load_collection_definition(cd.get(), p); | |
802 | - if (m1_shapes && cd->type == _interface_collection) | |
803 | - { | |
804 | - // don't know how to read M1 RLE shapes yet, so clear | |
805 | - // out and return true | |
806 | - cd->high_level_shapes.clear(); | |
807 | - cd->low_level_shapes.clear(); | |
808 | - cd->bitmaps.clear(); | |
809 | - return true; | |
810 | - } | |
811 | - header->status &= ~markPATCHED; | |
812 | - | |
813 | - // Convert CLUTS | |
814 | - SDL_RWseek(p, src_offset + cd->color_table_offset, RW_SEEK_SET); | |
815 | - load_clut(&cd->color_tables[0], cd->clut_count * cd->color_count, p); | |
816 | - | |
817 | - // Convert high-level shape definitions | |
818 | - SDL_RWseek(p, src_offset + cd->high_level_shape_offset_table_offset, RW_SEEK_SET); | |
819 | - | |
820 | - std::vector<uint32> t(cd->high_level_shape_count); | |
821 | - SDL_RWread(p, &t[0], sizeof(uint32), cd->high_level_shape_count); | |
822 | - byte_swap_memory(&t[0], _4byte, cd->high_level_shape_count); | |
823 | - for (int i = 0; i < cd->high_level_shape_count; i++) { | |
824 | - SDL_RWseek(p, src_offset + t[i], RW_SEEK_SET); | |
825 | - load_high_level_shape(cd->high_level_shapes[i], p); | |
826 | - } | |
827 | - | |
828 | - // Convert low-level shape definitions | |
829 | - SDL_RWseek(p, src_offset + cd->low_level_shape_offset_table_offset, RW_SEEK_SET); | |
830 | - t.resize(cd->low_level_shape_count); | |
831 | - SDL_RWread(p, &t[0], sizeof(uint32), cd->low_level_shape_count); | |
832 | - byte_swap_memory(&t[0], _4byte, cd->low_level_shape_count); | |
833 | - | |
834 | - for (int i = 0; i < cd->low_level_shape_count; i++) { | |
835 | - SDL_RWseek(p, src_offset + t[i], RW_SEEK_SET); | |
836 | - load_low_level_shape(&cd->low_level_shapes[i], p); | |
837 | - } | |
838 | - | |
839 | - // Convert bitmap definitions | |
840 | - SDL_RWseek(p, src_offset + cd->bitmap_offset_table_offset, RW_SEEK_SET); | |
841 | - t.resize(cd->bitmap_count); | |
842 | - SDL_RWread(p, &t[0], sizeof(uint32), cd->bitmap_count); | |
843 | - byte_swap_memory(&t[0], _4byte, cd->bitmap_count); | |
844 | - | |
845 | - for (int i = 0; i < cd->bitmap_count; i++) { | |
846 | - SDL_RWseek(p, src_offset + t[i], RW_SEEK_SET); | |
847 | - load_bitmap(cd->bitmaps[i], p); | |
848 | - } | |
849 | - | |
850 | - header->collection = cd.release(); | |
851 | - | |
852 | - if (strip) { | |
853 | - //!! don't know what to do | |
854 | - fprintf(stderr, "Stripped shapes not implemented\n"); | |
855 | - abort(); | |
856 | - } | |
857 | - | |
858 | - allocate_shading_tables(collection_index, strip); | |
859 | - | |
860 | - if (header->shading_tables == NULL) { | |
861 | - delete header->collection; | |
862 | - header->collection = NULL; | |
863 | - return false; | |
864 | - } | |
865 | - | |
866 | - // Everything OK | |
867 | - return true; | |
868 | -} | |
869 | - | |
870 | - | |
871 | -/* | |
872 | - * Unload collection | |
873 | - */ | |
874 | - | |
875 | -static void unload_collection(struct collection_header *header) | |
876 | -{ | |
877 | - assert(header->collection); | |
878 | - delete header->collection; | |
879 | - free(header->shading_tables); | |
880 | - header->collection = NULL; | |
881 | - header->shading_tables = NULL; | |
882 | -} | |
883 | - | |
884 | -#define ENDC_TAG FOUR_CHARS_TO_INT('e', 'n', 'd', 'c') | |
885 | -#define CLDF_TAG FOUR_CHARS_TO_INT('c', 'l', 'd', 'f') | |
886 | -#define HLSH_TAG FOUR_CHARS_TO_INT('h', 'l', 's', 'h') | |
887 | -#define LLSH_TAG FOUR_CHARS_TO_INT('l', 'l', 's', 'h') | |
888 | -#define BMAP_TAG FOUR_CHARS_TO_INT('b', 'm', 'a', 'p') | |
889 | -#define CTAB_TAG FOUR_CHARS_TO_INT('c', 't', 'a', 'b') | |
890 | - | |
891 | -std::vector<uint8> shapes_patch; | |
892 | -void set_shapes_patch_data(uint8 *data, size_t length) | |
893 | -{ | |
894 | - if (!length) | |
895 | - { | |
896 | - shapes_patch.clear(); | |
897 | - } | |
898 | - else | |
899 | - { | |
900 | - shapes_patch.resize(length); | |
901 | - memcpy(&shapes_patch[0], data, length); | |
902 | - } | |
903 | -} | |
904 | - | |
905 | -uint8* get_shapes_patch_data(size_t &length) | |
906 | -{ | |
907 | - length = shapes_patch.size(); | |
908 | - return length ? &shapes_patch[0] : 0; | |
909 | -} | |
910 | - | |
911 | -void load_shapes_patch(SDL_RWops *p, bool override_replacements) | |
912 | -{ | |
913 | - std::vector<int16> color_counts(MAXIMUM_COLLECTIONS); | |
914 | - int32 start = SDL_RWtell(p); | |
915 | - SDL_RWseek(p, 0, SEEK_END); | |
916 | - int32 end = SDL_RWtell(p); | |
917 | - | |
918 | - SDL_RWseek(p, start, SEEK_SET); | |
919 | - | |
920 | - bool done = false; | |
921 | - while (!done) | |
922 | - { | |
923 | - // is there more data to read? | |
924 | - if (SDL_RWtell(p) < end) | |
925 | - { | |
926 | - int32 collection_index = SDL_ReadBE32(p); | |
927 | - int32 patch_bit_depth = SDL_ReadBE32(p); | |
928 | - | |
929 | - bool collection_end = false; | |
930 | - while (!collection_end) | |
931 | - { | |
932 | - // read a tag | |
933 | - int32 tag = SDL_ReadBE32(p); | |
934 | - if (tag == ENDC_TAG) | |
935 | - { | |
936 | - collection_end = true; | |
937 | - } | |
938 | - else if (tag == CLDF_TAG) | |
939 | - { | |
940 | - // a collection follows directly | |
941 | - collection_header *header = get_collection_header(collection_index); | |
942 | - if (collection_loaded(header) && patch_bit_depth == 8) | |
943 | - { | |
944 | - load_collection_definition(header->collection, p); | |
945 | - color_counts[collection_index] = header->collection->color_count; | |
946 | - allocate_shading_tables(collection_index, false); | |
947 | - header->status|=markPATCHED; | |
948 | - | |
949 | - } else { | |
950 | - // get the color count (it's the only way to skip the CTAB_TAG | |
951 | - SDL_RWseek(p, 6, SEEK_CUR); | |
952 | - color_counts[collection_index] = SDL_ReadBE16(p); | |
953 | - SDL_RWseek(p, 544 - 8, SEEK_CUR); | |
954 | - } | |
955 | - } | |
956 | - else if (tag == HLSH_TAG) | |
957 | - { | |
958 | - collection_definition *cd = get_collection_definition(collection_index); | |
959 | - int32 high_level_shape_index = SDL_ReadBE32(p); | |
960 | - int32 size = SDL_ReadBE32(p); | |
961 | - int32 pos = SDL_RWtell(p); | |
962 | - if (cd && patch_bit_depth == 8 && high_level_shape_index < cd->high_level_shapes.size()) | |
963 | - { | |
964 | - load_high_level_shape(cd->high_level_shapes[high_level_shape_index], p); | |
965 | - SDL_RWseek(p, pos + size, SEEK_SET); | |
966 | - | |
967 | - } | |
968 | - else | |
969 | - { | |
970 | - SDL_RWseek(p, size, SEEK_CUR); | |
971 | - } | |
972 | - } | |
973 | - else if (tag == LLSH_TAG) | |
974 | - { | |
975 | - collection_definition *cd = get_collection_definition(collection_index); | |
976 | - int32 low_level_shape_index = SDL_ReadBE32(p); | |
977 | - if (cd && patch_bit_depth == 8 && low_level_shape_index < cd->low_level_shapes.size()) | |
978 | - { | |
979 | - load_low_level_shape(&cd->low_level_shapes[low_level_shape_index], p); | |
980 | - } | |
981 | - else | |
982 | - { | |
983 | - SDL_RWseek(p, 36, SEEK_CUR); | |
984 | - } | |
985 | - } | |
986 | - else if (tag == BMAP_TAG) | |
987 | - { | |
988 | - collection_definition *cd = get_collection_definition(collection_index); | |
989 | - int32 bitmap_index = SDL_ReadBE32(p); | |
990 | - int32 size = SDL_ReadBE32(p); | |
991 | - if (cd && patch_bit_depth == 8 && bitmap_index < cd->bitmaps.size()) | |
992 | - { | |
993 | - load_bitmap(cd->bitmaps[bitmap_index], p); | |
994 | - if (override_replacements) | |
995 | - { | |
996 | - get_bitmap_definition(collection_index, bitmap_index)->flags |= _PATCHED_BIT; | |
997 | - } | |
998 | - } | |
999 | - else | |
1000 | - { | |
1001 | - SDL_RWseek(p, size, SEEK_CUR); | |
1002 | - } | |
1003 | - } | |
1004 | - else if (tag == CTAB_TAG) | |
1005 | - { | |
1006 | - collection_definition *cd = get_collection_definition(collection_index); | |
1007 | - int32 color_table_index = SDL_ReadBE32(p); | |
1008 | - if (cd && patch_bit_depth == 8 && (color_table_index * cd->color_count < cd->color_tables.size())) | |
1009 | - { | |
1010 | - load_clut(&cd->color_tables[color_table_index], cd->color_count, p); | |
1011 | - } | |
1012 | - else | |
1013 | - { | |
1014 | - SDL_RWseek(p, color_counts[collection_index] * sizeof(rgb_color_value), SEEK_CUR); | |
1015 | - } | |
1016 | - } | |
1017 | - else | |
1018 | - { | |
1019 | - fprintf(stderr, "Unrecognized tag in patch file '%c%c%c%c'\n %x", tag >> 24, tag >> 16, tag >> 8, tag, tag); | |
1020 | - } | |
1021 | - } | |
1022 | - | |
1023 | - | |
1024 | - } else { | |
1025 | - done = true; | |
1026 | - } | |
1027 | - } | |
1028 | - | |
1029 | - | |
1030 | -} | |
1031 | - | |
1032 | -/* ---------- code */ | |
1033 | - | |
1034 | -/* --------- private code */ | |
1035 | - | |
1036 | -void initialize_shape_handler() | |
1037 | -{ | |
1038 | - // M1 uses the resource fork, but M2 and Moo use the data fork | |
1039 | - | |
1040 | - FileSpecifier File; | |
1041 | - get_default_shapes_spec(File); | |
1042 | - open_shapes_file(File); | |
1043 | - if (!ShapesFile.IsOpen()) | |
1044 | - alert_user(fatalError, strERRORS, badExtraFileLocations, ShapesFile.GetError()); | |
1045 | - else | |
1046 | - atexit(shutdown_shape_handler); | |
1047 | - | |
1048 | - initialize_pixmap_handler(); | |
1049 | -} | |
1050 | - | |
1051 | -void open_shapes_file(FileSpecifier& File) | |
1052 | -{ | |
1053 | - if (File.Open(M1ShapesFile) && M1ShapesFile.Check('.','2','5','6',128)) | |
1054 | - { | |
1055 | - m1_shapes = true; | |
1056 | - } | |
1057 | - else if (File.Open(ShapesFile)) | |
1058 | - { | |
1059 | - m1_shapes = false; | |
1060 | - // Load the collection headers; | |
1061 | - // need a buffer for the packed data | |
1062 | - int Size = MAXIMUM_COLLECTIONS*SIZEOF_collection_header; | |
1063 | - byte *CollHdrStream = new byte[Size]; | |
1064 | - if (!ShapesFile.Read(Size,CollHdrStream)) | |
1065 | - { | |
1066 | - ShapesFile.Close(); | |
1067 | - delete []CollHdrStream; | |
1068 | - return; | |
1069 | - } | |
1070 | - | |
1071 | - // Unpack them | |
1072 | - uint8 *S = CollHdrStream; | |
1073 | - int Count = MAXIMUM_COLLECTIONS; | |
1074 | - collection_header* ObjPtr = collection_headers; | |
1075 | - | |
1076 | - for (int k = 0; k < Count; k++, ObjPtr++) | |
1077 | - { | |
1078 | - StreamToValue(S,ObjPtr->status); | |
1079 | - StreamToValue(S,ObjPtr->flags); | |
1080 | - | |
1081 | - StreamToValue(S,ObjPtr->offset); | |
1082 | - StreamToValue(S,ObjPtr->length); | |
1083 | - StreamToValue(S,ObjPtr->offset16); | |
1084 | - StreamToValue(S,ObjPtr->length16); | |
1085 | - | |
1086 | - S += 6*2; | |
1087 | - | |
1088 | - ObjPtr->collection = NULL; // so unloading can work properly | |
1089 | - ObjPtr->shading_tables = NULL; // so unloading can work properly | |
1090 | - } | |
1091 | - | |
1092 | - assert((S - CollHdrStream) == Count*SIZEOF_collection_header); | |
1093 | - | |
1094 | - delete []CollHdrStream; | |
1095 | - | |
1096 | - // Load MML resources in file | |
1097 | - // Be sure to ignore not-found errors | |
1098 | -#if defined(mac) | |
1099 | - short SavedType, SavedError = get_game_error(&SavedType); | |
1100 | - XML_LoadFromResourceFork(File); | |
1101 | - set_game_error(SavedType,SavedError); | |
1102 | -#endif | |
1103 | - } | |
1104 | -} | |
1105 | - | |
1106 | -static void close_shapes_file(void) | |
1107 | -{ | |
1108 | - if (m1_shapes) | |
1109 | - { | |
1110 | - M1ShapesFile.Close(); | |
1111 | - } | |
1112 | - else | |
1113 | - { | |
1114 | - ShapesFile.Close(); | |
1115 | - } | |
1116 | -} | |
1117 | - | |
1118 | -static void shutdown_shape_handler(void) | |
1119 | -{ | |
1120 | - close_shapes_file(); | |
1121 | -} | |
1122 | - | |
1123 | -#ifdef mac | |
1124 | -const int POINTER_SIZE = sizeof(void *); | |
1125 | - | |
1126 | -static int AdjustToPointerBoundary(int x) | |
1127 | -{ | |
1128 | - return ((((x-1) / POINTER_SIZE) + 1) * POINTER_SIZE); | |
1129 | -} | |
1130 | - | |
1131 | -// Creates an unpacked collection and puts it into a long, flat stream like the original. | |
1132 | -byte *unpack_collection(byte *collection, int32 length, bool strip) | |
1133 | -{ | |
1134 | - | |
1135 | - // Set up blank values of these quantities | |
1136 | - byte *NewCollection = NULL; | |
1137 | - int32 *OffsetTable = NULL; | |
1138 | - | |
1139 | - try | |
1140 | - { | |
1141 | - // First, unpack the header into a temporary area | |
1142 | - if (length < SIZEOF_collection_definition) throw 13666; | |
1143 | - | |
1144 | - collection_definition Definition; | |
1145 | - uint8 *SBase = collection; | |
1146 | - uint8 *S = collection; | |
1147 | - | |
1148 | - StreamToValue(S,Definition.version); | |
1149 | - | |
1150 | - StreamToValue(S,Definition.type); | |
1151 | - StreamToValue(S,Definition.flags); | |
1152 | - | |
1153 | - StreamToValue(S,Definition.color_count); | |
1154 | - StreamToValue(S,Definition.clut_count); | |
1155 | - StreamToValue(S,Definition.color_table_offset); | |
1156 | - | |
1157 | - StreamToValue(S,Definition.high_level_shape_count); | |
1158 | - StreamToValue(S,Definition.high_level_shape_offset_table_offset); | |
1159 | - | |
1160 | - StreamToValue(S,Definition.low_level_shape_count); | |
1161 | - StreamToValue(S,Definition.low_level_shape_offset_table_offset); | |
1162 | - | |
1163 | - StreamToValue(S,Definition.bitmap_count); | |
1164 | - StreamToValue(S,Definition.bitmap_offset_table_offset); | |
1165 | - | |
1166 | - StreamToValue(S,Definition.pixels_to_world); | |
1167 | - | |
1168 | - StreamToValue(S,Definition.size); | |
1169 | - | |
1170 | - S += 253*2; | |
1171 | - assert((S - SBase) == SIZEOF_collection_definition); | |
1172 | - | |
1173 | - // We have enough information to estimate the size of the unpack collection chunk! | |
1174 | - int32 NewSize = length; | |
1175 | - | |
1176 | - // The header: | |
1177 | - NewSize += (sizeof(collection_definition) - SIZEOF_collection_definition) + POINTER_SIZE; | |
1178 | - | |
1179 | - // The colors: | |
1180 | - if (!(Definition.color_count >= 0)) throw 13666; | |
1181 | - if (!(Definition.clut_count >= 0)) throw 13666; | |
1182 | - int TotalColors = Definition.color_count * Definition.clut_count; | |
1183 | - NewSize += TotalColors*(sizeof(rgb_color_value) - SIZEOF_rgb_color_value) + POINTER_SIZE; | |
1184 | - | |
1185 | - // The sequence-offset table: | |
1186 | - NewSize += POINTER_SIZE; | |
1187 | - | |
1188 | - // The sequence data: | |
1189 | - if (!(Definition.high_level_shape_count >= 0)) throw 13666; | |
1190 | - NewSize += Definition.high_level_shape_count* | |
1191 | - ((sizeof(high_level_shape_definition) - SIZEOF_high_level_shape_definition) + POINTER_SIZE); | |
1192 | - | |
1193 | - if (!strip) | |
1194 | - { | |
1195 | - // The frame-offset table: | |
1196 | - NewSize += POINTER_SIZE; | |
1197 | - | |
1198 | - // The frame data: | |
1199 | - if (!(Definition.low_level_shape_count >= 0)) throw 13666; | |
1200 | - NewSize += Definition.low_level_shape_count* | |
1201 | - ((sizeof(low_level_shape_definition) - SIZEOF_low_level_shape_definition) + POINTER_SIZE); | |
1202 | - | |
1203 | - // The bitmap-offset table: | |
1204 | - NewSize += POINTER_SIZE; | |
1205 | - | |
1206 | - // The bitmap data: | |
1207 | - if (!(Definition.bitmap_count >= 0)) throw 13666; | |
1208 | - NewSize += Definition.bitmap_count* | |
1209 | - ((sizeof(bitmap_definition) - SIZEOF_bitmap_definition) + POINTER_SIZE); | |
1210 | - | |
1211 | - // The bitmap pointers: | |
1212 | - if (!(Definition.bitmap_offset_table_offset >= 0)) throw 13666; | |
1213 | - if (!(Definition.bitmap_offset_table_offset + | |
1214 | - Definition.bitmap_count*sizeof(int32) <= uint32(length))) throw 13666; | |
1215 | - uint8 *OffsetStream = collection + Definition.bitmap_offset_table_offset; | |
1216 | - for (int k=0; k<Definition.bitmap_count; k++) | |
1217 | - { | |
1218 | - int32 Offset; | |
1219 | - StreamToValue(OffsetStream,Offset); | |
1220 | - if (!(Offset >= 0 && Offset < (length - SIZEOF_bitmap_definition))) throw 13666; | |
1221 | - uint8 *S = collection + Offset; | |
1222 | - | |
1223 | - bitmap_definition Bitmap; | |
1224 | - | |
1225 | - StreamToValue(S,Bitmap.width); | |
1226 | - StreamToValue(S,Bitmap.height); | |
1227 | - StreamToValue(S,Bitmap.bytes_per_row); | |
1228 | - | |
1229 | - StreamToValue(S,Bitmap.flags); | |
1230 | - | |
1231 | - short NumScanlines = (Bitmap.flags&_COLUMN_ORDER_BIT) ? | |
1232 | - Bitmap.width : Bitmap.height; | |
1233 | - | |
1234 | - NewSize += NumScanlines*(sizeof(pixel8 *) - sizeof(int32)); | |
1235 | - } | |
1236 | - } | |
1237 | - | |
1238 | - // Blank out the new chunk and copy in the new header | |
1239 | - NewCollection = new byte[NewSize]; | |
1240 | - memset(NewCollection,0,NewSize); | |
1241 | - int32 NewCollLocation = 0; | |
1242 | - memcpy(NewCollection, &Definition, sizeof(collection_definition)); | |
1243 | - collection_definition& NewDefinition = *((collection_definition *)NewCollection); | |
1244 | - NewDefinition.size = NewSize; | |
1245 | - NewCollLocation += AdjustToPointerBoundary(sizeof(collection_definition)); | |
1246 | - | |
1247 | - // Copy in the colors | |
1248 | - if (!(Definition.color_table_offset >= 0)) throw 13666; | |
1249 | - if (!(Definition.color_table_offset + TotalColors*SIZEOF_rgb_color_value <= length)) throw 13666; | |
1250 | - rgb_color_value *Colors = (rgb_color_value *)(NewCollection + NewCollLocation); | |
1251 | - SBase = S = collection + Definition.color_table_offset; | |
1252 | - for (int k = 0; k < TotalColors; k++, Colors++) | |
1253 | - { | |
1254 | - Colors->flags = *(S++); | |
1255 | - Colors->value = *(S++); | |
1256 | - |
Part of diff was cut off due to size limit. Use your local client to view the full diff.