1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2006, 2007 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
25 //
26 // -----------------------------------------------------------------------
27
28 #include "GL/glew.h"
29
30 #include <SDL/SDL.h>
31 #include <SDL/SDL_opengl.h>
32
33 #include <algorithm>
34 #include <cmath>
35 #include <cstdio>
36 #include <fstream>
37 #include <functional>
38 #include <iostream>
39 #include <sstream>
40 #include <string>
41
42 #include "pygame/alphablit.h"
43 #include "systems/base/colour.h"
44 #include "systems/base/graphics_object.h"
45 #include "systems/base/graphics_object_data.h"
46 #include "systems/base/system_error.h"
47 #include "systems/sdl/sdl_graphics_system.h"
48 #include "systems/sdl/sdl_surface.h"
49 #include "systems/sdl/sdl_utils.h"
50 #include "systems/sdl/shaders.h"
51 #include "systems/sdl/texture.h"
52
53 unsigned int Texture::s_screen_width = 0;
54 unsigned int Texture::s_screen_height = 0;
55
56 unsigned int Texture::s_upload_buffer_size = 0;
57 std::unique_ptr<char[]> Texture::s_upload_buffer;
58
59 // -----------------------------------------------------------------------
60
SetScreenSize(const Size & s)61 void Texture::SetScreenSize(const Size& s) {
62 s_screen_width = s.width();
63 s_screen_height = s.height();
64 }
65
ScreenHeight()66 int Texture::ScreenHeight() { return s_screen_height; }
67
68 // -----------------------------------------------------------------------
69 // Texture
70 // -----------------------------------------------------------------------
Texture(SDL_Surface * surface,int x,int y,int w,int h,unsigned int bytes_per_pixel,int byte_order,int byte_type)71 Texture::Texture(SDL_Surface* surface,
72 int x,
73 int y,
74 int w,
75 int h,
76 unsigned int bytes_per_pixel,
77 int byte_order,
78 int byte_type)
79 : x_offset_(x),
80 y_offset_(y),
81 logical_width_(w),
82 logical_height_(h),
83 total_width_(surface->w),
84 total_height_(surface->h),
85 texture_width_(SafeSize(logical_width_)),
86 texture_height_(SafeSize(logical_height_)),
87 back_texture_id_(0),
88 is_upside_down_(false) {
89 glGenTextures(1, &texture_id_);
90 glBindTexture(GL_TEXTURE_2D, texture_id_);
91 DebugShowGLErrors();
92 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
93 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
94 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
95 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
96
97 if (w == total_width_ && h == total_height_) {
98 SDL_LockSurface(surface);
99 glTexImage2D(GL_TEXTURE_2D,
100 0,
101 bytes_per_pixel,
102 texture_width_,
103 texture_height_,
104 0,
105 byte_order,
106 byte_type,
107 NULL);
108 DebugShowGLErrors();
109
110 glTexSubImage2D(GL_TEXTURE_2D,
111 0,
112 0,
113 0,
114 surface->w,
115 surface->h,
116 byte_order,
117 byte_type,
118 surface->pixels);
119 DebugShowGLErrors();
120
121 SDL_UnlockSurface(surface);
122 } else {
123 // Cut out the current piece
124 char* pixel_data = uploadBuffer(surface->format->BytesPerPixel * w * h);
125 char* cur_dst_ptr = pixel_data;
126
127 SDL_LockSurface(surface);
128 {
129 char* cur_src_ptr = (char*)surface->pixels;
130 cur_src_ptr += surface->pitch * y;
131
132 int row_start = surface->format->BytesPerPixel * x;
133 int subrow_size = surface->format->BytesPerPixel * w;
134 for (int current_row = 0; current_row < h; ++current_row) {
135 memcpy(cur_dst_ptr, cur_src_ptr + row_start, subrow_size);
136 cur_dst_ptr += subrow_size;
137 cur_src_ptr += surface->pitch;
138 }
139 }
140 SDL_UnlockSurface(surface);
141
142 glTexImage2D(GL_TEXTURE_2D,
143 0,
144 bytes_per_pixel,
145 texture_width_,
146 texture_height_,
147 0,
148 byte_order,
149 byte_type,
150 NULL);
151 DebugShowGLErrors();
152
153 glTexSubImage2D(
154 GL_TEXTURE_2D, 0, 0, 0, w, h, byte_order, byte_type, pixel_data);
155 DebugShowGLErrors();
156 }
157 }
158
159 // -----------------------------------------------------------------------
160
Texture(render_to_texture,int width,int height)161 Texture::Texture(render_to_texture, int width, int height)
162 : x_offset_(0),
163 y_offset_(0),
164 logical_width_(width),
165 logical_height_(height),
166 total_width_(width),
167 total_height_(height),
168 texture_width_(0),
169 texture_height_(0),
170 texture_id_(0),
171 back_texture_id_(0),
172 is_upside_down_(true) {
173 glGenTextures(1, &texture_id_);
174 glBindTexture(GL_TEXTURE_2D, texture_id_);
175 DebugShowGLErrors();
176 // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
177 // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_REPEAT);
178 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
179 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
180
181 texture_width_ = SafeSize(logical_width_);
182 texture_height_ = SafeSize(logical_height_);
183
184 // This may fail.
185 glTexImage2D(GL_TEXTURE_2D,
186 0,
187 GL_RGBA,
188 texture_width_,
189 texture_height_,
190 0,
191 GL_RGB,
192 GL_UNSIGNED_BYTE,
193 NULL);
194 DebugShowGLErrors();
195
196 glCopyTexSubImage2D(
197 GL_TEXTURE_2D, 0, 0, 0, 0, 0, logical_width_, logical_height_);
198 DebugShowGLErrors();
199 }
200
201 // -----------------------------------------------------------------------
202
~Texture()203 Texture::~Texture() {
204 glDeleteTextures(1, &texture_id_);
205
206 if (back_texture_id_)
207 glDeleteTextures(1, &back_texture_id_);
208
209 DebugShowGLErrors();
210 }
211
212 // -----------------------------------------------------------------------
213
uploadBuffer(unsigned int size)214 char* Texture::uploadBuffer(unsigned int size) {
215 if (!s_upload_buffer || size > s_upload_buffer_size) {
216 s_upload_buffer.reset(new char[size]);
217 s_upload_buffer_size = size;
218 }
219
220 return s_upload_buffer.get();
221 }
222
223 // -----------------------------------------------------------------------
224
reupload(SDL_Surface * surface,int offset_x,int offset_y,int x,int y,int w,int h,unsigned int bytes_per_pixel,int byte_order,int byte_type)225 void Texture::reupload(SDL_Surface* surface,
226 int offset_x,
227 int offset_y,
228 int x,
229 int y,
230 int w,
231 int h,
232 unsigned int bytes_per_pixel,
233 int byte_order,
234 int byte_type) {
235 glBindTexture(GL_TEXTURE_2D, texture_id_);
236
237 if (w == total_width_ && h == total_height_) {
238 SDL_LockSurface(surface);
239
240 glTexSubImage2D(GL_TEXTURE_2D,
241 0,
242 0,
243 0,
244 surface->w,
245 surface->h,
246 byte_order,
247 byte_type,
248 surface->pixels);
249 DebugShowGLErrors();
250
251 SDL_UnlockSurface(surface);
252 } else {
253 // Cut out the current piece
254 char* pixel_data = uploadBuffer(surface->format->BytesPerPixel * w * h);
255 char* cur_dst_ptr = pixel_data;
256
257 SDL_LockSurface(surface);
258 {
259 char* cur_src_ptr = (char*)surface->pixels;
260 cur_src_ptr += surface->pitch * y;
261
262 int row_start = surface->format->BytesPerPixel * x;
263 int subrow_size = surface->format->BytesPerPixel * w;
264 for (int current_row = 0; current_row < h; ++current_row) {
265 memcpy(cur_dst_ptr, cur_src_ptr + row_start, subrow_size);
266 cur_dst_ptr += subrow_size;
267 cur_src_ptr += surface->pitch;
268 }
269 }
270 SDL_UnlockSurface(surface);
271
272 glTexSubImage2D(GL_TEXTURE_2D,
273 0,
274 offset_x,
275 offset_y,
276 w,
277 h,
278 byte_order,
279 byte_type,
280 pixel_data);
281 DebugShowGLErrors();
282 }
283 }
284
285 // -----------------------------------------------------------------------
286
readTextFile(const std::string & file)287 std::string readTextFile(const std::string& file) {
288 std::ifstream ifs(file.c_str());
289 if (!ifs) {
290 std::ostringstream oss;
291 oss << "Can't open text file: " << file;
292 throw SystemError(oss.str());
293 }
294
295 std::string out, line;
296 while (std::getline(ifs, line)) {
297 out += line;
298 out += '\n';
299 }
300
301 return out;
302 }
303
304 // -----------------------------------------------------------------------
305
306 // This is really broken and brain dead.
RenderToScreen(const Rect & src,const Rect & dst,int opacity)307 void Texture::RenderToScreen(const Rect& src, const Rect& dst, int opacity) {
308 int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
309 int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
310 if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
311 return;
312
313 // For the time being, we are dumb and assume that it's one texture
314
315 float thisx1 = float(x1) / texture_width_;
316 float thisy1 = float(y1) / texture_height_;
317 float thisx2 = float(x2) / texture_width_;
318 float thisy2 = float(y2) / texture_height_;
319
320 if (is_upside_down_) {
321 thisy1 = float(logical_height_ - y1) / texture_height_;
322 thisy2 = float(logical_height_ - y2) / texture_height_;
323 }
324
325 glBindTexture(GL_TEXTURE_2D, texture_id_);
326
327 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
328 glBegin(GL_QUADS);
329 {
330 glColor4ub(255, 255, 255, opacity);
331 glTexCoord2f(thisx1, thisy1);
332 glVertex2i(fdx1, fdy1);
333 glTexCoord2f(thisx2, thisy1);
334 glVertex2i(fdx2, fdy1);
335 glTexCoord2f(thisx2, thisy2);
336 glVertex2i(fdx2, fdy2);
337 glTexCoord2f(thisx1, thisy2);
338 glVertex2i(fdx1, fdy2);
339 }
340 glEnd();
341 glBlendFunc(GL_ONE, GL_ZERO);
342 }
343
344 // -----------------------------------------------------------------------
345
346 // TODO(erg): A function of this hairiness needs super more amounts of
347 // documentation.
RenderToScreenAsColorMask(const Rect & src,const Rect & dst,const RGBAColour & rgba,int filter)348 void Texture::RenderToScreenAsColorMask(const Rect& src,
349 const Rect& dst,
350 const RGBAColour& rgba,
351 int filter) {
352 if (filter == 0) {
353 if (GLEW_ARB_fragment_shader && GLEW_ARB_multitexture) {
354 render_to_screen_as_colour_mask_subtractive_glsl(src, dst, rgba);
355 } else {
356 render_to_screen_as_colour_mask_subtractive_fallback(src, dst, rgba);
357 }
358 } else {
359 render_to_screen_as_colour_mask_additive(src, dst, rgba);
360 }
361 }
362
363 // -----------------------------------------------------------------------
364
render_to_screen_as_colour_mask_subtractive_glsl(const Rect & src,const Rect & dst,const RGBAColour & rgba)365 void Texture::render_to_screen_as_colour_mask_subtractive_glsl(
366 const Rect& src,
367 const Rect& dst,
368 const RGBAColour& rgba) {
369 int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
370 int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
371 if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
372 return;
373
374 float thisx1 = float(x1) / texture_width_;
375 float thisy1 = float(y1) / texture_height_;
376 float thisx2 = float(x2) / texture_width_;
377 float thisy2 = float(y2) / texture_height_;
378
379 if (is_upside_down_) {
380 thisy1 = float(logical_height_ - y1) / texture_height_;
381 thisy2 = float(logical_height_ - y2) / texture_height_;
382 }
383
384 // If we haven't already, allocate video memory for the back
385 // texture.
386 //
387 // NOTE: Does this code deal with changing the dimensions of the
388 // text box? Does it matter?
389 if (back_texture_id_ == 0) {
390 glGenTextures(1, &back_texture_id_);
391 glBindTexture(GL_TEXTURE_2D, back_texture_id_);
392 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
393 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
394
395 // Generate this texture
396 glTexImage2D(GL_TEXTURE_2D,
397 0,
398 GL_RGBA,
399 texture_width_,
400 texture_height_,
401 0,
402 GL_RGB,
403 GL_UNSIGNED_BYTE,
404 NULL);
405 DebugShowGLErrors();
406 }
407
408 // Copy the current value of the region where we're going to render
409 // to a texture for input to the shader
410 glBindTexture(GL_TEXTURE_2D, back_texture_id_);
411 int ystart = int(s_screen_height - fdy1 - (fdy2 - fdy1));
412 int idx1 = int(fdx1);
413 glCopyTexSubImage2D(
414 GL_TEXTURE_2D, 0, 0, 0, idx1, ystart, texture_width_, texture_height_);
415 DebugShowGLErrors();
416
417 glUseProgramObjectARB(Shaders::getColorMaskProgram());
418
419 // Put the back_texture in texture slot zero and set this to be the
420 // texture "current_values" in the above shader program.
421 glActiveTextureARB(GL_TEXTURE0_ARB);
422 glEnable(GL_TEXTURE_2D);
423 glBindTexture(GL_TEXTURE_2D, back_texture_id_);
424 glUniform1iARB(Shaders::getColorMaskUniformCurrentValues(), 0);
425
426 // Put the mask in texture slot one and set this to be the
427 // texture "mask" in the above shader program.
428 glActiveTextureARB(GL_TEXTURE1_ARB);
429 glEnable(GL_TEXTURE_2D);
430 glBindTexture(GL_TEXTURE_2D, texture_id_);
431 glUniform1iARB(Shaders::getColorMaskUniformMask(), 1);
432
433 glDisable(GL_BLEND);
434
435 glBegin(GL_QUADS);
436 {
437 glColorRGBA(rgba);
438 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx1, thisy2);
439 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx1, thisy1);
440 glVertex2i(fdx1, fdy1);
441 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx2, thisy2);
442 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx2, thisy1);
443 glVertex2i(fdx2, fdy1);
444 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx2, thisy1);
445 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx2, thisy2);
446 glVertex2i(fdx2, fdy2);
447 glMultiTexCoord2fARB(GL_TEXTURE0_ARB, thisx1, thisy1);
448 glMultiTexCoord2fARB(GL_TEXTURE1_ARB, thisx1, thisy2);
449 glVertex2i(fdx1, fdy2);
450 }
451 glEnd();
452
453 glActiveTextureARB(GL_TEXTURE1_ARB);
454 glDisable(GL_TEXTURE_2D);
455 glActiveTextureARB(GL_TEXTURE0_ARB);
456
457 glUseProgramObjectARB(0);
458 glEnable(GL_BLEND);
459 glBlendFunc(GL_ONE, GL_ZERO);
460 }
461
462 // -----------------------------------------------------------------------
463
464 // This fallback does not accurately render the scene according to
465 // standard RealLive. This only negatively shades according to the
466 // alpha value, ignoring the rest of the \#WINDOW_ATTR colour.
467 //
468 // This will probably only occur with mesa software and people with
469 // graphics cards > 5 years old.
render_to_screen_as_colour_mask_subtractive_fallback(const Rect & src,const Rect & dst,const RGBAColour & rgba)470 void Texture::render_to_screen_as_colour_mask_subtractive_fallback(
471 const Rect& src,
472 const Rect& dst,
473 const RGBAColour& rgba) {
474 int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
475 int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
476 if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
477 return;
478
479 float thisx1 = float(x1) / texture_width_;
480 float thisy1 = float(y1) / texture_height_;
481 float thisx2 = float(x2) / texture_width_;
482 float thisy2 = float(y2) / texture_height_;
483
484 if (is_upside_down_) {
485 thisy1 = float(logical_height_ - y1) / texture_height_;
486 thisy2 = float(logical_height_ - y2) / texture_height_;
487 }
488
489 // First draw the mask
490 glBindTexture(GL_TEXTURE_2D, texture_id_);
491
492 /// SERIOUS WTF: gl_blend_func_separate causes a segmentation fault
493 /// under the current i810 driver for linux.
494 // glBlendFuncSeparate(GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA,
495 // GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
496 glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE_MINUS_SRC_ALPHA);
497
498 glBegin(GL_QUADS);
499 {
500 glColorRGBA(rgba);
501 glTexCoord2f(thisx1, thisy1);
502 glVertex2i(fdx1, fdy1);
503 glTexCoord2f(thisx2, thisy1);
504 glVertex2i(fdx2, fdy1);
505 glTexCoord2f(thisx2, thisy2);
506 glVertex2i(fdx2, fdy2);
507 glTexCoord2f(thisx1, thisy2);
508 glVertex2i(fdx1, fdy2);
509 }
510 glEnd();
511
512 glBlendFunc(GL_ONE, GL_ZERO);
513 }
514
515 // -----------------------------------------------------------------------
516
render_to_screen_as_colour_mask_additive(const Rect & src,const Rect & dst,const RGBAColour & rgba)517 void Texture::render_to_screen_as_colour_mask_additive(const Rect& src,
518 const Rect& dst,
519 const RGBAColour& rgba) {
520 int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
521 int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
522 if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
523 return;
524
525 float thisx1 = float(x1) / texture_width_;
526 float thisy1 = float(y1) / texture_height_;
527 float thisx2 = float(x2) / texture_width_;
528 float thisy2 = float(y2) / texture_height_;
529
530 if (is_upside_down_) {
531 thisy1 = float(logical_height_ - y1) / texture_height_;
532 thisy2 = float(logical_height_ - y2) / texture_height_;
533 }
534
535 // First draw the mask
536 glBindTexture(GL_TEXTURE_2D, texture_id_);
537 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
538
539 glBegin(GL_QUADS);
540 {
541 glColorRGBA(rgba);
542 glTexCoord2i(thisx1, thisy1);
543 glVertex2f(fdx1, fdy1);
544 glTexCoord2i(thisx2, thisy1);
545 glVertex2f(fdx2, fdy1);
546 glTexCoord2i(thisx2, thisy2);
547 glVertex2f(fdx2, fdy2);
548 glTexCoord2i(thisx1, thisy2);
549 glVertex2f(fdx1, fdy2);
550 }
551 glEnd();
552
553 glBlendFunc(GL_ONE, GL_ZERO);
554 }
555
556 // -----------------------------------------------------------------------
557
RenderToScreen(const Rect & src,const Rect & dst,const int opacity[4])558 void Texture::RenderToScreen(const Rect& src,
559 const Rect& dst,
560 const int opacity[4]) {
561 // For the time being, we are dumb and assume that it's one texture
562 int x1 = src.x(), y1 = src.y(), x2 = src.x2(), y2 = src.y2();
563 int fdx1 = dst.x(), fdy1 = dst.y(), fdx2 = dst.x2(), fdy2 = dst.y2();
564 if (!filterCoords(x1, y1, x2, y2, fdx1, fdy1, fdx2, fdy2))
565 return;
566
567 float thisx1 = float(x1) / texture_width_;
568 float thisy1 = float(y1) / texture_height_;
569 float thisx2 = float(x2) / texture_width_;
570 float thisy2 = float(y2) / texture_height_;
571
572 glBindTexture(GL_TEXTURE_2D, texture_id_);
573
574 // Blend when we have less opacity
575 if (std::find_if(opacity, opacity + 4, [](int o) { return o < 255; }) !=
576 opacity + 4) {
577 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
578 }
579
580 glBegin(GL_QUADS);
581 {
582 glColor4ub(255, 255, 255, opacity[0]);
583 glTexCoord2f(thisx1, thisy1);
584 glVertex2i(fdx1, fdy1);
585 glColor4ub(255, 255, 255, opacity[1]);
586 glTexCoord2f(thisx2, thisy1);
587 glVertex2i(fdx2, fdy1);
588 glColor4ub(255, 255, 255, opacity[2]);
589 glTexCoord2f(thisx2, thisy2);
590 glVertex2i(fdx2, fdy2);
591 glColor4ub(255, 255, 255, opacity[3]);
592 glTexCoord2f(thisx1, thisy2);
593 glVertex2i(fdx1, fdy2);
594 }
595 glEnd();
596 glBlendFunc(GL_ONE, GL_ZERO);
597 }
598
599 // -----------------------------------------------------------------------
600
RenderToScreenAsObject(const GraphicsObject & go,const SDLSurface & surface,const Rect & srcRect,const Rect & dstRect,int alpha)601 void Texture::RenderToScreenAsObject(const GraphicsObject& go,
602 const SDLSurface& surface,
603 const Rect& srcRect,
604 const Rect& dstRect,
605 int alpha) {
606 // This all needs to be pushed up out of the rendering code and down into
607 // either GraphicsObject or the individual GraphicsObjectData subclasses.
608
609 // TODO(erg): Temporary hack while I wait to convert all of this machinery to
610 // Rects.
611 int xSrc1 = srcRect.x();
612 int ySrc1 = srcRect.y();
613 int xSrc2 = srcRect.x2();
614 int ySrc2 = srcRect.y2();
615
616 int fdx1 = dstRect.x(), fdy1 = dstRect.y(), fdx2 = dstRect.x2(),
617 fdy2 = dstRect.y2();
618 if (!filterCoords(xSrc1, ySrc1, xSrc2, ySrc2, fdx1, fdy1, fdx2, fdy2)) {
619 return;
620 }
621
622 // Convert the pixel coordinates into [0,1) texture coordinates
623 float thisx1 = float(xSrc1) / texture_width_;
624 float thisy1 = float(ySrc1) / texture_height_;
625 float thisx2 = float(xSrc2) / texture_width_;
626 float thisy2 = float(ySrc2) / texture_height_;
627
628 glBindTexture(GL_TEXTURE_2D, texture_id_);
629
630 glPushMatrix();
631 {
632 // Translate to where the object starts.
633 glTranslatef(fdx1, fdy1, 0);
634
635 int width = fdx2 - fdx1;
636 int height = fdy2 - fdy1;
637
638 // Rotate the texture around the point (origin + position + reporigin)
639 float x_rep = (width / 2.0f) + go.rep_origin_x();
640 float y_rep = (height / 2.0f) + go.rep_origin_y();
641
642 glTranslatef(x_rep, y_rep, 0);
643 glRotatef(float(go.rotation()) / 10, 0, 0, 1);
644 glTranslatef(-x_rep, -y_rep, 0);
645
646 // RealLive has its own complex shading/tinting system which we implement
647 // in a shader if available. It's costly enough that we make sure we need
648 // to use it.
649 bool using_shader = false;
650 if ((go.light() || go.tint() != RGBColour::Black() ||
651 go.colour() != RGBAColour::Clear() || go.mono() || go.invert()) &&
652 GLEW_ARB_fragment_shader && GLEW_ARB_multitexture) {
653 // Image
654 glActiveTexture(GL_TEXTURE0_ARB);
655 glEnable(GL_TEXTURE_2D);
656 glBindTexture(GL_TEXTURE_2D, texture_id_);
657 glUseProgramObjectARB(Shaders::GetObjectProgram());
658 glUniform1iARB(Shaders::GetObjectUniformImage(), 0);
659
660 // Colour/Tint/Etc.
661 Shaders::loadObjectUniformFromGraphicsObject(go);
662
663 // Alpha.
664 glUniform1fARB(Shaders::GetObjectUniformAlpha(), alpha / 255.0f);
665
666 // Our final blending color has to be all white here.
667 using_shader = true;
668 } else {
669 // The shader takes care of the alpha for us, so we need to specify when
670 // not using it.
671 glColor4ub(255, 255, 255, alpha);
672 }
673
674 // Make this so that when we have composite 1, we're doing a pure
675 // additive blend, (ignoring the alpha channel?)
676 switch (go.composite_mode()) {
677 case 0:
678 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
679 break;
680 case 1:
681 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
682 break;
683 case 2: {
684 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
685 glBlendEquation(GL_FUNC_REVERSE_SUBTRACT);
686 break;
687 }
688 default: {
689 std::ostringstream oss;
690 oss << "Invalid composite_mode in render: " << go.composite_mode();
691 throw SystemError(oss.str());
692 }
693 }
694
695 glBegin(GL_QUADS);
696 {
697 glTexCoord2f(thisx1, thisy1);
698 glVertex2i(0, 0);
699 glTexCoord2f(thisx2, thisy1);
700 glVertex2i(width, 0);
701 glTexCoord2f(thisx2, thisy2);
702 glVertex2i(width, height);
703 glTexCoord2f(thisx1, thisy2);
704 glVertex2i(0, height);
705 }
706 glEnd();
707
708 if (using_shader) {
709 glUseProgramObjectARB(0);
710 }
711
712 glBlendEquation(GL_FUNC_ADD);
713 glBlendFunc(GL_ONE, GL_ZERO);
714 }
715 glPopMatrix();
716
717 DebugShowGLErrors();
718 }
719
720 // -----------------------------------------------------------------------
721
our_round(float r)722 static float our_round(float r) {
723 return (r > 0.0f) ? floor(r + 0.5f) : ceil(r - 0.5f);
724 }
725
filterCoords(int & x1,int & y1,int & x2,int & y2,int & dx1,int & dy1,int & dx2,int & dy2)726 bool Texture::filterCoords(int& x1,
727 int& y1,
728 int& x2,
729 int& y2,
730 int& dx1,
731 int& dy1,
732 int& dx2,
733 int& dy2) {
734 // POINT
735 using std::max;
736 using std::min;
737
738 // Input: raw image coordinates
739 // Output: false if this doesn't intersect with the texture piece we hold.
740 // true otherwise, and set the local coordinates
741 int w1 = x2 - x1;
742 int h1 = y2 - y1;
743
744 // First thing we do is an intersection test to see if this input
745 // range intersects the virtual range this Texture object holds.
746 //
747 /// @bug s/>/>=/?
748 if (x1 + w1 >= x_offset_ && x1 < x_offset_ + logical_width_ &&
749 y1 + h1 >= y_offset_ && y1 < y_offset_ + logical_height_) {
750 // Do an intersection test in terms of the virtual coordinates
751 int virX = max(x1, x_offset_);
752 int virY = max(y1, y_offset_);
753 int w = min(x1 + w1, x_offset_ + logical_width_) - max(x1, x_offset_);
754 int h = min(y1 + h1, y_offset_ + logical_height_) - max(y1, y_offset_);
755
756 // Adjust the destination coordinates
757 int dx_width = dx2 - dx1;
758 int dy_height = dy2 - dy1;
759 float dx1Off = (virX - x1) / float(w1);
760 dx1 = our_round(dx1 + (dx_width * dx1Off));
761 float dx2Off = w / float(w1);
762 dx2 = our_round(dx1 + (dx_width * dx2Off));
763 float dy1Off = (virY - y1) / float(h1);
764 dy1 = our_round(dy1 + (dy_height * dy1Off));
765 float dy2Off = h / float(h1);
766 dy2 = our_round(dy1 + (dy_height * dy2Off));
767
768 // Output the source intersection in real (instead of
769 // virtual) coordinates
770 x1 = virX - x_offset_;
771 x2 = x1 + w;
772 y1 = virY - y_offset_;
773 y2 = y1 + h;
774
775 return true;
776 }
777
778 return false;
779 }
780