Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

23
graphics/tinygl/LICENSE Normal file
View File

@@ -0,0 +1,23 @@
NOTE: This implementation of TinyGL contains additional code and
modifications added only for ScummVM project.
Look into the source code file "Changelog" for more info.
Copyright (c) 1997-2022 Fabrice Bellard
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

1121
graphics/tinygl/api.cpp Normal file

File diff suppressed because it is too large Load Diff

387
graphics/tinygl/arrays.cpp Normal file
View File

@@ -0,0 +1,387 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
#define NORLALIZE_SBYTE(n) ( ( (float) n * 2.0f + 1.0f ) / 255.0f )
#define NORLALIZE_UBYTE(n) ( (float) n / 255.0f )
#define NORLALIZE_SSHORT(n) ( ( (float) n * 2.0f + 1.0f ) / 65535.0f )
#define NORLALIZE_USHORT(n) ( (float) n / 65535.0f )
#define NORLALIZE_SINT(n) ( ( (float) n * 2.0f + 1.0f ) / 4294967295.0f )
#define NORLALIZE_UINT(n) ( (float) n / 4294967295.0f )
namespace TinyGL {
void GLContext::glopArrayElement(GLParam *param) {
int offset;
int states = client_states;
int idx = param[1].i;
if (states & COLOR_ARRAY) {
GLParam p[5];
int size = color_array_size;
offset = idx * color_array_stride;
switch (color_array_type) {
case TGL_UNSIGNED_BYTE: {
TGLubyte *array = (TGLubyte *)color_array + offset;
p[1].f = NORLALIZE_UBYTE(array[0]);
p[2].f = NORLALIZE_UBYTE(array[1]);
p[3].f = NORLALIZE_UBYTE(array[2]);
p[4].f = size > 3 ? NORLALIZE_UBYTE(array[3]) : 1.0f;
break;
}
case TGL_BYTE: {
TGLbyte *array = (TGLbyte *)color_array + offset;
p[1].f = NORLALIZE_SBYTE(array[0]);
p[2].f = NORLALIZE_SBYTE(array[1]);
p[3].f = NORLALIZE_SBYTE(array[2]);
p[4].f = size > 3 ? NORLALIZE_SBYTE(array[3]) : 1.0f;
break;
}
case TGL_UNSIGNED_INT: {
TGLuint *array = (TGLuint *)((TGLbyte *)color_array + offset);
p[1].f = NORLALIZE_UINT(array[0]);
p[2].f = NORLALIZE_UINT(array[1]);
p[3].f = NORLALIZE_UINT(array[2]);
p[4].f = size > 3 ? NORLALIZE_UINT(array[3]) : 1.0f;
break;
}
case TGL_INT: {
TGLint *array = (TGLint *)((TGLbyte *)color_array + offset);
p[1].f = NORLALIZE_SINT(array[0]);
p[2].f = NORLALIZE_SINT(array[1]);
p[3].f = NORLALIZE_SINT(array[2]);
p[4].f = size > 3 ? NORLALIZE_SINT(array[3]) : 1.0f;
break;
}
case TGL_UNSIGNED_SHORT: {
TGLushort *array = (TGLushort *)((TGLbyte *)color_array + offset);
p[1].f = NORLALIZE_USHORT(array[0]);
p[2].f = NORLALIZE_USHORT(array[1]);
p[3].f = NORLALIZE_USHORT(array[2]);
p[4].f = size > 3 ? NORLALIZE_USHORT(array[3]) : 1.0f;
break;
}
case TGL_SHORT: {
TGLshort *array = (TGLshort *)((TGLbyte *)color_array + offset);
p[1].f = NORLALIZE_SSHORT(array[0]);
p[2].f = NORLALIZE_SSHORT(array[1]);
p[3].f = NORLALIZE_SSHORT(array[2]);
p[4].f = size > 3 ? NORLALIZE_SSHORT(array[3]) : 1.0f;
break;
}
case TGL_FLOAT: {
TGLfloat *array = (TGLfloat *)((TGLbyte *)color_array + offset);
p[1].f = array[0];
p[2].f = array[1];
p[3].f = array[2];
p[4].f = size > 3 ? array[3] : 1.0f;
break;
}
case TGL_DOUBLE: {
TGLdouble *array = (TGLdouble *)((TGLbyte *)color_array + offset);
p[1].f = array[0];
p[2].f = array[1];
p[3].f = array[2];
p[4].f = size > 3 ? array[3] : 1.0f;
break;
}
default:
assert(0);
}
glopColor(p);
}
if (states & NORMAL_ARRAY) {
offset = idx * normal_array_stride;
current_normal.W = 0.0f;
switch (normal_array_type) {
case TGL_FLOAT: {
TGLfloat *array = (TGLfloat *)((TGLbyte *)normal_array + offset);
current_normal.X = array[0];
current_normal.Y = array[1];
current_normal.Z = array[2];
break;
}
case TGL_DOUBLE: {
TGLdouble *array = (TGLdouble *)((TGLbyte *)normal_array + offset);
current_normal.X = array[0];
current_normal.Y = array[1];
current_normal.Z = array[2];
break;
}
case TGL_INT: {
TGLint *array = (TGLint *)((TGLbyte *)normal_array + offset);
current_normal.X = NORLALIZE_SINT(array[0]);
current_normal.Y = NORLALIZE_SINT(array[1]);
current_normal.Z = NORLALIZE_SINT(array[2]);
break;
}
case TGL_SHORT: {
TGLshort *array = (TGLshort *)((TGLbyte *)normal_array + offset);
current_normal.X = NORLALIZE_SSHORT(array[0]);
current_normal.Y = NORLALIZE_SSHORT(array[1]);
current_normal.Z = NORLALIZE_SSHORT(array[2]);
break;
}
default:
assert(0);
}
}
if (states & TEXCOORD_ARRAY) {
int size = texcoord_array_size;
offset = idx * texcoord_array_stride;
switch (texcoord_array_type) {
case TGL_FLOAT: {
TGLfloat *array = (TGLfloat *)((TGLbyte *)texcoord_array + offset);
current_tex_coord.X = array[0];
current_tex_coord.Y = array[1];
current_tex_coord.Z = size > 2 ? array[2] : 0.0f;
current_tex_coord.W = size > 3 ? array[3] : 1.0f;
break;
}
case TGL_DOUBLE: {
TGLdouble *array = (TGLdouble *)((TGLbyte *)texcoord_array + offset);
current_tex_coord.X = array[0];
current_tex_coord.Y = array[1];
current_tex_coord.Z = size > 2 ? array[2] : 0.0f;
current_tex_coord.W = size > 3 ? array[3] : 1.0f;
break;
}
case TGL_INT: {
TGLint *array = (TGLint *)((TGLbyte *)texcoord_array + offset);
current_tex_coord.X = array[0];
current_tex_coord.Y = array[1];
current_tex_coord.Z = size > 2 ? array[2] : 0.0f;
current_tex_coord.W = size > 3 ? array[3] : 1.0f;
break;
}
case TGL_SHORT: {
TGLshort *array = (TGLshort *)((TGLbyte *)texcoord_array + offset);
current_tex_coord.X = array[0];
current_tex_coord.Y = array[1];
current_tex_coord.Z = size > 2 ? array[2] : 0.0f;
current_tex_coord.W = size > 3 ? array[3] : 1.0f;
break;
}
default:
assert(0);
}
}
if (states & VERTEX_ARRAY) {
GLParam p[5];
int size = vertex_array_size;
offset = idx * vertex_array_stride;
switch (vertex_array_type) {
case TGL_FLOAT: {
TGLfloat *array = (TGLfloat *)((TGLbyte *)vertex_array + offset);
p[1].f = array[0];
p[2].f = array[1];
p[3].f = size > 2 ? array[2] : 0.0f;
p[4].f = size > 3 ? array[3] : 1.0f;
break;
}
case TGL_DOUBLE: {
TGLdouble *array = (TGLdouble *)((TGLbyte *)vertex_array + offset);
p[1].f = array[0];
p[2].f = array[1];
p[3].f = size > 2 ? array[2] : 0.0f;
p[4].f = size > 3 ? array[3] : 1.0f;
break;
}
case TGL_INT: {
TGLint *array = (TGLint *)((TGLbyte *)vertex_array + offset);
p[1].f = array[0];
p[2].f = array[1];
p[3].f = size > 2 ? array[2] : 0.0f;
p[4].f = size > 3 ? array[3] : 1.0f;
break;
}
case TGL_SHORT: {
TGLshort *array = (TGLshort *)((TGLbyte *)vertex_array + offset);
p[1].f = array[0];
p[2].f = array[1];
p[3].f = size > 2 ? array[2] : 0.0f;
p[4].f = size > 3 ? array[3] : 1.0f;
break;
}
default:
assert(0);
}
glopVertex(p);
}
}
void GLContext::glopDrawArrays(GLParam *p) {
GLParam array_element[2];
GLParam begin[2];
begin[1].i = p[1].i;
glopBegin(begin);
for (int i = 0; i < p[3].i; i++) {
array_element[1].i = p[2].i + i;
glopArrayElement(array_element);
}
glopEnd(nullptr);
}
void GLContext::glopDrawElements(GLParam *p) {
GLParam array_element[2];
void *indices;
GLParam begin[2];
indices = (char *)p[4].p;
begin[1].i = p[1].i;
glopBegin(begin);
for (int i = 0; i < p[2].i; i++) {
switch (p[3].i) {
case TGL_UNSIGNED_BYTE:
array_element[1].i = ((TGLbyte *)indices)[i];
break;
case TGL_UNSIGNED_SHORT:
array_element[1].i = ((TGLshort *)indices)[i];
break;
case TGL_UNSIGNED_INT:
array_element[1].i = ((TGLint *)indices)[i];
break;
default:
assert(0);
break;
}
glopArrayElement(array_element);
}
glopEnd(nullptr);
}
void GLContext::gl_EnableClientState(GLParam *p) {
client_states |= p[1].i;
}
void GLContext::gl_DisableClientState(GLParam *p) {
client_states &= p[1].i;
}
void GLContext::gl_VertexPointer(GLParam *p) {
vertex_array_size = p[1].i;
vertex_array_type = p[2].i;
vertex_array = p[4].p;
switch (vertex_array_type) {
case TGL_FLOAT:
vertex_array_stride = p[3].i != 0 ? p[3].i : vertex_array_size * sizeof(TGLfloat);
break;
case TGL_DOUBLE:
vertex_array_stride = p[3].i != 0 ? p[3].i : vertex_array_size * sizeof(TGLdouble);
break;
case TGL_INT:
vertex_array_stride = p[3].i != 0 ? p[3].i : vertex_array_size * sizeof(TGLint);
break;
case TGL_SHORT:
vertex_array_stride = p[3].i != 0 ? p[3].i : vertex_array_size * sizeof(TGLshort);
break;
default:
assert(0);
break;
}
}
void GLContext::gl_ColorPointer(GLParam *p) {
color_array_size = p[1].i;
color_array_type = p[2].i;
color_array = p[4].p;
switch (color_array_type) {
case TGL_BYTE:
case TGL_UNSIGNED_BYTE:
color_array_stride = p[3].i != 0 ? p[3].i : color_array_stride * sizeof(TGLbyte);
break;
case TGL_SHORT:
case TGL_UNSIGNED_SHORT:
color_array_stride = p[3].i != 0 ? p[3].i : color_array_stride * sizeof(TGLshort);
break;
case TGL_INT:
case TGL_UNSIGNED_INT:
color_array_stride = p[3].i != 0 ? p[3].i : color_array_stride * sizeof(TGLint);
break;
case TGL_FLOAT:
color_array_stride = p[3].i != 0 ? p[3].i : color_array_stride * sizeof(TGLfloat);
break;
case TGL_DOUBLE:
color_array_stride = p[3].i != 0 ? p[3].i : color_array_stride * sizeof(TGLdouble);
break;
default:
assert(0);
break;
}
}
void GLContext::gl_NormalPointer(GLParam *p) {
normal_array_type = p[1].i;
normal_array = p[3].p;
switch (p[1].i) {
case TGL_FLOAT:
normal_array_stride = p[2].i != 0 ? p[2].i : 3 * sizeof(TGLfloat);
break;
case TGL_DOUBLE:
normal_array_stride = p[2].i != 0 ? p[2].i : 3 * sizeof(TGLdouble);
break;
case TGL_INT:
normal_array_stride = p[2].i != 0 ? p[2].i : 3 * sizeof(TGLint);
break;
case TGL_SHORT:
normal_array_stride = p[2].i != 0 ? p[2].i : 3 * sizeof(TGLshort);
break;
default:
assert(0);
break;
}
}
void GLContext::gl_TexCoordPointer(GLParam *p) {
texcoord_array_size = p[1].i;
texcoord_array_type = p[2].i;
texcoord_array = p[4].p;
switch (texcoord_array_type) {
case TGL_FLOAT:
texcoord_array_stride = p[3].i != 0 ? p[3].i : texcoord_array_size * sizeof(TGLfloat);
break;
case TGL_DOUBLE:
texcoord_array_stride = p[3].i != 0 ? p[3].i : texcoord_array_size * sizeof(TGLdouble);
break;
case TGL_INT:
texcoord_array_stride = p[3].i != 0 ? p[3].i : texcoord_array_size * sizeof(TGLint);
break;
case TGL_SHORT:
texcoord_array_stride = p[3].i != 0 ? p[3].i : texcoord_array_size * sizeof(TGLshort);
break;
default:
assert(0);
break;
}
}
} // end of namespace TinyGL

58
graphics/tinygl/clear.cpp Normal file
View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
#include "graphics/tinygl/zdirtyrect.h"
namespace TinyGL {
void GLContext::glopClearColor(GLParam *p) {
clear_color = Vector4(p[1].f, p[2].f, p[3].f, p[4].f);
}
void GLContext::glopClearDepth(GLParam *p) {
clear_depth = p[1].f;
}
void GLContext::glopClearStencil(GLParam *p) {
clear_stencil = p[1].i & 0xFF;
}
void GLContext::glopClear(GLParam *p) {
int mask = p[1].i;
int z = (int)(clear_depth * ((1 << ZB_Z_BITS) - 1));
int r = (int)(clear_color.X * 255);
int g = (int)(clear_color.Y * 255);
int b = (int)(clear_color.Z * 255);
int s = (int)(clear_stencil);
issueDrawCall(new ClearBufferDrawCall(mask & TGL_DEPTH_BUFFER_BIT, z,
mask & TGL_COLOR_BUFFER_BIT, r, g, b,
mask & TGL_STENCIL_BUFFER_BIT, s));
}
} // end of namespace TinyGL

469
graphics/tinygl/clip.cpp Normal file
View File

@@ -0,0 +1,469 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
#define CLIP_XMIN (1 << 0)
#define CLIP_XMAX (1 << 1)
#define CLIP_YMIN (1 << 2)
#define CLIP_YMAX (1 << 3)
#define CLIP_ZMIN (1 << 4)
#define CLIP_ZMAX (1 << 5)
void GLContext::gl_transform_to_viewport(GLVertex *v) {
float winv, result;
// coordinates
winv = (float)(1.0 / v->pc.W);
// Compute and clamp X coordinate
result = v->pc.X * winv * viewport.scale.X + viewport.trans.X;
if (result > (float)INT_MAX)
v->zp.x = INT_MAX;
else if (result < (float)INT_MIN)
v->zp.x = INT_MIN;
else
v->zp.x = (int)result;
// Compute and clamp Y coordinate
result = v->pc.Y * winv * viewport.scale.Y + viewport.trans.Y;
if (result > (float)INT_MAX)
v->zp.y = INT_MAX;
else if (result < (float)INT_MIN)
v->zp.y = INT_MIN;
else
v->zp.y = (int)result;
// Compute and clamp Z coordinate
result = v->pc.Z * winv * viewport.scale.Z + viewport.trans.Z;
if (result > (float)INT_MAX)
v->zp.z = INT_MAX;
else if (result < (float)INT_MIN)
v->zp.z = INT_MIN;
else
v->zp.z = (int)result;
// color
v->zp.r = (int)(v->color.X * ZB_POINT_RED_MAX);
v->zp.g = (int)(v->color.Y * ZB_POINT_GREEN_MAX);
v->zp.b = (int)(v->color.Z * ZB_POINT_BLUE_MAX);
v->zp.a = (int)(v->color.W * ZB_POINT_ALPHA_MAX);
// texture
if (texture_2d_enabled) {
v->zp.s = (int)(v->tex_coord.X * ZB_POINT_ST_MAX);
v->zp.t = (int)(v->tex_coord.Y * ZB_POINT_ST_MAX);
}
// fog
if (fog_enabled) {
v->zp.f = (int)(v->fog_factor * ZB_FOG_MAX);
}
}
void GLContext::gl_add_select1(int z1, int z2, int z3) {
int min, max;
min = max = z1;
if (z2 < min)
min = z2;
if (z3 < min)
min = z3;
if (z2 > max)
max = z2;
if (z3 > max)
max = z3;
gl_add_select(0xffffffff - min, 0xffffffff - max);
}
// point
void GLContext::gl_draw_point(GLVertex *p0) {
if (p0->clip_code == 0) {
if (render_mode == TGL_SELECT) {
gl_add_select(p0->zp.z, p0->zp.z);
} else {
fb->plot(&p0->zp);
}
}
}
// line
static inline void interpolate_color(GLContext *c, GLVertex *q, GLVertex *p0, GLVertex *p1, float t) {
if (c->current_shade_model == TGL_SMOOTH)
q->color = p0->color + (p1->color - p0->color) * t;
else
q->color = p0->color;
}
static inline void interpolate(GLContext *c, GLVertex *q, GLVertex *p0, GLVertex *p1, float t) {
q->pc = p0->pc + (p1->pc - p0->pc) * t;
interpolate_color(c, q, p0, p1, t);
}
// Line Clipping
// Line Clipping algorithm from 'Computer Graphics', Principles and
// Practice
static inline int ClipLine1(float denom, float num, float *tmin, float *tmax) {
float t;
if (denom > 0) {
t = num / denom;
if (t > *tmax)
return 0;
if (t > *tmin)
*tmin = t;
} else if (denom < 0) {
t = num / denom;
if (t < *tmin)
return 0;
if (t < *tmax)
*tmax = t;
} else if (num > 0)
return 0;
return 1;
}
void GLContext::gl_draw_line(GLVertex *p1, GLVertex *p2) {
float dx, dy, dz, dw, x1, y1, z1, w1;
float tmin, tmax;
GLVertex q1, q2;
int cc1, cc2;
cc1 = p1->clip_code;
cc2 = p2->clip_code;
if ((cc1 | cc2) == 0) {
if (render_mode == TGL_SELECT) {
gl_add_select1(p1->zp.z, p2->zp.z, p2->zp.z);
} else {
if (depth_test_enabled)
fb->fillLineZ(&p1->zp, &p2->zp);
else
fb->fillLine(&p1->zp, &p2->zp);
}
} else if ((cc1 & cc2) != 0) {
return;
} else {
dx = p2->pc.X - p1->pc.X;
dy = p2->pc.Y - p1->pc.Y;
dz = p2->pc.Z - p1->pc.Z;
dw = p2->pc.W - p1->pc.W;
x1 = p1->pc.X;
y1 = p1->pc.Y;
z1 = p1->pc.Z;
w1 = p1->pc.W;
tmin = 0;
tmax = 1;
if (ClipLine1(dx + dw, -x1 - w1, &tmin, &tmax) &&
ClipLine1(-dx + dw, x1 - w1, &tmin, &tmax) &&
ClipLine1(dy + dw, -y1 - w1, &tmin, &tmax) &&
ClipLine1(-dy + dw, y1 - w1, &tmin, &tmax) &&
ClipLine1(dz + dw, -z1 - w1, &tmin, &tmax) &&
ClipLine1(-dz + dw, z1 - w1, &tmin, &tmax)) {
interpolate(this, &q1, p1, p2, tmin);
interpolate(this, &q2, p1, p2, tmax);
gl_transform_to_viewport(&q1);
gl_transform_to_viewport(&q2);
if (depth_test_enabled)
fb->fillLineZ(&q1.zp, &q2.zp);
else
fb->fillLine(&q1.zp, &q2.zp);
}
}
}
// triangle
// Clipping
// We clip the segment [a,b] against the 6 planes of the normal volume.
// We compute the point 'c' of intersection and the value of the parameter 't'
// of the intersection if x=a+t(b-a).
#define clip_func(name, sign, dir, dir1, dir2) \
static float name(Vector4 *c, Vector4 *a, Vector4 *b) { \
float t, dX, dY, dZ, dW, den;\
dX = (b->X - a->X); \
dY = (b->Y - a->Y); \
dZ = (b->Z - a->Z); \
dW = (b->W - a->W); \
den = -(sign d ## dir) + dW; \
if (den == 0) \
t = 0; \
else \
t = (sign a->dir - a->W) / den; \
c-> dir1 = (a->dir1 + t * d ## dir1); \
c-> dir2 = (a->dir2 + t * d ## dir2); \
c->W = (a->W + t * dW); \
c-> dir = (sign c->W); \
return t; \
}
clip_func(clip_xmin, -, X, Y, Z)
clip_func(clip_xmax, +, X, Y, Z)
clip_func(clip_ymin, -, Y, X, Z)
clip_func(clip_ymax, +, Y, X, Z)
clip_func(clip_zmin, -, Z, X, Y)
clip_func(clip_zmax, +, Z, X, Y)
float(*const clip_proc[6])(Vector4 *, Vector4 *, Vector4 *) = {
clip_xmin, clip_xmax,
clip_ymin, clip_ymax,
clip_zmin, clip_zmax
};
static inline void updateTmp(GLContext *c, GLVertex *q, GLVertex *p0, GLVertex *p1, float t) {
interpolate_color(c, q, p0, p1, t);
if (c->texture_2d_enabled) {
// NOTE: This could be implemented with operator overloading,
// but i'm not 100% sure that we can completely disregard Z and W components so I'm leaving it like this for now.
q->tex_coord.X = (p0->tex_coord.X + (p1->tex_coord.X - p0->tex_coord.X) * t);
q->tex_coord.Y = (p0->tex_coord.Y + (p1->tex_coord.Y - p0->tex_coord.Y) * t);
}
q->clip_code = gl_clipcode(q->pc.X, q->pc.Y, q->pc.Z, q->pc.W);
if (q->clip_code == 0)
c->gl_transform_to_viewport(q);
}
void GLContext::gl_draw_triangle(GLVertex *p0, GLVertex *p1, GLVertex *p2) {
int co, c_and, cc[3], front;
float norm;
cc[0] = p0->clip_code;
cc[1] = p1->clip_code;
cc[2] = p2->clip_code;
co = cc[0] | cc[1] | cc[2];
// we handle the non clipped case here to go faster
if (co == 0) {
norm = (float)(p1->zp.x - p0->zp.x) * (float)(p2->zp.y - p0->zp.y) -
(float)(p2->zp.x - p0->zp.x) * (float)(p1->zp.y - p0->zp.y);
if (norm == 0)
return;
front = norm < 0.0;
front = front ^ current_front_face;
// back face culling
if (cull_face_enabled) {
// most used case first */
if (current_cull_face == TGL_BACK) {
if (front == 0)
return;
draw_triangle_front(this, p0, p1, p2);
} else if (current_cull_face == TGL_FRONT) {
if (front != 0)
return;
draw_triangle_back(this, p0, p1, p2);
} else {
return;
}
} else {
// no culling
if (front) {
draw_triangle_front(this, p0, p1, p2);
} else {
draw_triangle_back(this, p0, p1, p2);
}
}
} else {
c_and = cc[0] & cc[1] & cc[2];
if (c_and == 0) {
gl_draw_triangle_clip(p0, p1, p2, 0);
}
}
}
void GLContext::gl_draw_triangle_clip(GLVertex *p0, GLVertex *p1, GLVertex *p2, int clip_bit) {
int co, c_and, co1, cc[3], edge_flag_tmp, clip_mask;
GLVertex tmp1, tmp2, *q[3];
float tt;
cc[0] = p0->clip_code;
cc[1] = p1->clip_code;
cc[2] = p2->clip_code;
co = cc[0] | cc[1] | cc[2];
if (co == 0) {
gl_draw_triangle(p0, p1, p2);
} else {
c_and = cc[0] & cc[1] & cc[2];
// the triangle is completely outside
if (c_and != 0)
return;
// find the next direction to clip
while (clip_bit < 6 && (co & (1 << clip_bit)) == 0) {
clip_bit++;
}
// this test can be true only in case of rounding errors
if (clip_bit == 6) {
#if 0
printf("Error:\n");
printf("%f %f %f %f\n", p0->pc.X, p0->pc.Y, p0->pc.Z, p0->pc.W);
printf("%f %f %f %f\n", p1->pc.X, p1->pc.Y, p1->pc.Z, p1->pc.W);
printf("%f %f %f %f\n", p2->pc.X, p2->pc.Y, p2->pc.Z, p2->pc.W);
#endif
return;
}
clip_mask = 1 << clip_bit;
co1 = (cc[0] ^ cc[1] ^ cc[2]) & clip_mask;
if (co1) {
// one point outside
if (cc[0] & clip_mask) {
q[0] = p0; q[1] = p1; q[2] = p2;
} else if (cc[1] & clip_mask) {
q[0] = p1; q[1] = p2; q[2] = p0;
} else {
q[0] = p2; q[1] = p0; q[2] = p1;
}
tt = clip_proc[clip_bit](&tmp1.pc, &q[0]->pc, &q[1]->pc);
updateTmp(this, &tmp1, q[0], q[1], tt);
tt = clip_proc[clip_bit](&tmp2.pc, &q[0]->pc, &q[2]->pc);
updateTmp(this, &tmp2, q[0], q[2], tt);
tmp1.edge_flag = q[0]->edge_flag;
edge_flag_tmp = q[2]->edge_flag;
q[2]->edge_flag = 0;
gl_draw_triangle_clip(&tmp1, q[1], q[2], clip_bit + 1);
tmp2.edge_flag = 1;
tmp1.edge_flag = 0;
q[2]->edge_flag = edge_flag_tmp;
gl_draw_triangle_clip(&tmp2, &tmp1, q[2], clip_bit + 1);
} else {
// two points outside
if ((cc[0] & clip_mask) == 0) {
q[0] = p0; q[1] = p1; q[2] = p2;
} else if ((cc[1] & clip_mask) == 0) {
q[0] = p1; q[1] = p2; q[2] = p0;
} else {
q[0] = p2; q[1] = p0; q[2] = p1;
}
tt = clip_proc[clip_bit](&tmp1.pc, &q[0]->pc, &q[1]->pc);
updateTmp(this, &tmp1, q[0], q[1], tt);
tt = clip_proc[clip_bit](&tmp2.pc, &q[0]->pc, &q[2]->pc);
updateTmp(this, &tmp2, q[0], q[2], tt);
tmp1.edge_flag = 1;
tmp2.edge_flag = q[2]->edge_flag;
gl_draw_triangle_clip(q[0], &tmp1, &tmp2, clip_bit + 1);
}
}
}
void GLContext::gl_draw_triangle_select(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2) {
c->gl_add_select1(p0->zp.z, p1->zp.z, p2->zp.z);
}
int count_triangles, count_triangles_textured, count_pixels;
void GLContext::gl_draw_triangle_fill(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2) {
if (c->_profilingEnabled) {
int norm;
assert(p0->zp.x >= 0 && p0->zp.x < c->fb->getPixelBufferWidth());
assert(p0->zp.y >= 0 && p0->zp.y < c->fb->getPixelBufferHeight());
assert(p1->zp.x >= 0 && p1->zp.x < c->fb->getPixelBufferWidth());
assert(p1->zp.y >= 0 && p1->zp.y < c->fb->getPixelBufferHeight());
assert(p2->zp.x >= 0 && p2->zp.x < c->fb->getPixelBufferWidth());
assert(p2->zp.y >= 0 && p2->zp.y < c->fb->getPixelBufferHeight());
norm = (p1->zp.x - p0->zp.x) * (p2->zp.y - p0->zp.y) -
(p2->zp.x - p0->zp.x) * (p1->zp.y - p0->zp.y);
count_pixels += abs(norm) / 2;
count_triangles++;
}
if (!c->color_mask_red && !c->color_mask_green && !c->color_mask_blue && !c->color_mask_alpha) {
c->fb->fillTriangleDepthOnly(&p0->zp, &p1->zp, &p2->zp);
} else if (c->texture_2d_enabled && c->current_texture->images[0].pixmap) {
if (c->_profilingEnabled) {
count_triangles_textured++;
}
c->fb->setTexture(c->current_texture->images[0].pixmap, c->texture_wrap_s, c->texture_wrap_t);
if (c->current_shade_model == TGL_SMOOTH) {
c->fb->fillTriangleTextureMappingPerspectiveSmooth(&p0->zp, &p1->zp, &p2->zp);
} else {
c->fb->fillTriangleTextureMappingPerspectiveFlat(&p0->zp, &p1->zp, &p2->zp);
}
} else if (c->current_shade_model == TGL_SMOOTH) {
c->fb->fillTriangleSmooth(&p0->zp, &p1->zp, &p2->zp);
} else {
c->fb->fillTriangleFlat(&p0->zp, &p1->zp, &p2->zp);
}
}
// Render a clipped triangle in line mode
void GLContext::gl_draw_triangle_line(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2) {
if (c->depth_test_enabled) {
if (p0->edge_flag)
c->fb->fillLineZ(&p0->zp, &p1->zp);
if (p1->edge_flag)
c->fb->fillLineZ(&p1->zp, &p2->zp);
if (p2->edge_flag)
c->fb->fillLineZ(&p2->zp, &p0->zp);
} else {
if (p0->edge_flag)
c->fb->fillLine(&p0->zp, &p1->zp);
if (p1->edge_flag)
c->fb->fillLine(&p1->zp, &p2->zp);
if (p2->edge_flag)
c->fb->fillLine(&p2->zp, &p0->zp);
}
}
// Render a clipped triangle in point mode
void GLContext::gl_draw_triangle_point(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2) {
if (p0->edge_flag)
c->fb->plot(&p0->zp);
if (p1->edge_flag)
c->fb->plot(&p1->zp);
if (p2->edge_flag)
c->fb->plot(&p2->zp);
}
} // end of namespace TinyGL

View File

@@ -0,0 +1,146 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_TINYGL_COLORMASKS_H
#define GRAPHICS_TINYGL_COLORMASKS_H
#include "graphics/tinygl/gl.h"
namespace TinyGL {
template<uint Format, uint Type>
struct ColorMasks {
};
template<>
struct ColorMasks<TGL_RGB, TGL_UNSIGNED_SHORT_5_6_5> {
enum : uint {
kBytesPerPixel = 2,
kAlphaBits = 0,
kRedBits = 5,
kGreenBits = 6,
kBlueBits = 5,
kAlphaShift = 0,
kRedShift = kGreenBits+kBlueBits,
kGreenShift = kBlueBits,
kBlueShift = 0,
kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift,
kRedMask = ((1 << kRedBits) - 1) << kRedShift,
kGreenMask = ((1 << kGreenBits) - 1) << kGreenShift,
kBlueMask = ((1 << kBlueBits) - 1) << kBlueShift,
kRedBlueMask = kRedMask | kBlueMask,
};
typedef uint16 PixelType;
};
template<>
struct ColorMasks<TGL_RGBA, TGL_UNSIGNED_SHORT_5_5_5_1> {
enum {
kBytesPerPixel = 2,
kAlphaBits = 1,
kRedBits = 5,
kGreenBits = 5,
kBlueBits = 5,
kAlphaShift = 0,
kRedShift = kGreenBits+kBlueBits+kAlphaBits,
kGreenShift = kBlueBits+kAlphaBits,
kBlueShift = kAlphaBits,
kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift,
kRedMask = ((1 << kRedBits) - 1) << kRedShift,
kGreenMask = ((1 << kGreenBits) - 1) << kGreenShift,
kBlueMask = ((1 << kBlueBits) - 1) << kBlueShift,
kRedBlueMask = kRedMask | kBlueMask
};
typedef uint16 PixelType;
};
template<>
struct ColorMasks<TGL_RGBA, TGL_UNSIGNED_SHORT_4_4_4_4> {
enum {
kBytesPerPixel = 2,
kAlphaBits = 4,
kRedBits = 4,
kGreenBits = 4,
kBlueBits = 4,
kAlphaShift = 0,
kRedShift = kGreenBits+kBlueBits+kAlphaBits,
kGreenShift = kBlueBits+kAlphaBits,
kBlueShift = kAlphaBits,
kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift,
kRedMask = ((1 << kRedBits) - 1) << kRedShift,
kGreenMask = ((1 << kGreenBits) - 1) << kGreenShift,
kBlueMask = ((1 << kBlueBits) - 1) << kBlueShift,
kRedBlueMask = kRedMask | kBlueMask
};
typedef uint16 PixelType;
};
template<>
struct ColorMasks<TGL_RGBA, TGL_UNSIGNED_BYTE> {
enum {
kBytesPerPixel = 4,
kAlphaBits = 8,
kRedBits = 8,
kGreenBits = 8,
kBlueBits = 8,
#if defined(SCUMM_LITTLE_ENDIAN)
kAlphaShift = kRedBits+kGreenBits+kBlueBits,
kRedShift = 0,
kGreenShift = kRedBits,
kBlueShift = kRedBits+kGreenBits,
#else
kAlphaShift = 0,
kRedShift = kGreenBits+kBlueBits+kAlphaBits,
kGreenShift = kBlueBits+kAlphaBits,
kBlueShift = kAlphaBits,
#endif
kAlphaMask = ((1 << kAlphaBits) - 1) << kAlphaShift,
kRedMask = ((1 << kRedBits) - 1) << kRedShift,
kGreenMask = ((1 << kGreenBits) - 1) << kGreenShift,
kBlueMask = ((1 << kBlueBits) - 1) << kBlueShift,
kRedBlueMask = kRedMask | kBlueMask,
};
typedef uint32 PixelType;
};
} // End of namespace Graphics
#endif

92
graphics/tinygl/fog.cpp Normal file
View File

@@ -0,0 +1,92 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
void GLContext::glopFog(GLParam *p) {
int pname = p[1].i;
switch (pname) {
case TGL_FOG_MODE:
switch (p[2].i) {
case TGL_LINEAR:
case TGL_EXP:
case TGL_EXP2:
fog_mode = p[2].i;
break;
default:
error("tglFog: unknown fog mode");
return;
}
break;
case TGL_FOG_DENSITY:
if (p[2].f < 0.0f) {
error("tglFog: fog density negate param");
return;
}
fog_density = p[2].f;
break;
case TGL_FOG_START:
fog_start = p[2].f;
break;
case TGL_FOG_END:
fog_end = p[2].f;
break;
case TGL_FOG_COLOR:
fog_color = Vector4(clampf(p[2].f, 0.0f, 1.0f),
clampf(p[3].f, 0.0f, 1.0f),
clampf(p[4].f, 0.0f, 1.0f),
clampf(p[5].f, 0.0f, 1.0f));
break;
default:
error("tglFog: param not implemented");
return;
}
}
void GLContext::gl_calc_fog_factor(GLVertex *v) {
float eye_distance = sqrtf(v->ec.X * v->ec.X + v->ec.Y * v->ec.Y + v->ec.Z * v->ec.Z);
switch (fog_mode) {
case TGL_LINEAR: {
float fog_distance = 1.0f;
if (fog_start != fog_end)
fog_distance /= (fog_end - fog_start);
v->fog_factor = (fog_end - eye_distance) * fog_distance;
}
break;
case TGL_EXP:
v->fog_factor = expf(-fog_density * eye_distance);
break;
case TGL_EXP2:
v->fog_factor = expf(-(fog_density * fog_density * eye_distance * eye_distance));
break;
default:
v->fog_factor = 0;
break;
}
v->fog_factor = clampf(v->fog_factor, 0.0f, 1.0f);
}
} // end of namespace TinyGL

982
graphics/tinygl/get.cpp Normal file
View File

@@ -0,0 +1,982 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
#define MAX_INTEGER 2147483647
#define FLOAT_TO_INTEGER(f) ((TGLint)(f * MAX_INTEGER))
#define VALUE_TO_BOOLEAN(f) ((f) ? TGL_TRUE : TGL_FALSE)
void GLContext::gl_get_pname(TGLenum pname, union uglValue *data, eDataType &dataType) {
int mnr = 0;
switch (pname) {
case TGL_ACCUM_ALPHA_BITS:
// fall through
case TGL_ACCUM_BLUE_BITS:
// fall through
case TGL_ACCUM_CLEAR_VALUE:
// fall through
case TGL_ACCUM_GREEN_BITS:
// fall through
case TGL_ACCUM_RED_BITS:
data->_int = 0;
dataType = kIntType;
break;
case TGL_ALIASED_LINE_WIDTH_RANGE:
error("gl_get_pname: TGL_ALIASED_LINE_WIDTH_RANGE option not implemented");
break;
case TGL_ALIASED_POINT_SIZE_RANGE:
error("gl_get_pname: TGL_ALIASED_POINT_SIZE_RANGE option not implemented");
break;
case TGL_ALPHA_BIAS:
// fall through
case TGL_RED_BIAS:
// fall through
case TGL_GREEN_BIAS:
// fall through
case TGL_BLUE_BIAS:
// fall through
case TGL_DEPTH_BIAS:
data->_float = 0.0f;
dataType = kFloatType;
break;
case TGL_ALPHA_SCALE:
// fall through
case TGL_RED_SCALE:
// fall through
case TGL_GREEN_SCALE:
// fall through
case TGL_DEPTH_SCALE:
// fall through
case TGL_BLUE_SCALE:
data->_float = 1.0f;
dataType = kFloatType;
break;
case TGL_ALPHA_BITS:
data->_int = fb->getPixelFormat().aBits();
dataType = kIntType;
break;
case TGL_RED_BITS:
data->_int = fb->getPixelFormat().rBits();
dataType = kIntType;
break;
case TGL_GREEN_BITS:
data->_int = fb->getPixelFormat().gBits();
dataType = kIntType;
break;
case TGL_BLUE_BITS:
data->_int = fb->getPixelFormat().bBits();
dataType = kIntType;
break;
case TGL_ALPHA_TEST:
data->_int = (TGLint)alpha_test_enabled;
dataType = kIntType;
break;
case TGL_ALPHA_TEST_FUNC:
data->_int = alpha_test_func;
dataType = kIntType;
break;
case TGL_ALPHA_TEST_REF:
data->_float = alpha_test_ref_val / 255.0f;
dataType = kFloatType;
break;
case TGL_ATTRIB_STACK_DEPTH:
error("gl_get_pname: TGL_ALIASED_POINT_SIZE_RANGE option not implemented");
break;
case TGL_AUTO_NORMAL:
data->_int = 0;
dataType = kIntType;
break;
case TGL_AUX_BUFFERS:
error("gl_get_pname: TGL_AUX_BUFFERS option not implemented");
break;
case TGL_BLEND:
data->_int = (TGLint)blending_enabled;
dataType = kIntType;
break;
case TGL_BLEND_DST:
data->_int = destination_blending_factor;
dataType = kIntType;
break;
case TGL_BLEND_SRC:
data->_int = source_blending_factor;
dataType = kIntType;
break;
case TGL_CLIENT_ATTRIB_STACK_DEPTH:
error("gl_get_pname: TGL_CLIENT_ATTRIB_STACK_DEPTH option not implemented");
break;
case TGL_CLIP_PLANE0:
// fall through
case TGL_CLIP_PLANE1:
// fall through
case TGL_CLIP_PLANE2:
// fall through
case TGL_CLIP_PLANE3:
// fall through
case TGL_CLIP_PLANE4:
// fall through
case TGL_CLIP_PLANE5:
error("gl_get_pname: TGL_CLIP_PLANEx option not implemented");
break;
case TGL_COLOR_ARRAY:
data->_int = (TGLint)(color_array != nullptr);
dataType = kIntType;
break;
case TGL_COLOR_ARRAY_SIZE:
data->_int = color_array_size;
dataType = kIntType;
break;
case TGL_COLOR_ARRAY_STRIDE:
data->_int = color_array_stride;
dataType = kIntType;
break;
case TGL_COLOR_ARRAY_TYPE:
data->_int = color_array_type;
dataType = kIntType;
break;
case TGL_COLOR_CLEAR_VALUE:
data->_float4[0] = clear_color._v[0];
data->_float4[1] = clear_color._v[1];
data->_float4[2] = clear_color._v[2];
data->_float4[3] = clear_color._v[3];
dataType = kFloat4Type;
break;
case TGL_COLOR_LOGIC_OP:
error("gl_get_pname: TGL_COLOR_LOGIC_OP option not implemented");
break;
case TGL_COLOR_MATERIAL:
data->_int = (TGLint)color_material_enabled;
dataType = kIntType;
break;
case TGL_COLOR_MATERIAL_FACE:
data->_int = current_color_material_mode;
dataType = kIntType;
break;
case TGL_COLOR_MATERIAL_PARAMETER:
data->_int = current_color_material_type;
dataType = kIntType;
break;
case TGL_COLOR_WRITEMASK:
data->_int4[0] = (TGLint)TGL_TRUE;
data->_int4[1] = (TGLint)TGL_TRUE;
data->_int4[2] = (TGLint)TGL_TRUE;
data->_int4[3] = (TGLint)TGL_TRUE;
dataType = kInt4Type;
break;
case TGL_CULL_FACE:
data->_int = (TGLint)cull_face_enabled;
dataType = kIntType;
break;
case TGL_CULL_FACE_MODE:
data->_int = (TGLint)current_cull_face;
dataType = kIntType;
break;
case TGL_CURRENT_COLOR:
data->_float4[0] = current_color._v[0];
data->_float4[1] = current_color._v[1];
data->_float4[2] = current_color._v[2];
data->_float4[3] = current_color._v[3];
dataType = kFloat4Type;
break;
case TGL_CURRENT_INDEX:
error("gl_get_pname: TGL_CURRENT_INDEX not supported");
break;
case TGL_CURRENT_NORMAL:
data->_float4[0] = current_normal._v[0];
data->_float4[1] = current_normal._v[1];
data->_float4[2] = current_normal._v[2];
data->_float4[3] = current_normal._v[3];
dataType = kFloat4Type;
break;
case TGL_CURRENT_RASTER_INDEX:
error("gl_get_pname: TGL_CURRENT_RASTER_INDEX not supported");
break;
case TGL_CURRENT_RASTER_COLOR:
// fall through
case TGL_CURRENT_RASTER_DISTANCE:
// fall through
case TGL_CURRENT_RASTER_POSITION:
// fall through
case TGL_CURRENT_RASTER_POSITION_VALID:
// fall through
case TGL_CURRENT_RASTER_TEXTURE_COORDS:
error("gl_get_pname: TGL_CURRENT_RASTER_x options not implemented");
break;
case TGL_CURRENT_TEXTURE_COORDS:
data->_float4[0] = current_tex_coord._v[0];
data->_float4[1] = current_tex_coord._v[1];
data->_float4[2] = current_tex_coord._v[2];
data->_float4[3] = current_tex_coord._v[3];
dataType = kFloat4Type;
break;
case TGL_DEPTH_BITS:
data->_int = 16;
dataType = kIntType;
break;
case TGL_DEPTH_CLEAR_VALUE:
data->_float = clear_depth;
dataType = kFloatType;
break;
case TGL_DEPTH_FUNC:
data->_int = depth_func;
dataType = kIntType;
break;
case TGL_DEPTH_RANGE:
data->_float2[0] = 1.0f;
data->_float2[1] = -1.0f;
dataType = kFloat2Type;
break;
case TGL_DEPTH_TEST:
data->_int = (TGLint)depth_test_enabled;
dataType = kIntType;
break;
case TGL_DEPTH_WRITEMASK:
data->_int = (TGLint)depth_write_mask;
dataType = kIntType;
break;
case TGL_DITHER:
data->_int = (TGLint)TGL_FALSE;
dataType = kIntType;
break;
case TGL_DOUBLEBUFFER:
data->_int = (TGLint)TGL_FALSE;
dataType = kIntType;
break;
case TGL_DRAW_BUFFER:
error("gl_get_pname: TGL_DRAW_BUFFER option not implemented");
break;
case TGL_EDGE_FLAG:
data->_int = current_edge_flag;
dataType = kIntType;
break;
case TGL_EDGE_FLAG_ARRAY:
error("gl_get_pname: TGL_EDGE_FLAG_ARRAY option not implemented");
break;
case TGL_EDGE_FLAG_ARRAY_STRIDE:
error("gl_get_pname: TGL_EDGE_FLAG_ARRAY_STRIDE option not implemented");
break;
case TGL_FEEDBACK_BUFFER_SIZE:
// fall through
case TGL_FEEDBACK_BUFFER_TYPE:
error("gl_get_pname: TGL_FEEDBACK_BUFFER_x option not implemented");
break;
case TGL_FOG:
data->_int = (TGLint)fog_enabled;
dataType = kIntType;
break;
case TGL_FOG_COLOR:
data->_float4[0] = fog_color._v[0];
data->_float4[1] = fog_color._v[1];
data->_float4[2] = fog_color._v[2];
data->_float4[3] = fog_color._v[3];
dataType = kFloat4Type;
break;
case TGL_FOG_DENSITY:
data->_float = fog_density;
dataType = kFloatType;
break;
case TGL_FOG_END:
data->_float = fog_end;
dataType = kFloatType;
break;
case TGL_FOG_HINT:
data->_int = TGL_DONT_CARE;
dataType = kIntType;
break;
case TGL_FOG_INDEX:
error("gl_get_pname: TGL_FOG_INDEX not supported");
break;
case TGL_FOG_MODE:
data->_int = fog_mode;
dataType = kIntType;
break;
case TGL_FOG_START:
data->_float = fog_start;
dataType = kFloatType;
break;
case TGL_FRONT_FACE:
data->_int = (current_front_face == 0) ? TGL_CCW : TGL_CW;
dataType = kIntType;
break;
case TGL_INDEX_ARRAY:
// fall through
case TGL_INDEX_ARRAY_STRIDE:
// fall through
case TGL_INDEX_ARRAY_TYPE:
// fall through
case TGL_INDEX_BITS:
// fall through
case TGL_INDEX_CLEAR_VALUE:
// fall through
case TGL_INDEX_LOGIC_OP:
// fall through
case TGL_INDEX_MODE:
// fall through
case TGL_INDEX_OFFSET:
// fall through
case TGL_INDEX_SHIFT:
// fall through
case TGL_INDEX_WRITEMASK:
error("gl_get_pname: TGL_INDEX_x not supported");
break;
case TGL_LIGHT0:
data->_int = (TGLint)lights[0].enabled;
dataType = kIntType;
break;
case TGL_LIGHT1:
data->_int = (TGLint)lights[1].enabled;
dataType = kIntType;
break;
case TGL_LIGHT2:
data->_int = (TGLint)lights[2].enabled;
dataType = kIntType;
break;
case TGL_LIGHT3:
data->_int = (TGLint)lights[3].enabled;
dataType = kIntType;
break;
case TGL_LIGHT4:
data->_int = (TGLint)lights[4].enabled;
dataType = kIntType;
break;
case TGL_LIGHT5:
data->_int = (TGLint)lights[5].enabled;
dataType = kIntType;
break;
case TGL_LIGHT6:
data->_int = (TGLint)lights[6].enabled;
dataType = kIntType;
break;
case TGL_LIGHT7:
data->_int = (TGLint)lights[7].enabled;
dataType = kIntType;
break;
case TGL_LIGHTING:
data->_int = (TGLint)lighting_enabled;
dataType = kIntType;
break;
case TGL_LIGHT_MODEL_AMBIENT:
data->_float4[0] = ambient_light_model._v[0];
data->_float4[1] = ambient_light_model._v[1];
data->_float4[2] = ambient_light_model._v[2];
data->_float4[3] = ambient_light_model._v[3];
dataType = kFloat4Type;
break;
case TGL_LIGHT_MODEL_COLOR_CONTROL:
error("gl_get_pname: TGL_LIGHT_MODEL_COLOR_CONTROL option not implemented");
break;
case TGL_LIGHT_MODEL_LOCAL_VIEWER:
data->_int = local_light_model;
dataType = kIntType;
break;
case TGL_LIGHT_MODEL_TWO_SIDE:
data->_int = light_model_two_side;
dataType = kIntType;
break;
case TGL_LINE_SMOOTH:
data->_int = (TGLint)TGL_FALSE;
dataType = kIntType;
break;
case TGL_LINE_SMOOTH_HINT:
data->_int = TGL_DONT_CARE;
dataType = kIntType;
break;
case TGL_LINE_STIPPLE:
// fall through
case TGL_LINE_STIPPLE_PATTERN:
// fall through
case TGL_LINE_STIPPLE_REPEAT:
// fall through
error("gl_get_pname: TGL_LINE_STIPPLE_x option not implemented");
break;
case TGL_LINE_WIDTH:
// fall through
case TGL_LINE_WIDTH_GRANULARITY:
// fall through
case TGL_LINE_WIDTH_RANGE:
error("gl_get_pname: TGL_LINE_x option not implemented");
break;
case TGL_LIST_BASE:
// fall through
case TGL_LIST_INDEX:
// fall through
case TGL_LIST_MODE:
error("gl_get_pname: TGL_LIST_x option not implemented");
break;
case TGL_LOGIC_OP_MODE:
error("gl_get_pname: TGL_LOGIC_OP_MODE option not implemented");
break;
case TGL_MAP1_COLOR_4:
// fall through
case TGL_MAP1_GRID_DOMAIN:
// fall through
case TGL_MAP1_GRID_SEGMENTS:
// fall through
case TGL_MAP1_INDEX:
// fall through
case TGL_MAP1_NORMAL:
// fall through
case TGL_MAP1_TEXTURE_COORD_1:
// fall through
case TGL_MAP1_TEXTURE_COORD_2:
// fall through
case TGL_MAP1_TEXTURE_COORD_3:
// fall through
case TGL_MAP1_TEXTURE_COORD_4:
// fall through
case TGL_MAP1_VERTEX_3:
// fall through
case TGL_MAP1_VERTEX_4:
// fall through
case TGL_MAP2_COLOR_4:
// fall through
case TGL_MAP2_GRID_DOMAIN:
// fall through
case TGL_MAP2_GRID_SEGMENTS:
// fall through
case TGL_MAP2_INDEX:
// fall through
case TGL_MAP2_NORMAL:
// fall through
case TGL_MAP2_TEXTURE_COORD_1:
// fall through
case TGL_MAP2_TEXTURE_COORD_2:
// fall through
case TGL_MAP2_TEXTURE_COORD_3:
// fall through
case TGL_MAP2_TEXTURE_COORD_4:
// fall through
case TGL_MAP2_VERTEX_3:
// fall through
case TGL_MAP2_VERTEX_4:
error("gl_get_pname: TGL_MAPx option not implemented");
break;
case TGL_MAP_COLOR:
// fall through
case TGL_MAP_STENCIL:
error("gl_get_pname: TGL_MAP_x not supported");
break;
case TGL_MATRIX_MODE:
data->_int = matrix_mode;
dataType = kIntType;
break;
case TGL_MAX_ATTRIB_STACK_DEPTH:
error("gl_get_pname: TGL_MAX_ATTRIB_STACK_DEPTH option not implemented");
break;
case TGL_MAX_CLIENT_ATTRIB_STACK_DEPTH:
error("gl_get_pname: TGL_MAX_CLIENT_ATTRIB_STACK_DEPTH option not implemented");
break;
case TGL_MAX_CLIP_PLANES:
error("gl_get_pname: TGL_MAX_CLIP_PLANES option not implemented");
break;
case TGL_MAX_ELEMENTS_INDICES:
// fall through
case TGL_MAX_ELEMENTS_VERTICES:
error("gl_get_pname: TGL_MAX_ELEMENTS_x option not implemented");
break;
case TGL_MAX_EVAL_ORDER:
error("gl_get_pname: TGL_MAX_EVAL_ORDER option not implemented");
break;
case TGL_MAX_LIGHTS:
data->_int = T_MAX_LIGHTS;
dataType = kIntType;
break;
case TGL_MAX_LIST_NESTING:
error("gl_get_pname: TGL_MAX_LIST_NESTING option not implemented");
break;
case TGL_MAX_MODELVIEW_STACK_DEPTH:
data->_int = MAX_MODELVIEW_STACK_DEPTH;
dataType = kIntType;
break;
case TGL_MAX_NAME_STACK_DEPTH:
data->_int = MAX_NAME_STACK_DEPTH;
dataType = kIntType;
break;
case TGL_MAX_PIXEL_MAP_TABLE:
error("gl_get_pname: TGL_MAX_PIXEL_MAP_TABLE option not implemented");
break;
case TGL_MAX_PROJECTION_STACK_DEPTH:
data->_int = MAX_PROJECTION_STACK_DEPTH;
dataType = kIntType;
break;
case TGL_MAX_TEXTURE_SIZE:
data->_int = _textureSize;
dataType = kIntType;
break;
case TGL_MAX_TEXTURE_STACK_DEPTH:
data->_int = MAX_TEXTURE_STACK_DEPTH;
dataType = kIntType;
break;
case TGL_MAX_VIEWPORT_DIMS:
error("gl_get_pname: TGL_MAX_VIEWPORT_DIMS option not implemented");
break;
case TGL_TEXTURE_MATRIX:
mnr++;
// fall through
case TGL_PROJECTION_MATRIX:
mnr++;
// fall through
case TGL_MODELVIEW_MATRIX: {
float *p = &matrix_stack_ptr[mnr]->_m[0][0];
for (int i = 0; i < 4; i++) {
data->_float16[i * 4 + 0] = p[0];
data->_float16[i * 4 + 1] = p[4];
data->_float16[i * 4 + 2] = p[8];
data->_float16[i * 4 + 3] = p[12];
p++;
}
}
dataType = kFloat16Type;
break;
case TGL_MODELVIEW_STACK_DEPTH:
error("gl_get_pname: TGL_MODELVIEW_STACK_DEPTH option not implemented");
break;
case TGL_NAME_STACK_DEPTH:
error("gl_get_pname: TGL_NAME_STACK_DEPTH option not implemented");
break;
case TGL_NORMALIZE:
data->_int = 0;
dataType = kIntType;
break;
case TGL_NORMAL_ARRAY:
data->_int = (TGLint)(normal_array != nullptr);
dataType = kIntType;
break;
case TGL_NORMAL_ARRAY_STRIDE:
data->_int = normal_array_stride;
dataType = kIntType;
break;
case TGL_NORMAL_ARRAY_TYPE:
data->_int = normal_array_type;
dataType = kIntType;
break;
case TGL_PACK_ALIGNMENT:
// fall through
case TGL_PACK_LSB_FIRST:
// fall through
case TGL_PACK_ROW_LENGTH:
// fall through
case TGL_PACK_SKIP_PIXELS:
// fall through
case TGL_PACK_SKIP_ROWS:
// fall through
case TGL_PACK_SWAP_BYTES:
error("gl_get_pname: TGL_PACK_x option not implemented");
break;
case TGL_PERSPECTIVE_CORRECTION_HINT:
data->_int = TGL_DONT_CARE;
break;
case TGL_PIXEL_MAP_A_TO_A_SIZE:
// fall through
case TGL_PIXEL_MAP_B_TO_B_SIZE:
// fall through
case TGL_PIXEL_MAP_G_TO_G_SIZE:
// fall through
case TGL_PIXEL_MAP_I_TO_A_SIZE:
// fall through
case TGL_PIXEL_MAP_I_TO_B_SIZE:
// fall through
case TGL_PIXEL_MAP_I_TO_G_SIZE:
// fall through
case TGL_PIXEL_MAP_I_TO_I_SIZE:
// fall through
case TGL_PIXEL_MAP_I_TO_R_SIZE:
// fall through
case TGL_PIXEL_MAP_R_TO_R_SIZE:
// fall through
case TGL_PIXEL_MAP_S_TO_S_SIZE:
error("gl_get_pname: TGL_PIXEL_MAP_x option not implemented");
break;
case TGL_POINT_SIZE:
// fall through
case TGL_POINT_SIZE_GRANULARITY:
// fall through
case TGL_POINT_SIZE_RANGE:
// fall through
case TGL_POINT_SMOOTH:
// fall through
case TGL_POINT_SMOOTH_HINT:
error("gl_get_pname: TGL_POINT_x option not implemented");
break;
case TGL_POLYGON_MODE:
data->_float2[0] = polygon_mode_front;
data->_float2[1] = polygon_mode_back;
dataType = kFloat2Type;
break;
case TGL_POLYGON_OFFSET_FACTOR:
data->_float = offset_factor;
dataType = kFloatType;
break;
case TGL_POLYGON_OFFSET_FILL:
data->_int = (offset_states & TGL_OFFSET_FILL) != 0 ? 1 : 0;
dataType = kIntType;
break;
case TGL_POLYGON_OFFSET_LINE:
data->_int = (offset_states & TGL_OFFSET_LINE) != 0 ? 1 : 0;
dataType = kIntType;
break;
case TGL_POLYGON_OFFSET_POINT:
data->_int = (offset_states & TGL_OFFSET_POINT) != 0 ? 1 : 0;
dataType = kIntType;
break;
case TGL_POLYGON_OFFSET_UNITS:
data->_float = offset_units;
dataType = kFloatType;
break;
case TGL_POLYGON_SMOOTH:
// fall through
case TGL_POLYGON_SMOOTH_HINT:
error("gl_get_pname: TGL_POLYGON_SMOOTHx option not implemented");
break;
case TGL_POLYGON_STIPPLE:
error("gl_get_pname: TGL_POLYGON_STIPPLE option not implemented");
break;
case TGL_PROJECTION_STACK_DEPTH:
error("gl_get_pname: TGL_PROJECTION_STACK_DEPTH option not implemented");
break;
case TGL_READ_BUFFER:
error("gl_get_pname: TGL_READ_BUFFER option not implemented");
break;
case TGL_RENDER_MODE:
error("gl_get_pname: TGL_RENDER_MODE option not implemented");
break;
case TGL_RGBA_MODE:
data->_int = 1;
dataType = kIntType;
break;
case TGL_SCISSOR_BOX:
data->_int4[0] = scissor[0];
data->_int4[1] = scissor[1];
data->_int4[2] = scissor[2];
data->_int4[3] = scissor[3];
dataType = kInt4Type;
break;
case TGL_SCISSOR_TEST:
data->_int = (TGLint)scissor_test_enabled;
dataType = kIntType;
break;
case TGL_SELECTION_BUFFER_SIZE:
error("gl_get_pname: TGL_SELECTION_BUFFER_SIZE option not implemented");
break;
case TGL_SHADE_MODEL:
data->_int = current_shade_model;
dataType = kIntType;
break;
case TGL_STENCIL_BITS:
data->_int = (TGLint)stencil_buffer_supported;
dataType = kIntType;
break;
case TGL_STENCIL_CLEAR_VALUE:
data->_float = clear_stencil;
dataType = kFloatType;
break;
case TGL_STENCIL_FAIL:
data->_int = (TGLint)stencil_sfail;
dataType = kIntType;
break;
case TGL_STENCIL_FUNC:
data->_int = (TGLint)stencil_test_func;
dataType = kIntType;
break;
case TGL_STENCIL_PASS_DEPTH_FAIL:
data->_int = (TGLint)TGL_FALSE;
dataType = kIntType;
break;
case TGL_STENCIL_PASS_DEPTH_PASS:
data->_int = (TGLint)TGL_FALSE;
dataType = kIntType;
break;
case TGL_STENCIL_REF:
data->_int = (TGLint)stencil_ref_val;
dataType = kIntType;
break;
case TGL_STENCIL_TEST:
data->_int = (TGLint)stencil_test_enabled;
dataType = kIntType;
break;
case TGL_STENCIL_VALUE_MASK:
data->_int = (TGLint)stencil_mask;
dataType = kIntType;
break;
case TGL_STENCIL_WRITEMASK:
data->_int = (TGLint)stencil_write_mask;
dataType = kUintType;
break;
case TGL_STEREO:
data->_int = (TGLint)TGL_FALSE;
dataType = kIntType;
break;
case TGL_SUBPIXEL_BITS:
error("gl_get_pname: TGL_SUBPIXEL_BITS option not implemented");
break;
case TGL_TEXTURE_1D:
error("gl_get_pname: TGL_TEXTURE_1D option not implemented");
break;
case TGL_TEXTURE_2D:
data->_int = (TGLint)texture_2d_enabled;
dataType = kIntType;
break;
case TGL_TEXTURE_BINDING_1D:
// fall through
case TGL_TEXTURE_BINDING_2D:
// fall through
case TGL_TEXTURE_BINDING_3D:
error("gl_get_pname: TGL_TEXTURE_BINDING_x option not implemented");
break;
case TGL_TEXTURE_COORD_ARRAY:
data->_int = (texcoord_array != nullptr) ? 1 : 0;
dataType = kIntType;
break;
case TGL_TEXTURE_COORD_ARRAY_SIZE:
data->_int = texcoord_array_size;
dataType = kIntType;
break;
case TGL_TEXTURE_COORD_ARRAY_STRIDE:
data->_int = texcoord_array_stride;
dataType = kIntType;
break;
case TGL_TEXTURE_COORD_ARRAY_TYPE:
data->_int = texcoord_array_type;
dataType = kIntType;
break;
case TGL_TEXTURE_GEN_Q:
// fall through
case TGL_TEXTURE_GEN_R:
// fall through
case TGL_TEXTURE_GEN_S:
// fall through
case TGL_TEXTURE_GEN_T:
error("gl_get_pname: TGL_TEXTURE_GEN_x option not implemented");
break;
case TGL_TEXTURE_STACK_DEPTH:
error("gl_get_pname: TGL_TEXTURE_STACK_DEPTH option not implemented");
break;
case TGL_UNPACK_ALIGNMENT:
// fall through
case TGL_UNPACK_LSB_FIRST:
// fall through
case TGL_UNPACK_ROW_LENGTH:
// fall through
case TGL_UNPACK_SKIP_IMAGES:
// fall through
case TGL_UNPACK_SKIP_PIXELS:
// fall through
case TGL_UNPACK_SKIP_ROWS:
// fall through
case TGL_UNPACK_SWAP_BYTES:
error("gl_get_pname: TGL_UNPACK_x option not implemented");
break;
case TGL_VERTEX_ARRAY:
data->_int = (vertex_array != nullptr) ? 1 : 0;
dataType = kIntType;
break;
case TGL_VERTEX_ARRAY_SIZE:
data->_int = vertex_array_size;
dataType = kIntType;
break;
case TGL_VERTEX_ARRAY_STRIDE:
data->_int = vertex_array_stride;
dataType = kIntType;
break;
case TGL_VERTEX_ARRAY_TYPE:
data->_int = vertex_array_type;
dataType = kIntType;
break;
case TGL_VIEWPORT:
data->_int4[0] = viewport.xmin;
data->_int4[1] = viewport.ymin;
data->_int4[2] = viewport.xsize;
data->_int4[3] = viewport.ysize;
dataType = kInt4Type;
break;
case TGL_ZOOM_X:
// fall through
case TGL_ZOOM_Y:
error("gl_get_pname: TGL_ZOOM_x option not implemented");
break;
default:
error("gl_get_pname: unknown option");
break;
}
}
void GLContext::gl_GetIntegerv(TGLenum pname, TGLint *data) {
eDataType dataType;
union uglValue tmpData;
gl_get_pname(pname, &tmpData, dataType);
switch (dataType) {
case kIntType:
data[0] = tmpData._int;
break;
case kInt4Type:
for (int i = 0; i < 4; i++)
data[i] = tmpData._int4[i];
break;
case kUintType:
data[0] = tmpData._int;
break;
case kFloatType:
data[0] = FLOAT_TO_INTEGER(tmpData._float);
break;
case kFloat2Type:
for (int i = 0; i < 2; i++)
data[i] = FLOAT_TO_INTEGER(tmpData._float2[i]);
break;
case kFloat4Type:
for (int i = 0; i < 4; i++)
data[i] = FLOAT_TO_INTEGER(tmpData._float4[i]);
break;
case kFloat16Type:
for (int i = 0; i < 16; i++)
data[i] = FLOAT_TO_INTEGER(tmpData._float16[i]);
break;
default:
assert("gl_GetIntegerv: unknown data type");
break;
}
}
void GLContext::gl_GetFloatv(TGLenum pname, TGLfloat *data) {
eDataType dataType;
union uglValue tmpData;
gl_get_pname(pname, &tmpData, dataType);
switch (dataType) {
case kIntType:
data[0] = tmpData._int;
break;
case kInt4Type:
for (int i = 0; i < 4; i++)
data[i] = tmpData._int4[i];
break;
case kUintType:
data[0] = (TGLuint)tmpData._int;
break;
case kFloatType:
data[0] = tmpData._float;
break;
case kFloat2Type:
for (int i = 0; i < 2; i++)
data[i] = tmpData._float2[i];
break;
case kFloat4Type:
for (int i = 0; i < 4; i++)
data[i] = tmpData._float4[i];
break;
case kFloat16Type:
for (int i = 0; i < 16; i++)
data[i] = tmpData._float16[i];
break;
default:
assert("gl_GetFloatv: unknown data type");
break;
}
}
void GLContext::gl_GetDoublev(TGLenum pname, TGLdouble *data) {
eDataType dataType;
union uglValue tmpData;
gl_get_pname(pname, &tmpData, dataType);
switch (dataType) {
case kIntType:
data[0] = tmpData._int;
break;
case kInt4Type:
for (int i = 0; i < 4; i++)
data[i] = tmpData._int4[i];
break;
case kUintType:
data[0] = (TGLuint)tmpData._int;
break;
case kFloatType:
data[0] = tmpData._float;
break;
case kFloat2Type:
for (int i = 0; i < 2; i++)
data[i] = tmpData._float2[i];
break;
case kFloat4Type:
for (int i = 0; i < 4; i++)
data[i] = tmpData._float4[i];
break;
case kFloat16Type:
for (int i = 0; i < 16; i++)
data[i] = tmpData._float16[i];
break;
default:
assert("gl_GetDoublev: unknown data type");
break;
}
}
void GLContext::gl_GetBooleanv(TGLenum pname, TGLboolean *data) {
eDataType dataType;
union uglValue tmpData;
gl_get_pname(pname, &tmpData, dataType);
switch (dataType) {
case kIntType:
data[0] = VALUE_TO_BOOLEAN(tmpData._int);
break;
case kInt4Type:
for (int i = 0; i < 4; i++)
data[i] = VALUE_TO_BOOLEAN(tmpData._int4[i]);
break;
case kUintType:
data[0] = VALUE_TO_BOOLEAN(tmpData._int);
break;
case kFloatType:
data[0] = VALUE_TO_BOOLEAN(tmpData._float);
break;
case kFloat2Type:
for (int i = 0; i < 2; i++)
data[i] = VALUE_TO_BOOLEAN(tmpData._float2[i]);
break;
case kFloat4Type:
for (int i = 0; i < 4; i++)
data[i] = VALUE_TO_BOOLEAN(tmpData._float4[i]);
break;
case kFloat16Type:
for (int i = 0; i < 16; i++)
data[i] = VALUE_TO_BOOLEAN(tmpData._float16[i]);
break;
default:
assert("gl_GetBooleanv: unknown data type");
break;
}
}
} // end of namespace TinyGL

1210
graphics/tinygl/gl.h Normal file

File diff suppressed because it is too large Load Diff

377
graphics/tinygl/init.cpp Normal file
View File

@@ -0,0 +1,377 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "common/singleton.h"
#include "common/array.h"
#include "graphics/tinygl/tinygl.h"
#include "graphics/tinygl/zgl.h"
#include "graphics/tinygl/zblit.h"
#include "graphics/tinygl/zdirtyrect.h"
namespace TinyGL {
class GLContextArray : public Common::Singleton<GLContextArray> {
private:
Common::Array<GLContext *> _glContextArray;
public:
GLContext *createContext() {
GLContext *ctx = new GLContext;
_glContextArray.push_back(ctx);
return ctx;
}
void destroyContext(ContextHandle *handle) {
for (Common::Array<GLContext *>::iterator it = _glContextArray.begin(); it != _glContextArray.end(); it++) {
if (*it == (GLContext *)handle) {
(*it)->deinit();
delete *it;
_glContextArray.erase(it);
break;
}
}
}
void destroyContexts() {
for (Common::Array<GLContext *>::iterator it = _glContextArray.begin(); it != _glContextArray.end(); it++) {
if (*it != nullptr) {
(*it)->deinit();
delete *it;
}
}
_glContextArray.clear();
}
bool existsContexts() {
return _glContextArray.size() != 0;
}
GLContext *getContext(ContextHandle *handle) {
for (Common::Array<GLContext *>::iterator it = _glContextArray.begin(); it != _glContextArray.end(); it++) {
if ((*it) == (GLContext *)handle) {
return *it;
}
}
return nullptr;
}
};
} // end of namespace TinyGL
namespace Common {
DECLARE_SINGLETON(TinyGL::GLContextArray);
}
namespace TinyGL {
GLContext *gl_ctx;
GLContext *gl_get_context() {
assert(gl_ctx);
return gl_ctx;
}
ContextHandle *createContext(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize,
bool enableStencilBuffer, bool dirtyRectsEnable, uint32 drawCallMemorySize) {
gl_ctx = GLContextArray::instance().createContext();
gl_ctx->init(screenW, screenH, pixelFormat, textureSize, enableStencilBuffer,
dirtyRectsEnable, drawCallMemorySize);
return (ContextHandle *)gl_ctx;
}
void destroyContext() {
GLContextArray::instance().destroyContexts();
GLContextArray::destroy();
gl_ctx = nullptr;
}
void destroyContext(ContextHandle *handle) {
GLContextArray::instance().destroyContext(handle);
if ((GLContext *)handle == gl_ctx)
gl_ctx = nullptr;
if (!GLContextArray::instance().existsContexts())
GLContextArray::destroy();
}
void setContext(ContextHandle *handle) {
GLContext *ctx = GLContextArray::instance().getContext(handle);
if (ctx == nullptr) {
error("TinyGL: makeContextCurrent() Failed get context");
}
gl_ctx = ctx;
}
void GLContext::initSharedState() {
GLSharedState *s = &shared_state;
s->lists = (GLList **)gl_zalloc(sizeof(GLList *) * MAX_DISPLAY_LISTS);
s->texture_hash_table = (GLTexture **)gl_zalloc(sizeof(GLTexture *) * TEXTURE_HASH_TABLE_SIZE);
}
void GLContext::endSharedState() {
GLSharedState *s = &shared_state;
for (int i = 0; i < MAX_DISPLAY_LISTS; i++) {
// TODO
}
gl_free(s->lists);
gl_free(s->texture_hash_table);
}
void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize,
bool enableStencilBuffer, bool dirtyRectsEnable, uint32 drawCallMemorySize) {
GLViewport *v;
_enableDirtyRectangles = dirtyRectsEnable;
stencil_buffer_supported = enableStencilBuffer;
fb = new TinyGL::FrameBuffer(screenW, screenH, pixelFormat, enableStencilBuffer);
renderRect = Common::Rect(0, 0, screenW, screenH);
if ((textureSize & (textureSize - 1)))
error("glInit: texture size not power of two: %d", textureSize);
if (textureSize <= 1 || textureSize > 4096)
error("glInit: texture size not allowed: %d", textureSize);
_textureSize = textureSize;
fb->setTextureSizeAndMask(textureSize, (textureSize - 1) << ZB_POINT_ST_FRAC_BITS);
fb->setTextureEnvironment(&_texEnv);
// allocate GLVertex array
vertex_max = POLYGON_MAX_VERTEX;
vertex = (GLVertex *)gl_malloc(POLYGON_MAX_VERTEX * sizeof(GLVertex));
// viewport
v = &viewport;
v->xmin = 0;
v->ymin = 0;
v->xsize = screenW;
v->ysize = screenH;
v->updated = 1;
// shared state
initSharedState();
// lists
exec_flag = 1;
compile_flag = 0;
print_flag = 0;
in_begin = 0;
// lights
for (int i = 0; i < T_MAX_LIGHTS; i++) {
GLLight *l = &lights[i];
l->ambient = Vector4(0, 0, 0, 1);
if (i == 0) {
l->diffuse = Vector4(1, 1, 1, 1);
l->specular = Vector4(1, 1, 1, 1);
l->has_specular = true;
} else {
l->diffuse = Vector4(0, 0, 0, 1);
l->specular = Vector4(0, 0, 0, 1);
l->has_specular = false;
}
l->position = Vector4(0, 0, 1, 0);
l->spot_direction = Vector3(0, 0, -1);
l->spot_exponent = 0;
l->spot_cutoff = 180;
l->attenuation[0] = 1;
l->attenuation[1] = 0;
l->attenuation[2] = 0;
l->cos_spot_cutoff = -1.0f;
l->norm_spot_direction = Vector3(0, 0, -1);
l->norm_position = Vector3(0, 0, 1);
l->enabled = 0;
l->next = nullptr;
l->prev = nullptr;
}
first_light = nullptr;
ambient_light_model = Vector4(0.2f, 0.2f, 0.2f, 1);
local_light_model = 0;
lighting_enabled = 0;
light_model_two_side = 0;
// default materials */
for (int i = 0; i < 2; i++) {
GLMaterial *m = &materials[i];
m->emission = Vector4(0, 0, 0, 1);
m->ambient = Vector4(0.2f, 0.2f, 0.2f, 1);
m->diffuse = Vector4(0.8f, 0.8f, 0.8f, 1);
m->specular = Vector4(0, 0, 0, 1);
m->has_specular = false;
m->shininess = 0;
}
current_color_material_mode = TGL_FRONT_AND_BACK;
current_color_material_type = TGL_AMBIENT_AND_DIFFUSE;
color_material_enabled = 0;
// textures
texture_2d_enabled = false;
current_texture = default_texture = alloc_texture(0);
maxTextureName = 0;
texture_mag_filter = TGL_LINEAR;
texture_min_filter = TGL_NEAREST_MIPMAP_LINEAR;
colorAssociationList.push_back({Graphics::PixelFormat::createFormatRGBA32(), TGL_RGBA, TGL_UNSIGNED_BYTE});
colorAssociationList.push_back({Graphics::PixelFormat::createFormatRGB24(), TGL_RGB, TGL_UNSIGNED_BYTE});
colorAssociationList.push_back({Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), TGL_RGB, TGL_UNSIGNED_SHORT_5_6_5});
colorAssociationList.push_back({Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0), TGL_RGBA, TGL_UNSIGNED_SHORT_5_5_5_1});
colorAssociationList.push_back({Graphics::PixelFormat(2, 4, 4, 4, 4, 12, 8, 4, 0), TGL_RGBA, TGL_UNSIGNED_SHORT_4_4_4_4});
// default state
current_color = Vector4(1.0f, 1.0f, 1.0f, 1.0f);
current_normal = Vector4(1.0f, 0.0f, 0.0f, 0.0f);
current_edge_flag = 1;
current_tex_coord = Vector4(0.0f, 0.0f, 0.0f, 1.0f);
polygon_mode_front = TGL_FILL;
polygon_mode_back = TGL_FILL;
current_front_face = 0; // 0 = GL_CCW 1 = GL_CW
current_cull_face = TGL_BACK;
current_shade_model = TGL_SMOOTH;
cull_face_enabled = 0;
// scissor
scissor_test_enabled = false;
scissor[0] = scissor[1] = 0;
scissor[2] = screenW;
scissor[3] = screenH;
// fog
fog_enabled = false;
fog_mode = TGL_EXP;
fog_color = Vector4(0.0f, 0.0f, 0.0f, 0.0f);
fog_density = 1.0f;
fog_start = 0.0f;
fog_end = 0.0f;
// clear
clear_color = Vector4(0.0f, 0.0f, 0.0f, 0.0f);
clear_depth = 1.0f;
clear_stencil = 0;
// selection
render_mode = TGL_RENDER;
select_buffer = nullptr;
name_stack_size = 0;
// blending
blending_enabled = false;
source_blending_factor = TGL_ONE;
destination_blending_factor = TGL_ZERO;
// alpha test
alpha_test_enabled = false;
alpha_test_func = TGL_ALWAYS;
alpha_test_ref_val = 0;
// depth test
depth_test_enabled = false;
depth_func = TGL_LESS;
depth_write_mask = true;
// stipple
polygon_stipple_enabled = false;
memset(polygon_stipple_pattern, 0xff, sizeof(polygon_stipple_pattern));
// stencil
stencil_test_enabled = false;
stencil_test_func = TGL_ALWAYS;
stencil_ref_val = 0;
stencil_mask = 0xff;
stencil_write_mask = 0xff;
stencil_sfail = TGL_KEEP;
stencil_dpfail = TGL_KEEP;
stencil_dppass = TGL_KEEP;
// matrix
matrix_mode = 0;
matrix_stack_depth_max[0] = MAX_MODELVIEW_STACK_DEPTH;
matrix_stack_depth_max[1] = MAX_PROJECTION_STACK_DEPTH;
matrix_stack_depth_max[2] = MAX_TEXTURE_STACK_DEPTH;
for (int i = 0; i < 3; i++) {
matrix_stack[i] = (Matrix4 *)gl_zalloc(matrix_stack_depth_max[i] * sizeof(Matrix4));
matrix_stack_ptr[i] = matrix_stack[i];
}
tglMatrixMode(TGL_PROJECTION);
tglLoadIdentity();
tglMatrixMode(TGL_TEXTURE);
tglLoadIdentity();
tglMatrixMode(TGL_MODELVIEW);
tglLoadIdentity();
matrix_model_projection_updated = 1;
// opengl 1.1 arrays
client_states = 0;
// opengl 1.1 polygon offset
offset_states = 0;
offset_factor = 0.0f;
offset_units = 0.0f;
// clear the resize callback function pointer
gl_resize_viewport = nullptr;
// specular buffer
specbuf_first = nullptr;
specbuf_used_counter = 0;
specbuf_num_buffers = 0;
// color mask
color_mask_red = color_mask_green = color_mask_blue = color_mask_alpha = true;
_currentAllocatorIndex = 0;
_drawCallAllocator[0].initialize(drawCallMemorySize);
_drawCallAllocator[1].initialize(drawCallMemorySize);
_debugRectsEnabled = false;
_profilingEnabled = false;
}
void GLContext::deinit() {
disposeDrawCallLists();
disposeResources();
specbuf_cleanup();
for (int i = 0; i < 3; i++)
gl_free(matrix_stack[i]);
free_texture(default_texture);
endSharedState();
gl_free(vertex);
delete fb;
}
} // end of namespace TinyGL

318
graphics/tinygl/light.cpp Normal file
View File

@@ -0,0 +1,318 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
void GLContext::glopMaterial(GLParam *p) {
int mode = p[1].i;
int type = p[2].i;
Vector4 v(p[3].f, p[4].f, p[5].f, p[6].f);
GLMaterial *m;
if (mode == TGL_FRONT_AND_BACK) {
p[1].i = TGL_FRONT;
glopMaterial(p);
mode = TGL_BACK;
}
if (mode == TGL_FRONT)
m = &materials[0];
else
m = &materials[1];
switch (type) {
case TGL_EMISSION:
m->emission = v;
break;
case TGL_AMBIENT:
m->ambient = v;
break;
case TGL_DIFFUSE:
m->diffuse = v;
break;
case TGL_SPECULAR:
m->specular = v;
m->has_specular = v.X != 0 || v.Y != 0 || v.Z != 0 || v.W != 1;
break;
case TGL_SHININESS:
m->shininess = v.X;
m->shininess_i = (int)(v.X / 128.0f) * SPECULAR_BUFFER_RESOLUTION;
break;
case TGL_AMBIENT_AND_DIFFUSE:
m->diffuse = v;
m->ambient = v;
break;
default:
assert(0);
}
}
void GLContext::glopColorMaterial(GLParam *p) {
int mode = p[1].i;
int type = p[2].i;
current_color_material_mode = mode;
current_color_material_type = type;
}
void GLContext::glopLight(GLParam *p) {
int light = p[1].i;
int type = p[2].i;
Vector4 v(p[3].f, p[4].f, p[5].f, p[6].f);
GLLight *l;
assert(light >= TGL_LIGHT0 && light < TGL_LIGHT0 + T_MAX_LIGHTS);
l = &lights[light - TGL_LIGHT0];
switch (type) {
case TGL_AMBIENT:
l->ambient = v;
break;
case TGL_DIFFUSE:
l->diffuse = v;
break;
case TGL_SPECULAR:
l->specular = v;
l->has_specular = v.X != 0 || v.Y != 0 || v.Z != 0 || v.W != 1;
break;
case TGL_POSITION: {
Vector4 pos;
matrix_stack_ptr[0]->transform(v, pos);
l->position = pos;
if (l->position.W == 0) {
l->norm_position.X = pos.X;
l->norm_position.Y = pos.Y;
l->norm_position.Z = pos.Z;
l->norm_position.normalize();
}
}
break;
case TGL_SPOT_DIRECTION:
l->spot_direction.X = v.X;
l->spot_direction.Y = v.Y;
l->spot_direction.Z = v.Z;
matrix_stack_ptr[0]->transform3x3(l->spot_direction, l->norm_spot_direction);
l->norm_spot_direction.normalize();
break;
case TGL_SPOT_EXPONENT:
l->spot_exponent = v.X;
break;
case TGL_SPOT_CUTOFF: {
float a = v.X;
assert(a == 180 || (a >= 0 && a <= 90));
l->spot_cutoff = a;
if (a != 180)
l->cos_spot_cutoff = (float)(cos(a * (float)M_PI / 180.0));
}
break;
case TGL_CONSTANT_ATTENUATION:
l->attenuation[0] = v.X;
break;
case TGL_LINEAR_ATTENUATION:
l->attenuation[1] = v.X;
break;
case TGL_QUADRATIC_ATTENUATION:
l->attenuation[2] = v.X;
break;
default:
assert(0);
}
}
void GLContext::glopLightModel(GLParam *p) {
int pname = p[1].i;
switch (pname) {
case TGL_LIGHT_MODEL_AMBIENT:
ambient_light_model = Vector4(p[2].f, p[3].f, p[4].f, p[5].f);
break;
case TGL_LIGHT_MODEL_LOCAL_VIEWER:
local_light_model = (int)p[2].f;
break;
case TGL_LIGHT_MODEL_TWO_SIDE:
light_model_two_side = (int)p[2].f;
break;
default:
warning("glopLightModel: illegal pname: 0x%x", pname);
break;
}
}
void GLContext::gl_enable_disable_light(int light, int v) {
GLLight *l = &lights[light];
if (v && !l->enabled) {
l->enabled = 1;
if (first_light != l) {
l->next = first_light;
if (first_light)
first_light->prev = l;
first_light = l;
l->prev = nullptr;
}
} else if (!v && l->enabled) {
l->enabled = 0;
if (!l->prev)
first_light = l->next;
else
l->prev->next = l->next;
if (l->next)
l->next->prev = l->prev;
}
}
// non optimized lightning model
void GLContext::gl_shade_vertex(GLVertex *v) {
float R, G, B, A;
GLMaterial *m;
GLLight *l;
Vector3 n, s, d;
float dist, tmp, att, dot, dot_spot, dot_spec;
int twoside = light_model_two_side;
m = &materials[0];
n = v->normal;
R = m->emission.X + m->ambient.X * ambient_light_model.X;
G = m->emission.Y + m->ambient.Y * ambient_light_model.Y;
B = m->emission.Z + m->ambient.Z * ambient_light_model.Z;
A = clampf(m->diffuse.W, 0, 1);
for (l = first_light; l != nullptr; l = l->next) {
float lR, lB, lG;
// ambient
lR = l->ambient.X * m->ambient.X;
lG = l->ambient.Y * m->ambient.Y;
lB = l->ambient.Z * m->ambient.Z;
if (l->position.W == 0) {
// light at infinity
d.X = l->norm_position.X;
d.Y = l->norm_position.Y;
d.Z = l->norm_position.Z;
dist = 1;
att = 1;
} else {
// distance attenuation
d.X = l->position.X - v->ec.X;
d.Y = l->position.Y - v->ec.Y;
d.Z = l->position.Z - v->ec.Z;
dist = sqrt(d.X * d.X + d.Y * d.Y + d.Z * d.Z);
att = 1.0f / (l->attenuation[0] +
dist * (l->attenuation[1] +
dist * l->attenuation[2]));
}
dot = d.X * n.X + d.Y * n.Y + d.Z * n.Z;
if (twoside && dot < 0)
dot = -dot;
if (dot > 0) {
tmp = 1 / dist;
d *= tmp;
dot *= tmp;
// diffuse light
lR += dot * l->diffuse.X * m->diffuse.X;
lG += dot * l->diffuse.Y * m->diffuse.Y;
lB += dot * l->diffuse.Z * m->diffuse.Z;
const bool is_spotlight = l->spot_cutoff != 180;
const bool has_specular = l->has_specular && m->has_specular;
if (is_spotlight || has_specular) {
if (is_spotlight) {
dot_spot = -(d.X * l->norm_spot_direction.X +
d.Y * l->norm_spot_direction.Y +
d.Z * l->norm_spot_direction.Z);
if (twoside && dot_spot < 0)
dot_spot = -dot_spot;
if (dot_spot < l->cos_spot_cutoff) {
// no contribution
continue;
} else {
// TODO: optimize
if (l->spot_exponent > 0) {
att = att * pow(dot_spot, l->spot_exponent);
}
}
}
if (has_specular) {
if (local_light_model) {
Vector3 vcoord;
vcoord.X = v->ec.X;
vcoord.Y = v->ec.Y;
vcoord.Z = v->ec.Z;
vcoord.normalize();
s.X = d.X - vcoord.X;
s.Y = d.Y - vcoord.Y;
s.Z = d.Z - vcoord.Z;
} else {
s.X = d.X;
s.Y = d.Y;
s.Z = (float)(d.Z + 1.0);
}
dot_spec = n.X * s.X + n.Y * s.Y + n.Z * s.Z;
if (twoside && dot_spec < 0)
dot_spec = -dot_spec;
if (dot_spec > 0) {
GLSpecBuf *specbuf;
int idx;
dot_spec = dot_spec / sqrt(s.X * s.X + s.Y * s.Y + s.Z * s.Z);
// TODO: optimize
// testing specular buffer code
// dot_spec= pow(dot_spec,m->shininess)
specbuf = specbuf_get_buffer(m->shininess_i, m->shininess);
tmp = dot_spec * SPECULAR_BUFFER_SIZE;
if (tmp > SPECULAR_BUFFER_SIZE)
idx = SPECULAR_BUFFER_SIZE;
else
idx = (int)tmp;
dot_spec = specbuf->buf[idx];
lR += dot_spec * l->specular.X * m->specular.X;
lG += dot_spec * l->specular.Y * m->specular.Y;
lB += dot_spec * l->specular.Z * m->specular.Z;
}
}
}
}
R += att * lR;
G += att * lG;
B += att * lB;
}
v->color.X = clampf(R, 0, 1);
v->color.Y = clampf(G, 0, 1);
v->color.Z = clampf(B, 0, 1);
v->color.W = A;
}
} // end of namespace TinyGL

244
graphics/tinygl/list.cpp Normal file
View File

@@ -0,0 +1,244 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "common/streamdebug.h"
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
#define ADD_OP(aa, bb, ff) \
static void glop ## aa (GLContext *c, GLParam *p) { \
c->glop ## aa (p); \
}
#include "graphics/tinygl/opinfo.h"
static const char *const op_table_str[] = {
#define ADD_OP(a, b, c) "gl" #a " " #c,
#include "graphics/tinygl/opinfo.h"
};
static void (*const op_table_func[])(GLContext *, GLParam *) = {
#define ADD_OP(a, b, c) glop ## a ,
#include "graphics/tinygl/opinfo.h"
};
static const int op_table_size[] = {
#define ADD_OP(a, b, c) b + 1 ,
#include "graphics/tinygl/opinfo.h"
};
GLList *GLContext::find_list(uint list) {
return shared_state.lists[list];
}
void GLContext::delete_list(int list) {
GLList *l = find_list(list);
assert(l);
// free param buffer
GLParamBuffer *pb = l->first_op_buffer;
while (pb) {
GLParamBuffer *pb1 = pb->next;
gl_free(pb);
pb = pb1;
}
gl_free(l);
shared_state.lists[list] = nullptr;
}
GLList *GLContext::alloc_list(int list) {
GLList *l = (GLList *)gl_zalloc(sizeof(GLList));
GLParamBuffer *ob = (GLParamBuffer *)gl_zalloc(sizeof(GLParamBuffer));
ob->next = nullptr;
l->first_op_buffer = ob;
ob->ops[0].op = OP_EndList;
shared_state.lists[list] = l;
return l;
}
static void gl_print_op(GLParam *p) {
Common::StreamDebug debug = streamDbg();
int op = p[0].op;
p++;
const char *s = op_table_str[op];
while (*s != 0) {
if (*s == '%') {
s++;
switch (*s++) {
case 'f':
debug << p[0].f;
break;
default:
debug << p[0].i;
break;
}
p++;
} else {
debug << *s;
s++;
}
}
debug << "\n";
}
void GLContext::gl_compile_op(GLParam *p) {
int op = p[0].op;
int op_size = op_table_size[op];
int index = current_op_buffer_index;
GLParamBuffer *ob = current_op_buffer;
// we should be able to add a NextBuffer opcode
if ((index + op_size) > (OP_BUFFER_MAX_SIZE - 2)) {
GLParamBuffer *ob1 = (GLParamBuffer *)gl_zalloc(sizeof(GLParamBuffer));
ob1->next = nullptr;
ob->next = ob1;
ob->ops[index].op = OP_NextBuffer;
ob->ops[index + 1].p = (void *)ob1;
current_op_buffer = ob1;
ob = ob1;
index = 0;
}
for (int i = 0; i < op_size; i++) {
ob->ops[index] = p[i];
index++;
}
current_op_buffer_index = index;
}
void GLContext::gl_add_op(GLParam *p) {
GLContext *c = gl_get_context();
int op = p[0].op;
if (exec_flag) {
op_table_func[op](c, p);
}
if (compile_flag) {
gl_compile_op(p);
}
if (print_flag) {
gl_print_op(p);
}
}
// this opcode is never called directly
void GLContext::glopEndList(GLParam *) {
assert(0);
}
// this opcode is never called directly
void GLContext::glopNextBuffer(GLParam *) {
assert(0);
}
void GLContext::glopCallList(GLParam *p) {
uint list = p[1].ui;
GLList *l = find_list(list);
if (!l)
error("list %d not defined", list);
p = l->first_op_buffer->ops;
while (1) {
int op = p[0].op;
if (op == OP_EndList)
break;
if (op == OP_NextBuffer) {
p = (GLParam *)p[1].p;
} else {
op_table_func[op](this, p);
p += op_table_size[op];
}
}
}
void GLContext::gl_NewList(TGLuint list, TGLenum mode) {
assert(mode == TGL_COMPILE || mode == TGL_COMPILE_AND_EXECUTE);
assert(compile_flag == 0);
GLList *l = find_list(list);
if (l)
delete_list(list);
l = alloc_list(list);
current_op_buffer = l->first_op_buffer;
current_op_buffer_index = 0;
compile_flag = 1;
exec_flag = (mode == TGL_COMPILE_AND_EXECUTE);
}
void GLContext::gl_EndList() {
GLParam p[1];
assert(compile_flag == 1);
// end of list
p[0].op = OP_EndList;
gl_compile_op(p);
compile_flag = 0;
exec_flag = 1;
}
TGLboolean GLContext::gl_IsList(TGLuint list) {
GLList *l = find_list(list);
return (l != nullptr);
}
TGLuint GLContext::gl_GenLists(TGLsizei range) {
GLList **lists = shared_state.lists;
int count = 0;
for (int i = 0; i < MAX_DISPLAY_LISTS; i++) {
if (!lists[i]) {
count++;
if (count == range) {
uint list = i - range + 1;
for (int j = 0; j < range; j++) {
alloc_list(list + j);
}
return list;
}
} else {
count = 0;
}
}
return 0;
}
} // end of namespace TinyGL

251
graphics/tinygl/matrix.cpp Normal file
View File

@@ -0,0 +1,251 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#define FORBIDDEN_SYMBOL_EXCEPTION_fprintf
#define FORBIDDEN_SYMBOL_EXCEPTION_stderr
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
void GLContext::gl_print_matrix(const float *m) {
for (int i = 0; i < 4; i++) {
fprintf(stderr, "%f %f %f %f\n", m[i], m[4 + i], m[8 + i], m[12 + i]);
}
}
static inline void gl_matrix_update(GLContext *c) {
c->matrix_model_projection_updated |= (c->matrix_mode <= 1);
}
void GLContext::glopMatrixMode(GLParam *p) {
int mode = p[1].i;
switch (mode) {
case TGL_MODELVIEW:
matrix_mode = 0;
break;
case TGL_PROJECTION:
matrix_mode = 1;
break;
case TGL_TEXTURE:
matrix_mode = 2;
break;
default:
assert(0);
}
}
void GLContext::glopLoadMatrix(GLParam *p) {
Matrix4 *m;
GLParam *q;
m = matrix_stack_ptr[matrix_mode];
q = p + 1;
for (int i = 0; i < 4; i++) {
m->_m[0][i] = q[0].f;
m->_m[1][i] = q[1].f;
m->_m[2][i] = q[2].f;
m->_m[3][i] = q[3].f;
q += 4;
}
gl_matrix_update(this);
}
void GLContext::glopLoadIdentity(GLParam *) {
matrix_stack_ptr[matrix_mode]->identity();
gl_matrix_update(this);
}
void GLContext::glopMultMatrix(GLParam *p) {
Matrix4 m;
GLParam *q;
q = p + 1;
for (int i = 0; i < 4; i++) {
m._m[0][i] = q[0].f;
m._m[1][i] = q[1].f;
m._m[2][i] = q[2].f;
m._m[3][i] = q[3].f;
q += 4;
}
*matrix_stack_ptr[matrix_mode] *= m;
gl_matrix_update(this);
}
void GLContext::glopPushMatrix(GLParam *) {
int n = matrix_mode;
Matrix4 *m;
assert((matrix_stack_ptr[n] - matrix_stack[n] + 1) < matrix_stack_depth_max[n]);
m = ++matrix_stack_ptr[n];
m[0] = m[-1];
gl_matrix_update(this);
}
void GLContext::glopPopMatrix(GLParam *) {
int n = matrix_mode;
assert(matrix_stack_ptr[n] > matrix_stack[n]);
matrix_stack_ptr[n]--;
gl_matrix_update(this);
}
void GLContext::glopRotate(GLParam *p) {
Matrix4 m;
float u[3];
float angle;
int dir_code;
angle = (float)(p[1].f * (float)M_PI / 180.0);
u[0] = p[2].f;
u[1] = p[3].f;
u[2] = p[4].f;
// simple case detection
dir_code = ((u[0] != 0) << 2) | ((u[1] != 0) << 1) | (u[2] != 0);
switch (dir_code) {
case 0:
m.identity();
break;
case 4:
if (u[0] < 0)
angle = -angle;
m.rotation(angle, 0);
break;
case 2:
if (u[1] < 0)
angle = -angle;
m.rotation(angle, 1);
break;
case 1:
if (u[2] < 0)
angle = -angle;
m.rotation(angle, 2);
break;
default: {
float cost, sint;
// normalize vector
float len = u[0] * u[0] + u[1] * u[1] + u[2] * u[2];
if (len == 0.0f)
return;
len = 1.0f / sqrt(len);
u[0] *= len;
u[1] *= len;
u[2] *= len;
// store cos and sin values
cost = cos(angle);
sint = sin(angle);
// fill in the values
m._m[3][0] = 0.0f;
m._m[3][2] = 0.0f;
m._m[0][3] = 0.0f;
m._m[1][3] = 0.0f;
m._m[2][3] = 0.0f;
m._m[3][3] = 1.0f;
// do the math
m._m[0][0] = u[0] * u[0] + cost * (1 - u[0] * u[0]);
m._m[1][0] = u[0] * u[1] * (1 -cost) - u[2] * sint;
m._m[2][0] = u[2] * u[0] * (1 -cost) + u[1] * sint;
m._m[0][1] = u[0] * u[1] * (1 -cost) + u[2] * sint;
m._m[1][1] = u[1] * u[1] + cost * (1 - u[1] * u[1]);
m._m[2][1] = u[1] * u[2] * (1 - cost) - u[0] * sint;
m._m[0][2] = u[2] * u[0] * (1 - cost) - u[1] * sint;
m._m[1][2] = u[1] * u[2] * (1 - cost) + u[0] * sint;
m._m[2][2] = u[2] * u[2] + cost * (1 - u[2] * u[2]);
}
}
*matrix_stack_ptr[matrix_mode] *= m;
gl_matrix_update(this);
}
void GLContext::glopScale(GLParam *p) {
matrix_stack_ptr[matrix_mode]->scale(p[1].f, p[2].f, p[3].f);
gl_matrix_update(this);
}
void GLContext::glopTranslate(GLParam *p) {
matrix_stack_ptr[matrix_mode]->translate(p[1].f, p[2].f, p[3].f);
gl_matrix_update(this);
}
void GLContext::glopFrustum(GLParam *p) {
float left = p[1].f;
float right = p[2].f;
float bottom = p[3].f;
float top = p[4].f;
float nearp = p[5].f;
float farp = p[6].f;
Matrix4 m = Matrix4::frustum(left, right, bottom, top, nearp, farp);
*matrix_stack_ptr[matrix_mode] *= m;
gl_matrix_update(this);
}
void GLContext::glopOrtho(GLParam *p) {
float *r;
TinyGL::Matrix4 m;
float left = p[1].f;
float right = p[2].f;
float bottom = p[3].f;
float top = p[4].f;
float zNear = p[5].f;
float zFar = p[6].f;
float a = 2.0f / (right - left);
float b = 2.0f / (top - bottom);
float c = -2.0f / (zFar - zNear);
float tx = -(right + left) / (right - left);
float ty = -(top + bottom) / (top - bottom);
float tz = -(zFar + zNear) / (zFar - zNear);
r = &m._m[0][0];
r[0] = a; r[1] = 0; r[2] = 0; r[3] = tx;
r[4] = 0; r[5] = b; r[6] = 0; r[7] = ty;
r[8] = 0; r[9] = 0; r[10] = c; r[11] = tz;
r[12] = 0; r[13] = 0; r[14] = 0; r[15] = 1;
*matrix_stack_ptr[matrix_mode] *= m;
gl_matrix_update(this);
}
} // end of namespace TinyGL

View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
// Memory allocator for TinyGL
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
// modify these functions so that they suit your needs
void gl_free(void *p) {
free(p);
}
void *gl_malloc(int size) {
return malloc(size);
}
void *gl_zalloc(int size) {
return calloc(1, size);
}
void *gl_realloc(void *p, int size) {
return realloc(p, size);
}
} // end of namespace TinyGL

253
graphics/tinygl/misc.cpp Normal file
View File

@@ -0,0 +1,253 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
void GLContext::glopViewport(GLParam *p) {
int xsize, ysize, xmin, ymin, xsize_req, ysize_req;
xmin = p[1].i;
ymin = p[2].i;
xsize = p[3].i;
ysize = p[4].i;
// we may need to resize the zbuffer
if (viewport.xmin != xmin || viewport.ymin != ymin ||
viewport.xsize != xsize || viewport.ysize != ysize) {
xsize_req = xmin + xsize;
ysize_req = ymin + ysize;
if (gl_resize_viewport && gl_resize_viewport(&xsize_req, &ysize_req) != 0) {
error("glViewport: error while resizing display");
}
xsize = xsize_req - xmin;
ysize = ysize_req - ymin;
if (xsize <= 0 || ysize <= 0) {
error("glViewport: size too small");
}
viewport.xmin = xmin;
viewport.ymin = ymin;
viewport.xsize = xsize;
viewport.ysize = ysize;
viewport.updated = 1;
}
}
void GLContext::glopScissor(GLParam *p) {
scissor[0] = p[1].i;
scissor[1] = p[2].i;
scissor[2] = p[3].i;
scissor[3] = p[4].i;
}
void GLContext::glopEnableDisable(GLParam *p) {
int code = p[1].i;
int v = p[2].i;
switch (code) {
case TGL_CULL_FACE:
cull_face_enabled = v != 0;
break;
case TGL_LIGHTING:
lighting_enabled = v != 0;
break;
case TGL_COLOR_MATERIAL:
color_material_enabled = v != 0;
break;
case TGL_FOG:
fog_enabled = v != 0;
break;
case TGL_TEXTURE_2D:
texture_2d_enabled = v != 0;
break;
case TGL_NORMALIZE:
normalize_enabled = v != 0;
break;
case TGL_DEPTH_TEST:
depth_test_enabled = v != 0;
break;
case TGL_ALPHA_TEST:
alpha_test_enabled = v != 0;
break;
case TGL_POLYGON_STIPPLE:
polygon_stipple_enabled = v != 0;
break;
case TGL_STENCIL_TEST:
stencil_test_enabled = v != 0;
break;
case TGL_SCISSOR_TEST:
scissor_test_enabled = v != 0;
break;
case TGL_BLEND:
blending_enabled = v != 0;
break;
case TGL_POLYGON_OFFSET_FILL:
if (v)
offset_states |= TGL_OFFSET_FILL;
else
offset_states &= ~TGL_OFFSET_FILL;
break;
case TGL_POLYGON_OFFSET_POINT:
if (v)
offset_states |= TGL_OFFSET_POINT;
else
offset_states &= ~TGL_OFFSET_POINT;
break;
case TGL_POLYGON_OFFSET_LINE:
if (v)
offset_states |= TGL_OFFSET_LINE;
else
offset_states &= ~TGL_OFFSET_LINE;
break;
case TGL_TWO_COLOR_STIPPLE:
two_color_stipple_enabled = v != 0;
break;
default:
if (code >= TGL_LIGHT0 && code < TGL_LIGHT0 + T_MAX_LIGHTS) {
gl_enable_disable_light(code - TGL_LIGHT0, v);
} else {
//warning("glEnableDisable: 0x%X not supported.", code);
}
break;
}
}
void GLContext::glopBlendFunc(GLParam *p) {
source_blending_factor = p[1].i;
destination_blending_factor = p[2].i;
}
void GLContext::glopAlphaFunc(GLParam *p) {
alpha_test_func = p[1].i;
alpha_test_ref_val = (int)(p[2].f * 255);
}
void GLContext::glopDepthFunc(GLParam *p) {
depth_func = p[1].i;
}
void GLContext::glopStencilFunc(GLParam *p) {
TGLenum func = p[1].i;
TGLint ref = p[2].i;
TGLuint mask = p[3].ui;
if (func < TGL_NEVER || func > TGL_ALWAYS)
return;
if (ref < 0)
ref = 0;
else if (ref > 255)
ref = 255;
stencil_test_func = func;
stencil_ref_val = (byte)ref;
stencil_mask = (byte)mask;
}
void GLContext::glopStencilOp(GLParam *p) {
stencil_sfail = p[1].i;
stencil_dpfail = p[2].i;
stencil_dppass = p[3].i;
}
void GLContext::glopShadeModel(GLParam *p) {
int code = p[1].i;
current_shade_model = code;
}
void GLContext::glopCullFace(GLParam *p) {
int code = p[1].i;
current_cull_face = code;
}
void GLContext::glopFrontFace(GLParam *p) {
int code = p[1].i;
current_front_face = code;
}
void GLContext::glopPolygonMode(GLParam *p) {
int face = p[1].i;
int mode = p[2].i;
switch (face) {
case TGL_BACK:
polygon_mode_back = mode;
break;
case TGL_FRONT:
polygon_mode_front = mode;
break;
case TGL_FRONT_AND_BACK:
polygon_mode_front = mode;
polygon_mode_back = mode;
break;
default:
assert(0);
}
}
void GLContext::glopHint(GLParam *) {
// do nothing
}
void GLContext::glopPolygonStipple(GLParam *p) {
for (int i = 0; i < 128; i++) {
polygon_stipple_pattern[i] = p[i + 1].ui;
}
}
void GLContext::glopStippleColor(GLParam *p) {
int r = (int)p[1].f;
int g = (int)p[2].f;
int b = (int)p[3].f;
stippleColor = r << 16 | g << 8 | b;
}
void GLContext::glopPolygonOffset(GLParam *p) {
offset_factor = p[1].f;
offset_units = p[2].f;
}
void GLContext::glopColorMask(GLParam *p) {
color_mask_red = p[1].i == TGL_TRUE;
color_mask_green = p[2].i == TGL_TRUE;
color_mask_blue = p[3].i == TGL_TRUE;
color_mask_alpha = p[4].i == TGL_TRUE;
}
void GLContext::glopDepthMask(GLParam *p) {
depth_write_mask = p[1].i == TGL_TRUE;
}
void GLContext::glopStencilMask(TinyGL::GLParam *p) {
stencil_write_mask = p[1].ui;
}
} // end of namespace TinyGL

108
graphics/tinygl/opinfo.h Normal file
View File

@@ -0,0 +1,108 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
ADD_OP(Color, 4, "%f %f %f %f")
ADD_OP(TexCoord, 4, "%f %f %f %f")
ADD_OP(EdgeFlag, 1, "%d")
ADD_OP(Normal, 3, "%f %f %f")
ADD_OP(Begin, 1, "%C")
ADD_OP(Vertex, 4, "%f %f %f %f")
ADD_OP(End, 0, "")
ADD_OP(EnableDisable, 2, "%C %d")
ADD_OP(MatrixMode, 1, "%C")
ADD_OP(LoadMatrix, 16, "")
ADD_OP(LoadIdentity, 0, "")
ADD_OP(MultMatrix, 16, "")
ADD_OP(PushMatrix, 0, "")
ADD_OP(PopMatrix, 0, "")
ADD_OP(Rotate, 4, "%f %f %f %f")
ADD_OP(Translate, 3, "%f %f %f")
ADD_OP(Scale, 3, "%f %f %f")
ADD_OP(Ortho, 6, "%f %f %f %f %f %f")
ADD_OP(Scissor, 4, "%d %d %d %d")
ADD_OP(Viewport, 4, "%d %d %d %d")
ADD_OP(Frustum, 6, "%f %f %f %f %f %f")
ADD_OP(Material, 6, "%C %C %f %f %f %f")
ADD_OP(ColorMaterial, 2, "%C %C")
ADD_OP(Light, 6, "%C %C %f %f %f %f")
ADD_OP(LightModel, 5, "%C %f %f %f %f")
ADD_OP(Clear, 1, "%d")
ADD_OP(ClearColor, 4, "%f %f %f %f")
ADD_OP(ClearDepth, 1, "%f")
ADD_OP(ClearStencil, 1, "%d")
ADD_OP(InitNames, 0, "")
ADD_OP(PushName, 1, "%d")
ADD_OP(PopName, 0, "")
ADD_OP(LoadName, 1, "%d")
ADD_OP(TexImage2D, 9, "%d %d %d %d %d %d %d %d %d")
ADD_OP(BindTexture, 2, "%C %d")
ADD_OP(TexEnv, 7, "%C %C %C %f %f %f %f")
ADD_OP(TexParameter, 7, "%C %C %C %f %f %f %f")
ADD_OP(ShadeModel, 1, "%C")
ADD_OP(CullFace, 1, "%C")
ADD_OP(FrontFace, 1, "%C")
ADD_OP(PolygonMode, 2, "%C %C")
ADD_OP(ColorMask, 1, "%08x")
ADD_OP(DepthMask, 1, "%d")
ADD_OP(StencilMask, 1, "%d")
ADD_OP(BlendFunc, 2, "%d %d")
ADD_OP(AlphaFunc, 2, "%d %f")
ADD_OP(DepthFunc, 1, "%d")
ADD_OP(StencilFunc, 3, "%C %d %d")
ADD_OP(StencilOp, 3, "%C %C %C")
ADD_OP(PolygonStipple, 128, "%d")
ADD_OP(StippleColor, 3, "%f %f %f")
ADD_OP(Fog, 5, "%d %f %f %f %f")
ADD_OP(CallList, 1, "%d")
ADD_OP(Hint, 2, "%C %C")
// special opcodes
ADD_OP(EndList, 0, "")
ADD_OP(NextBuffer, 1, "%p")
// opengl 1.1 arrays
ADD_OP(ArrayElement, 1, "%d")
ADD_OP(DrawArrays, 3, "%C %d %d")
ADD_OP(DrawElements, 4, "%C %d %C %p")
// opengl 1.1 polygon offset
ADD_OP(PolygonOffset, 2, "%f %f")
#undef ADD_OP

View File

@@ -0,0 +1,125 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/tinygl/pixelbuffer.h"
namespace Graphics {
PixelBuffer::PixelBuffer()
: _buffer(nullptr),
_dispose(DisposeAfterUse::NO) {
}
PixelBuffer::PixelBuffer(const PixelFormat &format, int buffersize, DisposeAfterUse::Flag dispose)
: _buffer(nullptr),
_dispose(DisposeAfterUse::NO) {
create(format, buffersize, dispose);
}
PixelBuffer::PixelBuffer(const PixelFormat &format, byte *buffer)
: _buffer(buffer),
_format(format),
_dispose(DisposeAfterUse::NO) {
}
PixelBuffer::PixelBuffer(const PixelBuffer &buf) {
*this = buf;
}
PixelBuffer::~PixelBuffer() {
if (_dispose == DisposeAfterUse::YES)
free();
}
void PixelBuffer::create(const Graphics::PixelFormat &format, int buffersize, DisposeAfterUse::Flag dispose) {
if (_dispose == DisposeAfterUse::YES)
free();
_format = format;
_dispose = dispose;
_buffer = new byte[buffersize * format.bytesPerPixel];
}
void PixelBuffer::create(int buffersize, DisposeAfterUse::Flag dispose) {
if (_dispose == DisposeAfterUse::YES)
free();
_dispose = dispose;
_buffer = new byte[buffersize * _format.bytesPerPixel];
}
void PixelBuffer::set(const Graphics::PixelFormat &format, byte *buffer) {
if (_dispose == DisposeAfterUse::YES)
free();
_format = format;
_buffer = buffer;
}
void PixelBuffer::free() {
delete[] _buffer;
_buffer = nullptr;
}
void PixelBuffer::clear(uint length) {
memset(_buffer, 0, length * _format.bytesPerPixel);
}
void PixelBuffer::copyBuffer(int thisFrom, int otherFrom, int length, const PixelBuffer &buf) {
if (buf._format.bytesPerPixel == _format.bytesPerPixel &&
buf._format.rShift == _format.rShift &&
buf._format.gShift == _format.gShift &&
buf._format.bShift == _format.bShift &&
buf._format.rLoss == _format.rLoss &&
buf._format.gLoss == _format.gLoss &&
buf._format.bLoss == _format.bLoss && (
_format.aLoss == 8 ||
buf._format.aLoss == _format.aLoss
)
) {
memcpy(_buffer + thisFrom * _format.bytesPerPixel, buf._buffer + otherFrom * _format.bytesPerPixel, length * _format.bytesPerPixel);
} else {
uint8 r, g, b, a;
for (int i = 0; i < length; ++i) {
buf.getARGBAt(i + otherFrom, a, r, g, b);
setPixelAt(i + thisFrom, a, r, g, b);
}
}
}
PixelBuffer &PixelBuffer::operator=(const PixelBuffer &buf) {
_buffer = buf._buffer;
_format = buf._format;
_dispose = DisposeAfterUse::NO;
return *this;
}
PixelBuffer &PixelBuffer::operator=(byte *buffer) {
_buffer = buffer;
_dispose = DisposeAfterUse::NO;
return *this;
}
}

View File

@@ -0,0 +1,262 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_PIXELBUFFER_H
#define GRAPHICS_PIXELBUFFER_H
#include "common/types.h"
#include "common/endian.h"
#include "common/textconsole.h"
#include "graphics/pixelformat.h"
namespace Graphics {
/**
* @defgroup graphics_pixelbuffer Pixel buffers
* @ingroup graphics
*
* @brief Class for managing pixel buffers.
*
* @{
*/
class PixelBuffer {
public:
/**
* Construct an empty PixelBuffer.
*/
PixelBuffer();
/**
* Construct a PixelBuffer, allocating the internal buffer.
*
* @param format The format of the pixels in this buffer.
* @param buffersize The number of pixels the buffer will store.
* @param dispose If YES the internal buffer will be deleted when this object is destroyed,
*/
PixelBuffer(const Graphics::PixelFormat &format, int buffersize, DisposeAfterUse::Flag dispose);
/**
* Construct a PixelBuffer, using an already allocated buffer.
*
* @param format The format of the pixels in this buffer.
* @param buffer The raw buffer containing the pixels.
*/
PixelBuffer(const Graphics::PixelFormat &format, byte *buffer);
/**
* Copy constructor.
* The internal buffer will NOT be duplicated, it will be shared between the instances.
*/
PixelBuffer(const PixelBuffer &buf);
/**
* Destroy the object.
*/
~PixelBuffer();
/**
* Initialize the buffer.
*
* @param format The format of the pixels.
* @param buffersize The number of pixels the buffer will store.
* @param dispose If YES the internal buffer will be deleted when this object is destroyed,
*/
void create(const Graphics::PixelFormat &format, int buffersize, DisposeAfterUse::Flag dispose);
/**
* Initialize the buffer, using the already set pixel format.
* @note If the pixel format was not set before the results are undefined.
*
* @param buffersize The number of pixels the buffer will store.
* @param dispose If YES the internal buffer will be deleted when this object is destroyed,
*/
void create(int buffersize, DisposeAfterUse::Flag dispose);
/**
* Initialize the buffer.
*
* @param format The format of the pixels in this buffer.
* @param buffer The raw buffer containing the pixels.
*/
void set(const Graphics::PixelFormat &format, byte *buffer);
/**
* Delete the internal pixel buffer.
*/
void free();
/**
* Reset the value of the pixels.
*
* @param length The length of the buffer, in pixels.
*/
void clear(uint length);
/**
* Set the value of the pixel at index 'pixel' to 'value',
*/
inline void setPixelAt(int pixel, uint32 value) {
switch (_format.bytesPerPixel) {
case 2:
((uint16 *) _buffer)[pixel] = value;
return;
case 3:
pixel *= 3;
#if defined(SCUMM_BIG_ENDIAN)
_buffer[pixel + 0] = (value >> 16) & 0xFF;
_buffer[pixel + 1] = (value >> 8) & 0xFF;
_buffer[pixel + 2] = value & 0xFF;
#elif defined(SCUMM_LITTLE_ENDIAN)
_buffer[pixel + 0] = value & 0xFF;
_buffer[pixel + 1] = (value >> 8) & 0xFF;
_buffer[pixel + 2] = (value >> 16) & 0xFF;
#endif
return;
case 4:
((uint32 *) _buffer)[pixel] = value;
return;
}
error("setPixelAt: Unhandled bytesPerPixel %d", int(_format.bytesPerPixel));
}
/**
* Set the value of a pixel. The pixel will be converted from a pixel in another PixelBuffer,
* at the same index.
*
* @param pixel The index of the pixel to set.
* @param buf The buffer storing the source pixel.
*/
inline void setPixelAt(int pixel, const PixelBuffer &buf) { setPixelAt(pixel, buf, pixel); }
/**
* Set the value of a pixel. The pixel will be converted from a pixel in another PixelBuffer.
*
* @param thisPix The index of the pixel to set.
* @param buf The buffer storing the source pixel.
* @param otherPix The index of the source pixel in 'buf'.
*/
inline void setPixelAt(int thisPix, const PixelBuffer &buf, int otherPix) {
if (_format == buf._format) {
memcpy(getRawBuffer(thisPix), buf.getRawBuffer(otherPix), _format.bytesPerPixel);
return;
}
uint8 a, r, g, b;
buf.getARGBAt(otherPix, a, r, g, b);
setPixelAt(thisPix, a, r, g, b);
}
/**
* Set a pixel, from RGB values.
*/
inline void setPixelAt(int pixel, uint8 r, uint8 g, uint8 b) { setPixelAt(pixel, _format.RGBToColor(r, g, b)); }
/**
* Set a pixel, from ARGB values.
*/
inline void setPixelAt(int pixel, uint8 a, uint8 r, uint8 g, uint8 b) { setPixelAt(pixel, _format.ARGBToColor(a, r, g, b)); }
/**
* Copy some pixels from a buffer. The pixels will be converted, storing the same ARGB value.
*
* @param from The starting index.
* @param length The number of pixels to copy.
* @param buf The source buffer.
*/
inline void copyBuffer(int from, int length, const PixelBuffer &buf) { copyBuffer(from, from, length, buf); }
/**
* Copy some pixels from a buffer. The pixels will be converted, storing the same ARGB value.
*
* @param thisFrom The starting index.
* @param otherFrom The starting index in the source buffer.
* @param length The number of pixels to copy.
* @param buf The source buffer.
*/
void copyBuffer(int thisFrom, int otherFrom, int length, const PixelBuffer &buf);
/**
* Shift the internal buffer of some pixels, losing some pixels at the start of the buffer.
* The pixels lost will NOT be deleted.
*/
inline void shiftBy(int amount) { _buffer += amount * _format.bytesPerPixel; }
/**
* Return the encoded value of the pixel at the given index.
*/
inline uint32 getValueAt(int i) const {
switch (_format.bytesPerPixel) {
case 2:
return ((uint16 *) _buffer)[i];
case 3:
i *= 3;
#if defined(SCUMM_BIG_ENDIAN)
return (_buffer[i + 0] << 16) | (_buffer[i + 1] << 8) | _buffer[i + 2];
#elif defined(SCUMM_LITTLE_ENDIAN)
return _buffer[i + 0] | (_buffer[i + 1] << 8) | (_buffer[i + 2] << 16);
#endif
case 4:
return ((uint32 *) _buffer)[i];
}
error("getValueAt: Unhandled bytesPerPixel %d", int(_format.bytesPerPixel));
}
/**
* Return the RGB value of the pixel at the given index.
*/
inline void getRGBAt(int i, uint8 &r, uint8 &g, uint8 &b) const { _format.colorToRGB(getValueAt(i), r, g, b); }
/**
* Return the ARGB value of the pixel at the given index.
*/
inline void getARGBAt(int i, uint8 &a, uint8 &r, uint8 &g, uint8 &b) const { _format.colorToARGB(getValueAt(i), a, r, g, b); }
/**
* Return the internal buffer.
*/
inline byte *getRawBuffer() const { return _buffer; }
/**
* Return the internal buffer, pointing at the wanted pixel.
*/
inline byte *getRawBuffer(int pixel) const { return _buffer + _format.bytesPerPixel * pixel; }
/**
* Return the pixel format used.
*/
inline const PixelFormat &getFormat() const { return _format; }
/**
* Copy a PixelBuffer object.
* The internal buffer will NOT be duplicated, it will be shared between the instances.
*/
PixelBuffer &operator=(const PixelBuffer &buf);
/**
* Set the internal buffer to an already allocated array.
*
* @param buffer The pointer to the array.
*/
PixelBuffer &operator=(byte *buffer);
/**
* Check if the interal buffer is allocated.
*
* @returns true if allocated.
*/
inline operator bool() const { return (_buffer); }
private:
byte *_buffer;
Graphics::PixelFormat _format;
DisposeAfterUse::Flag _dispose;
};
}
/** @} */
#endif

136
graphics/tinygl/select.cpp Normal file
View File

@@ -0,0 +1,136 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
TGLint GLContext::gl_RenderMode(TGLenum mode) {
int result = 0;
switch (render_mode) {
case TGL_RENDER:
break;
case TGL_SELECT:
if (select_overflow) {
result = -select_hits;
} else {
result = select_hits;
}
select_overflow = 0;
select_ptr = select_buffer;
name_stack_size = 0;
break;
default:
assert(0);
}
switch (mode) {
case TGL_RENDER:
render_mode = TGL_RENDER;
break;
case TGL_SELECT:
render_mode = TGL_SELECT;
assert(select_buffer != nullptr);
select_ptr = select_buffer;
select_hits = 0;
select_overflow = 0;
select_hit = nullptr;
break;
default:
assert(0);
}
return result;
}
void GLContext::gl_SelectBuffer(TGLsizei size, TGLuint *buffer) {
assert(render_mode != TGL_SELECT);
select_buffer = buffer;
select_size = size;
}
void GLContext::glopInitNames(GLParam *) {
if (render_mode == TGL_SELECT) {
name_stack_size = 0;
select_hit = nullptr;
}
}
void GLContext::glopPushName(GLParam *p) {
if (render_mode == TGL_SELECT) {
assert(name_stack_size < MAX_NAME_STACK_DEPTH);
name_stack[name_stack_size++] = p[1].i;
select_hit = nullptr;
}
}
void GLContext::glopPopName(GLParam *) {
if (render_mode == TGL_SELECT) {
assert(name_stack_size > 0);
name_stack_size--;
select_hit = nullptr;
}
}
void GLContext::glopLoadName(GLParam *p) {
if (render_mode == TGL_SELECT) {
assert(name_stack_size > 0);
name_stack[name_stack_size - 1] = p[1].i;
select_hit = nullptr;
}
}
void GLContext::gl_add_select(uint zmin, uint zmax) {
uint *ptr;
int n;
if (!select_overflow) {
if (!select_hit) {
n = name_stack_size;
if ((select_ptr - select_buffer + 3 + n) > select_size) {
select_overflow = 1;
} else {
ptr = select_ptr;
select_hit = ptr;
*ptr++ = name_stack_size;
*ptr++ = zmin;
*ptr++ = zmax;
for (int i = 0; i < n; i++)
*ptr++ = name_stack[i];
select_ptr = ptr;
select_hits++;
}
} else {
if (zmin < select_hit[1])
select_hit[1] = zmin;
if (zmax > select_hit[2])
select_hit[2] = zmax;
}
}
}
} // end of namespace TinyGL

View File

@@ -0,0 +1,85 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
static void calc_buf(GLSpecBuf *buf, const float shininess) {
float val, inc;
val = 0.0f;
inc = 1.0f / SPECULAR_BUFFER_SIZE;
for (int i = 0; i <= SPECULAR_BUFFER_SIZE; i++) {
buf->buf[i] = pow(val, shininess);
val += inc;
}
}
GLSpecBuf *GLContext::specbuf_get_buffer(const int shininess_i, const float shininess) {
GLSpecBuf *found, *oldest;
found = oldest = specbuf_first;
while (found && found->shininess_i != shininess_i) {
if (found->last_used < oldest->last_used) {
oldest = found;
}
found = found->next;
}
if (found) {
found->last_used = specbuf_used_counter++;
return found;
}
if (!oldest || specbuf_num_buffers < MAX_SPECULAR_BUFFERS) {
// create new buffer
GLSpecBuf *buf = (GLSpecBuf *)gl_malloc(sizeof(GLSpecBuf));
if (!buf)
error("could not allocate specular buffer");
specbuf_num_buffers++;
buf->next = specbuf_first;
specbuf_first = buf;
buf->last_used = specbuf_used_counter++;
buf->shininess_i = shininess_i;
calc_buf(buf, shininess);
return buf;
}
//overwrite the lru buffer
oldest->shininess_i = shininess_i;
oldest->last_used = specbuf_used_counter++;
calc_buf(oldest, shininess);
return oldest;
}
void GLContext::specbuf_cleanup() {
GLSpecBuf *buf, *next;
buf = specbuf_first;
for (int i = 0; i < specbuf_num_buffers; ++i) {
next = buf->next;
gl_free(buf);
buf = next;
}
}
} // end of namespace TinyGL

View File

@@ -0,0 +1,339 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/tinygl/gl.h"
#include "graphics/tinygl/zgl.h"
#include "graphics/tinygl/zbuffer.h"
#include "graphics/tinygl/colormasks.h"
#include "graphics/tinygl/pixelbuffer.h"
#include "graphics/tinygl/texelbuffer.h"
namespace TinyGL {
#define ZB_POINT_ST_UNIT (1 << ZB_POINT_ST_FRAC_BITS)
#define ZB_POINT_ST_FRAC_MASK (ZB_POINT_ST_UNIT - 1)
TexelBuffer::TexelBuffer(uint width, uint height, uint textureSize, int internalformat) {
assert(width);
assert(height);
assert(textureSize);
_width = width;
_height = height;
_fracTextureUnit = textureSize << ZB_POINT_ST_FRAC_BITS;
_fracTextureMask = _fracTextureUnit - 1;
_widthRatio = (float) width / textureSize;
_heightRatio = (float) height / textureSize;
_internalformat = internalformat;
}
static inline uint wrap(uint wrap_mode, int coord, uint _fracTextureUnit, uint _fracTextureMask) {
switch (wrap_mode) {
case TGL_MIRRORED_REPEAT:
if (coord & _fracTextureUnit)
return _fracTextureMask - (coord & _fracTextureMask);
return coord & _fracTextureMask;
case TGL_CLAMP_TO_EDGE:
if (coord < 0)
return 0;
if ((uint) coord > _fracTextureMask)
return _fracTextureMask;
return coord;
default:
// Fall through
case TGL_REPEAT:
return coord & _fracTextureMask;
}
}
void TexelBuffer::getARGBAt(
uint wrap_s, uint wrap_t,
int s, int t,
uint8 &a, uint8 &r, uint8 &g, uint8 &b
) const {
uint x, y;
x = wrap(wrap_s, s, _fracTextureUnit, _fracTextureMask) * _widthRatio;
y = wrap(wrap_t, t, _fracTextureUnit, _fracTextureMask) * _heightRatio;
getARGBAt(
(x >> ZB_POINT_ST_FRAC_BITS) + (y >> ZB_POINT_ST_FRAC_BITS) * _width,
x & ZB_POINT_ST_FRAC_MASK, y & ZB_POINT_ST_FRAC_MASK,
a, r, g, b
);
}
// Nearest: store texture in original size.
class BaseNearestTexelBuffer : public TexelBuffer {
public:
BaseNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat);
~BaseNearestTexelBuffer();
protected:
byte *_buf;
Graphics::PixelFormat _format;
};
BaseNearestTexelBuffer::BaseNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat)
: TexelBuffer(width, height, textureSize, internalformat), _format(format) {
uint count = _width * _height * _format.bytesPerPixel;
_buf = (byte *)gl_malloc(count);
memcpy(_buf, buf, count);
}
BaseNearestTexelBuffer::~BaseNearestTexelBuffer() {
gl_free(_buf);
}
template<uint Format, uint Type>
class NearestTexelBuffer final : public BaseNearestTexelBuffer {
public:
NearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat)
: BaseNearestTexelBuffer(buf, format, width, height, textureSize, internalformat) {}
protected:
void getARGBAt(
uint pixel,
uint, uint,
uint8 &a, uint8 &r, uint8 &g, uint8 &b
) const override {
Pixel col = *(((const Pixel *)_buf) + pixel);
_format.colorToARGBT<ColorMask>(col, a, r, g, b);
}
typedef ColorMasks<Format, Type> ColorMask;
typedef typename ColorMask::PixelType Pixel;
};
template<>
class NearestTexelBuffer<TGL_RGB, TGL_UNSIGNED_BYTE> final : public BaseNearestTexelBuffer {
public:
NearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat)
: BaseNearestTexelBuffer(buf, format, width, height, textureSize, internalformat) {}
protected:
void getARGBAt(
uint pixel,
uint, uint,
uint8 &a, uint8 &r, uint8 &g, uint8 &b
) const override {
byte *col = _buf + (pixel * 3);
a = 0xff;
r = col[0];
g = col[1];
b = col[2];
}
};
TexelBuffer *createNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &pf, uint format, uint type, uint width, uint height, uint textureSize, int internalformat) {
if (format == TGL_RGBA && type == TGL_UNSIGNED_BYTE) {
return new NearestTexelBuffer<TGL_RGBA, TGL_UNSIGNED_BYTE>(
buf, pf,
width, height,
textureSize,
internalformat
);
} else if (format == TGL_RGB && type == TGL_UNSIGNED_BYTE) {
return new NearestTexelBuffer<TGL_RGB, TGL_UNSIGNED_BYTE>(
buf, pf,
width, height,
textureSize,
internalformat
);
} else if (format == TGL_RGB && type == TGL_UNSIGNED_SHORT_5_6_5) {
return new NearestTexelBuffer<TGL_RGB, TGL_UNSIGNED_SHORT_5_6_5>(
buf, pf,
width, height,
textureSize,
internalformat
);
} else if (format == TGL_RGBA && type == TGL_UNSIGNED_SHORT_5_5_5_1) {
return new NearestTexelBuffer<TGL_RGBA, TGL_UNSIGNED_SHORT_5_5_5_1>(
buf, pf,
width, height,
textureSize,
internalformat
);
} else if (format == TGL_RGBA && type == TGL_UNSIGNED_SHORT_4_4_4_4) {
return new NearestTexelBuffer<TGL_RGBA, TGL_UNSIGNED_SHORT_4_4_4_4>(
buf, pf,
width, height,
textureSize,
internalformat
);
} else {
error("TinyGL texture: format 0x%04x and type 0x%04x combination not supported", format, type);
}
}
// Bilinear: each texture coordinates corresponds to the 4 original image
// pixels linear interpolation has to work on, so that they are near each
// other in CPU data cache, and a single actual memory fetch happens. This
// allows applying linear filtering at render time at a very low performance
// cost. As we expect to work on small-ish textures (512*512 ?) the 4x memory
// usage increase should be negligible.
class BilinearTexelBuffer : public TexelBuffer {
public:
BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat);
~BilinearTexelBuffer();
protected:
void getARGBAt(
uint pixel,
uint ds, uint dt,
uint8 &a, uint8 &r, uint8 &g, uint8 &b
) const override;
private:
uint32 *_texels;
};
#define A_OFFSET (0 * 4)
#define R_OFFSET (1 * 4)
#define G_OFFSET (2 * 4)
#define B_OFFSET (3 * 4)
#define P00_OFFSET 0
#define P01_OFFSET 1
#define P10_OFFSET 2
#define P11_OFFSET 3
#define PIXEL_PER_TEXEL_SHIFT 2
BilinearTexelBuffer::BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat)
: TexelBuffer(width, height, textureSize, internalformat) {
const Graphics::PixelBuffer src(format, buf);
uint pixel00_offset = 0, pixel11_offset, pixel01_offset, pixel10_offset;
uint8 *texel8;
uint32 *texel32;
texel32 = _texels = (uint32 *)gl_malloc((_width * _height << PIXEL_PER_TEXEL_SHIFT) * sizeof(uint32));
for (uint y = 0; y < _height; y++) {
for (uint x = 0; x < _width; x++) {
texel8 = (uint8 *)texel32;
pixel11_offset = pixel00_offset + _width + 1;
src.getARGBAt(
pixel00_offset,
*(texel8 + P00_OFFSET + A_OFFSET),
*(texel8 + P00_OFFSET + R_OFFSET),
*(texel8 + P00_OFFSET + G_OFFSET),
*(texel8 + P00_OFFSET + B_OFFSET)
);
if ((x + 1) == _width) {
pixel11_offset -= 1;
pixel01_offset = pixel00_offset;
} else
pixel01_offset = pixel00_offset + 1;
src.getARGBAt(
pixel01_offset,
*(texel8 + P01_OFFSET + A_OFFSET),
*(texel8 + P01_OFFSET + R_OFFSET),
*(texel8 + P01_OFFSET + G_OFFSET),
*(texel8 + P01_OFFSET + B_OFFSET)
);
if ((y + 1) == _height) {
pixel11_offset -= _width;
pixel10_offset = pixel00_offset;
} else
pixel10_offset = pixel00_offset + _width;
src.getARGBAt(
pixel10_offset,
*(texel8 + P10_OFFSET + A_OFFSET),
*(texel8 + P10_OFFSET + R_OFFSET),
*(texel8 + P10_OFFSET + G_OFFSET),
*(texel8 + P10_OFFSET + B_OFFSET)
);
src.getARGBAt(
pixel11_offset,
*(texel8 + P11_OFFSET + A_OFFSET),
*(texel8 + P11_OFFSET + R_OFFSET),
*(texel8 + P11_OFFSET + G_OFFSET),
*(texel8 + P11_OFFSET + B_OFFSET)
);
texel32 += 1 << PIXEL_PER_TEXEL_SHIFT;
pixel00_offset++;
}
}
}
BilinearTexelBuffer::~BilinearTexelBuffer() {
gl_free(_texels);
}
static inline int interpolate(int v00, int v01, int v10, int xf, int yf) {
return v00 + (((v01 - v00) * xf + (v10 - v00) * yf) >> ZB_POINT_ST_FRAC_BITS);
}
void BilinearTexelBuffer::getARGBAt(
uint pixel,
uint ds, uint dt,
uint8 &a, uint8 &r, uint8 &g, uint8 &b
) const {
uint p00_offset, p01_offset, p10_offset;
uint8 *texel = (uint8 *)(_texels + (pixel << PIXEL_PER_TEXEL_SHIFT));
if ((ds + dt) > ZB_POINT_ST_UNIT) {
p00_offset = P11_OFFSET;
p10_offset = P01_OFFSET;
p01_offset = P10_OFFSET;
ds = ZB_POINT_ST_UNIT - ds;
dt = ZB_POINT_ST_UNIT - dt;
} else {
p00_offset = P00_OFFSET;
p10_offset = P10_OFFSET;
p01_offset = P01_OFFSET;
}
a = interpolate(
*(texel + p00_offset + A_OFFSET),
*(texel + p01_offset + A_OFFSET),
*(texel + p10_offset + A_OFFSET),
ds,
dt
);
r = interpolate(
*(texel + p00_offset + R_OFFSET),
*(texel + p01_offset + R_OFFSET),
*(texel + p10_offset + R_OFFSET),
ds,
dt
);
g = interpolate(
*(texel + p00_offset + G_OFFSET),
*(texel + p01_offset + G_OFFSET),
*(texel + p10_offset + G_OFFSET),
ds,
dt
);
b = interpolate(
*(texel + p00_offset + B_OFFSET),
*(texel + p01_offset + B_OFFSET),
*(texel + p10_offset + B_OFFSET),
ds,
dt
);
}
TexelBuffer *createBilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &pf, uint format, uint type, uint width, uint height, uint textureSize, int internalformat) {
return new BilinearTexelBuffer(
buf, pf,
width, height,
textureSize,
internalformat
);
}
} // end of namespace TinyGL

View File

@@ -0,0 +1,58 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_TEXELBUFFER_H
#define GRAPHICS_TEXELBUFFER_H
#include "graphics/pixelformat.h"
namespace TinyGL {
class TexelBuffer {
public:
TexelBuffer(uint width, uint height, uint textureSize, int internalformat);
virtual ~TexelBuffer() {};
inline int internalformat() const { return _internalformat; }
void getARGBAt(
uint wrap_s, uint wrap_t,
int s, int t,
uint8 &a, uint8 &r, uint8 &g, uint8 &b
) const;
protected:
virtual void getARGBAt(
uint pixel,
uint ds, uint dt,
uint8 &a, uint8 &r, uint8 &g, uint8 &b
) const = 0;
uint _width, _height, _fracTextureUnit, _fracTextureMask;
float _widthRatio, _heightRatio;
int _internalformat;
};
TexelBuffer *createNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &pf, uint format, uint type, uint width, uint height, uint textureSize, int internalformat);
TexelBuffer *createBilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &pf, uint format, uint type, uint width, uint height, uint textureSize, int internalformat);
} // end of namespace TinyGL
#endif

357
graphics/tinygl/texture.cpp Normal file
View File

@@ -0,0 +1,357 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
// Texture Manager
#include "common/endian.h"
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
GLTexture *GLContext::find_texture(uint h) {
GLTexture *t;
t = shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
while (t) {
if (t->handle == h)
return t;
t = t->next;
}
return nullptr;
}
void GLContext::free_texture(GLTexture *t) {
GLTexture **ht;
GLImage *im;
assert(t);
if (!t->prev) {
ht = &shared_state.texture_hash_table[t->handle % TEXTURE_HASH_TABLE_SIZE];
*ht = t->next;
} else {
t->prev->next = t->next;
}
if (t->next)
t->next->prev = t->prev;
for (int i = 0; i < MAX_TEXTURE_LEVELS; i++) {
im = &t->images[i];
if (im->pixmap) {
delete im->pixmap;
im->pixmap = nullptr;
}
}
gl_free(t);
}
GLTexture *GLContext::alloc_texture(uint h) {
GLTexture *t, **ht;
t = (GLTexture *)gl_zalloc(sizeof(GLTexture));
ht = &shared_state.texture_hash_table[h % TEXTURE_HASH_TABLE_SIZE];
t->next = *ht;
t->prev = nullptr;
if (t->next)
t->next->prev = t;
*ht = t;
t->handle = h;
t->disposed = false;
t->versionNumber = 0;
return t;
}
void GLContext::glopBindTexture(GLParam *p) {
int target = p[1].i;
uint texture = p[2].ui;
GLTexture *t;
assert(target == TGL_TEXTURE_2D);
(void)target;
t = find_texture(texture);
if (!t) {
t = alloc_texture(texture);
}
current_texture = t;
}
void GLContext::glopTexImage2D(GLParam *p) {
int target = p[1].i;
int level = p[2].i;
int internalformat = p[3].i;
int width = p[4].i;
int height = p[5].i;
int border = p[6].i;
uint format = (uint)p[7].i;
uint type = (uint)p[8].i;
byte *pixels = (byte *)p[9].p;
GLImage *im;
if (target != TGL_TEXTURE_2D)
error("tglTexImage2D: target not handled");
if (level < 0 || level >= MAX_TEXTURE_LEVELS)
error("tglTexImage2D: invalid level");
if (internalformat != TGL_RGBA && internalformat != TGL_RGB)
error("tglTexImage2D: invalid internalformat");
if (border != 0)
error("tglTexImage2D: invalid border");
assert (current_texture);
current_texture->versionNumber++;
im = &current_texture->images[level];
im->xsize = _textureSize;
im->ysize = _textureSize;
if (im->pixmap) {
delete im->pixmap;
im->pixmap = nullptr;
}
if (pixels) {
uint filter;
Graphics::PixelFormat pf;
bool found = false;
Common::Array<struct tglColorAssociation>::const_iterator it = colorAssociationList.begin();
for (; it != colorAssociationList.end(); it++) {
if (it->format == format &&
it->type == type) {
pf = it->pf;
found = true;
break;
}
}
if (!found)
error("TinyGL texture: format 0x%04x and type 0x%04x combination not supported", format, type);
if (width > _textureSize || height > _textureSize)
filter = texture_mag_filter;
else
filter = texture_min_filter;
switch (filter) {
case TGL_LINEAR_MIPMAP_NEAREST:
case TGL_LINEAR_MIPMAP_LINEAR:
case TGL_LINEAR:
im->pixmap = createBilinearTexelBuffer(
pixels, pf,
format, type,
width, height,
_textureSize,
internalformat
);
break;
default:
im->pixmap = createNearestTexelBuffer(
pixels, pf,
format, type,
width, height,
_textureSize,
internalformat
);
break;
}
}
}
// TODO: not all tests are done
void GLContext::glopTexEnv(GLParam *p) {
int target = p[1].i;
int pname = p[2].i;
int param = p[3].i;
if (target != TGL_TEXTURE_ENV) {
error:
error("tglTexEnv: unsupported option");
}
switch (pname) {
case TGL_TEXTURE_ENV_MODE:
if (param == TGL_REPLACE ||
param == TGL_MODULATE ||
param == TGL_DECAL ||
param == TGL_BLEND ||
param == TGL_ADD ||
param == TGL_COMBINE)
_texEnv.envMode = param;
else
goto error;
break;
case TGL_COMBINE_RGB:
if (param == TGL_REPLACE ||
param == TGL_MODULATE ||
param == TGL_ADD)
_texEnv.combineRGB = param;
else
goto error;
break;
case TGL_COMBINE_ALPHA:
if (param == TGL_REPLACE ||
param == TGL_MODULATE ||
param == TGL_ADD)
_texEnv.combineAlpha = param;
else
goto error;
break;
case TGL_SOURCE0_RGB:
case TGL_SOURCE1_RGB:
{
GLTextureEnvArgument *op = pname == TGL_SOURCE0_RGB ? &_texEnv.arg0 : &_texEnv.arg1;
if (param == TGL_TEXTURE ||
param == TGL_PRIMARY_COLOR ||
param == TGL_CONSTANT)
op->sourceRGB = param;
else
goto error;
break;
}
case TGL_SOURCE0_ALPHA:
case TGL_SOURCE1_ALPHA:
{
GLTextureEnvArgument *op = pname == TGL_SOURCE0_ALPHA ? &_texEnv.arg0 : &_texEnv.arg1;
if (param == TGL_TEXTURE ||
param == TGL_PRIMARY_COLOR ||
param == TGL_CONSTANT)
op->sourceAlpha = param;
else
goto error;
break;
}
case TGL_OPERAND0_RGB:
case TGL_OPERAND1_RGB:
{
GLTextureEnvArgument *op = pname == TGL_OPERAND0_RGB ? &_texEnv.arg0 : &_texEnv.arg1;
if (param == TGL_SRC_COLOR ||
param == TGL_ONE_MINUS_SRC_COLOR ||
param == TGL_SRC_ALPHA)
op->operandRGB = param;
else
goto error;
break;
}
case TGL_OPERAND0_ALPHA:
case TGL_OPERAND1_ALPHA:
{
GLTextureEnvArgument *op = pname == TGL_OPERAND0_ALPHA ? &_texEnv.arg0 : &_texEnv.arg1;
if (param == TGL_SRC_ALPHA ||
param == TGL_ONE_MINUS_SRC_ALPHA)
op->operandAlpha = param;
else
goto error;
break;
}
case TGL_TEXTURE_ENV_COLOR:
{
_texEnv.constR = (byte)clampf(p[4].f * 255.0f, 0, 255.0f);
_texEnv.constG = (byte)clampf(p[5].f * 255.0f, 0, 255.0f);
_texEnv.constB = (byte)clampf(p[6].f * 255.0f, 0, 255.0f);
_texEnv.constA = (byte)clampf(p[7].f * 255.0f, 0, 255.0f);
break;
}
default:
goto error;
}
}
// TODO: not all tests are done
void GLContext::glopTexParameter(GLParam *p) {
int target = p[1].i;
int pname = p[2].i;
int param = p[3].i;
if (target != TGL_TEXTURE_2D) {
error:
error("tglTexParameter: unsupported option");
}
switch (pname) {
case TGL_TEXTURE_WRAP_S:
texture_wrap_s = param;
break;
case TGL_TEXTURE_WRAP_T:
texture_wrap_t = param;
break;
case TGL_TEXTURE_MAG_FILTER:
switch (param) {
case TGL_NEAREST:
case TGL_LINEAR:
texture_mag_filter = param;
break;
default:
goto error;
}
break;
case TGL_TEXTURE_MIN_FILTER:
switch (param) {
case TGL_LINEAR_MIPMAP_NEAREST:
case TGL_LINEAR_MIPMAP_LINEAR:
case TGL_NEAREST_MIPMAP_NEAREST:
case TGL_NEAREST_MIPMAP_LINEAR:
case TGL_NEAREST:
case TGL_LINEAR:
texture_min_filter = param;
break;
default:
goto error;
}
break;
default:
;
}
}
void GLContext::gl_PixelStore(TGLenum pname, TGLint param) {
if (pname != TGL_UNPACK_ALIGNMENT || param != 1) {
error("tglPixelStore: unsupported option");
}
}
void GLContext::gl_GenTextures(TGLsizei n, TGLuint *textures) {
for (int i = 0; i < n; i++) {
textures[i] = maxTextureName + i + 1;
}
maxTextureName += n;
}
void GLContext::gl_DeleteTextures(TGLsizei n, const TGLuint *textures) {
for (int i = 0; i < n; i++) {
TinyGL::GLTexture *t = find_texture(textures[i]);
if (t) {
if (t == current_texture) {
current_texture = default_texture;
}
t->disposed = true;
}
}
}
} // end of namespace TinyGL

47
graphics/tinygl/tinygl.h Normal file
View File

@@ -0,0 +1,47 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_TINYGL_H
#define GRAPHICS_TINYGL_H
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "graphics/tinygl/gl.h"
#include "graphics/tinygl/zblit_public.h"
namespace TinyGL {
typedef void *ContextHandle;
ContextHandle *createContext(int screenW, int screenH, Graphics::PixelFormat pixelFormat,
int textureSize, bool enableStencilBuffer, bool dirtyRectsEnable,
uint32 drawCallMemorySize = 5 * 1024 * 1024);
void destroyContext();
void destroyContext(ContextHandle *handle);
void setContext(ContextHandle *handle);
void presentBuffer();
void presentBuffer(Common::List<Common::Rect> &dirtyAreas);
void getSurfaceRef(Graphics::Surface &surface);
Graphics::Surface *copyFromFrameBuffer(const Graphics::PixelFormat &dstFormat);
} // end of namespace TinyGL
#endif

267
graphics/tinygl/vertex.cpp Normal file
View File

@@ -0,0 +1,267 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zgl.h"
#include "graphics/tinygl/zdirtyrect.h"
namespace TinyGL {
void GLContext::glopNormal(GLParam *p) {
current_normal.X = p[1].f;
current_normal.Y = p[2].f;
current_normal.Z = p[3].f;
current_normal.W = 0.0f;
}
void GLContext::glopTexCoord(GLParam *p) {
current_tex_coord.X = p[1].f;
current_tex_coord.Y = p[2].f;
current_tex_coord.Z = p[3].f;
current_tex_coord.W = p[4].f;
}
void GLContext::glopEdgeFlag(GLParam *p) {
current_edge_flag = p[1].i;
}
void GLContext::glopColor(GLParam *p) {
current_color.X = p[1].f;
current_color.Y = p[2].f;
current_color.Z = p[3].f;
current_color.W = p[4].f;
if (color_material_enabled) {
GLParam q[7];
q[0].op = OP_Material;
q[1].i = current_color_material_mode;
q[2].i = current_color_material_type;
q[3].f = p[1].f;
q[4].f = p[2].f;
q[5].f = p[3].f;
q[6].f = p[4].f;
glopMaterial(q);
}
}
void GLContext::gl_eval_viewport() {
GLViewport *v;
float zsize = (1 << (ZB_Z_BITS + ZB_POINT_Z_FRAC_BITS));
v = &viewport;
// v->ymin needs to be upside down for transformation
int ymin = fb->getPixelBufferHeight() - v->ysize - v->ymin;
v->trans.X = (float)(((v->xsize - 0.5) / 2.0) + v->xmin);
v->trans.Y = (float)(((v->ysize - 0.5) / 2.0) + ymin);
v->trans.Z = (float)(((zsize - 0.5) / 2.0) + ((1 << ZB_POINT_Z_FRAC_BITS)) / 2);
v->scale.X = (float)((v->xsize - 0.5) / 2.0);
// v->ysize needs to be upside down for scaling
v->scale.Y = (float)(-(v->ysize - 0.5) / 2.0);
v->scale.Z = (float)(-((zsize - 0.5) / 2.0));
}
void GLContext::glopBegin(GLParam *p) {
int type;
assert(in_begin == 0);
type = p[1].i;
begin_type = type;
in_begin = 1;
vertex_n = 0;
vertex_cnt = 0;
if (matrix_model_projection_updated) {
if (lighting_enabled) {
// precompute inverse modelview
matrix_model_view_inv = *matrix_stack_ptr[0];
matrix_model_view_inv.invert();
matrix_model_view_inv.transpose();
} else {
// precompute projection matrix
matrix_model_projection = (*matrix_stack_ptr[1]) * (*matrix_stack_ptr[0]);
// test to accelerate computation
matrix_model_projection_no_w_transform = 0;
if (matrix_model_projection._m[3][0] == 0.0 && matrix_model_projection._m[3][1] == 0.0 && matrix_model_projection._m[3][2] == 0.0)
matrix_model_projection_no_w_transform = 1;
}
matrix_model_projection_updated = 0;
}
// test if the texture matrix is not Identity
apply_texture_matrix = !matrix_stack_ptr[2]->isIdentity();
// viewport
if (viewport.updated) {
gl_eval_viewport();
viewport.updated = 0;
}
// triangle drawing functions
if (render_mode == TGL_SELECT) {
draw_triangle_front = gl_draw_triangle_select;
draw_triangle_back = gl_draw_triangle_select;
} else {
switch (polygon_mode_front) {
case TGL_POINT:
draw_triangle_front = gl_draw_triangle_point;
break;
case TGL_LINE:
draw_triangle_front = gl_draw_triangle_line;
break;
default:
draw_triangle_front = gl_draw_triangle_fill;
break;
}
switch (polygon_mode_back) {
case TGL_POINT:
draw_triangle_back = gl_draw_triangle_point;
break;
case TGL_LINE:
draw_triangle_back = gl_draw_triangle_line;
break;
default:
draw_triangle_back = gl_draw_triangle_fill;
break;
}
}
}
// coords, tranformation, clip code and projection
// TODO : handle all cases
void GLContext::gl_vertex_transform(GLVertex *v) {
Matrix4 *m;
if (lighting_enabled || fog_enabled) {
// eye coordinates needed for lighting and fog
m = matrix_stack_ptr[0];
m->transform3x4(v->coord, v->ec);
}
if (fog_enabled) {
gl_calc_fog_factor(v);
}
if (lighting_enabled) {
// projection coordinates
m = matrix_stack_ptr[1];
m->transform(v->ec, v->pc);
m = &matrix_model_view_inv;
m->transform3x3(current_normal, v->normal);
if (normalize_enabled) {
v->normal.normalize();
}
} else {
// no eye coordinates needed, no normal
// NOTE: W = 1 is assumed
m = &matrix_model_projection;
m->transform3x4(v->coord, v->pc);
if (matrix_model_projection_no_w_transform) {
v->pc.W = (m->_m[3][3]);
}
v->normal.X = v->normal.Y = v->normal.Z = 0;
v->ec.X = v->ec.Y = v->ec.Z = v->ec.W = 0;
}
v->clip_code = gl_clipcode(v->pc.X, v->pc.Y, v->pc.Z, v->pc.W);
}
void GLContext::glopVertex(GLParam *p) {
GLVertex *v;
int n, cnt;
assert(in_begin != 0);
n = vertex_n;
cnt = vertex_cnt;
cnt++;
vertex_cnt = cnt;
// quick fix to avoid crashes on large polygons
if (n >= vertex_max) {
GLVertex *newarray;
vertex_max <<= 1; // just double size
newarray = (GLVertex *)gl_realloc(vertex, sizeof(GLVertex) * vertex_max);
if (!newarray) {
error("unable to allocate GLVertex array.");
}
vertex = newarray;
}
// new vertex entry
v = &vertex[n];
n++;
v->coord.X = p[1].f;
v->coord.Y = p[2].f;
v->coord.Z = p[3].f;
v->coord.W = p[4].f;
gl_vertex_transform(v);
// color
if (lighting_enabled) {
gl_shade_vertex(v);
} else {
v->color = current_color;
}
// tex coords
if (texture_2d_enabled) {
if (apply_texture_matrix) {
matrix_stack_ptr[2]->transform(current_tex_coord, v->tex_coord);
} else {
v->tex_coord = current_tex_coord;
}
}
// precompute the mapping to the viewport
if (v->clip_code == 0)
gl_transform_to_viewport(v);
// edge flag
v->edge_flag = current_edge_flag;
vertex_n = n;
}
void GLContext::glopEnd(GLParam *) {
assert(in_begin == 1);
if (vertex_cnt > 0) {
issueDrawCall(new RasterizationDrawCall());
}
in_begin = 0;
}
} // end of namespace TinyGL

855
graphics/tinygl/zblit.cpp Normal file
View File

@@ -0,0 +1,855 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/array.h"
#include "graphics/tinygl/zblit.h"
#include "graphics/tinygl/zgl.h"
#include "graphics/tinygl/pixelbuffer.h"
#include "graphics/tinygl/zdirtyrect.h"
#include "graphics/tinygl/gl.h"
#include "graphics/blit.h"
#include <math.h>
namespace TinyGL {
Common::Point transformPoint(float x, float y, int rotation);
Common::Rect rotateRectangle(int x, int y, int width, int height, int rotation, int originX, int originY);
struct BlitImage {
public:
BlitImage() : _isDisposed(false), _version(0), _binaryTransparent(false), _opaque(true), _zBuffer(false), _refcount(1) { }
void loadData(const Graphics::Surface &surface, uint32 colorKey, bool applyColorKey, bool zBuffer) {
_lines.clear();
_zBuffer = zBuffer;
if (_zBuffer) {
_surface.copyFrom(surface);
return;
}
int size = surface.w * surface.h;
Graphics::PixelBuffer buffer(surface.format, (byte *)const_cast<void *>(surface.getPixels()));
_opaque = true;
if (surface.format.aBits() > 0) {
for (int x = 0; x < size; x++) {
uint8 r, g, b, a;
buffer.getARGBAt(x, a, r, g, b);
if (a != 0xFF) {
_opaque = false;
}
}
}
if (_opaque && !applyColorKey) {
GLContext *c = gl_get_context();
_surface.convertFrom(surface, c->fb->getPixelFormat());
_binaryTransparent = false;
} else {
const Graphics::PixelFormat textureFormat = Graphics::PixelFormat::createFormatRGBA32();
_surface.convertFrom(surface, textureFormat);
Graphics::PixelBuffer dataBuffer(textureFormat, (byte *)const_cast<void *>(_surface.getPixels()));
if (applyColorKey) {
for (int x = 0; x < size; x++) {
if (buffer.getValueAt(x) == colorKey) {
// Color keyed pixels become transparent white.
dataBuffer.setPixelAt(x, 0, 255, 255, 255);
_opaque = false;
}
}
}
// Create opaque lines data.
// A line of pixels can not wrap more that one line of the image, since it would break
// blitting of bitmaps with a non-zero x position.
Graphics::PixelBuffer srcBuf = dataBuffer;
_binaryTransparent = true;
for (int y = 0; y < surface.h; y++) {
int start = -1;
for (int x = 0; x < surface.w; ++x) {
// We found a transparent pixel, so save a line from 'start' to the pixel before this.
uint8 r, g, b, a;
srcBuf.getARGBAt(x, a, r, g, b);
if (a != 0 && a != 0xFF) {
_binaryTransparent = false;
}
if (a == 0 && start >= 0) {
_lines.push_back(Line(start, y, x - start, srcBuf.getRawBuffer(start), textureFormat));
start = -1;
} else if (a != 0 && start == -1) {
start = x;
}
}
// end of the bitmap line. if start is an actual pixel save the line.
if (start >= 0) {
_lines.push_back(Line(start, y, surface.w - start, srcBuf.getRawBuffer(start), textureFormat));
}
srcBuf.shiftBy(surface.w);
}
}
_version++;
}
int getVersion() const {
return _version;
}
~BlitImage() {
_surface.free();
}
struct Line {
int _x;
int _y;
int _bpp;
int _length;
byte *_pixels;
Line() : _x(0), _y(0), _length(0), _pixels(nullptr) { }
Line(int x, int y, int length, byte *pixels, const Graphics::PixelFormat &textureFormat) :
_x(x), _y(y), _bpp(gl_get_context()->fb->getPixelBufferBpp()), _length(length) {
_pixels = (byte *)gl_zalloc(_length * _bpp);
Graphics::crossBlit(_pixels, pixels, _length * _bpp, _length * _bpp, _length, 1,
gl_get_context()->fb->getPixelFormat(), textureFormat);
}
Line &operator=(const Line &other) {
if (this == &other)
return *this;
_x = other._x;
_y = other._y;
if (_length != other._length || _bpp != other._bpp) {
_pixels = (byte *)gl_realloc(_pixels, other._length * other._bpp);
_length = other._length;
_bpp = other._bpp;
}
memcpy(_pixels, other._pixels, _length * _bpp);
return *this;
}
Line(const Line& other) : _x(other._x), _y(other._y), _bpp(other._bpp), _length(other._length) {
_pixels = (byte *)gl_zalloc(_length * _bpp);
memcpy(_pixels, other._pixels, _length * _bpp);
}
~Line() {
gl_free(_pixels);
}
};
bool clipBlitImage(TinyGL::GLContext *c, int &srcX, int &srcY, int &srcWidth, int &srcHeight, int &width, int &height, int &dstX, int &dstY, int &clampWidth, int &clampHeight) {
if (srcWidth == 0 || srcHeight == 0) {
srcWidth = _surface.w;
srcHeight = _surface.h;
}
if (width == 0 && height == 0) {
width = srcWidth;
height = srcHeight;
}
const Common::Rect &clippingRect = c->fb->getClippingRectangle();
if (dstX >= clippingRect.right || dstY >= clippingRect.bottom)
return false;
if (dstX + width < clippingRect.left || dstY + height < clippingRect.top) {
return false;
}
if (dstX < clippingRect.left) {
srcX += (clippingRect.left - dstX);
width -= (clippingRect.left - dstX);
dstX = clippingRect.left;
}
if (dstY < clippingRect.top) {
srcY += (clippingRect.top - dstY);
height -= (clippingRect.top - dstY);
dstY = clippingRect.top;
}
if (width < 0 || height < 0) {
return false;
}
if (dstX + width >= clippingRect.right) {
clampWidth = clippingRect.right - dstX;
} else {
clampWidth = width;
}
if (dstY + height >= clippingRect.bottom) {
clampHeight = clippingRect.bottom - dstY;
} else {
clampHeight = height;
}
return true;
}
// Blits an image to the z buffer.
// The function only supports clipped blitting without any type of transformation or tinting.
void tglBlitZBuffer(int dstX, int dstY) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
assert(_zBuffer);
int clampWidth, clampHeight;
int width = _surface.w, height = _surface.h;
int srcWidth = 0, srcHeight = 0;
int srcX = 0, srcY = 0;
if (clipBlitImage(c, srcX, srcY, srcWidth, srcHeight, width, height, dstX, dstY, clampWidth, clampHeight) == false)
return;
int fbWidth = c->fb->getPixelBufferWidth();
Graphics::PixelBuffer srcBuf(_surface.format, (byte *)const_cast<void *>(_surface.getPixels())); // Blit image buffer
Graphics::PixelBuffer dstBuf(_surface.format, (byte *)const_cast<uint *>(c->fb->getZBuffer())); // TinyGL z buffer
srcBuf.shiftBy(srcY * _surface.w);
dstBuf.shiftBy(dstY * fbWidth);
for (int y = 0; y < clampHeight; y++) {
dstBuf.copyBuffer(dstX, srcX, clampWidth, srcBuf);
dstBuf.shiftBy(fbWidth);
srcBuf.shiftBy(_surface.w);
}
}
void tglBlitOpaque(int dstX, int dstY, int srcX, int srcY, int srcWidth, int srcHeight);
template <bool kDisableColoring, bool kDisableBlending, bool kEnableAlphaBlending>
void tglBlitRLE(int dstX, int dstY, int srcX, int srcY, int srcWidth, int srcHeight, float aTint, float rTint, float gTint, float bTint);
template <bool kDisableBlending, bool kDisableColoring, bool kFlipVertical, bool kFlipHorizontal>
void tglBlitSimple(int dstX, int dstY, int srcX, int srcY, int srcWidth, int srcHeight, float aTint, float rTint, float gTint, float bTint);
template <bool kDisableBlending, bool kDisableColoring, bool kFlipVertical, bool kFlipHorizontal>
void tglBlitScale(int dstX, int dstY, int width, int height, int srcX, int srcY, int srcWidth, int srcHeight, float aTint, float rTint, float gTint, float bTint);
template <bool kDisableBlending, bool kDisableColoring, bool kFlipVertical, bool kFlipHorizontal>
void tglBlitRotoScale(int dstX, int dstY, int width, int height, int srcX, int srcY, int srcWidth, int srcHeight, int rotation,
int originX, int originY, float aTint, float rTint, float gTint, float bTint);
//Utility function that calls the correct blitting function.
template <bool kDisableBlending, bool kDisableColoring, bool kDisableTransform, bool kFlipVertical, bool kFlipHorizontal, bool kEnableAlphaBlending, bool kEnableOpaqueBlit>
void tglBlitGeneric(const BlitTransform &transform) {
assert(!_zBuffer);
if (kDisableTransform) {
if (kEnableOpaqueBlit && kDisableColoring && kFlipVertical == false && kFlipHorizontal == false) {
tglBlitOpaque(transform._destinationRectangle.left, transform._destinationRectangle.top,
transform._sourceRectangle.left, transform._sourceRectangle.top,
transform._sourceRectangle.width() , transform._sourceRectangle.height());
} else if ((kDisableBlending || kEnableAlphaBlending) && kFlipVertical == false && kFlipHorizontal == false) {
tglBlitRLE<kDisableColoring, kDisableBlending, kEnableAlphaBlending>(transform._destinationRectangle.left,
transform._destinationRectangle.top, transform._sourceRectangle.left, transform._sourceRectangle.top,
transform._sourceRectangle.width() , transform._sourceRectangle.height(), transform._aTint,
transform._rTint, transform._gTint, transform._bTint);
} else {
tglBlitSimple<kDisableBlending, kDisableColoring, kFlipVertical, kFlipHorizontal>(transform._destinationRectangle.left,
transform._destinationRectangle.top, transform._sourceRectangle.left, transform._sourceRectangle.top,
transform._sourceRectangle.width() , transform._sourceRectangle.height(),
transform._aTint, transform._rTint, transform._gTint, transform._bTint);
}
} else {
if (transform._rotation == 0) {
tglBlitScale<kDisableBlending, kDisableColoring, kFlipVertical, kFlipHorizontal>(transform._destinationRectangle.left,
transform._destinationRectangle.top, transform._destinationRectangle.width(), transform._destinationRectangle.height(),
transform._sourceRectangle.left, transform._sourceRectangle.top, transform._sourceRectangle.width(), transform._sourceRectangle.height(),
transform._aTint, transform._rTint, transform._gTint, transform._bTint);
} else {
tglBlitRotoScale<kDisableBlending, kDisableColoring, kFlipVertical, kFlipHorizontal>(transform._destinationRectangle.left,
transform._destinationRectangle.top, transform._destinationRectangle.width(), transform._destinationRectangle.height(),
transform._sourceRectangle.left, transform._sourceRectangle.top, transform._sourceRectangle.width(),
transform._sourceRectangle.height(), transform._rotation, transform._originX, transform._originY, transform._aTint,
transform._rTint, transform._gTint, transform._bTint);
}
}
}
int getWidth() const { return _surface.w; }
int getHeight() const { return _surface.h; }
void incRefCount() { _refcount++; }
void dispose() { if (--_refcount == 0) _isDisposed = true; }
bool isDisposed() const { return _isDisposed; }
bool isOpaque() const { return _opaque; }
private:
bool _isDisposed;
bool _binaryTransparent;
bool _opaque;
bool _zBuffer;
Common::Array<Line> _lines;
Graphics::Surface _surface;
int _version;
int _refcount;
};
} // end of namespace TinyGL
void tglGetBlitImageSize(TinyGL::BlitImage *blitImage, int &width, int &height) {
width = blitImage->getWidth();
height = blitImage->getHeight();
}
void tglIncBlitImageRef(TinyGL::BlitImage *blitImage) {
blitImage->incRefCount();
}
int tglGetBlitImageVersion(TinyGL::BlitImage *blitImage) {
return blitImage->getVersion();
}
TinyGL::BlitImage *tglGenBlitImage() {
TinyGL::GLContext *c = TinyGL::gl_get_context();
TinyGL::BlitImage *image = new TinyGL::BlitImage();
c->_blitImages.push_back(image);
return image;
}
void tglUploadBlitImage(TinyGL::BlitImage *blitImage, const Graphics::Surface& surface, uint32 colorKey, bool applyColorKey, bool zBuffer) {
if (blitImage != nullptr) {
blitImage->loadData(surface, colorKey, applyColorKey, zBuffer);
}
}
void tglDeleteBlitImage(TinyGL::BlitImage *blitImage) {
if (blitImage != nullptr) {
blitImage->dispose();
}
}
namespace TinyGL {
void BlitImage::tglBlitOpaque(int dstX, int dstY, int srcX, int srcY, int srcWidth, int srcHeight) {
GLContext *c = gl_get_context();
int clampWidth, clampHeight;
int width = srcWidth, height = srcHeight;
if (clipBlitImage(c, srcX, srcY, srcWidth, srcHeight, width, height, dstX, dstY, clampWidth, clampHeight) == false)
return;
int fbPitch = c->fb->getPixelBufferPitch();
int fbBpp = c->fb->getPixelBufferBpp();
byte *fbBuf = c->fb->getPixelBuffer() + (dstX * fbBpp) + (dstY * fbPitch);
Graphics::crossBlit(fbBuf, (const byte *)_surface.getBasePtr(srcX, srcY),
fbPitch, _surface.pitch, clampWidth, clampHeight,
c->fb->getPixelFormat(), _surface.format);
}
// This function uses RLE encoding to skip transparent bitmap parts
// This blit only supports tinting but it will fall back to simpleBlit
// if flipping is required (or anything more complex than that, including rotationd and scaling).
template <bool kDisableColoring, bool kDisableBlending, bool kEnableAlphaBlending>
void BlitImage::tglBlitRLE(int dstX, int dstY, int srcX, int srcY, int srcWidth, int srcHeight, float aTint, float rTint, float gTint, float bTint) {
GLContext *c = gl_get_context();
int clampWidth, clampHeight;
int width = srcWidth, height = srcHeight;
if (clipBlitImage(c, srcX, srcY, srcWidth, srcHeight, width, height, dstX, dstY, clampWidth, clampHeight) == false)
return;
if (aTint <= 0.0f)
return;
int fbWidth = c->fb->getPixelBufferWidth();
Graphics::PixelBuffer srcBuf(_surface.format, (byte *)_surface.getPixels());
srcBuf.shiftBy(srcX + (srcY * _surface.w));
Graphics::PixelBuffer dstBuf(c->fb->getPixelFormat(), c->fb->getPixelBuffer());
dstBuf.shiftBy(dstY * fbWidth + dstX);
int kBytesPerPixel = c->fb->getPixelFormat().bytesPerPixel;
uint32 lineIndex = 0;
int maxY = srcY + clampHeight;
int maxX = srcX + clampWidth;
while (lineIndex < _lines.size() && _lines[lineIndex]._y < srcY) {
lineIndex++;
}
if (_opaque) { // This is the degenerate case of RLE where the whole image is affected
for (int y = 0; y < clampHeight; y++) {
if (kDisableColoring) {
memcpy(dstBuf.getRawBuffer(y * fbWidth),
srcBuf.getRawBuffer(y * _surface.w), clampWidth * kBytesPerPixel);
} else {
for(int x = 0; x < clampWidth; x++) {
byte aDst, rDst, gDst, bDst;
srcBuf.getARGBAt(y * _surface.w + x, aDst, rDst, gDst, bDst);
c->fb->writePixel((dstX + x) + (dstY + y) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
}
}
}
} else if (_binaryTransparent || (kDisableBlending || !kEnableAlphaBlending)) { // If bitmap is binary transparent or if we need complex forms of blending (not just alpha) we need to use writePixel, which is slower
while (lineIndex < _lines.size() && _lines[lineIndex]._y < maxY) {
const BlitImage::Line &l = _lines[lineIndex];
if (l._x < maxX && l._x + l._length > srcX) {
int length = l._length;
int skipStart = (l._x < srcX) ? (srcX - l._x) : 0;
length -= skipStart;
int skipEnd = (l._x + l._length > maxX) ? (l._x + l._length - maxX) : 0;
length -= skipEnd;
int xStart = MAX(l._x - srcX, 0);
if (kDisableColoring) {
memcpy(dstBuf.getRawBuffer((l._y - srcY) * fbWidth + xStart),
l._pixels + skipStart * kBytesPerPixel, length * kBytesPerPixel);
} else {
for(int x = xStart; x < xStart + length; x++) {
byte aDst, rDst, gDst, bDst;
srcBuf.getARGBAt((l._y - srcY) * _surface.w + x, aDst, rDst, gDst, bDst);
c->fb->writePixel((dstX + x) + (dstY + (l._y - srcY)) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
}
}
}
lineIndex++;
}
} else { // Otherwise can use setPixel in some cases which speeds up things quite a bit
while (lineIndex < _lines.size() && _lines[lineIndex]._y < maxY) {
const BlitImage::Line &l = _lines[lineIndex];
if (l._x < maxX && l._x + l._length > srcX) {
int length = l._length;
int skipStart = (l._x < srcX) ? (srcX - l._x) : 0;
length -= skipStart;
int skipEnd = (l._x + l._length > maxX) ? (l._x + l._length - maxX) : 0;
length -= skipEnd;
int xStart = MAX(l._x - srcX, 0);
if (kDisableColoring && (kEnableAlphaBlending == false || kDisableBlending)) {
memcpy(dstBuf.getRawBuffer((l._y - srcY) * fbWidth + xStart),
l._pixels + skipStart * kBytesPerPixel, length * kBytesPerPixel);
} else {
for(int x = xStart; x < xStart + length; x++) {
byte aDst, rDst, gDst, bDst;
srcBuf.getARGBAt((l._y - srcY) * _surface.w + x, aDst, rDst, gDst, bDst);
if (kDisableColoring) {
if (aDst != 0xFF) {
c->fb->writePixel((dstX + x) + (dstY + (l._y - srcY)) * fbWidth, aDst, rDst, gDst, bDst);
} else {
dstBuf.setPixelAt(x + (l._y - srcY) * fbWidth, aDst, rDst, gDst, bDst);
}
} else {
c->fb->writePixel((dstX + x) + (dstY + (l._y - srcY)) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
}
}
}
}
lineIndex++;
}
}
}
// This blit function is called when flipping is needed but transformation isn't.
template <bool kDisableBlending, bool kDisableColoring, bool kFlipVertical, bool kFlipHorizontal>
void BlitImage::tglBlitSimple(int dstX, int dstY, int srcX, int srcY, int srcWidth, int srcHeight, float aTint, float rTint, float gTint, float bTint) {
GLContext *c = gl_get_context();
int clampWidth, clampHeight;
int width = srcWidth, height = srcHeight;
if (clipBlitImage(c, srcX, srcY, srcWidth, srcHeight, width, height, dstX, dstY, clampWidth, clampHeight) == false)
return;
Graphics::PixelBuffer srcBuf(_surface.format, (byte *)_surface.getPixels());
if (kFlipVertical) {
srcBuf.shiftBy(((srcHeight - srcY - 1) * _surface.w));
} else {
srcBuf.shiftBy((srcY * _surface.w));
}
Graphics::PixelBuffer dstBuf(c->fb->getPixelFormat(), c->fb->getPixelBuffer());
int fbWidth = c->fb->getPixelBufferWidth();
for (int y = 0; y < clampHeight; y++) {
for (int x = 0; x < clampWidth; ++x) {
byte aDst, rDst, gDst, bDst;
if (kFlipHorizontal) {
srcBuf.getARGBAt(srcX + clampWidth - x, aDst, rDst, gDst, bDst);
} else {
srcBuf.getARGBAt(srcX + x, aDst, rDst, gDst, bDst);
}
// Those branches are needed to favor speed: avoiding writePixel always yield a huge performance boost when blitting images.
if (kDisableColoring) {
if (kDisableBlending && aDst != 0) {
dstBuf.setPixelAt((dstX + x) + (dstY + y) * fbWidth, aDst, rDst, gDst, bDst);
} else {
c->fb->writePixel((dstX + x) + (dstY + y) * fbWidth, aDst, rDst, gDst, bDst);
}
} else {
if (kDisableBlending && aDst * aTint != 0) {
dstBuf.setPixelAt((dstX + x) + (dstY + y) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
} else {
c->fb->writePixel((dstX + x) + (dstY + y) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
}
}
}
if (kFlipVertical) {
srcBuf.shiftBy(-_surface.w);
} else {
srcBuf.shiftBy(_surface.w);
}
}
}
// This function is called when scale is needed: it uses a simple nearest
// filter to scale the blit image before copying it to the screen.
template <bool kDisableBlending, bool kDisableColoring, bool kFlipVertical, bool kFlipHorizontal>
void BlitImage::tglBlitScale(int dstX, int dstY, int width, int height, int srcX, int srcY, int srcWidth, int srcHeight,
float aTint, float rTint, float gTint, float bTint) {
GLContext *c = gl_get_context();
int clampWidth, clampHeight;
if (clipBlitImage(c, srcX, srcY, srcWidth, srcHeight, width, height, dstX, dstY, clampWidth, clampHeight) == false)
return;
Graphics::PixelBuffer srcBuf(_surface.format, (byte *)_surface.getPixels());
srcBuf.shiftBy(srcX + (srcY * _surface.w));
Graphics::PixelBuffer dstBuf(c->fb->getPixelFormat(), c->fb->getPixelBuffer());
int fbWidth = c->fb->getPixelBufferWidth();
for (int y = 0; y < clampHeight; y++) {
for (int x = 0; x < clampWidth; ++x) {
byte aDst, rDst, gDst, bDst;
int xSource, ySource;
if (kFlipVertical) {
ySource = clampHeight - y - 1;
} else {
ySource = y;
}
if (kFlipHorizontal) {
xSource = clampWidth - x - 1;
} else {
xSource = x;
}
srcBuf.getARGBAt(((ySource * srcHeight) / height) * _surface.w + ((xSource * srcWidth) / width), aDst, rDst, gDst, bDst);
if (kDisableColoring) {
if (kDisableBlending && aDst != 0) {
dstBuf.setPixelAt((dstX + x) + (dstY + y) * fbWidth, aDst, rDst, gDst, bDst);
} else {
c->fb->writePixel((dstX + x) + (dstY + y) * fbWidth, aDst, rDst, gDst, bDst);
}
} else {
if (kDisableBlending && aDst * aTint != 0) {
dstBuf.setPixelAt((dstX + x) + (dstY + y) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
} else {
c->fb->writePixel((dstX + x) + (dstY + y) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
}
}
}
}
}
/*
The below two functions are adapted from SDL_rotozoom.c,
taken from SDL_gfx-2.0.18.
Its copyright notice:
=============================================================================
SDL_rotozoom.c: rotozoomer, zoomer and shrinker for 32bit or 8bit surfaces
Copyright (C) 2001-2012 Andreas Schiffler
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source
distribution.
Andreas Schiffler -- aschiffler at ferzkopp dot net
=============================================================================
The functions have been adapted for different structures and coordinate
systems.
*/
template <bool kDisableBlending, bool kDisableColoring, bool kFlipVertical, bool kFlipHorizontal>
void BlitImage::tglBlitRotoScale(int dstX, int dstY, int width, int height, int srcX, int srcY, int srcWidth, int srcHeight, int rotation,
int originX, int originY, float aTint, float rTint, float gTint, float bTint) {
GLContext *c = gl_get_context();
int clampWidth, clampHeight;
if (clipBlitImage(c, srcX, srcY, srcWidth, srcHeight, width, height, dstX, dstY, clampWidth, clampHeight) == false)
return;
Graphics::PixelBuffer srcBuf(_surface.format, (byte *)_surface.getPixels());
srcBuf.shiftBy(srcX + (srcY * _surface.w));
int fbWidth = c->fb->getPixelBufferWidth();
Graphics::PixelBuffer dstBuf(c->fb->getPixelFormat(), c->fb->getPixelBuffer());
// Transform destination rectangle accordingly.
Common::Rect destinationRectangle = rotateRectangle(dstX, dstY, width, height, rotation, originX, originY);
if (dstX + destinationRectangle.width() > fbWidth)
clampWidth = fbWidth - dstX;
else
clampWidth = destinationRectangle.width();
if (dstY + destinationRectangle.height() > c->fb->getPixelBufferHeight())
clampHeight = c->fb->getPixelBufferHeight() - dstY;
else
clampHeight = destinationRectangle.height();
uint32 invAngle = 360 - (rotation % 360);
float invCos = cos(invAngle * (float)M_PI / 180.0f);
float invSin = sin(invAngle * (float)M_PI / 180.0f);
int icosx = (int)(invCos * (65536.0f * srcWidth / width));
int isinx = (int)(invSin * (65536.0f * srcWidth / width));
int icosy = (int)(invCos * (65536.0f * srcHeight / height));
int isiny = (int)(invSin * (65536.0f * srcHeight / height));
int xd = (srcX + originX) << 16;
int yd = (srcY + originY) << 16;
int cx = originX * ((float)width / srcWidth);
int cy = originY * ((float)height / srcHeight);
int ax = -icosx * cx;
int ay = -isiny * cx;
int sw = width - 1;
int sh = height - 1;
for (int y = 0; y < clampHeight; y++) {
int t = cy - y;
int sdx = ax + (isinx * t) + xd;
int sdy = ay - (icosy * t) + yd;
for (int x = 0; x < clampWidth; ++x) {
byte aDst, rDst, gDst, bDst;
int dx = (sdx >> 16);
int dy = (sdy >> 16);
if (kFlipHorizontal) {
dx = sw - dx;
}
if (kFlipVertical) {
dy = sh - dy;
}
if ((dx >= 0) && (dy >= 0) && (dx < srcWidth) && (dy < srcHeight)) {
srcBuf.getARGBAt(dy * _surface.w + dx, aDst, rDst, gDst, bDst);
if (kDisableColoring) {
if (kDisableBlending && aDst != 0) {
dstBuf.setPixelAt((dstX + x) + (dstY + y) * fbWidth, aDst, rDst, gDst, bDst);
} else {
c->fb->writePixel((dstX + x) + (dstY + y) * fbWidth, aDst, rDst, gDst, bDst);
}
} else {
if (kDisableBlending && aDst * aTint != 0) {
dstBuf.setPixelAt((dstX + x) + (dstY + y) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
} else {
c->fb->writePixel((dstX + x) + (dstY + y) * fbWidth, aDst * aTint, rDst * rTint, gDst * gTint, bDst * bTint);
}
}
}
sdx += icosx;
sdy += isiny;
}
}
}
} // end of namespace TinyGL
void tglBlit(TinyGL::BlitImage *blitImage, int x, int y) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
TinyGL::BlitTransform transform(x, y);
c->issueDrawCall(new TinyGL::BlittingDrawCall(blitImage, transform, TinyGL::BlittingDrawCall::BlitMode_Regular));
}
void tglBlit(TinyGL::BlitImage *blitImage, const TinyGL::BlitTransform &transform) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
c->issueDrawCall(new TinyGL::BlittingDrawCall(blitImage, transform, TinyGL::BlittingDrawCall::BlitMode_Regular));
}
void tglBlitFast(TinyGL::BlitImage *blitImage, int x, int y) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
TinyGL::BlitTransform transform(x, y);
c->issueDrawCall(new TinyGL::BlittingDrawCall(blitImage, transform, TinyGL::BlittingDrawCall::BlitMode_Fast));
}
void tglBlitZBuffer(TinyGL::BlitImage *blitImage, int x, int y) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
TinyGL::BlitTransform transform(x, y);
c->issueDrawCall(new TinyGL::BlittingDrawCall(blitImage, transform, TinyGL::BlittingDrawCall::BlitMode_ZBuffer));
}
namespace TinyGL {
namespace Internal {
template <bool kEnableAlphaBlending, bool kEnableOpaqueBlit, bool kDisableColor, bool kDisableTransform, bool kDisableBlend>
void tglBlit(BlitImage *blitImage, const BlitTransform &transform) {
if (transform._flipHorizontally) {
if (transform._flipVertically) {
blitImage->tglBlitGeneric<kDisableBlend, kDisableColor, kDisableTransform, true, true, kEnableAlphaBlending, kEnableOpaqueBlit>(transform);
} else {
blitImage->tglBlitGeneric<kDisableBlend, kDisableColor, kDisableTransform, false, true, kEnableAlphaBlending, kEnableOpaqueBlit>(transform);
}
} else if (transform._flipVertically) {
blitImage->tglBlitGeneric<kDisableBlend, kDisableColor, kDisableTransform, true, false, kEnableAlphaBlending, kEnableOpaqueBlit>(transform);
} else {
blitImage->tglBlitGeneric<kDisableBlend, kDisableColor, kDisableTransform, false, false, kEnableAlphaBlending, kEnableOpaqueBlit>(transform);
}
}
template <bool kEnableAlphaBlending, bool kEnableOpaqueBlit, bool kDisableColor, bool kDisableTransform>
void tglBlit(BlitImage *blitImage, const BlitTransform &transform, bool disableBlend) {
if (disableBlend) {
tglBlit<kEnableAlphaBlending, kEnableOpaqueBlit, kDisableColor, kDisableTransform, true>(blitImage, transform);
} else {
tglBlit<kEnableAlphaBlending, kEnableOpaqueBlit, kDisableColor, kDisableTransform, false>(blitImage, transform);
}
}
template <bool kEnableAlphaBlending, bool kEnableOpaqueBlit, bool kDisableColor>
void tglBlit(BlitImage *blitImage, const BlitTransform &transform, bool disableTransform, bool disableBlend) {
if (disableTransform) {
tglBlit<kEnableAlphaBlending, kEnableOpaqueBlit, kDisableColor, true>(blitImage, transform, disableBlend);
} else {
tglBlit<kEnableAlphaBlending, kEnableOpaqueBlit, kDisableColor, false>(blitImage, transform, disableBlend);
}
}
template <bool kEnableAlphaBlending, bool kEnableOpaqueBlit>
void tglBlit(BlitImage *blitImage, const BlitTransform &transform, bool disableColor, bool disableTransform, bool disableBlend) {
if (disableColor) {
tglBlit<kEnableAlphaBlending, kEnableOpaqueBlit, true>(blitImage, transform, disableTransform, disableBlend);
} else {
tglBlit<kEnableAlphaBlending, kEnableOpaqueBlit, false>(blitImage, transform, disableTransform, disableBlend);
}
}
template <bool kEnableAlphaBlending>
void tglBlit(BlitImage *blitImage, const BlitTransform &transform, bool enableOpaqueBlit, bool disableColor, bool disableTransform, bool disableBlend) {
if (enableOpaqueBlit) {
tglBlit<kEnableAlphaBlending, true>(blitImage, transform, disableColor, disableTransform, disableBlend);
} else {
tglBlit<kEnableAlphaBlending, false>(blitImage, transform, disableColor, disableTransform, disableBlend);
}
}
void tglBlit(BlitImage *blitImage, const BlitTransform &transform) {
GLContext *c = gl_get_context();
bool disableColor = transform._aTint == 1.0f && transform._bTint == 1.0f && transform._gTint == 1.0f && transform._rTint == 1.0f;
bool disableTransform = transform._destinationRectangle.width() == 0 && transform._destinationRectangle.height() == 0 && transform._rotation == 0;
bool disableBlend = c->blending_enabled == false;
bool enableAlphaBlending = c->source_blending_factor == TGL_SRC_ALPHA && c->destination_blending_factor == TGL_ONE_MINUS_SRC_ALPHA;
bool enableOpaqueBlit = blitImage->isOpaque()
&& (c->source_blending_factor == TGL_ONE || c->source_blending_factor == TGL_SRC_ALPHA)
&& (c->destination_blending_factor == TGL_ZERO || c->destination_blending_factor == TGL_ONE_MINUS_SRC_ALPHA);
if (enableAlphaBlending) {
tglBlit<true>(blitImage, transform, enableOpaqueBlit, disableColor, disableTransform, disableBlend);
} else {
tglBlit<false>(blitImage, transform, enableOpaqueBlit, disableColor, disableTransform, disableBlend);
}
}
void tglBlitFast(BlitImage *blitImage, int x, int y) {
BlitTransform transform(x, y);
if (blitImage->isOpaque()) {
blitImage->tglBlitGeneric<true, true, true, false, false, false, true>(transform);
} else {
blitImage->tglBlitGeneric<true, true, true, false, false, false, false>(transform);
}
}
void tglBlitZBuffer(BlitImage *blitImage, int x, int y) {
blitImage->tglBlitZBuffer(x, y);
}
void tglCleanupImages() {
GLContext *c = gl_get_context();
Common::List<BlitImage *>::iterator it = c->_blitImages.begin();
while (it != c->_blitImages.end()) {
if ((*it)->isDisposed()) {
delete (*it);
it = c->_blitImages.erase(it);
} else {
++it;
}
}
}
} // end of namespace Internal
Common::Point transformPoint(float x, float y, int rotation) {
float rotateRad = rotation * (float)M_PI / 180.0f;
Common::Point newPoint;
newPoint.x = x * cos(rotateRad) - y * sin(rotateRad);
newPoint.y = x * sin(rotateRad) + y * cos(rotateRad);
return newPoint;
}
Common::Rect rotateRectangle(int x, int y, int width, int height, int rotation, int originX, int originY) {
Common::Point nw, ne, sw, se;
nw = transformPoint(x - originX, y - originY, rotation);
ne = transformPoint(x + width - originX, y - originY, rotation);
sw = transformPoint(x + width - originX, y + height - originY, rotation);
se = transformPoint(x - originX, y + height - originY, rotation);
float top = MIN(nw.y, MIN(ne.y, MIN(sw.y, se.y)));
float bottom = MAX(nw.y, MAX(ne.y, MAX(sw.y, se.y)));
float left = MIN(nw.x, MIN(ne.x, MIN(sw.x, se.x)));
float right = MAX(nw.x, MAX(ne.x, MAX(sw.x, se.x)));
Common::Rect res;
res.top = (int32)(floor(top)) + originY;
res.bottom = (int32)(ceil(bottom)) + originY;
res.left = (int32)(floor(left)) + originX;
res.right = (int32)(ceil(right)) + originX;
return res;
}
} // end of namespace TinyGL

52
graphics/tinygl/zblit.h Normal file
View File

@@ -0,0 +1,52 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_TINYGL_ZBLIT_H_
#define GRAPHICS_TINYGL_ZBLIT_H_
#include "common/rect.h"
#include "graphics/surface.h"
#include "graphics/tinygl/zblit_public.h"
namespace TinyGL {
struct BlitImage;
namespace Internal {
/**
@brief Performs a cleanup of disposed blit images.
*/
void tglCleanupImages(); // This function checks if any blit image is to be cleaned up and deletes it.
// Documentation for those is the same as the one before, only those function are the one that actually execute the correct code path.
void tglBlit(BlitImage *blitImage, const BlitTransform &transform);
// Disables blending, transforms and tinting.
void tglBlitFast(BlitImage *blitImage, int x, int y);
void tglBlitZBuffer(BlitImage *blitImage, int x, int y);
} // end of namespace Internal
} // end of namespace TinyGL
#endif // GRAPHICS_TINYGL_ZBLIT_H_

View File

@@ -0,0 +1,157 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_TINYGL_ZBLIT_PUBLIC_H
#define GRAPHICS_TINYGL_ZBLIT_PUBLIC_H
#include "common/rect.h"
#include "graphics/surface.h"
namespace TinyGL {
struct BlitTransform {
BlitTransform(int dstX, int dstY) : _rotation(0), _originX(0), _originY(0), _aTint(1.0f),
_rTint(1.0f), _gTint(1.0f), _bTint(1.0), _flipHorizontally(false),
_flipVertically(false) {
_destinationRectangle.translate(dstX, dstY);
}
void sourceRectangle(int srcX, int srcY, int srcWidth, int srcHeight) {
_sourceRectangle.left = srcX;
_sourceRectangle.top = srcY;
_sourceRectangle.setWidth(srcWidth);
_sourceRectangle.setHeight(srcHeight);
}
void tint(float aTint, float rTint = 1.0f, float gTint = 1.0f, float bTint = 1.0f) {
_aTint = aTint;
_rTint = rTint;
_gTint = gTint;
_bTint = bTint;
}
void scale(int width, int height) {
_destinationRectangle.setWidth(width);
_destinationRectangle.setHeight(height);
}
void rotate(int rotation, int originX, int originY) {
_rotation = rotation;
_originX = originX;
_originY = originY;
}
void flip(bool verticalFlip, bool horizontalFlip) {
_flipVertically = verticalFlip;
_flipHorizontally = horizontalFlip;
}
bool operator==(const BlitTransform &other) const {
return
_sourceRectangle == other._sourceRectangle && _destinationRectangle == other._destinationRectangle &&
_rotation == other._rotation && _originX == other._originX && _originY == other._originY &&
_aTint == other._aTint && _rTint == other._rTint && _gTint == other._gTint && _bTint == other._bTint &&
_flipHorizontally == other._flipHorizontally && _flipVertically == other._flipVertically;
}
Common::Rect _sourceRectangle;
Common::Rect _destinationRectangle;
int _rotation;
int _originX, _originY;
float _aTint, _rTint, _gTint, _bTint;
bool _flipHorizontally, _flipVertically;
};
struct BlitImage;
} // end of namespace TinyGL
/**
@brief Generates a new blit image.
@return returns an opaque pointer to the blit image.
*/
TinyGL::BlitImage *tglGenBlitImage();
/**
@brief Copies a surface data into the provided blit image.
@param pointer to the blit image.
@param reference to the surface that's being copied
@param color key value for alpha color keying
@param boolean that enables alpha color keying
*/
void tglUploadBlitImage(TinyGL::BlitImage *blitImage, const Graphics::Surface &surface, uint32 colorKey, bool applyColorKey, bool zBuffer = false);
/**
@brief Destroys an instance of blit image.
@param pointer to the blit image.
*/
void tglDeleteBlitImage(TinyGL::BlitImage *blitImage);
/**
@brief Getter for current blit image width and height
@param pointer to the blit image.
@param reference to the width variable
@param reference to the height variable
*/
void tglGetBlitImageSize(TinyGL::BlitImage *blitImage, int &width, int &height);
/**
@brief Provides a way to check if the image has been updated.
@param pointer to the blit image.
@param boolean that enables alpha color keying
*/
int tglGetBlitImageVersion(TinyGL::BlitImage *blitImage);
/**
@brief Blits an image to the color buffer.
@param pointer to the blit image.
@param blit transform information.
*/
void tglBlit(TinyGL::BlitImage *blitImage, const TinyGL::BlitTransform &transform);
/**
@brief Blits an image to the color buffer.
@param pointer to the blit image.
@param x destination coordinate.
@param y destination coordinate.
*/
void tglBlit(TinyGL::BlitImage *blitImage, int x, int y);
/**
@brief Blits an image to the color buffer without performinc any type of blending, image transformation or tinting.
@param pointer to the blit image.
@param x destination coordinate.
@param y destination coordinate.
*/
void tglBlitFast(TinyGL::BlitImage *blitImage, int x, int y);
/**
@brief Blits an image to the depth buffer.
@param pointer to the blit image.
@param x destination coordinate.
@param y destination coordinate.
*/
void tglBlitZBuffer(TinyGL::BlitImage *blitImage, int x, int y);
void tglIncBlitImageRef(TinyGL::BlitImage *blitImage);
#endif // GRAPHICS_TINYGL_ZBLIT_PUBLIC_H

487
graphics/tinygl/zbuffer.cpp Normal file
View File

@@ -0,0 +1,487 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
// Z buffer: 16,32 bits Z / 16 bits color
#include "common/scummsys.h"
#include "common/endian.h"
#include "common/memory.h"
#include "graphics/tinygl/zbuffer.h"
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
FrameBuffer::FrameBuffer(int width, int height, const Graphics::PixelFormat &format, bool enableStencilBuffer) {
_pbufWidth = width;
_pbufHeight = height;
_pbufFormat = format;
_pbufBpp = _pbufFormat.bytesPerPixel;
_pbufPitch = (_pbufWidth * _pbufBpp + 3) & ~3;
_pbuf = (byte *)gl_zalloc(_pbufHeight * _pbufPitch * sizeof(byte));
_zbuf = (uint *)gl_zalloc(_pbufWidth * _pbufHeight * sizeof(uint));
if (enableStencilBuffer)
_sbuf = (byte *)gl_zalloc(_pbufWidth * _pbufHeight * sizeof(byte));
else
_sbuf = nullptr;
_offscreenBuffer.pbuf = _pbuf;
_offscreenBuffer.zbuf = _zbuf;
_currentTexture = nullptr;
_clippingEnabled = false;
}
FrameBuffer::~FrameBuffer() {
gl_free(_pbuf);
gl_free(_zbuf);
if (_sbuf)
gl_free(_sbuf);
}
Buffer *FrameBuffer::genOffscreenBuffer() {
Buffer *buf = (Buffer *)gl_malloc(sizeof(Buffer));
buf->pbuf = (byte *)gl_zalloc(_pbufHeight * _pbufPitch);
buf->zbuf = (uint *)gl_zalloc(_pbufWidth * _pbufHeight * sizeof(uint));
return buf;
}
void FrameBuffer::delOffscreenBuffer(Buffer *buf) {
gl_free(buf->pbuf);
gl_free(buf->zbuf);
gl_free(buf);
}
void FrameBuffer::clear(int clearZ, int z, int clearColor, int r, int g, int b,
bool clearStencil, int stencilValue) {
if (_clippingEnabled) {
clearRegion(_clipRectangle.left, _clipRectangle.top,
_clipRectangle.width(), _clipRectangle.height(),
clearZ, z, clearColor, r, g, b, clearStencil, stencilValue);
return;
}
if (clearZ) {
const uint8 *zc = (const uint8 *)&z;
uint i;
for (i = 1; i < sizeof(z) && zc[0] == zc[i]; i++) { ; }
if (i == sizeof(z)) {
// All "z" bytes are identical, use memset (fast)
memset(_zbuf, zc[0], sizeof(uint) * _pbufWidth * _pbufHeight);
} else {
// Cannot use memset, use a variant working on integers (possibly slower)
Common::memset32((uint32 *)_zbuf, z, _pbufWidth * _pbufHeight);
}
}
if (clearColor) {
byte *pp = _pbuf;
uint32 color = _pbufFormat.RGBToColor(r, g, b);
const uint8 *colorc = (uint8 *)&color;
uint i;
for (i = 1; i < sizeof(color) && colorc[0] == colorc[i]; i++) { ; }
if (i == sizeof(color)) {
// All "color" bytes are identical, use memset (fast)
memset(pp, colorc[0], _pbufPitch * _pbufHeight);
} else {
// Cannot use memset, use a variant working on shorts/ints (possibly slower)
switch(_pbufBpp) {
case 2:
Common::memset16((uint16 *)pp, color, _pbufWidth * _pbufHeight);
break;
case 4:
Common::memset32((uint32 *)pp, color, _pbufWidth * _pbufHeight);
break;
default:
error("Unsupported pixel size %i", _pbufBpp);
}
}
}
if (_sbuf && clearStencil) {
memset(_sbuf, stencilValue, _pbufWidth * _pbufHeight);
}
}
void FrameBuffer::clearRegion(int x, int y, int w, int h, bool clearZ, int z,
bool clearColor, int r, int g, int b, bool clearStencil, int stencilValue) {
if (clearZ) {
int height = h;
uint *zbuf = _zbuf + (y * _pbufWidth) + x;
const uint8 *zc = (const uint8 *)&z;
uint i;
for (i = 1; i < sizeof(z) && zc[0] == zc[i]; i++) { ; }
if (i == sizeof(z)) {
// All "z" bytes are identical, use memset (fast)
while (height--) {
memset(zbuf, zc[0], sizeof(*zbuf) * w);
zbuf += _pbufWidth;
}
} else {
// Cannot use memset, use a variant working on integers (possibly slower)
while (height--) {
Common::memset32((uint32 *)zbuf, z, w);
zbuf += _pbufWidth;
}
}
}
if (clearColor) {
int height = h;
byte *pp = _pbuf + y * _pbufPitch + x * _pbufBpp;
uint32 color = _pbufFormat.RGBToColor(r, g, b);
const uint8 *colorc = (uint8 *)&color;
uint i;
for (i = 1; i < sizeof(color) && colorc[0] == colorc[i]; i++) { ; }
if (i == sizeof(color)) {
// All "color" bytes are identical, use memset (fast)
while (height--) {
memset(pp, colorc[0], _pbufBpp * w);
pp += _pbufPitch;
}
} else {
// Cannot use memset, use a variant working on shorts/ints (possibly slower)
while (height--) {
switch(_pbufBpp) {
case 2:
Common::memset16((uint16 *)pp, color, w);
break;
case 4:
Common::memset32((uint32 *)pp, color, w);
break;
default:
error("Unsupported pixel size %i", _pbufBpp);
}
pp += _pbufPitch;
}
}
}
if (_sbuf && clearStencil) {
byte *pp = _sbuf + y * _pbufWidth + x;
for (int i = y; i < y + h; i++) {
memset(pp, stencilValue, w);
pp += _pbufWidth;
}
}
}
inline static void blitPixel(uint8 offset, uint *from_z, uint *to_z, uint z_length, byte *from_color, byte *to_color, uint color_length) {
const uint d = from_z[offset];
if (d > to_z[offset]) {
memcpy(to_color + offset, from_color + offset, color_length);
memcpy(to_z + offset, &d, z_length);
}
}
void FrameBuffer::blitOffscreenBuffer(Buffer *buf) {
// TODO: could be faster, probably.
#define UNROLL_COUNT 16
if (buf->used) {
const int pixel_bytes = _pbufBpp;
const int unrolled_pixel_bytes = pixel_bytes * UNROLL_COUNT;
byte *to = _pbuf;
byte *from = buf->pbuf;
uint *to_z = _zbuf;
uint *from_z = buf->zbuf;
int count = _pbufWidth * _pbufHeight;
while (count >= UNROLL_COUNT) {
blitPixel(0x0, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x1, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x2, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x3, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x4, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x5, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x6, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x7, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x8, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0x9, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xA, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xB, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xC, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xD, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xE, from_z, to_z, sizeof(int), from, to, pixel_bytes);
blitPixel(0xF, from_z, to_z, sizeof(int), from, to, pixel_bytes);
count -= UNROLL_COUNT;
to += unrolled_pixel_bytes;
from += unrolled_pixel_bytes;
to_z += UNROLL_COUNT;
}
switch (count) {
case 0xF: blitPixel(0xE, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xE: blitPixel(0xD, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xD: blitPixel(0xC, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xC: blitPixel(0xB, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xB: blitPixel(0xA, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0xA: blitPixel(0x9, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x9: blitPixel(0x8, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x8: blitPixel(0x7, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x7: blitPixel(0x6, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x6: blitPixel(0x5, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x5: blitPixel(0x4, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x4: blitPixel(0x3, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x3: blitPixel(0x2, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x2: blitPixel(0x1, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x1: blitPixel(0x0, from_z, to_z, sizeof(int), from, to, pixel_bytes); // fall through
case 0x0: break;
}
}
#undef UNROLL_COUNT
}
void FrameBuffer::selectOffscreenBuffer(Buffer *buf) {
if (buf) {
_pbuf = buf->pbuf;
_zbuf = buf->zbuf;
buf->used = true;
} else {
_pbuf = _offscreenBuffer.pbuf;
_zbuf = _offscreenBuffer.zbuf;
}
}
void FrameBuffer::clearOffscreenBuffer(Buffer *buf) {
memset(buf->pbuf, 0, _pbufHeight * _pbufPitch);
memset(buf->zbuf, 0, _pbufHeight * _pbufWidth * sizeof(uint));
buf->used = false;
}
void getSurfaceRef(Graphics::Surface &surface) {
GLContext *c = gl_get_context();
assert(c->fb);
c->fb->getSurfaceRef(surface);
}
Graphics::Surface *copyFromFrameBuffer(const Graphics::PixelFormat &dstFormat) {
GLContext *c = gl_get_context();
assert(c->fb);
return c->fb->copyFromFrameBuffer(dstFormat);
}
static byte satAdd(byte a, byte b) {
// from: https://web.archive.org/web/20190213215419/https://locklessinc.com/articles/sat_arithmetic/
byte r = a + b;
return (byte)(r | -(r < a));
}
static byte sat16_to_8(uint32 x) {
x = (x + 128) >> 8; // rounding 16 to 8
return (byte)(x | -!!(x >> 8)); // branchfree saturation
}
static byte fpMul(byte a, byte b) {
// from: https://community.khronos.org/t/precision-curiosity-1-255-or-1-256/40539/11
// correct would be (a*b)/255 but that is slow, instead we use (a*b) * 257/256 / 256
// this also implicitly saturates
uint32 r = a * b;
return (byte)((r + (r >> 8) + 127) >> 8);
}
void FrameBuffer::applyTextureEnvironment(
int internalformat,
uint previousA, uint previousR, uint previousG, uint previousB,
byte &texA, byte &texR, byte &texG, byte &texB)
{
// summary notation is used from https://registry.khronos.org/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml
// previousARGB is still in 16bit fixed-point format
// texARGB is both input and output
// GL_RGB/GL_RGBA might be identical as TexelBuffer returns As=1
struct Arg {
byte a, r, g, b;
};
const auto getCombineArg = [&](const GLTextureEnvArgument &mode) -> Arg {
Arg op = {}, opColor = {};
// Source values
switch (mode.sourceRGB) {
case TGL_TEXTURE:
opColor.a = texA;
opColor.r = texR;
opColor.g = texG;
opColor.b = texB;
break;
case TGL_PRIMARY_COLOR:
opColor.a = sat16_to_8(previousA);
opColor.r = sat16_to_8(previousR);
opColor.g = sat16_to_8(previousG);
opColor.b = sat16_to_8(previousB);
break;
case TGL_CONSTANT:
opColor.a = _textureEnv->constA;
opColor.r = _textureEnv->constR;
opColor.g = _textureEnv->constG;
opColor.b = _textureEnv->constB;
break;
default:
assert(false && "Invalid texture environment arg color source");
break;
}
switch (mode.sourceAlpha) {
case TGL_TEXTURE:
op.a = texA;
break;
case TGL_PRIMARY_COLOR:
op.a = sat16_to_8(previousA);
break;
case TGL_CONSTANT:
op.a = _textureEnv->constA;
break;
default:
assert(false && "Invalid texture environment arg alpha source");
break;
}
// Operands
switch (mode.operandRGB) {
case TGL_SRC_COLOR:
op.r = opColor.r; // intermediate values were necessary for operandRGB == TGL_SRC_ALPHA
op.g = opColor.g;
op.b = opColor.b;
break;
case TGL_ONE_MINUS_SRC_COLOR:
op.r = 255 - opColor.r;
op.g = 255 - opColor.g;
op.b = 255 - opColor.b;
break;
case TGL_SRC_ALPHA:
op.r = op.g = op.b = opColor.a;
break;
default:
assert(false && "Invalid texture environment arg color operand");
break;
}
switch (mode.operandAlpha) {
case TGL_SRC_ALPHA:
break;
case TGL_ONE_MINUS_SRC_ALPHA:
op.a = 255 - op.a;
break;
default:
assert(false && "Invalid texture environment arg alpha operand");
break;
}
return op;
};
switch (_textureEnv->envMode) {
case TGL_REPLACE:
// GL_RGB: Cs | Ap
// GL_RGBA: Cs | As
texA = internalformat == TGL_RGBA ? texA : sat16_to_8(previousA);
break;
case TGL_MODULATE:
{
// GL_RGB: CpCs | Ap
// GL_RGBA: CpCs | ApAs
applyModulation(previousA, previousR, previousG, previousB, texA, texR, texG, texB);
break;
}
case TGL_DECAL:
{
// GL_RGB: Cs | Ap
// GL_RGBA: Cp(1-As) + CsAs | Ap
texR = satAdd(fpMul(sat16_to_8(previousR), 255 - texA), fpMul(texR, texA));
texG = satAdd(fpMul(sat16_to_8(previousG), 255 - texA), fpMul(texG, texA));
texB = satAdd(fpMul(sat16_to_8(previousB), 255 - texA), fpMul(texB, texA));
texA = sat16_to_8(previousA);
break;
}
case TGL_ADD:
{
// GL_RGB: Cp + Cs | Ap
// GL_RGBA: Cp + Cs | ApAs
texA = fpMul(sat16_to_8(previousA), texA);
texR = satAdd(sat16_to_8(previousR), texR);
texG = satAdd(sat16_to_8(previousG), texG);
texB = satAdd(sat16_to_8(previousB), texB);
break;
}
case TGL_BLEND:
{
// GL_RGB: Cp(1-Cs) + CcCs | Ap
// GL_RGBA: Cp(1-Cs) + CcCs | ApAs
texA = fpMul(sat16_to_8(previousA), texA);
texR = satAdd(fpMul(sat16_to_8(previousR), 255 - texR), fpMul(_textureEnv->constR, texR));
texG = satAdd(fpMul(sat16_to_8(previousG), 255 - texG), fpMul(_textureEnv->constG, texG));
texB = satAdd(fpMul(sat16_to_8(previousB), 255 - texB), fpMul(_textureEnv->constB, texB));
break;
}
case TGL_COMBINE:
{
Arg arg0 = getCombineArg(_textureEnv->arg0);
Arg arg1 = getCombineArg(_textureEnv->arg1);
switch (_textureEnv->combineRGB) {
case TGL_REPLACE:
texR = arg0.r;
texG = arg0.g;
texB = arg0.b;
break;
case TGL_MODULATE:
texR = fpMul(arg0.r, arg1.r);
texG = fpMul(arg0.g, arg1.g);
texB = fpMul(arg0.b, arg1.b);
break;
case TGL_ADD:
texR = satAdd(arg0.r, arg1.r);
texG = satAdd(arg0.g, arg1.g);
texB = satAdd(arg0.b, arg1.b);
break;
default:
assert(false && "Invalid texture environment color combine");
break;
}
switch (_textureEnv->combineAlpha) {
case TGL_REPLACE:
texA = arg0.a;
break;
case TGL_MODULATE:
texA = fpMul(arg0.a, arg1.a);
break;
case TGL_ADD:
texA = satAdd(arg0.a, arg1.a);
break;
default:
assert(false && "Invalid texture environment alpha combine");
}
break;
}
default:
assert(false && "Invalid texture environment mode");
break;
}
}
void FrameBuffer::applyModulation(
uint previousA, uint previousR, uint previousG, uint previousB,
byte &texA, byte &texR, byte &texG, byte &texB) {
texA = fpMul(sat16_to_8(previousA), texA);
texR = fpMul(sat16_to_8(previousR), texR);
texG = fpMul(sat16_to_8(previousG), texG);
texB = fpMul(sat16_to_8(previousB), texB);
}
} // end of namespace TinyGL

870
graphics/tinygl/zbuffer.h Normal file
View File

@@ -0,0 +1,870 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#ifndef GRAPHICS_TINYGL_ZBUFFER_H_
#define GRAPHICS_TINYGL_ZBUFFER_H_
#include "graphics/surface.h"
#include "graphics/tinygl/texelbuffer.h"
#include "graphics/tinygl/gl.h"
#include "common/rect.h"
#include "common/textconsole.h"
namespace TinyGL {
// Z buffer
#define ZB_Z_BITS 16
#define ZB_FOG_BITS 16
#define ZB_FOG_MAX ( (1 << ZB_FOG_BITS) - 1 )
#define ZB_POINT_Z_FRAC_BITS 14
#define ZB_POINT_ST_FRAC_BITS 14
#define ZB_POINT_ST_FRAC_SHIFT (ZB_POINT_ST_FRAC_BITS - 1)
#define ZB_POINT_ST_MAX ( (_textureSize << ZB_POINT_ST_FRAC_BITS) - 1 )
#define ZB_POINT_RED_BITS 16
#define ZB_POINT_RED_FRAC_BITS 8
#define ZB_POINT_RED_FRAC_SHIFT (ZB_POINT_RED_FRAC_BITS - 1)
#define ZB_POINT_RED_MAX ( (1 << ZB_POINT_RED_BITS) - 1 )
#define ZB_POINT_GREEN_BITS 16
#define ZB_POINT_GREEN_FRAC_BITS 8
#define ZB_POINT_GREEN_FRAC_SHIFT (ZB_POINT_GREEN_FRAC_BITS - 1)
#define ZB_POINT_GREEN_MAX ( (1 << ZB_POINT_GREEN_BITS) - 1 )
#define ZB_POINT_BLUE_BITS 16
#define ZB_POINT_BLUE_FRAC_BITS 8
#define ZB_POINT_BLUE_FRAC_SHIFT (ZB_POINT_BLUE_FRAC_BITS - 1)
#define ZB_POINT_BLUE_MAX ( (1 << ZB_POINT_BLUE_BITS) - 1 )
#define ZB_POINT_ALPHA_BITS 16
#define ZB_POINT_ALPHA_FRAC_BITS 8
#define ZB_POINT_ALPHA_FRAC_SHIFT (ZB_POINT_ALPHA_FRAC_BITS - 1)
#define ZB_POINT_ALPHA_MAX ( (1 << ZB_POINT_ALPHA_BITS) - 1 )
#define RGB_TO_PIXEL(r, g, b) _pbufFormat.ARGBToColor(255, r, g, b) // Default to 255 alpha aka solid colour.
static const int DRAW_DEPTH_ONLY = 0;
static const int DRAW_FLAT = 1;
static const int DRAW_SMOOTH = 2;
struct GLTextureEnv; // defined in zdirtyrect.h
struct Buffer {
byte *pbuf;
uint *zbuf;
bool used;
};
struct ZBufferPoint {
int x, y, z; // integer coordinates in the zbuffer
int s, t; // coordinates for the mapping
int r, g, b, a; // color indexes
float sz, tz; // temporary coordinates for mapping
int f; // fog factor
bool operator==(const ZBufferPoint &other) const {
return
x == other.x &&
y == other.y &&
z == other.z &&
s == other.s &&
t == other.t &&
r == other.r &&
g == other.g &&
b == other.b &&
a == other.a;
}
};
struct FrameBuffer {
FrameBuffer(int width, int height, const Graphics::PixelFormat &format, bool enableStencilBuffer);
~FrameBuffer();
Graphics::PixelFormat getPixelFormat() {
return _pbufFormat;
}
byte *getPixelBuffer() {
return _pbuf;
}
int getPixelBufferWidth() {
return _pbufWidth;
}
int getPixelBufferHeight() {
return _pbufHeight;
}
int getPixelBufferPitch() {
return _pbufPitch;
}
int getPixelBufferBpp() {
return _pbufBpp;
}
const uint *getZBuffer() {
return _zbuf;
}
Graphics::Surface *copyFromFrameBuffer(const Graphics::PixelFormat &dstFormat) {
Graphics::Surface tmp;
tmp.init(_pbufWidth, _pbufHeight, _pbufPitch, _pbuf, _pbufFormat);
return tmp.convertTo(dstFormat);
}
void getSurfaceRef(Graphics::Surface &surface) {
surface.init(_pbufWidth, _pbufHeight, _pbufPitch, _pbuf, _pbufFormat);
}
private:
FORCEINLINE void setPixelAt(int pixel, uint32 value) {
switch (_pbufBpp) {
case 2:
((uint16 *) _pbuf)[pixel] = value;
return;
case 3:
pixel *= 3;
#if defined(SCUMM_BIG_ENDIAN)
_pbuf[pixel + 0] = (value >> 16) & 0xFF;
_pbuf[pixel + 1] = (value >> 8) & 0xFF;
_pbuf[pixel + 2] = value & 0xFF;
#elif defined(SCUMM_LITTLE_ENDIAN)
_pbuf[pixel + 0] = value & 0xFF;
_pbuf[pixel + 1] = (value >> 8) & 0xFF;
_pbuf[pixel + 2] = (value >> 16) & 0xFF;
#endif
return;
case 4:
((uint32 *) _pbuf)[pixel] = value;
return;
}
error("setPixelAt: Unhandled bytesPerPixel %d", int(_pbufBpp));
}
FORCEINLINE uint32 getPixelAt(int i) const {
switch (_pbufBpp) {
case 2:
return ((uint16 *) _pbuf)[i];
case 3:
i *= 3;
#if defined(SCUMM_BIG_ENDIAN)
return (_pbuf[i + 0] << 16) | (_pbuf[i + 1] << 8) | _pbuf[i + 2];
#elif defined(SCUMM_LITTLE_ENDIAN)
return _pbuf[i + 0] | (_pbuf[i + 1] << 8) | (_pbuf[i + 2] << 16);
#endif
case 4:
return ((uint32 *) _pbuf)[i];
}
error("getPixelAt: Unhandled bytesPerPixel %d", int(_pbufBpp));
}
FORCEINLINE bool compareDepth(uint &zSrc, uint &zDst) {
if (!_depthTestEnabled)
return true;
switch (_depthFunc) {
case TGL_NEVER:
break;
case TGL_LESS:
if (zDst < zSrc)
return true;
break;
case TGL_EQUAL:
if (zDst == zSrc)
return true;
break;
case TGL_LEQUAL:
if (zDst <= zSrc)
return true;
break;
case TGL_GREATER:
if (zDst > zSrc)
return true;
break;
case TGL_NOTEQUAL:
if (zDst != zSrc)
return true;
break;
case TGL_GEQUAL:
if (zDst >= zSrc)
return true;
break;
case TGL_ALWAYS:
return true;
}
return false;
}
FORCEINLINE bool checkAlphaTest(byte aSrc) {
if (!_alphaTestEnabled)
return true;
switch (_alphaTestFunc) {
case TGL_NEVER:
break;
case TGL_LESS:
if (aSrc < _alphaTestRefVal)
return true;
break;
case TGL_EQUAL:
if (aSrc == _alphaTestRefVal)
return true;
break;
case TGL_LEQUAL:
if (aSrc <= _alphaTestRefVal)
return true;
break;
case TGL_GREATER:
if (aSrc > _alphaTestRefVal)
return true;
break;
case TGL_NOTEQUAL:
if (aSrc != _alphaTestRefVal)
return true;
break;
case TGL_GEQUAL:
if (aSrc >= _alphaTestRefVal)
return true;
break;
case TGL_ALWAYS:
return true;
}
return false;
}
FORCEINLINE bool stencilTest(byte sSrc) {
switch (_stencilTestFunc) {
case TGL_NEVER:
break;
case TGL_LESS:
if ((_stencilRefVal & _stencilMask) < (sSrc & _stencilMask))
return true;
break;
case TGL_LEQUAL:
if ((_stencilRefVal & _stencilMask) <= (sSrc & _stencilMask))
return true;
break;
case TGL_GREATER:
if ((_stencilRefVal & _stencilMask) > (sSrc & _stencilMask))
return true;
break;
case TGL_GEQUAL:
if ((_stencilRefVal & _stencilMask) >= (sSrc & _stencilMask))
return true;
break;
case TGL_EQUAL:
if ((_stencilRefVal & _stencilMask) == (sSrc & _stencilMask))
return true;
break;
case TGL_NOTEQUAL:
if ((_stencilRefVal & _stencilMask) != (sSrc & _stencilMask))
return true;
break;
case TGL_ALWAYS:
return true;
}
return false;
}
FORCEINLINE void stencilOp(bool stencilTestResult, bool depthTestResult, byte *sDst) {
int op = !stencilTestResult ? _stencilSfail : !depthTestResult ? _stencilDpfail : _stencilDppass;
byte oldValue = *sDst;
byte newValue = oldValue;
switch (op) {
case TGL_KEEP:
return;
case TGL_ZERO:
newValue = 0;
break;
case TGL_REPLACE:
newValue = _stencilRefVal;
break;
case TGL_INCR:
if (newValue < 255)
newValue++;
break;
case TGL_INCR_WRAP:
newValue++;
break;
case TGL_DECR:
if (newValue > 0)
newValue--;
break;
case TGL_DECR_WRAP:
newValue--;
break;
case TGL_INVERT:
newValue = ~newValue;
}
*sDst = (newValue & _stencilWriteMask) | (oldValue & ~_stencilWriteMask);
}
template <bool kEnableAlphaTest, bool kBlendingEnabled>
FORCEINLINE void writePixel(int pixel, int value) {
writePixel<kEnableAlphaTest, kBlendingEnabled, false>(pixel, value, 0);
}
template <bool kEnableAlphaTest, bool kBlendingEnabled, bool kDepthWrite>
FORCEINLINE void writePixel(int pixel, int value, uint z) {
if (kBlendingEnabled == false) {
setPixelAt(pixel, value);
if (kDepthWrite) {
_zbuf[pixel] = z;
}
} else {
byte rSrc, gSrc, bSrc, aSrc;
_pbufFormat.colorToARGB(value, aSrc, rSrc, gSrc, bSrc);
writePixel<kEnableAlphaTest, kBlendingEnabled, kDepthWrite>(pixel, aSrc, rSrc, gSrc, bSrc, z);
}
}
FORCEINLINE void writePixel(int pixel, int value) {
if (_alphaTestEnabled) {
writePixel<true>(pixel, value);
} else {
writePixel<false>(pixel, value);
}
}
template <bool kDepthWrite, bool kSmoothMode, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kStippleEnabled, bool kDepthTestEnabled>
void putPixelNoTexture(int fbOffset, uint *pz, byte *ps, int _a,
int x, int y, uint &z, uint &r, uint &g, uint &b, uint &a,
int &dzdx, int &drdx, int &dgdx, int &dbdx, uint dadx,
uint &fog, int fog_r, int fog_g, int fog_b, int &dfdx);
enum class ColorMode {
NoInterpolation,
Default, // GL_TEXTURE_ENV_MODE == GL_MODULATE
CustomTexEnv
};
template <bool kDepthWrite, ColorMode kColorMode, bool kSmoothMode, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kDepthTestEnabled>
void putPixelTexture(int fbOffset, const TexelBuffer *texture,
uint wrap_s, uint wrap_t, uint *pz, byte *ps, int _a,
int x, int y, uint &z, int &t, int &s,
uint &r, uint &g, uint &b, uint &a,
int &dzdx, int &dsdx, int &dtdx, int &drdx, int &dgdx, int &dbdx, uint dadx,
uint &fog, int fog_r, int fog_g, int fog_b, int &dfdx);
template <bool kDepthWrite, bool kEnableScissor, bool kStencilEnabled, bool kStippleEnabled, bool kDepthTestEnabled>
void putPixelDepth(uint *pz, byte *ps, int _a, int x, int y, uint &z, int &dzdx);
template <bool kEnableAlphaTest>
FORCEINLINE void writePixel(int pixel, int value) {
if (_blendingEnabled) {
writePixel<kEnableAlphaTest, true>(pixel, value);
} else {
writePixel<kEnableAlphaTest, false>(pixel, value);
}
}
FORCEINLINE void writePixel(int pixel, byte rSrc, byte gSrc, byte bSrc) {
writePixel(pixel, 255, rSrc, gSrc, bSrc);
}
FORCEINLINE bool scissorPixel(int x, int y) {
return !_clipRectangle.contains(x, y);
}
public:
FORCEINLINE void writePixel(int pixel, byte aSrc, byte rSrc, byte gSrc, byte bSrc) {
if (_alphaTestEnabled) {
writePixel<true>(pixel, aSrc, rSrc, gSrc, bSrc);
} else {
writePixel<false>(pixel, aSrc, rSrc, gSrc, bSrc);
}
}
private:
template <bool kEnableAlphaTest>
FORCEINLINE void writePixel(int pixel, byte aSrc, byte rSrc, byte gSrc, byte bSrc) {
if (_blendingEnabled) {
writePixel<kEnableAlphaTest, true>(pixel, aSrc, rSrc, gSrc, bSrc);
} else {
writePixel<kEnableAlphaTest, false>(pixel, aSrc, rSrc, gSrc, bSrc);
}
}
template <bool kEnableAlphaTest, bool kBlendingEnabled>
FORCEINLINE void writePixel(int pixel, byte aSrc, byte rSrc, byte gSrc, byte bSrc) {
writePixel<kEnableAlphaTest, kBlendingEnabled, false>(pixel, aSrc, rSrc, gSrc, bSrc, 0);
}
template <bool kEnableAlphaTest, bool kBlendingEnabled, bool kDepthWrite>
FORCEINLINE void writePixel(int pixel, byte aSrc, byte rSrc, byte gSrc, byte bSrc, uint z) {
writePixel<kEnableAlphaTest, kBlendingEnabled, kDepthWrite, false>(pixel, aSrc, rSrc, gSrc, bSrc, z, 0.0f, 0, 0, 0);
}
template <bool kEnableAlphaTest, bool kBlendingEnabled, bool kDepthWrite, bool kFogMode>
FORCEINLINE void writePixel(int pixel, byte aSrc, byte rSrc, byte gSrc, byte bSrc, float z, uint fog, byte fog_r, byte fog_g, byte fog_b) {
if (kEnableAlphaTest) {
if (!checkAlphaTest(aSrc))
return;
}
if (kDepthWrite) {
_zbuf[pixel] = z;
}
if (kFogMode) {
int oneMinusFog = (1 << ZB_FOG_BITS) - fog;
int finalR = (rSrc * fog + fog_r * oneMinusFog) >> ZB_FOG_BITS;
int finalG = (gSrc * fog + fog_g * oneMinusFog) >> ZB_FOG_BITS;
int finalB = (bSrc * fog + fog_b * oneMinusFog) >> ZB_FOG_BITS;
if (finalR > 255) {
rSrc = 255;
} else {
rSrc = finalR;
}
if (finalG > 255) {
gSrc = 255;
} else {
gSrc = finalG;
}
if (finalB > 255) {
bSrc = 255;
} else {
bSrc = finalB;
}
}
if (!kBlendingEnabled) {
setPixelAt(pixel, _pbufFormat.ARGBToColor(aSrc, rSrc, gSrc, bSrc));
} else {
byte rDst, gDst, bDst, aDst;
_pbufFormat.colorToARGB(getPixelAt(pixel), aDst, rDst, gDst, bDst);
switch (_sourceBlendingFactor) {
case TGL_ZERO:
rSrc = gSrc = bSrc = 0;
break;
case TGL_ONE:
break;
case TGL_DST_COLOR:
rSrc = (rDst * rSrc) >> 8;
gSrc = (gDst * gSrc) >> 8;
bSrc = (bDst * bSrc) >> 8;
break;
case TGL_ONE_MINUS_DST_COLOR:
rSrc = (rSrc * (255 - rDst)) >> 8;
gSrc = (gSrc * (255 - gDst)) >> 8;
bSrc = (bSrc * (255 - bDst)) >> 8;
break;
case TGL_SRC_ALPHA:
rSrc = (rSrc * aSrc) >> 8;
gSrc = (gSrc * aSrc) >> 8;
bSrc = (bSrc * aSrc) >> 8;
break;
case TGL_ONE_MINUS_SRC_ALPHA:
rSrc = (rSrc * (255 - aSrc)) >> 8;
gSrc = (gSrc * (255 - aSrc)) >> 8;
bSrc = (bSrc * (255 - aSrc)) >> 8;
break;
case TGL_DST_ALPHA:
rSrc = (rSrc * aDst) >> 8;
gSrc = (gSrc * aDst) >> 8;
bSrc = (bSrc * aDst) >> 8;
break;
case TGL_ONE_MINUS_DST_ALPHA:
rSrc = (rSrc * (255 - aDst)) >> 8;
gSrc = (gSrc * (255 - aDst)) >> 8;
bSrc = (bSrc * (255 - aDst)) >> 8;
break;
default:
break;
}
switch (_destinationBlendingFactor) {
case TGL_ZERO:
rDst = gDst = bDst = 0;
break;
case TGL_ONE:
break;
case TGL_DST_COLOR:
rDst = (rDst * rSrc) >> 8;
gDst = (gDst * gSrc) >> 8;
bDst = (bDst * bSrc) >> 8;
break;
case TGL_ONE_MINUS_DST_COLOR:
rDst = (rDst * (255 - rSrc)) >> 8;
gDst = (gDst * (255 - gSrc)) >> 8;
bDst = (bDst * (255 - bSrc)) >> 8;
break;
case TGL_SRC_ALPHA:
rDst = (rDst * aSrc) >> 8;
gDst = (gDst * aSrc) >> 8;
bDst = (bDst * aSrc) >> 8;
break;
case TGL_ONE_MINUS_SRC_ALPHA:
rDst = (rDst * (255 - aSrc)) >> 8;
gDst = (gDst * (255 - aSrc)) >> 8;
bDst = (bDst * (255 - aSrc)) >> 8;
break;
case TGL_DST_ALPHA:
rDst = (rDst * aDst) >> 8;
gDst = (gDst * aDst) >> 8;
bDst = (bDst * aDst) >> 8;
break;
case TGL_ONE_MINUS_DST_ALPHA:
rDst = (rDst * (255 - aDst)) >> 8;
gDst = (gDst * (255 - aDst)) >> 8;
bDst = (bDst * (255 - aDst)) >> 8;
break;
case TGL_SRC_ALPHA_SATURATE: {
int factor = aSrc < 1 - aDst ? aSrc : 1 - aDst;
rDst = (rDst * factor) >> 8;
gDst = (gDst * factor) >> 8;
bDst = (bDst * factor) >> 8;
}
break;
default:
break;
}
int finalR = rDst + rSrc;
int finalG = gDst + gSrc;
int finalB = bDst + bSrc;
if (finalR > 255) {
finalR = 255;
}
if (finalG > 255) {
finalG = 255;
}
if (finalB > 255) {
finalB = 255;
}
setPixelAt(pixel, _pbufFormat.RGBToColor(finalR, finalG, finalB));
}
}
public:
void clear(int clear_z, int z, int clear_color, int r, int g, int b,
bool clearStencil, int stencilValue);
void clearRegion(int x, int y, int w, int h, bool clearZ, int z,
bool clearColor, int r, int g, int b, bool clearStencil, int stencilValue);
const Common::Rect &getClippingRectangle() const {
return _clipRectangle;
}
void setupScissor(bool enable, const int (&scissor)[4], const Common::Rect *clippingRectangle) {
_clippingEnabled = enable || clippingRectangle;
if (enable && clippingRectangle) {
_clipRectangle = clippingRectangle->findIntersectingRect(Common::Rect(
scissor[0],
// all viewport calculations are already flipped upside down
_pbufHeight - scissor[1] - scissor[3],
scissor[0] + scissor[2],
_pbufHeight - scissor[1]));
} else if (enable) {
_clipRectangle = Common::Rect(
scissor[0],
// all viewport calculations are already flipped upside down
_pbufHeight - scissor[1] - scissor[3],
scissor[0] + scissor[2],
_pbufHeight - scissor[1]);
} else if (clippingRectangle) {
_clipRectangle = *clippingRectangle;
} else {
_clipRectangle = Common::Rect(0, 0, _pbufWidth, _pbufHeight);
}
}
void enableBlending(bool enable) {
_blendingEnabled = enable;
}
void setBlendingFactors(int sFactor, int dFactor) {
_sourceBlendingFactor = sFactor;
_destinationBlendingFactor = dFactor;
}
void enableAlphaTest(bool enable) {
_alphaTestEnabled = enable;
}
void setAlphaTestFunc(int func, int ref) {
_alphaTestFunc = func;
_alphaTestRefVal = ref;
}
void enableDepthTest(bool enable) {
_depthTestEnabled = enable;
}
void setDepthFunc(int func) {
_depthFunc = func;
}
void enableDepthWrite(bool enable) {
_depthWrite = enable;
}
void enableStencilTest(bool enable) {
_stencilTestEnabled = enable;
}
void setStencilWriteMask(uint stencilWriteMask) {
_stencilWriteMask = stencilWriteMask;
}
void enablePolygonStipple(bool enable) {
_polygonStippleEnabled = enable;
}
void setPolygonStipplePattern(const byte *stipple) {
_polygonStipplePattern = stipple;
}
void enableTwoColorStipple(bool enable) {
_twoColorStippleEnabled = enable;
}
void setStippleColor(int r, int g, int b) {
_stippleColor = RGB_TO_PIXEL(r, g, b);
}
void setStencilTestFunc(int stencilFunc, byte stencilValue, byte stencilMask) {
_stencilTestFunc = stencilFunc;
_stencilRefVal = stencilValue;
_stencilMask = stencilMask;
}
void setStencilOp(int stencilSfail, int stencilDpfail, int stencilDppass) {
_stencilSfail = stencilSfail;
_stencilDpfail = stencilDpfail;
_stencilDppass = stencilDppass;
}
void setOffsetStates(int offsetStates) {
_offsetStates = offsetStates;
}
void setOffsetFactor(float offsetFactor) {
_offsetFactor = offsetFactor;
}
void setOffsetUnits(float offsetUnits) {
_offsetUnits = offsetUnits;
}
void setTexture(const TexelBuffer *texture, uint wraps, uint wrapt) {
_currentTexture = texture;
_wrapS = wraps;
_wrapT = wrapt;
}
void setTextureEnvironment(const GLTextureEnv *texEnv) {
_textureEnv = texEnv;
}
void setTextureSizeAndMask(int textureSize, int textureSizeMask) {
_textureSize = textureSize;
_textureSizeMask = textureSizeMask;
}
void setFogEnabled(bool enable) {
_fogEnabled = enable;
}
void setFogColor(float colorR, float colorG, float colorB) {
_fogColorR = colorR;
_fogColorG = colorG;
_fogColorB = colorB;
}
private:
/**
* Blit the buffer to the screen buffer, checking the depth of the pixels.
* Eack pixel is copied if and only if its depth value is bigger than the
* depth value of the screen pixel, so if it is 'above'.
*/
Buffer *genOffscreenBuffer();
void delOffscreenBuffer(Buffer *buffer);
void blitOffscreenBuffer(Buffer *buffer);
void selectOffscreenBuffer(Buffer *buffer);
void clearOffscreenBuffer(Buffer *buffer);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode, bool kAlphaTestEnabled, bool kEnableScissor,
bool kBlendingEnabled, bool kStencilEnabled, bool kStippleEnabled, bool kDepthTestEnabled>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode, bool kAlphaTestEnabled, bool kEnableScissor,
bool kBlendingEnabled, bool kStencilEnabled, bool kStippleEnabled>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode, bool kAlphaTestEnabled, bool kEnableScissor,
bool kBlendingEnabled, bool kStencilEnabled>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode, bool enableAlphaTest, bool kEnableScissor, bool kBlendingEnabled>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode, bool enableAlphaTest, bool kEnableScissor>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode, bool enableAlphaTest>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
template <bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite>
void fillTriangleTextureMapping(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
public:
void fillTriangleTextureMappingPerspectiveSmooth(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
void fillTriangleTextureMappingPerspectiveFlat(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
void fillTriangleDepthOnly(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
void fillTriangleFlat(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
void fillTriangleSmooth(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
void plot(ZBufferPoint *p);
void fillLine(ZBufferPoint *p1, ZBufferPoint *p2);
void fillLineZ(ZBufferPoint *p1, ZBufferPoint *p2);
private:
void fillLineFlatZ(ZBufferPoint *p1, ZBufferPoint *p2);
void fillLineInterpZ(ZBufferPoint *p1, ZBufferPoint *p2);
void fillLineFlat(ZBufferPoint *p1, ZBufferPoint *p2);
void fillLineInterp(ZBufferPoint *p1, ZBufferPoint *p2);
template <bool kDepthWrite>
FORCEINLINE void putPixel(uint pixelOffset, int color, int x, int y, uint z);
template <bool kDepthWrite, bool kEnableScissor>
FORCEINLINE void putPixel(uint pixelOffset, int color, int x, int y, uint z);
template <bool kEnableScissor>
FORCEINLINE void putPixel(uint pixelOffset, int color, int x, int y);
template <bool kInterpRGB, bool kInterpZ, bool kDepthWrite>
void drawLine(const ZBufferPoint *p1, const ZBufferPoint *p2);
template <bool kInterpRGB, bool kInterpZ, bool kDepthWrite, bool kEnableScissor>
void drawLine(const ZBufferPoint *p1, const ZBufferPoint *p2);
void applyTextureEnvironment(
int internalformat,
uint previousA, uint previousR, uint previousG, uint previousB,
byte &texA, byte &texR, byte &texG, byte &texB);
// the same as GL_TEXTURE_ENV_MODE == GL_MODULATE as fast-path for the default mode
void applyModulation(
uint previousA, uint previousR, uint previousG, uint previousB,
byte &texA, byte &texR, byte &texG, byte &texB);
Buffer _offscreenBuffer;
byte *_pbuf;
int _pbufWidth;
int _pbufHeight;
int _pbufPitch;
Graphics::PixelFormat _pbufFormat;
int _pbufBpp;
uint *_zbuf;
byte *_sbuf;
bool _enableStencil;
int _textureSize;
int _textureSizeMask;
Common::Rect _clipRectangle;
bool _clippingEnabled;
const TexelBuffer *_currentTexture;
const GLTextureEnv *_textureEnv;
uint _wrapS, _wrapT;
bool _blendingEnabled;
int _sourceBlendingFactor;
int _destinationBlendingFactor;
bool _alphaTestEnabled;
int _alphaTestFunc;
int _alphaTestRefVal;
bool _depthTestEnabled;
bool _depthWrite;
bool _stencilTestEnabled;
int _stencilTestFunc;
byte _stencilRefVal;
byte _stencilMask;
byte _stencilWriteMask;
int _stencilSfail;
int _stencilDpfail;
int _stencilDppass;
bool _polygonStippleEnabled;
bool _twoColorStippleEnabled;
uint32 _stippleColor;
const byte *_polygonStipplePattern;
int _depthFunc;
int _offsetStates;
float _offsetFactor;
float _offsetUnits;
bool _fogEnabled;
float _fogColorR;
float _fogColorG;
float _fogColorB;
};
// memory.c
void gl_free(void *p);
void *gl_malloc(int size);
void *gl_zalloc(int size);
void *gl_realloc(void *p, int size);
} // end of namespace TinyGL
#endif

View File

@@ -0,0 +1,823 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "graphics/tinygl/zdirtyrect.h"
#include "graphics/tinygl/zgl.h"
#include "graphics/tinygl/gl.h"
#include "common/debug.h"
namespace TinyGL {
GLTextureEnvArgument::GLTextureEnvArgument()
: sourceRGB(TGL_TEXTURE)
, operandRGB(TGL_SRC_COLOR)
, sourceAlpha(TGL_TEXTURE)
, operandAlpha(TGL_SRC_ALPHA) {}
GLTextureEnv::GLTextureEnv()
: envMode(TGL_MODULATE)
, combineRGB(TGL_REPLACE)
, combineAlpha(TGL_REPLACE)
, constA(255), constR(255), constG(255), constB(255) {}
bool GLTextureEnv::isDefault() const {
return envMode == TGL_MODULATE;
}
void GLContext::issueDrawCall(DrawCall *drawCall) {
if (_enableDirtyRectangles && drawCall->getDirtyRegion().isEmpty())
return;
_drawCallsQueue.push_back(drawCall);
}
void GLContext::debugDrawRectangle(Common::Rect rect, int r, int g, int b) {
int fbWidth = fb->getPixelBufferWidth();
if (rect.left < 0)
rect.left = 0;
if (rect.right >= fbWidth)
rect.right = fbWidth - 1;
if (rect.top < 0)
rect.top = 0;
if (rect.bottom >= fb->getPixelBufferHeight())
rect.bottom = fb->getPixelBufferHeight() - 1;
for (int x = rect.left; x < rect.right; x++) {
fb->writePixel(rect.top * fbWidth + x, 255, r, g, b);
fb->writePixel((rect.bottom - 1) * fbWidth + x, 255, r, g, b);
}
for (int y = rect.top; y < rect.bottom; y++) {
fb->writePixel(y * fbWidth + rect.left, 255, r, g, b);
fb->writePixel(y * fbWidth + rect.right - 1, 255, r, g, b);
}
}
struct DirtyRectangle {
Common::Rect rectangle;
int r, g, b;
DirtyRectangle() {
r = 0;
g = 0;
b = 0;
}
DirtyRectangle(Common::Rect rect, int red, int green, int blue) {
rectangle = rect;
r = red;
g = green;
b = blue;
}
};
void GLContext::disposeResources() {
// Dispose textures and resources.
bool allDisposed = true;
do {
allDisposed = true;
for (int i = 0; i < TEXTURE_HASH_TABLE_SIZE; i++) {
GLTexture *t = shared_state.texture_hash_table[i];
while (t) {
if (t->disposed) {
free_texture(t);
allDisposed = false;
break;
}
t = t->next;
}
}
} while (allDisposed == false);
Internal::tglCleanupImages();
}
void GLContext::disposeDrawCallLists() {
for (const auto &drawCall : _previousFrameDrawCallsQueue) {
delete drawCall;
}
_previousFrameDrawCallsQueue.clear();
for (auto &drawCall : _drawCallsQueue) {
delete drawCall;
}
_drawCallsQueue.clear();
}
static inline void _appendDirtyRectangle(const DrawCall &call, Common::List<DirtyRectangle> &rectangles, int r, int g, int b) {
Common::Rect dirty_region = call.getDirtyRegion();
if (rectangles.empty() || dirty_region != rectangles.back().rectangle)
rectangles.push_back(DirtyRectangle(dirty_region, r, g, b));
}
void GLContext::presentBufferDirtyRects(Common::List<Common::Rect> &dirtyAreas) {
typedef Common::List<DrawCall *>::const_iterator DrawCallIterator;
typedef Common::List<DirtyRectangle>::iterator RectangleIterator;
Common::List<DirtyRectangle> rectangles;
DrawCallIterator itFrame = _drawCallsQueue.begin();
DrawCallIterator endFrame = _drawCallsQueue.end();
DrawCallIterator itPrevFrame = _previousFrameDrawCallsQueue.begin();
DrawCallIterator endPrevFrame = _previousFrameDrawCallsQueue.end();
// Compare draw calls.
for ( ; itPrevFrame != endPrevFrame && itFrame != endFrame;
++itPrevFrame, ++itFrame) {
const DrawCall &currentCall = **itFrame;
const DrawCall &previousCall = **itPrevFrame;
if (previousCall != currentCall) {
_appendDirtyRectangle(previousCall, rectangles, 255, 255, 255);
_appendDirtyRectangle(currentCall, rectangles, 255, 0, 0);
}
}
for ( ; itPrevFrame != endPrevFrame; ++itPrevFrame) {
_appendDirtyRectangle(**itPrevFrame, rectangles, 255, 255, 255);
}
for ( ; itFrame != endFrame; ++itFrame) {
_appendDirtyRectangle(**itFrame, rectangles, 255, 0, 0);
}
// This loop increases outer rectangle coordinates to favor merging of adjacent rectangles.
for (auto &rect : rectangles) {
rect.rectangle.right++;
rect.rectangle.bottom++;
}
// Merge coalesce dirty rects.
bool restartMerge;
do {
restartMerge = false;
for (RectangleIterator it1 = rectangles.begin(); it1 != rectangles.end(); ++it1) {
for (RectangleIterator it2 = rectangles.begin(); it2 != rectangles.end();) {
if (it1 != it2) {
if ((*it1).rectangle.intersects((*it2).rectangle)) {
(*it1).rectangle.extend((*it2).rectangle);
it2 = rectangles.erase(it2);
restartMerge = true;
} else {
++it2;
}
} else {
++it2;
}
}
}
} while(restartMerge);
for (RectangleIterator it1 = rectangles.begin(); it1 != rectangles.end(); ++it1) {
RectangleIterator it2 = it1;
it2++;
while (it2 != rectangles.end()) {
if ((*it1).rectangle.contains((*it2).rectangle)) {
it2 = rectangles.erase(it2);
} else {
++it2;
}
}
}
for (auto &rect : rectangles) {
rect.rectangle.clip(renderRect);
}
if (!rectangles.empty()) {
for (auto &rect : rectangles) {
dirtyAreas.push_back(rect.rectangle);
}
// Execute draw calls.
for (auto &drawCall : _drawCallsQueue) {
Common::Rect drawCallRegion = drawCall->getDirtyRegion();
for (auto &rect : rectangles) {
Common::Rect dirtyRegion = rect.rectangle;
if (dirtyRegion.intersects(drawCallRegion)) {
drawCall->execute(true, &dirtyRegion);
}
}
}
if (_debugRectsEnabled) {
// Draw debug rectangles.
// Note: white rectangles are rectangle that contained other rectangles
// blue rectangles are rectangle merged from other rectangles
// red rectangles are original dirty rects
fb->enableBlending(false);
fb->enableAlphaTest(false);
for (auto &rect : rectangles) {
debugDrawRectangle(rect.rectangle, rect.r, rect.g, rect.b);
}
fb->enableBlending(blending_enabled);
fb->enableAlphaTest(alpha_test_enabled);
}
}
// Dispose not necessary draw calls.
for (auto &p : _previousFrameDrawCallsQueue) {
delete p;
}
_previousFrameDrawCallsQueue = _drawCallsQueue;
_drawCallsQueue.clear();
disposeResources();
_currentAllocatorIndex = (_currentAllocatorIndex + 1) & 0x1;
_drawCallAllocator[_currentAllocatorIndex].reset();
}
void GLContext::presentBufferSimple(Common::List<Common::Rect> &dirtyAreas) {
dirtyAreas.push_back(Common::Rect(fb->getPixelBufferWidth(), fb->getPixelBufferHeight()));
for (const auto &drawCall : _drawCallsQueue) {
drawCall->execute(true);
delete drawCall;
}
_drawCallsQueue.clear();
disposeResources();
_drawCallAllocator[_currentAllocatorIndex].reset();
}
void presentBuffer(Common::List<Common::Rect> &dirtyAreas) {
GLContext *c = gl_get_context();
if (c->_enableDirtyRectangles) {
c->presentBufferDirtyRects(dirtyAreas);
} else {
c->presentBufferSimple(dirtyAreas);
}
}
void presentBuffer() {
Common::List<Common::Rect> dirtyAreas;
presentBuffer(dirtyAreas);
}
bool DrawCall::operator==(const DrawCall &other) const {
if (_type == other._type) {
switch (_type) {
case DrawCall_Rasterization:
return *(const RasterizationDrawCall *)this == (const RasterizationDrawCall &)other;
break;
case DrawCall_Blitting:
return *(const BlittingDrawCall *)this == (const BlittingDrawCall &)other;
break;
case DrawCall_Clear:
return *(const ClearBufferDrawCall *)this == (const ClearBufferDrawCall &)other;
break;
default:
return false;
}
} else {
return false;
}
}
RasterizationDrawCall::RasterizationDrawCall() : DrawCall(DrawCall_Rasterization) {
GLContext *c = gl_get_context();
_vertexCount = c->vertex_cnt;
_vertex = (GLVertex *) Internal::allocateFrame(_vertexCount * sizeof(GLVertex));
_drawTriangleFront = c->draw_triangle_front;
_drawTriangleBack = c->draw_triangle_back;
memcpy(_vertex, c->vertex, sizeof(GLVertex) * _vertexCount);
_state = captureState();
if (c->_enableDirtyRectangles) {
computeDirtyRegion();
}
}
void RasterizationDrawCall::computeDirtyRegion() {
int clip_code = 0xf;
for (int i = 0; i < _vertexCount; i++) {
clip_code &= _vertex[i].clip_code;
}
if (!clip_code) {
GLContext *c = gl_get_context();
int xmax = c->fb->getPixelBufferWidth() - 1;
int ymax = c->fb->getPixelBufferHeight() - 1;
int left = xmax, right = 0, top = ymax, bottom = 0;
for (int i = 0; i < _vertexCount; i++) {
GLVertex *v = &_vertex[i];
if (v->clip_code)
c->gl_transform_to_viewport(v);
left = MIN(left, v->clip_code & 0x1 ? 0 : v->zp.x);
right = MAX(right, v->clip_code & 0x2 ? xmax : v->zp.x);
bottom = MAX(bottom, v->clip_code & 0x4 ? ymax : v->zp.y);
top = MIN(top, v->clip_code & 0x8 ? 0 : v->zp.y);
}
// Note: clipping outside of Rect is required despite above clip_code checks,
// as vertices far on the Z axis will overflow X and/or Y coordinates.
// This happens in EMI intro, for example.
_dirtyRegion = Common::Rect(
MAX(0, left),
MAX(0, top),
MIN(right, xmax) + 1,
MIN(bottom, ymax) + 1
);
}
}
void RasterizationDrawCall::execute(bool restoreState, const Common::Rect *clippingRectangle) const {
GLContext *c = gl_get_context();
RasterizationDrawCall::RasterizationState backupState;
if (restoreState) {
backupState = captureState();
}
applyState(_state, clippingRectangle);
GLVertex *prevVertex = c->vertex;
int prevVertexCount = c->vertex_cnt;
c->vertex = _vertex;
c->vertex_cnt = _vertexCount;
c->draw_triangle_front = (gl_draw_triangle_func)_drawTriangleFront;
c->draw_triangle_back = (gl_draw_triangle_func)_drawTriangleBack;
int n = c->vertex_n;
int cnt = c->vertex_cnt;
switch (c->begin_type) {
case TGL_POINTS:
for(int i = 0; i < cnt; i++) {
c->gl_draw_point(&c->vertex[i]);
}
break;
case TGL_LINES:
for(int i = 0; i < cnt / 2; i++) {
c->gl_draw_line(&c->vertex[i * 2], &c->vertex[i * 2 + 1]);
}
break;
case TGL_LINE_LOOP:
c->gl_draw_line(&c->vertex[cnt - 1], &c->vertex[0]);
// Fall through...
case TGL_LINE_STRIP:
for(int i = 0; i < cnt - 1; i++) {
c->gl_draw_line(&c->vertex[i], &c->vertex[i + 1]);
}
break;
case TGL_TRIANGLES:
for(int i = 0; i < cnt; i += 3) {
c->gl_draw_triangle(&c->vertex[i], &c->vertex[i + 1], &c->vertex[i + 2]);
}
break;
case TGL_TRIANGLE_STRIP:
while (cnt >= 3) {
// needed to respect triangle orientation
switch (cnt & 1) {
case 0:
c->gl_draw_triangle(&c->vertex[2], &c->vertex[1], &c->vertex[0]);
break;
case 1:
c->gl_draw_triangle(&c->vertex[0], &c->vertex[1], &c->vertex[2]);
break;
}
cnt--;
c->vertex++;
}
break;
case TGL_TRIANGLE_FAN:
for(int i = 1; i < cnt - 1; i++) {
c->gl_draw_triangle(&c->vertex[0], &c->vertex[i], &c->vertex[i + 1]);
}
break;
case TGL_QUADS:
for(int i = 0; i < cnt; i += 4) {
c->vertex[i + 2].edge_flag = 0;
c->gl_draw_triangle(&c->vertex[i], &c->vertex[i + 1], &c->vertex[i + 2]);
c->vertex[i + 2].edge_flag = 1;
c->vertex[i + 0].edge_flag = 0;
c->gl_draw_triangle(&c->vertex[i], &c->vertex[i + 2], &c->vertex[i + 3]);
}
break;
case TGL_QUAD_STRIP:
for( ; n >= 4; n -= 2) {
c->gl_draw_triangle(&c->vertex[0], &c->vertex[1], &c->vertex[2]);
c->gl_draw_triangle(&c->vertex[1], &c->vertex[3], &c->vertex[2]);
for (int i = 0; i < 2; i++) {
c->vertex[i] = c->vertex[i + 2];
}
}
break;
case TGL_POLYGON: {
for (int i = c->vertex_cnt; i >= 3; i--) {
c->gl_draw_triangle(&c->vertex[i - 1], &c->vertex[0], &c->vertex[i - 2]);
}
break;
}
default:
error("glBegin: type %x not handled", c->begin_type);
}
c->vertex = prevVertex;
c->vertex_cnt = prevVertexCount;
if (restoreState) {
applyState(backupState, nullptr);
}
}
RasterizationDrawCall::RasterizationState RasterizationDrawCall::captureState() const {
RasterizationState state;
GLContext *c = gl_get_context();
state.enableScissor = c->scissor_test_enabled;
state.enableBlending = c->blending_enabled;
state.sfactor = c->source_blending_factor;
state.dfactor = c->destination_blending_factor;
state.alphaTestEnabled = c->alpha_test_enabled;
state.alphaFunc = c->alpha_test_func;
state.alphaRefValue = c->alpha_test_ref_val;
state.depthTestEnabled = c->depth_test_enabled;
state.depthFunction = c->depth_func;
state.depthWriteMask = c->depth_write_mask;
state.stencilTestEnabled = c->stencil_test_enabled;
state.stencilTestFunc = c->stencil_test_func;
state.stencilValue = c->stencil_ref_val;
state.stencilMask = c->stencil_mask;
state.stencilWriteMask = c->stencil_write_mask;
state.stencilSfail = c->stencil_sfail;
state.stencilDpfail = c->stencil_dpfail;
state.stencilDppass = c->stencil_dppass;
state.polygonStippleEnabled = c->polygon_stipple_enabled;
state.offsetStates = c->offset_states;
state.offsetFactor = c->offset_factor;
state.offsetUnits = c->offset_units;
state.cullFaceEnabled = c->cull_face_enabled;
state.beginType = c->begin_type;
state.colorMaskRed = c->color_mask_red;
state.colorMaskGreen = c->color_mask_green;
state.colorMaskBlue = c->color_mask_blue;
state.colorMaskAlpha = c->color_mask_alpha;
state.currentFrontFace = c->current_front_face;
state.currentShadeModel = c->current_shade_model;
state.polygonModeBack = c->polygon_mode_back;
state.polygonModeFront = c->polygon_mode_front;
state.texture2DEnabled = c->texture_2d_enabled;
state.texture = c->current_texture;
state.wrapS = c->texture_wrap_s;
state.wrapT = c->texture_wrap_t;
state.textureEnv = c->_texEnv;
state.lightingEnabled = c->lighting_enabled;
state.textureVersion = c->current_texture->versionNumber;
state.fogEnabled = c->fog_enabled;
state.fogColorR = c->fog_color.X;
state.fogColorG = c->fog_color.Y;
state.fogColorB = c->fog_color.Z;
state.stippleColor = c->stippleColor;
state.two_color_stipple_enabled = c->two_color_stipple_enabled;
memcpy(state.scissor, c->scissor, sizeof(state.scissor));
memcpy(state.viewportScaling, c->viewport.scale._v, sizeof(c->viewport.scale._v));
memcpy(state.viewportTranslation, c->viewport.trans._v, sizeof(c->viewport.trans._v));
memcpy(state.polygonStipplePattern, c->polygon_stipple_pattern, sizeof(c->polygon_stipple_pattern));
return state;
}
void RasterizationDrawCall::applyState(const RasterizationDrawCall::RasterizationState &state, const Common::Rect *clippingRectangle) const {
GLContext *c = gl_get_context();
c->fb->setupScissor(state.enableScissor, state.scissor, clippingRectangle);
c->fb->enableBlending(state.enableBlending);
c->fb->setBlendingFactors(state.sfactor, state.dfactor);
c->fb->enableAlphaTest(state.alphaTestEnabled);
c->fb->setAlphaTestFunc(state.alphaFunc, state.alphaRefValue);
c->fb->setDepthFunc(state.depthFunction);
c->fb->enableDepthWrite(state.depthWriteMask);
c->fb->enableDepthTest(state.depthTestEnabled);
c->fb->enableStencilTest(state.stencilTestEnabled);
c->fb->setStencilWriteMask(state.stencilWriteMask);
c->fb->setStencilTestFunc(state.stencilTestFunc, state.stencilValue, state.stencilMask);
c->fb->setStencilOp(state.stencilSfail, state.stencilDpfail, state.stencilDppass);
c->fb->setOffsetStates(state.offsetStates);
c->fb->setOffsetFactor(state.offsetFactor);
c->fb->setOffsetUnits(state.offsetUnits);
c->fb->setFogEnabled(state.fogEnabled);
c->fb->setFogColor(state.fogColorR, state.fogColorG, state.fogColorB);
c->fb->setPolygonStipplePattern(state.polygonStipplePattern);
c->fb->setStippleColor(state.stippleColor >> 16, (state.stippleColor >> 8) & 0xff, state.stippleColor & 0xff);
c->fb->enableTwoColorStipple(state.two_color_stipple_enabled);
c->fb->enablePolygonStipple(state.polygonStippleEnabled);
c->scissor_test_enabled = state.enableScissor;
c->blending_enabled = state.enableBlending;
c->source_blending_factor = state.sfactor;
c->destination_blending_factor = state.dfactor;
c->alpha_test_enabled = state.alphaTestEnabled;
c->alpha_test_func = state.alphaFunc;
c->alpha_test_ref_val = state.alphaRefValue;
c->depth_test_enabled = state.depthTestEnabled;
c->depth_func = state.depthFunction;
c->depth_write_mask = state.depthWriteMask;
c->stencil_test_enabled = state.stencilTestEnabled;
c->stencil_test_func = state.stencilTestFunc;
c->stencil_ref_val = state.stencilValue;
c->stencil_mask = state.stencilMask;
c->stencil_write_mask = state.stencilWriteMask;
c->stencil_sfail = state.stencilSfail;
c->stencil_dpfail = state.stencilDpfail;
c->stencil_dppass = state.stencilDppass;
c->offset_states = state.offsetStates;
c->offset_factor = state.offsetFactor;
c->offset_units = state.offsetUnits;
c->lighting_enabled = state.lightingEnabled;
c->cull_face_enabled = state.cullFaceEnabled;
c->begin_type = state.beginType;
c->color_mask_red = state.colorMaskRed;
c->color_mask_green = state.colorMaskGreen;
c->color_mask_blue = state.colorMaskBlue;
c->color_mask_alpha = state.colorMaskAlpha;
c->current_front_face = state.currentFrontFace;
c->current_shade_model = state.currentShadeModel;
c->polygon_mode_back = state.polygonModeBack;
c->polygon_mode_front = state.polygonModeFront;
c->texture_2d_enabled = state.texture2DEnabled;
c->current_texture = state.texture;
c->texture_wrap_s = state.wrapS;
c->texture_wrap_t = state.wrapT;
c->_texEnv = state.textureEnv;
c->fog_enabled = state.fogEnabled;
c->fog_color = Vector4(state.fogColorR, state.fogColorG, state.fogColorB, 1.0f);
memcpy(c->scissor, state.scissor, sizeof(c->scissor));
memcpy(c->viewport.scale._v, state.viewportScaling, sizeof(c->viewport.scale._v));
memcpy(c->viewport.trans._v, state.viewportTranslation, sizeof(c->viewport.trans._v));
}
bool RasterizationDrawCall::operator==(const RasterizationDrawCall &other) const {
if (_vertexCount == other._vertexCount &&
_drawTriangleFront == other._drawTriangleFront &&
_drawTriangleBack == other._drawTriangleBack &&
_state == other._state) {
for (int i = 0; i < _vertexCount; i++) {
if ((_vertex[i] != other._vertex[i])) {
return false;
}
}
return true;
}
return false;
}
BlittingDrawCall::BlittingDrawCall(BlitImage *image, const BlitTransform &transform, BlittingMode blittingMode) : DrawCall(DrawCall_Blitting), _transform(transform), _mode(blittingMode), _image(image) {
tglIncBlitImageRef(image);
_blitState = captureState();
_imageVersion = tglGetBlitImageVersion(image);
if (gl_get_context()->_enableDirtyRectangles) {
computeDirtyRegion();
}
}
BlittingDrawCall::~BlittingDrawCall() {
tglDeleteBlitImage(_image);
}
void BlittingDrawCall::execute(bool restoreState, const Common::Rect *clippingRectangle) const {
BlittingState backupState;
if (restoreState) {
backupState = captureState();
}
applyState(_blitState, clippingRectangle);
switch (_mode) {
case BlittingDrawCall::BlitMode_Regular:
Internal::tglBlit(_image, _transform);
break;
case BlittingDrawCall::BlitMode_Fast:
Internal::tglBlitFast(_image, _transform._destinationRectangle.left, _transform._destinationRectangle.top);
break;
case BlittingDrawCall::BlitMode_ZBuffer:
Internal::tglBlitZBuffer(_image, _transform._destinationRectangle.left, _transform._destinationRectangle.top);
break;
default:
break;
}
if (restoreState) {
applyState(backupState, nullptr);
}
}
BlittingDrawCall::BlittingState BlittingDrawCall::captureState() const {
BlittingState state;
TinyGL::GLContext *c = gl_get_context();
state.enableScissor = c->scissor_test_enabled;
state.enableBlending = c->blending_enabled;
state.sfactor = c->source_blending_factor;
state.dfactor = c->destination_blending_factor;
state.alphaTest = c->alpha_test_enabled;
state.alphaFunc = c->alpha_test_func;
state.alphaRefValue = c->alpha_test_ref_val;
state.depthTestEnabled = c->depth_test_enabled;
memcpy(state.scissor, c->scissor, sizeof(state.scissor));
return state;
}
void BlittingDrawCall::applyState(const BlittingState &state, const Common::Rect *clippingRectangle) const {
TinyGL::GLContext *c = gl_get_context();
c->fb->setupScissor(state.enableScissor, state.scissor, clippingRectangle);
c->fb->enableBlending(state.enableBlending);
c->fb->setBlendingFactors(state.sfactor, state.dfactor);
c->fb->enableAlphaTest(state.alphaTest);
c->fb->setAlphaTestFunc(state.alphaFunc, state.alphaRefValue);
c->fb->enableDepthTest(state.depthTestEnabled);
c->scissor_test_enabled = state.enableScissor;
c->blending_enabled = state.enableBlending;
c->source_blending_factor = state.sfactor;
c->destination_blending_factor = state.dfactor;
c->alpha_test_enabled = state.alphaTest;
c->alpha_test_func = state.alphaFunc;
c->alpha_test_ref_val = state.alphaRefValue;
c->depth_test_enabled = state.depthTestEnabled;
memcpy(c->scissor, state.scissor, sizeof(c->scissor));
}
void BlittingDrawCall::computeDirtyRegion() {
int blitWidth = _transform._destinationRectangle.width();
int blitHeight = _transform._destinationRectangle.height();
if (blitWidth == 0) {
if (_transform._sourceRectangle.width() != 0) {
blitWidth = _transform._sourceRectangle.width();
} else {
tglGetBlitImageSize(_image, blitWidth, blitHeight);
}
}
if (blitHeight == 0) {
if (_transform._sourceRectangle.height() != 0) {
blitHeight = _transform._sourceRectangle.height();
} else {
tglGetBlitImageSize(_image, blitWidth, blitHeight);
}
}
if (blitWidth == 0 || blitHeight == 0) {
_dirtyRegion = Common::Rect();
} else {
_dirtyRegion = Common::Rect(
_transform._destinationRectangle.left,
_transform._destinationRectangle.top,
_transform._destinationRectangle.left + blitWidth + 1,
_transform._destinationRectangle.top + blitHeight + 1
);
_dirtyRegion.clip(gl_get_context()->renderRect);
}
}
bool BlittingDrawCall::operator==(const BlittingDrawCall &other) const {
return
_mode == other._mode &&
_image == other._image &&
_transform == other._transform &&
_blitState == other._blitState &&
_imageVersion == tglGetBlitImageVersion(other._image);
}
ClearBufferDrawCall::ClearBufferDrawCall(bool clearZBuffer, int zValue,
bool clearColorBuffer, int rValue, int gValue, int bValue,
bool clearStencilBuffer, int stencilValue)
: _clearZBuffer(clearZBuffer), _clearColorBuffer(clearColorBuffer), _zValue(zValue),
_rValue(rValue), _gValue(gValue), _bValue(bValue), _clearStencilBuffer(clearStencilBuffer),
_stencilValue(stencilValue), DrawCall(DrawCall_Clear) {
_clearState = captureState();
TinyGL::GLContext *c = gl_get_context();
if (c->_enableDirtyRectangles) {
_dirtyRegion = c->renderRect;
}
}
void ClearBufferDrawCall::execute(bool restoreState, const Common::Rect *clippingRectangle) const {
ClearBufferState backupState;
if (restoreState) {
backupState = captureState();
}
applyState(_clearState, clippingRectangle);
TinyGL::GLContext *c = gl_get_context();
c->fb->clear(_clearZBuffer, _zValue, _clearColorBuffer, _rValue, _gValue, _bValue, _clearStencilBuffer, _stencilValue);
if (restoreState) {
applyState(backupState, nullptr);
}
}
ClearBufferDrawCall::ClearBufferState ClearBufferDrawCall::captureState() const {
ClearBufferState state;
TinyGL::GLContext *c = gl_get_context();
state.enableScissor = c->scissor_test_enabled;
memcpy(state.scissor, c->scissor, sizeof(state.scissor));
return state;
}
void ClearBufferDrawCall::applyState(const ClearBufferState &state, const Common::Rect *clippingRectangle) const {
TinyGL::GLContext *c = gl_get_context();
c->fb->setupScissor(state.enableScissor, state.scissor, clippingRectangle);
c->scissor_test_enabled = state.enableScissor;
memcpy(c->scissor, state.scissor, sizeof(c->scissor));
}
bool ClearBufferDrawCall::operator==(const ClearBufferDrawCall &other) const {
return
_clearZBuffer == other._clearZBuffer &&
_clearColorBuffer == other._clearColorBuffer &&
_clearStencilBuffer == other._clearStencilBuffer &&
_rValue == other._rValue &&
_gValue == other._gValue &&
_bValue == other._bValue &&
_zValue == other._zValue &&
_stencilValue == other._stencilValue &&
_clearState == other._clearState;
}
bool RasterizationDrawCall::RasterizationState::operator==(const RasterizationState &other) const {
return
enableScissor == other.enableScissor &&
enableBlending == other.enableBlending &&
sfactor == other.sfactor &&
dfactor == other.dfactor &&
alphaTestEnabled == other.alphaTestEnabled &&
alphaFunc == other.alphaFunc &&
alphaRefValue == other.alphaRefValue &&
depthTestEnabled == other.depthTestEnabled &&
depthFunction == other.depthFunction &&
depthWriteMask == other.depthWriteMask &&
stencilTestEnabled == other.stencilTestEnabled &&
stencilTestFunc == other.stencilTestFunc &&
stencilValue == other.stencilValue &&
stencilMask == other.stencilMask &&
stencilWriteMask == other.stencilWriteMask &&
stencilSfail == other.stencilSfail &&
stencilDpfail == other.stencilDpfail &&
stencilDppass == other.stencilDppass &&
offsetStates == other.offsetStates &&
offsetFactor == other.offsetFactor &&
offsetUnits == other.offsetUnits &&
lightingEnabled == other.lightingEnabled &&
cullFaceEnabled == other.cullFaceEnabled &&
beginType == other.beginType &&
colorMaskRed == other.colorMaskRed &&
colorMaskGreen == other.colorMaskGreen &&
colorMaskBlue == other.colorMaskBlue &&
colorMaskAlpha == other.colorMaskAlpha &&
currentFrontFace == other.currentFrontFace &&
currentShadeModel == other.currentShadeModel &&
polygonModeBack == other.polygonModeBack &&
polygonModeFront == other.polygonModeFront &&
texture2DEnabled == other.texture2DEnabled &&
texture == other.texture &&
textureVersion == texture->versionNumber &&
fogEnabled == other.fogEnabled &&
fogColorR == other.fogColorR &&
fogColorG == other.fogColorG &&
fogColorB == other.fogColorB &&
scissor[0] == other.scissor[0] &&
scissor[1] == other.scissor[1] &&
scissor[2] == other.scissor[2] &&
scissor[3] == other.scissor[3] &&
viewportTranslation[0] == other.viewportTranslation[0] &&
viewportTranslation[1] == other.viewportTranslation[1] &&
viewportTranslation[2] == other.viewportTranslation[2] &&
viewportScaling[0] == other.viewportScaling[0] &&
viewportScaling[1] == other.viewportScaling[1] &&
viewportScaling[2] == other.viewportScaling[2];
}
void *Internal::allocateFrame(int size) {
GLContext *c = gl_get_context();
return c->_drawCallAllocator[c->_currentAllocatorIndex].allocate(size);
}
} // end of namespace TinyGL

View File

@@ -0,0 +1,260 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef GRAPHICS_TINYGL_ZRECT_H
#define GRAPHICS_TINYGL_ZRECT_H
#include "common/types.h"
#include "common/rect.h"
#include "common/array.h"
#include "graphics/tinygl/zblit.h"
namespace TinyGL {
namespace Internal {
void *allocateFrame(int size);
}
struct GLContext;
struct GLVertex;
struct GLTexture;
struct GLTextureEnvArgument {
GLTextureEnvArgument();
uint
sourceRGB,
operandRGB,
sourceAlpha,
operandAlpha;
};
struct GLTextureEnv {
GLTextureEnv();
bool isDefault() const;
uint envMode, combineRGB, combineAlpha;
byte constA, constR, constG, constB;
GLTextureEnvArgument arg0, arg1;
};
class DrawCall {
public:
enum DrawCallType {
DrawCall_Rasterization,
DrawCall_Blitting,
DrawCall_Clear
};
DrawCall(DrawCallType type) : _type(type) { }
virtual ~DrawCall() { }
bool operator==(const DrawCall &other) const;
bool operator!=(const DrawCall &other) const {
return !(*this == other);
}
virtual void execute(bool restoreState, const Common::Rect *clippingRectangle = nullptr) const = 0;
DrawCallType getType() const { return _type; }
virtual const Common::Rect getDirtyRegion() const { return _dirtyRegion; }
protected:
Common::Rect _dirtyRegion;
private:
DrawCallType _type;
};
class ClearBufferDrawCall : public DrawCall {
public:
ClearBufferDrawCall(bool clearZBuffer, int zValue, bool clearColorBuffer, int rValue, int gValue, int bValue, bool clearStencilBuffer, int stencilValue);
virtual ~ClearBufferDrawCall() { }
bool operator==(const ClearBufferDrawCall &other) const;
virtual void execute(bool restoreState, const Common::Rect *clippingRectangle = nullptr) const;
void *operator new(size_t size) {
return Internal::allocateFrame(size);
}
void operator delete(void *p) { }
private:
bool _clearZBuffer, _clearColorBuffer, _clearStencilBuffer;
int _rValue, _gValue, _bValue, _zValue, _stencilValue;
struct ClearBufferState {
bool enableScissor;
int scissor[4];
bool operator==(const ClearBufferState &other) const {
return
enableScissor == other.enableScissor &&
scissor[0] == other.scissor[0] &&
scissor[1] == other.scissor[1] &&
scissor[2] == other.scissor[2] &&
scissor[3] == other.scissor[3];
}
};
ClearBufferState captureState() const;
void applyState(const ClearBufferState &state, const Common::Rect *clippingRectangle) const;
ClearBufferState _clearState;
};
// Encapsulate a rasterization call: it might execute either a triangle or line rasterization.
class RasterizationDrawCall : public DrawCall {
public:
RasterizationDrawCall();
virtual ~RasterizationDrawCall() { }
bool operator==(const RasterizationDrawCall &other) const;
virtual void execute(bool restoreState, const Common::Rect *clippingRectangle = nullptr) const;
void *operator new(size_t size) {
return Internal::allocateFrame(size);
}
void operator delete(void *p) { }
private:
void computeDirtyRegion();
typedef void (*gl_draw_triangle_func_ptr)(GLContext *c, TinyGL::GLVertex *p0, TinyGL::GLVertex *p1, TinyGL::GLVertex *p2);
int _vertexCount;
GLVertex *_vertex;
gl_draw_triangle_func_ptr _drawTriangleFront, _drawTriangleBack;
struct RasterizationState {
bool enableScissor;
int scissor[4];
int beginType;
int currentFrontFace;
int cullFaceEnabled;
bool colorMaskRed;
bool colorMaskGreen;
bool colorMaskBlue;
bool colorMaskAlpha;
bool depthTestEnabled;
int depthFunction;
int depthWriteMask;
bool texture2DEnabled;
int currentShadeModel;
int polygonModeBack;
int polygonModeFront;
int lightingEnabled;
bool enableBlending;
int sfactor;
int dfactor;
int textureVersion;
int offsetStates;
float offsetFactor;
float offsetUnits;
float viewportTranslation[3];
float viewportScaling[3];
bool alphaTestEnabled;
int alphaFunc;
int alphaRefValue;
bool stencilTestEnabled;
int stencilTestFunc;
byte stencilValue;
byte stencilMask;
byte stencilWriteMask;
int stencilSfail;
int stencilDpfail;
int stencilDppass;
bool polygonStippleEnabled;
byte polygonStipplePattern[128];
uint32 stippleColor;
bool two_color_stipple_enabled;
GLTexture *texture;
uint wrapS, wrapT;
GLTextureEnv textureEnv;
bool fogEnabled;
float fogColorR;
float fogColorG;
float fogColorB;
bool operator==(const RasterizationState &other) const;
};
RasterizationState _state;
RasterizationState captureState() const;
void applyState(const RasterizationState &state, const Common::Rect *clippingRectangle) const;
};
// Encapsulate a blit call: it might execute either a color buffer or z buffer blit.
class BlittingDrawCall : public DrawCall {
public:
enum BlittingMode {
BlitMode_Regular,
BlitMode_Fast,
BlitMode_ZBuffer
};
BlittingDrawCall(BlitImage *image, const BlitTransform &transform, BlittingMode blittingMode);
virtual ~BlittingDrawCall();
bool operator==(const BlittingDrawCall &other) const;
virtual void execute(bool restoreState, const Common::Rect *clippingRectangle = nullptr) const;
BlittingMode getBlittingMode() const { return _mode; }
void *operator new(size_t size) {
return Internal::allocateFrame(size);
}
void operator delete(void *p) { }
private:
void computeDirtyRegion();
BlitImage *_image;
BlitTransform _transform;
BlittingMode _mode;
int _imageVersion;
struct BlittingState {
bool enableScissor;
int scissor[4];
bool enableBlending;
int sfactor, dfactor;
bool alphaTest;
int alphaFunc, alphaRefValue;
int depthTestEnabled;
bool operator==(const BlittingState &other) const {
return
enableScissor == other.enableScissor &&
scissor[0] == other.scissor[0] &&
scissor[1] == other.scissor[1] &&
scissor[2] == other.scissor[2] &&
scissor[3] == other.scissor[3] &&
enableBlending == other.enableBlending &&
sfactor == other.sfactor &&
dfactor == other.dfactor &&
alphaTest == other.alphaTest &&
alphaFunc == other.alphaFunc &&
alphaRefValue == other.alphaRefValue &&
depthTestEnabled == other.depthTestEnabled;
}
};
BlittingState captureState() const;
void applyState(const BlittingState &state, const Common::Rect *clippingRectangle) const;
BlittingState _blitState;
};
} // end of namespace TinyGL
#endif

592
graphics/tinygl/zgl.h Normal file
View File

@@ -0,0 +1,592 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#ifndef TGL_ZGL_H
#define TGL_ZGL_H
#include "common/util.h"
#include "common/textconsole.h"
#include "common/array.h"
#include "common/list.h"
#include "common/scummsys.h"
#include "graphics/pixelformat.h"
#include "graphics/surface.h"
#include "graphics/tinygl/gl.h"
#include "graphics/tinygl/zbuffer.h"
#include "graphics/tinygl/zmath.h"
#include "graphics/tinygl/zblit.h"
#include "graphics/tinygl/zdirtyrect.h"
#include "graphics/tinygl/texelbuffer.h"
namespace TinyGL {
enum {
#define ADD_OP(a,b,c) OP_ ## a ,
#include "graphics/tinygl/opinfo.h"
DUMMY
};
// initially # of allocated GLVertexes (will grow when necessary)
#define POLYGON_MAX_VERTEX 16
// Max # of specular light pow buffers
#define MAX_SPECULAR_BUFFERS 8
// # of entries in specular buffer
#define SPECULAR_BUFFER_SIZE 1024
// specular buffer granularity
#define SPECULAR_BUFFER_RESOLUTION 1024
#define MAX_MODELVIEW_STACK_DEPTH 35
#define MAX_PROJECTION_STACK_DEPTH 8
#define MAX_TEXTURE_STACK_DEPTH 8
#define MAX_NAME_STACK_DEPTH 64
#define MAX_TEXTURE_LEVELS 11
#define T_MAX_LIGHTS 32
#define VERTEX_HASH_SIZE 1031
#define MAX_DISPLAY_LISTS 1024
#define OP_BUFFER_MAX_SIZE 512
#define TGL_OFFSET_FILL 0x1
#define TGL_OFFSET_LINE 0x2
#define TGL_OFFSET_POINT 0x4
enum eDataType {
kIntType,
kInt4Type,
kUintType,
kFloatType,
kFloat2Type,
kFloat4Type,
kFloat16Type
};
union uglValue {
TGLint _int;
TGLint _int4[4];
TGLfloat _float;
TGLfloat _float2[2];
TGLfloat _float4[4];
TGLfloat _float16[16];
};
struct GLSpecBuf {
int shininess_i;
int last_used;
float buf[SPECULAR_BUFFER_SIZE + 1];
struct GLSpecBuf *next;
};
struct GLLight {
Vector4 ambient;
Vector4 diffuse;
Vector4 specular;
bool has_specular;
Vector4 position;
Vector3 spot_direction;
float spot_exponent;
float spot_cutoff;
float attenuation[3];
// precomputed values
float cos_spot_cutoff;
Vector3 norm_spot_direction;
Vector3 norm_position;
// we use a linked list to know which are the enabled lights
int enabled;
struct GLLight *next, *prev;
};
struct GLMaterial {
Vector4 emission;
Vector4 ambient;
Vector4 diffuse;
Vector4 specular;
bool has_specular;
float shininess;
// computed values
int shininess_i;
int do_specular;
};
struct GLViewport {
int xmin, ymin, xsize, ysize;
Vector3 scale;
Vector3 trans;
int updated;
};
union GLParam {
int op;
float f;
int i;
uint ui;
void *p;
};
struct GLParamBuffer {
GLParam ops[OP_BUFFER_MAX_SIZE];
struct GLParamBuffer *next;
};
struct GLList {
GLParamBuffer *first_op_buffer;
// TODO: extensions for a hash table or a better allocating scheme
};
struct GLVertex {
int edge_flag;
Vector3 normal;
Vector4 coord;
Vector4 tex_coord;
Vector4 color;
float fog_factor;
// computed values
Vector4 ec; // eye coordinates
Vector4 pc; // coordinates in the normalized volume
int clip_code; // clip code
ZBufferPoint zp; // integer coordinates for the rasterization
bool operator==(const GLVertex &other) const {
return
edge_flag == other.edge_flag &&
normal == other.normal &&
coord == other.coord &&
tex_coord == other.tex_coord &&
color == other.color &&
ec == other.ec &&
pc == other.pc &&
clip_code == other.clip_code &&
zp == other.zp;
}
bool operator!=(const GLVertex &other) const {
return !(*this == other);
}
};
struct GLImage {
TexelBuffer *pixmap;
int xsize, ysize;
};
// textures
#define TEXTURE_HASH_TABLE_SIZE 256
struct GLTexture {
GLImage images[MAX_TEXTURE_LEVELS];
uint handle;
int versionNumber;
struct GLTexture *next, *prev;
bool disposed;
};
struct tglColorAssociation {
Graphics::PixelFormat pf;
TGLuint format;
TGLuint type;
};
// shared state
struct GLSharedState {
GLList **lists;
GLTexture **texture_hash_table;
};
/**
* A linear allocator implementation.
* The allocator can be initialized to a specific buffer size only once.
* The allocation scheme is pretty simple: pointers are returned relative to a current memory position,
* the allocator starts with an offset of 0 and increases its offset by the allocated amount every time.
* Memory is released through the method free(), care has to be taken to call the destructors of the deallocated objects either manually (for complex struct arrays) or
* by overriding the delete operator (with an empty implementation).
*/
class LinearAllocator {
public:
LinearAllocator() {
_memoryBuffer = nullptr;
_memorySize = 0;
_memoryPosition = 0;
}
void initialize(size_t newSize) {
assert(_memoryBuffer == nullptr);
void *newBuffer = gl_malloc(newSize);
if (newBuffer == nullptr) {
error("Couldn't allocate memory for linear allocator.");
}
_memoryBuffer = newBuffer;
_memorySize = newSize;
}
~LinearAllocator() {
if (_memoryBuffer != nullptr) {
gl_free(_memoryBuffer);
}
}
void *allocate(size_t size) {
if (_memoryPosition + size >= _memorySize) {
error("Allocator out of memory: couldn't allocate more memory from linear allocator.");
}
size_t returnPos = _memoryPosition;
_memoryPosition += size;
return ((byte *)_memoryBuffer) + returnPos;
}
void reset() {
_memoryPosition = 0;
}
private:
void *_memoryBuffer;
size_t _memorySize;
size_t _memoryPosition;
};
struct GLContext;
typedef void (*gl_draw_triangle_func)(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2);
// display context
struct GLContext {
// Z buffer
FrameBuffer *fb;
Common::Rect renderRect;
// scissor
bool scissor_test_enabled;
int scissor[4];
// blending
bool blending_enabled;
int source_blending_factor;
int destination_blending_factor;
// alpha blending
bool alpha_test_enabled;
int alpha_test_func;
int alpha_test_ref_val;
// Internal texture size
int _textureSize;
// lights
GLLight lights[T_MAX_LIGHTS];
GLLight *first_light;
Vector4 ambient_light_model;
int local_light_model;
bool lighting_enabled;
int light_model_two_side;
// materials
GLMaterial materials[2];
bool color_material_enabled;
int current_color_material_mode;
int current_color_material_type;
// textures
GLTexture *current_texture, *default_texture;
uint maxTextureName;
bool texture_2d_enabled;
int texture_mag_filter;
int texture_min_filter;
uint texture_wrap_s;
uint texture_wrap_t;
GLTextureEnv _texEnv;
Common::Array<struct tglColorAssociation> colorAssociationList;
// shared state
GLSharedState shared_state;
// current list
GLParamBuffer *current_op_buffer;
int current_op_buffer_index;
int exec_flag, compile_flag, print_flag;
// matrix
int matrix_mode;
Matrix4 *matrix_stack[3];
Matrix4 *matrix_stack_ptr[3];
int matrix_stack_depth_max[3];
Matrix4 matrix_model_view_inv;
Matrix4 matrix_model_projection;
int matrix_model_projection_updated;
int matrix_model_projection_no_w_transform;
int apply_texture_matrix;
// viewport
GLViewport viewport;
// current state
int polygon_mode_back;
int polygon_mode_front;
int current_front_face;
int current_shade_model;
int current_cull_face;
bool cull_face_enabled;
bool normalize_enabled;
gl_draw_triangle_func draw_triangle_front, draw_triangle_back;
// selection
int render_mode;
uint *select_buffer;
int select_size;
uint *select_ptr, *select_hit;
int select_overflow;
int select_hits;
// names
uint name_stack[MAX_NAME_STACK_DEPTH];
int name_stack_size;
// clear
float clear_depth;
Vector4 clear_color;
int clear_stencil;
// current vertex state
Vector4 current_color;
Vector4 current_normal;
Vector4 current_tex_coord;
int current_edge_flag;
// glBegin / glEnd
int in_begin;
int begin_type;
int vertex_n, vertex_cnt;
int vertex_max;
GLVertex *vertex;
// opengl 1.1 arrays
TGLvoid *vertex_array;
int vertex_array_size;
int vertex_array_stride;
int vertex_array_type;
TGLvoid *normal_array;
int normal_array_stride;
int normal_array_type;
TGLvoid *color_array;
int color_array_size;
int color_array_stride;
int color_array_type;
TGLvoid *texcoord_array;
int texcoord_array_size;
int texcoord_array_stride;
int texcoord_array_type;
int client_states;
// opengl 1.1 polygon offset
float offset_factor;
float offset_units;
int offset_states;
// specular buffer. could probably be shared between contexts,
// but that wouldn't be 100% thread safe
GLSpecBuf *specbuf_first;
int specbuf_used_counter;
int specbuf_num_buffers;
// opaque structure for user's use
void *opaque;
// resize viewport function
int (*gl_resize_viewport)(int *xsize, int *ysize);
// depth test
bool depth_test_enabled;
int depth_func;
bool depth_write_mask;
// stencil
bool stencil_buffer_supported;
bool stencil_test_enabled;
int stencil_test_func;
byte stencil_ref_val;
byte stencil_mask;
byte stencil_write_mask;
int stencil_sfail;
int stencil_dpfail;
int stencil_dppass;
bool color_mask_red;
bool color_mask_green;
bool color_mask_blue;
bool color_mask_alpha;
bool fog_enabled;
int fog_mode;
Vector4 fog_color;
float fog_density;
float fog_start;
float fog_end;
bool _enableDirtyRectangles;
// stipple
bool polygon_stipple_enabled;
byte polygon_stipple_pattern[128];
uint32 stippleColor;
bool two_color_stipple_enabled;
// blit test
Common::List<BlitImage *> _blitImages;
// Draw call queue
Common::List<DrawCall *> _drawCallsQueue;
Common::List<DrawCall *> _previousFrameDrawCallsQueue;
int _currentAllocatorIndex;
LinearAllocator _drawCallAllocator[2];
bool _debugRectsEnabled;
bool _profilingEnabled;
void gl_vertex_transform(GLVertex *v);
void gl_calc_fog_factor(GLVertex *v);
void gl_get_pname(TGLenum pname, union uglValue *data, eDataType &dataType);
public:
// The glop* functions exposed to public, however they are only for internal use.
// Calling them from outside of TinyGL is forbidden
#define ADD_OP(a, b, d) void glop ## a (GLParam *p);
#include "graphics/tinygl/opinfo.h"
void gl_add_op(GLParam *p);
void gl_compile_op(GLParam *p);
void gl_eval_viewport();
void gl_transform_to_viewport(GLVertex *v);
void gl_draw_triangle(GLVertex *p0, GLVertex *p1, GLVertex *p2);
void gl_draw_line(GLVertex *p0, GLVertex *p1);
void gl_draw_point(GLVertex *p0);
static void gl_draw_triangle_point(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2);
static void gl_draw_triangle_line(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2);
static void gl_draw_triangle_fill(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2);
static void gl_draw_triangle_select(GLContext *c, GLVertex *p0, GLVertex *p1, GLVertex *p2);
void gl_draw_triangle_clip(GLVertex *p0, GLVertex *p1, GLVertex *p2, int clip_bit);
void gl_add_select(uint zmin, uint zmax);
void gl_add_select1(int z1, int z2, int z3);
void gl_enable_disable_light(int light, int v);
void gl_shade_vertex(GLVertex *v);
void gl_GetIntegerv(TGLenum pname, TGLint *data);
void gl_GetFloatv(TGLenum pname, TGLfloat *data);
void gl_GetDoublev(TGLenum pname, TGLdouble *data);
void gl_GetBooleanv(TGLenum pname, TGLboolean *data);
void gl_EnableClientState(GLParam *p);
void gl_DisableClientState(GLParam *p);
void gl_VertexPointer(GLParam *p);
void gl_ColorPointer(GLParam *p);
void gl_NormalPointer(GLParam *p);
void gl_TexCoordPointer(GLParam *p);
GLTexture *alloc_texture(uint h);
GLTexture *find_texture(uint h);
void free_texture(GLTexture *t);
void gl_GenTextures(TGLsizei n, TGLuint *textures);
void gl_DeleteTextures(TGLsizei n, const TGLuint *textures);
void gl_PixelStore(TGLenum pname, TGLint param);
void issueDrawCall(DrawCall *drawCall);
void disposeResources();
void disposeDrawCallLists();
void presentBufferDirtyRects(Common::List<Common::Rect> &dirtyAreas);
void presentBufferSimple(Common::List<Common::Rect> &dirtyAreas);
void debugDrawRectangle(Common::Rect rect, int r, int g, int b);
GLSpecBuf *specbuf_get_buffer(const int shininess_i, const float shininess);
void specbuf_cleanup();
TGLint gl_RenderMode(TGLenum mode);
void gl_SelectBuffer(TGLsizei size, TGLuint *buffer);
GLList *alloc_list(int list);
GLList *find_list(uint list);
void delete_list(int list);
void gl_NewList(TGLuint list, TGLenum mode);
void gl_EndList();
TGLboolean gl_IsList(TGLuint list);
TGLuint gl_GenLists(TGLsizei range);
void initSharedState();
void endSharedState();
void init(int screenW, int screenH, Graphics::PixelFormat pixelFormat, int textureSize,
bool enableStencilBuffer, bool dirtyRectsEnable, uint32 drawCallMemorySize);
void deinit();
void gl_print_matrix(const float *m);
void gl_debug(int mode) {
print_flag = mode;
}
};
extern GLContext *gl_ctx;
GLContext *gl_get_context();
#define VERTEX_ARRAY 0x0001
#define COLOR_ARRAY 0x0002
#define NORMAL_ARRAY 0x0004
#define TEXCOORD_ARRAY 0x0008
// this clip epsilon is needed to avoid some rounding errors after
// several clipping stages
#define CLIP_EPSILON (1E-5)
static inline int gl_clipcode(float x, float y, float z, float w1) {
float w;
w = (float)(w1 * (1.0 + CLIP_EPSILON));
return (x < -w) | ((x > w) << 1) | ((y < -w) << 2) | ((y > w) << 3) | ((z < -w) << 4) | ((z > w) << 5);
}
static inline float clampf(float a, float min, float max) {
if (a < min)
return min;
if (a > max)
return max;
else
return a;
}
} // end of namespace TinyGL
#endif

201
graphics/tinygl/zline.cpp Normal file
View File

@@ -0,0 +1,201 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "graphics/tinygl/zbuffer.h"
namespace TinyGL {
template <bool kDepthWrite>
FORCEINLINE void FrameBuffer::putPixel(uint pixelOffset, int color, int x, int y, uint z) {
if (_clippingEnabled)
putPixel<kDepthWrite, true>(pixelOffset, color, x, y, z);
else
putPixel<kDepthWrite, false>(pixelOffset, color, x, y, z);
}
template <bool kDepthWrite, bool kEnableScissor>
FORCEINLINE void FrameBuffer::putPixel(uint pixelOffset, int color, int x, int y, uint z) {
if (kEnableScissor && scissorPixel(x, y)) {
return;
}
uint *pz = _zbuf + pixelOffset;
if (compareDepth(z, *pz)) {
writePixel<true, true, kDepthWrite>(pixelOffset, color, z);
}
}
template <bool kEnableScissor>
FORCEINLINE void FrameBuffer::putPixel(uint pixelOffset, int color, int x, int y) {
if (kEnableScissor && scissorPixel(x, y)) {
return;
}
writePixel<true, true>(pixelOffset, color);
}
template <bool kInterpRGB, bool kInterpZ, bool kDepthWrite>
void FrameBuffer::drawLine(const ZBufferPoint *p1, const ZBufferPoint *p2) {
if (_clippingEnabled)
drawLine<kInterpRGB, kInterpZ, kDepthWrite, true>(p1, p2);
else
drawLine<kInterpRGB, kInterpZ, kDepthWrite, false>(p1, p2);
}
template <bool kInterpRGB, bool kInterpZ, bool kDepthWrite, bool kEnableScissor>
void FrameBuffer::drawLine(const ZBufferPoint *p1, const ZBufferPoint *p2) {
// Based on Bresenham's line algorithm, as implemented in
// https://rosettacode.org/wiki/Bitmap/Bresenham%27s_line_algorithm#C
// with a loop exit condition based on the (unidimensional) taxicab
// distance between p1 and p2 (which is cheap to compute and
// rounding-error-free) so that interpolations are possible without
// code duplication.
// Where we are in unidimensional framebuffer coordinate
unsigned int pixelOffset = p1->y * _pbufWidth + p1->x;
// and in 2d
int x = p1->x;
int y = p1->y;
// How to move on each axis, in both coordinates systems
const int dx = abs(p2->x - p1->x);
const int inc_x = p1->x < p2->x ? 1 : -1;
const int dy = abs(p2->y - p1->y);
const int inc_y = p1->y < p2->y ? 1 : -1;
const int inc_y_pixel = p1->y < p2->y ? _pbufWidth : -_pbufWidth;
// When to move on each axis
int err = (dx > dy ? dx : -dy) / 2;
int e2;
// How many moves
int n = dx > dy ? dx : dy;
// kInterpZ
unsigned int z;
int sz;
// kInterpRGB
int r = p1->r >> (ZB_POINT_RED_BITS - 8);
int g = p1->g >> (ZB_POINT_GREEN_BITS - 8);
int b = p1->b >> (ZB_POINT_BLUE_BITS - 8);
int color = RGB_TO_PIXEL(r, g, b);
int sr, sg, sb;
if (kInterpZ) {
if (n == 0)
return;
sz = (p2->z - p1->z) / n;
z = p1->z;
}
if (kInterpRGB) {
sr = ((p2->r - p1->r) / n) >> (ZB_POINT_RED_BITS - 8);
sg = ((p2->g - p1->g) / n) >> (ZB_POINT_GREEN_BITS - 8);
sb = ((p2->b - p1->b) / n) >> (ZB_POINT_BLUE_BITS - 8);
}
while (n--) {
if (kInterpZ)
putPixel<kDepthWrite, kEnableScissor>(pixelOffset, color, x, y, z);
else
putPixel<kEnableScissor>(pixelOffset, color, x, y);
e2 = err;
if (e2 > -dx) {
err -= dy;
pixelOffset += inc_x;
x += inc_x;
}
if (e2 < dy) {
err += dx;
pixelOffset += inc_y_pixel;
y += inc_y;
}
if (kInterpZ)
z += sz;
if (kInterpRGB) {
r += sr;
g += sg;
b += sb;
color = RGB_TO_PIXEL(r, g, b);
}
}
}
void FrameBuffer::plot(ZBufferPoint *p) {
const uint pixelOffset = p->y * _pbufWidth + p->x;
const int col = RGB_TO_PIXEL(p->r, p->g, p->b);
const uint z = p->z;
if (_depthWrite && _depthTestEnabled)
putPixel<true>(pixelOffset, col, p->x, p->y, z);
else
putPixel<false>(pixelOffset, col, p->x, p->y, z);
}
void FrameBuffer::fillLineFlatZ(ZBufferPoint *p1, ZBufferPoint *p2) {
if (_depthWrite && _depthTestEnabled)
drawLine<false, true, true>(p1, p2);
else
drawLine<false, true, false>(p1, p2);
}
// line with color interpolation
void FrameBuffer::fillLineInterpZ(ZBufferPoint *p1, ZBufferPoint *p2) {
if (_depthWrite && _depthTestEnabled)
drawLine<true, true, true>(p1, p2);
else
drawLine<true, true, false>(p1, p2);
}
// no Z interpolation
void FrameBuffer::fillLineFlat(ZBufferPoint *p1, ZBufferPoint *p2) {
if (_depthWrite && _depthTestEnabled)
drawLine<false, false, true>(p1, p2);
else
drawLine<false, false, false>(p1, p2);
}
void FrameBuffer::fillLineInterp(ZBufferPoint *p1, ZBufferPoint *p2) {
if (_depthWrite && _depthTestEnabled)
drawLine<false, true, true>(p1, p2);
else
drawLine<false, true, false>(p1, p2);
}
void FrameBuffer::fillLineZ(ZBufferPoint *p1, ZBufferPoint *p2) {
// choose if the line should have its color interpolated or not
if (p1->r == p2->r && p1->g == p2->g && p1->b == p2->b)
fillLineFlatZ(p1, p2);
else
fillLineInterpZ(p1, p2);
}
void FrameBuffer::fillLine(ZBufferPoint *p1, ZBufferPoint *p2) {
// choose if the line should have its color interpolated or not
if (p1->r == p2->r && p1->g == p2->g && p1->b == p2->b)
fillLineFlat(p1, p2);
else
fillLineInterp(p1, p2);
}
} // end of namespace TinyGL

341
graphics/tinygl/zmath.cpp Normal file
View File

@@ -0,0 +1,341 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "common/scummsys.h"
#include "graphics/tinygl/zmath.h"
namespace TinyGL {
// Inversion of a 4x4 matrix.
// It's not just unrolling, this is a different implementation that directly
// uses the formula whereas the previous one is using another method (which is generic and thus, slower)
static int MatrixInverse(float *m) {
double inv[16];
inv[0] = m[5] * m[10] * m[15] -
m[5] * m[11] * m[14] -
m[9] * m[6] * m[15] +
m[9] * m[7] * m[14] +
m[13] * m[6] * m[11] -
m[13] * m[7] * m[10];
inv[4] = -m[4] * m[10] * m[15] +
m[4] * m[11] * m[14] +
m[8] * m[6] * m[15] -
m[8] * m[7] * m[14] -
m[12] * m[6] * m[11] +
m[12] * m[7] * m[10];
inv[8] = m[4] * m[9] * m[15] -
m[4] * m[11] * m[13] -
m[8] * m[5] * m[15] +
m[8] * m[7] * m[13] +
m[12] * m[5] * m[11] -
m[12] * m[7] * m[9];
inv[12] = -m[4] * m[9] * m[14] +
m[4] * m[10] * m[13] +
m[8] * m[5] * m[14] -
m[8] * m[6] * m[13] -
m[12] * m[5] * m[10] +
m[12] * m[6] * m[9];
inv[1] = -m[1] * m[10] * m[15] +
m[1] * m[11] * m[14] +
m[9] * m[2] * m[15] -
m[9] * m[3] * m[14] -
m[13] * m[2] * m[11] +
m[13] * m[3] * m[10];
inv[5] = m[0] * m[10] * m[15] -
m[0] * m[11] * m[14] -
m[8] * m[2] * m[15] +
m[8] * m[3] * m[14] +
m[12] * m[2] * m[11] -
m[12] * m[3] * m[10];
inv[9] = -m[0] * m[9] * m[15] +
m[0] * m[11] * m[13] +
m[8] * m[1] * m[15] -
m[8] * m[3] * m[13] -
m[12] * m[1] * m[11] +
m[12] * m[3] * m[9];
inv[13] = m[0] * m[9] * m[14] -
m[0] * m[10] * m[13] -
m[8] * m[1] * m[14] +
m[8] * m[2] * m[13] +
m[12] * m[1] * m[10] -
m[12] * m[2] * m[9];
inv[2] = m[1] * m[6] * m[15] -
m[1] * m[7] * m[14] -
m[5] * m[2] * m[15] +
m[5] * m[3] * m[14] +
m[13] * m[2] * m[7] -
m[13] * m[3] * m[6];
inv[6] = -m[0] * m[6] * m[15] +
m[0] * m[7] * m[14] +
m[4] * m[2] * m[15] -
m[4] * m[3] * m[14] -
m[12] * m[2] * m[7] +
m[12] * m[3] * m[6];
inv[10] = m[0] * m[5] * m[15] -
m[0] * m[7] * m[13] -
m[4] * m[1] * m[15] +
m[4] * m[3] * m[13] +
m[12] * m[1] * m[7] -
m[12] * m[3] * m[5];
inv[14] = -m[0] * m[5] * m[14] +
m[0] * m[6] * m[13] +
m[4] * m[1] * m[14] -
m[4] * m[2] * m[13] -
m[12] * m[1] * m[6] +
m[12] * m[2] * m[5];
inv[3] = -m[1] * m[6] * m[11] +
m[1] * m[7] * m[10] +
m[5] * m[2] * m[11] -
m[5] * m[3] * m[10] -
m[9] * m[2] * m[7] +
m[9] * m[3] * m[6];
inv[7] = m[0] * m[6] * m[11] -
m[0] * m[7] * m[10] -
m[4] * m[2] * m[11] +
m[4] * m[3] * m[10] +
m[8] * m[2] * m[7] -
m[8] * m[3] * m[6];
inv[11] = -m[0] * m[5] * m[11] +
m[0] * m[7] * m[9] +
m[4] * m[1] * m[11] -
m[4] * m[3] * m[9] -
m[8] * m[1] * m[7] +
m[8] * m[3] * m[5];
inv[15] = m[0] * m[5] * m[10] -
m[0] * m[6] * m[9] -
m[4] * m[1] * m[10] +
m[4] * m[2] * m[9] +
m[8] * m[1] * m[6] -
m[8] * m[2] * m[5];
double det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
if (det == 0)
return false;
det = 1.0 / det;
for (int i = 0; i < 16; i++) {
m[i] = inv[i] * det;
}
return true;
}
void Vector3::normalize() {
float n = sqrt(X * X + Y * Y + Z * Z);
if (n != 0) {
X /= n;
Y /= n;
Z /= n;
}
}
Vector4::Vector4(const Vector3 &vec, float w) {
X = vec.X;
Y = vec.Y;
Z = vec.Z;
W = w;
}
void Matrix4::identity() {
memset(_m, 0, sizeof(_m));
_m[0][0] = 1.0f;
_m[1][1] = 1.0f;
_m[2][2] = 1.0f;
_m[3][3] = 1.0f;
}
Matrix4 Matrix4::transpose() const {
Matrix4 a;
a._m[0][0] = _m[0][0];
a._m[0][1] = _m[1][0];
a._m[0][2] = _m[2][0];
a._m[0][3] = _m[3][0];
a._m[1][0] = _m[0][1];
a._m[1][1] = _m[1][1];
a._m[1][2] = _m[2][1];
a._m[1][3] = _m[3][1];
a._m[2][0] = _m[0][2];
a._m[2][1] = _m[1][2];
a._m[2][2] = _m[2][2];
a._m[2][3] = _m[3][2];
a._m[3][0] = _m[0][3];
a._m[3][1] = _m[1][3];
a._m[3][2] = _m[2][3];
a._m[3][3] = _m[3][3];
return a;
}
void Matrix4::transpose() {
Matrix4 tmp = *this;
_m[0][0] = tmp._m[0][0];
_m[0][1] = tmp._m[1][0];
_m[0][2] = tmp._m[2][0];
_m[0][3] = tmp._m[3][0];
_m[1][0] = tmp._m[0][1];
_m[1][1] = tmp._m[1][1];
_m[1][2] = tmp._m[2][1];
_m[1][3] = tmp._m[3][1];
_m[2][0] = tmp._m[0][2];
_m[2][1] = tmp._m[1][2];
_m[2][2] = tmp._m[2][2];
_m[2][3] = tmp._m[3][2];
_m[3][0] = tmp._m[0][3];
_m[3][1] = tmp._m[1][3];
_m[3][2] = tmp._m[2][3];
_m[3][3] = tmp._m[3][3];
}
Matrix4 Matrix4::inverseOrtho() const {
Matrix4 a;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
a._m[i][j] = _m[j][i];
}
}
a._m[3][0] = 0.0f;
a._m[3][1] = 0.0f;
a._m[3][2] = 0.0f;
a._m[3][3] = 1.0f;
for (int i = 0; i < 3; i++) {
float s = 0;
for (int j = 0; j < 3; j++) {
s -= _m[j][i] * _m[j][3];
}
a._m[i][3] = s;
}
return a;
}
Matrix4 Matrix4::inverse() const {
Matrix4 result = *this;
MatrixInverse((float *)result._m);
return result;
}
void Matrix4::rotation(float t, int u) {
float s, c;
int v, w;
identity();
if ((v = u + 1) > 2)
v = 0;
if ((w = v + 1) > 2)
w = 0;
s = sin(t);
c = cos(t);
_m[v][v] = c;
_m[v][w] = -s;
_m[w][v] = s;
_m[w][w] = c;
}
bool Matrix4::isIdentity() const {
//NOTE: This might need to be implemented in a fault-tolerant way.
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (i == j) {
if (_m[i][j] != 1.0) {
return false;
}
} else if (_m[i][j] != 0.0) {
return false;
}
}
}
return true;
}
void Matrix4::invert() {
MatrixInverse((float *)_m);
}
Matrix4 Matrix4::frustum(float left, float right, float bottom, float top, float nearp, float farp) {
float x, y, A, B, C, D;
x = (float)((2.0 * nearp) / (right - left));
y = (float)((2.0 * nearp) / (top - bottom));
A = (right + left) / (right - left);
B = (top + bottom) / (top - bottom);
C = -(farp + nearp) / (farp - nearp);
D = (float)(-(2.0 * farp * nearp) / (farp - nearp));
Matrix4 m;
m._m[0][0] = x; m._m[0][1] = 0; m._m[0][2] = A; m._m[0][3] = 0;
m._m[1][0] = 0; m._m[1][1] = y; m._m[1][2] = B; m._m[1][3] = 0;
m._m[2][0] = 0; m._m[2][1] = 0; m._m[2][2] = C; m._m[2][3] = D;
m._m[3][0] = 0; m._m[3][1] = 0; m._m[3][2] = -1; m._m[3][3] = 0;
return m;
}
void Matrix4::translate(float x, float y, float z) {
_m[0][3] += _m[0][0] * x + _m[0][1] * y + _m[0][2] * z;
_m[1][3] += _m[1][0] * x + _m[1][1] * y + _m[1][2] * z;
_m[2][3] += _m[2][0] * x + _m[2][1] * y + _m[2][2] * z;
_m[3][3] += _m[3][0] * x + _m[3][1] * y + _m[3][2] * z;
}
void Matrix4::scale(float x, float y, float z) {
_m[0][0] *= x; _m[0][1] *= y; _m[0][2] *= z;
_m[1][0] *= x; _m[1][1] *= y; _m[1][2] *= z;
_m[2][0] *= x; _m[2][1] *= y; _m[2][2] *= z;
_m[3][0] *= x; _m[3][1] *= y; _m[3][2] *= z;
}
} // end of namespace TinyGL

270
graphics/tinygl/zmath.h Normal file
View File

@@ -0,0 +1,270 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#ifndef GRAPHICS_TINYGL_ZMATH_H
#define GRAPHICS_TINYGL_ZMATH_H
namespace TinyGL {
#define X _v[0]
#define Y _v[1]
#define Z _v[2]
#define W _v[3]
// Matrix & Vertex
class Vector3 {
public:
Vector3() { }
Vector3(float x, float y, float z) {
X = x;
Y = y;
Z = z;
}
void normalize();
float getLength() const { return sqrt(X * X + Y * Y + Z * Z); }
bool operator==(const Vector3 &other) const {
return X == other.X && Y == other.Y && Z == other.Z;
}
bool operator!=(const Vector3 &other) const {
return X != other.X || Y != other.Y || Z != other.Z;
}
Vector3 operator-() const {
return Vector3(-X, -Y, -Z);
}
Vector3 operator*(float factor) const {
return Vector3(X * factor, Y * factor, Z * factor);
}
Vector3 operator+(const Vector3 &other) const {
return Vector3(X + other.X, Y + other.Y, Z + other.Z);
}
Vector3 operator-(const Vector3 &other) const {
return Vector3(X - other.X, Y - other.Y, Z - other.Z);
}
Vector3 &operator*=(float factor) {
X *= factor;
Y *= factor;
Z *= factor;
return *this;
}
Vector3 &operator+=(float value) {
X += value;
Y += value;
Z += value;
return *this;
}
Vector3 &operator-=(float value) {
X -= value;
Y -= value;
Z -= value;
return *this;
}
float _v[3];
};
class Vector4 {
public:
Vector4() { }
Vector4(const Vector3 &vec, float w);
Vector4(float x, float y, float z, float w) {
X = x;
Y = y;
Z = z;
W = w;
}
bool operator==(const Vector4 &other) const {
return X == other.X && Y == other.Y && Z == other.Z && W == other.W;
}
bool operator!=(const Vector4 &other) const {
return X != other.X || Y != other.Y || Z != other.Z || W != other.W;
}
Vector4 operator-() const {
return Vector4(-X, -Y, -Z, -W);
}
Vector4 operator*(float factor) const {
return Vector4(X * factor, Y * factor, Z * factor,W * factor);
}
Vector4 operator+(const Vector4 &other) const {
return Vector4(X + other.X, Y + other.Y, Z + other.Z, W + other.W);
}
Vector4 operator-(const Vector4 &other) const {
return Vector4(X - other.X, Y - other.Y, Z - other.Z, W - other.W);
}
Vector4 &operator*=(float factor) {
X *= factor;
Y *= factor;
Z *= factor;
W *= factor;
return *this;
}
Vector4 &operator+=(float value) {
X += value;
Y += value;
Z += value;
W += value;
return *this;
}
Vector4 &operator-=(float value) {
X -= value;
Y -= value;
Z -= value;
W -= value;
return *this;
}
float _v[4];
};
class Matrix4 {
public:
Matrix4() { }
bool isIdentity() const;
inline Matrix4 operator+(const Matrix4 &b) const {
Matrix4 result;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
result._m[i][j] = _m[i][j] + b._m[i][j];
}
}
return result;
}
inline Matrix4 operator-(const Matrix4 &b) const {
Matrix4 result;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
result._m[i][j] = _m[i][j] - b._m[i][j];
}
}
return result;
}
inline Matrix4 operator*(const Matrix4 &b) const {
Matrix4 result;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
float s = 0.0;
for (int k = 0; k < 4; k++)
s += _m[i][k] * b._m[k][j];
result._m[i][j] = s;
}
}
return result;
}
inline Matrix4 &operator*=(const Matrix4 &b) {
Matrix4 a = *this;
float s;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
s = 0.0;
for (int k = 0; k < 4; k++)
s += a._m[i][k] * b._m[k][j];
_m[i][j] = s;
}
}
return *this;
}
void scale(float x, float y, float z);
void translate(float x, float y, float z);
void identity();
void rotation(float t, int);
void invert();
void transpose();
Matrix4 transpose() const;
Matrix4 inverseOrtho() const;
Matrix4 inverse() const;
static Matrix4 frustum(float left, float right, float bottom, float top, float nearp, float farp);
inline void transform(const Vector3 &vector, Vector3 &out) const {
out.X = vector.X * _m[0][0] + vector.Y * _m[0][1] + vector.Z * _m[0][2] + _m[0][3];
out.Y = vector.X * _m[1][0] + vector.Y * _m[1][1] + vector.Z * _m[1][2] + _m[1][3];
out.Z = vector.X * _m[2][0] + vector.Y * _m[2][1] + vector.Z * _m[2][2] + _m[2][3];
}
// Transform the vector as if this were a 3x3 matrix.
inline void transform3x3(const Vector3 &vector, Vector3 &out) const {
out.X = vector.X * _m[0][0] + vector.Y * _m[0][1] + vector.Z * _m[0][2];
out.Y = vector.X * _m[1][0] + vector.Y * _m[1][1] + vector.Z * _m[1][2];
out.Z = vector.X * _m[2][0] + vector.Y * _m[2][1] + vector.Z * _m[2][2];
}
// Transform the vector as if this were a 3x3 matrix.
inline void transform3x3(const Vector4 &vector, Vector3 &out) const {
out.X = vector.X * _m[0][0] + vector.Y * _m[0][1] + vector.Z * _m[0][2];
out.Y = vector.X * _m[1][0] + vector.Y * _m[1][1] + vector.Z * _m[1][2];
out.Z = vector.X * _m[2][0] + vector.Y * _m[2][1] + vector.Z * _m[2][2];
}
// Transform the vector as if this were a 3x4 matrix.
inline void transform3x4(const Vector4 &vector, Vector4 &out) const {
out.X = vector.X * _m[0][0] + vector.Y * _m[0][1] + vector.Z * _m[0][2] + _m[0][3];
out.Y = vector.X * _m[1][0] + vector.Y * _m[1][1] + vector.Z * _m[1][2] + _m[1][3];
out.Z = vector.X * _m[2][0] + vector.Y * _m[2][1] + vector.Z * _m[2][2] + _m[2][3];
out.W = vector.X * _m[3][0] + vector.Y * _m[3][1] + vector.Z * _m[3][2] + _m[3][3];
}
inline void transform(const Vector4 &vector, Vector4 &out) const {
out.X = vector.X * _m[0][0] + vector.Y * _m[0][1] + vector.Z * _m[0][2] + vector.W * _m[0][3];
out.Y = vector.X * _m[1][0] + vector.Y * _m[1][1] + vector.Z * _m[1][2] + vector.W * _m[1][3];
out.Z = vector.X * _m[2][0] + vector.Y * _m[2][1] + vector.Z * _m[2][2] + vector.W * _m[2][3];
out.W = vector.X * _m[3][0] + vector.Y * _m[3][1] + vector.Z * _m[3][2] + vector.W * _m[3][3];
}
float _m[4][4];
};
} // end of namespace TinyGL
#endif

View File

@@ -0,0 +1,850 @@
/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
/*
* This file is based on, or a modified version of code from TinyGL (C) 1997-2022 Fabrice Bellard,
* which is licensed under the MIT license (see LICENSE).
* It also has modifications by the ResidualVM-team, which are covered under the GPLv2 (or later).
*/
#include "common/endian.h"
#include "graphics/tinygl/texelbuffer.h"
#include "graphics/tinygl/zbuffer.h"
#include "graphics/tinygl/zgl.h"
namespace TinyGL {
static const int NB_INTERP = 8;
static bool applyStipplePattern(int x, int y, const byte *stipple) {
int stippleX = x % 32;
int stippleY = y % 32;
int byteIndex = stippleY * 4 + (stippleX / 8); // 4 bytes per row
int bitIndex = stippleX % 8;
byte bitmask = 1 << (7 - bitIndex);
return (stipple[byteIndex] & bitmask);
}
template <bool kDepthWrite, bool kSmoothMode, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kStippleEnabled, bool kDepthTestEnabled>
void FrameBuffer::putPixelNoTexture(int fbOffset, uint *pz, byte *ps, int _a,
int x, int y, uint &z, uint &r, uint &g, uint &b, uint &a,
int &dzdx, int &drdx, int &dgdx, int &dbdx, uint dadx,
uint &fog, int fog_r, int fog_g, int fog_b, int &dfdx) {
bool useStippleColor = false;
if (kEnableScissor && scissorPixel(x + _a, y)) {
goto end;
}
if (kStippleEnabled && applyStipplePattern(x + _a, y, _polygonStipplePattern)) {
if (_twoColorStippleEnabled)
useStippleColor = true;
else
goto end;
}
if (kStencilEnabled) {
bool stencilResult = stencilTest(ps[_a]);
if (!stencilResult) {
stencilOp(false, true, ps + _a);
goto end;
}
}
bool depthTestResult;
if (kDepthTestEnabled) {
depthTestResult = compareDepth(z, pz[_a]);
} else {
depthTestResult = true;
}
if (kStencilEnabled) {
stencilOp(true, depthTestResult, ps + _a);
}
if (depthTestResult) {
if (useStippleColor) {
uint sb = (_stippleColor >> 16) & 0xFF;
uint sg = (_stippleColor >> 8) & 0xFF;
uint sr = _stippleColor & 0xFF;
writePixel<kEnableAlphaTest, kEnableBlending, kDepthWrite, kFogMode>
(fbOffset + _a, a >> (ZB_POINT_ALPHA_BITS - 8), sr, sg, sb, z, fog, fog_r, fog_g, fog_b);
} else {
writePixel<kEnableAlphaTest, kEnableBlending, kDepthWrite, kFogMode>
(fbOffset + _a, a >> (ZB_POINT_ALPHA_BITS - 8), r >> (ZB_POINT_RED_BITS - 8), g >> (ZB_POINT_GREEN_BITS - 8), b >> (ZB_POINT_BLUE_BITS - 8),
z, fog, fog_r, fog_g, fog_b);
}
}
end:
z += dzdx;
if (kFogMode) {
fog += dfdx;
}
if (kSmoothMode) {
r += drdx;
g += dgdx;
b += dbdx;
a += dadx;
}
}
template <bool kDepthWrite, FrameBuffer::ColorMode kColorMode, bool kSmoothMode, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kDepthTestEnabled>
void FrameBuffer::putPixelTexture(int fbOffset, const TexelBuffer *texture,
uint wrap_s, uint wrap_t, uint *pz, byte *ps, int _a,
int x, int y, uint &z, int &t, int &s,
uint &r, uint &g, uint &b, uint &a,
int &dzdx, int &dsdx, int &dtdx, int &drdx, int &dgdx, int &dbdx, uint dadx,
uint &fog, int fog_r, int fog_g, int fog_b, int &dfdx) {
if (kEnableScissor && scissorPixel(x + _a, y)) {
goto end;
}
if (kStencilEnabled) {
bool stencilResult = stencilTest(ps[_a]);
if (!stencilResult) {
stencilOp(false, true, ps + _a);
goto end;
}
}
bool depthTestResult;
if (kDepthTestEnabled) {
depthTestResult = compareDepth(z, pz[_a]);
} else {
depthTestResult = true;
}
if (kStencilEnabled) {
stencilOp(true, depthTestResult, ps + _a);
}
if (depthTestResult) {
uint8 c_a, c_r, c_g, c_b;
texture->getARGBAt(wrap_s, wrap_t, s, t, c_a, c_r, c_g, c_b);
switch (kColorMode) {
case ColorMode::NoInterpolation:
break;
case ColorMode::Default:
applyModulation(a, r, g, b, c_a, c_r, c_g, c_b);
break;
case ColorMode::CustomTexEnv:
applyTextureEnvironment(
texture->internalformat(),
a, r, g, b,
c_a, c_r, c_g, c_b);
break;
default:
// this would be a "if constexpr" and "static_assert" on C++17
assert(false && "Unimplemented color mode");
break;
}
writePixel<kEnableAlphaTest, kEnableBlending, kDepthWrite, kFogMode>(fbOffset + _a, c_a, c_r, c_g, c_b, z, fog, fog_r, fog_g, fog_b);
}
end:
z += dzdx;
s += dsdx;
t += dtdx;
if (kFogMode) {
fog += dfdx;
}
if (kSmoothMode) {
a += dadx;
r += drdx;
g += dgdx;
b += dbdx;
}
}
template <bool kDepthWrite, bool kEnableScissor, bool kStencilEnabled, bool kStippleEnabled, bool kDepthTestEnabled>
void FrameBuffer::putPixelDepth(uint *pz, byte *ps, int _a, int x, int y, uint &z, int &dzdx) {
if (kEnableScissor && scissorPixel(x + _a, y)) {
goto end;
}
/*if (kStippleEnabled && !applyStipplePattern(x + _a, y, _polygonStipplePattern)) {
return;
}*/
if (kStencilEnabled) {
bool stencilResult = stencilTest(ps[_a]);
if (!stencilResult) {
stencilOp(false, true, ps + _a);
goto end;
}
}
bool depthTestResult;
if (kDepthTestEnabled) {
depthTestResult = compareDepth(z, pz[_a]);
} else {
depthTestResult = true;
}
if (kStencilEnabled) {
stencilOp(true, depthTestResult, ps + _a);
}
if (kDepthWrite && depthTestResult) {
pz[_a] = z;
}
end:
z += dzdx;
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode, bool kAlphaTestEnabled, bool kEnableScissor,
bool kBlendingEnabled, bool kStencilEnabled, bool kStippleEnabled, bool kDepthTestEnabled>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const TexelBuffer *texture;
float fdzdx = 0, fndzdx = 0, ndszdx = 0, ndtzdx = 0;
ZBufferPoint *tp, *pr1 = 0, *pr2 = 0, *l1 = 0, *l2 = 0;
float fdx1, fdx2, fdy1, fdy2, fz0, d1, d2;
uint *pz1 = nullptr;
byte *ps1 = nullptr;
int part, update_left = 1, update_right = 1;
int nb_lines, dx1, dy1, tmp, dx2, dy2, y;
int error = 0, derror = 0;
int x1 = 0, dxdy_min = 0, dxdy_max = 0;
// warning: x2 is multiplied by 2^16
int x2 = 0, dx2dy2 = 0;
int z1 = 0, dzdx = 0, dzdy = 0, dzdl_min = 0, dzdl_max = 0;
int f1 = 0, dfdx = 0, dfdy = 0, dfdl_min = 0, dfdl_max = 0;
int r1 = 0, drdx = 0, drdy = 0, drdl_min = 0, drdl_max = 0;
int g1 = 0, dgdx = 0, dgdy = 0, dgdl_min = 0, dgdl_max = 0;
int b1 = 0, dbdx = 0, dbdy = 0, dbdl_min = 0, dbdl_max = 0;
int a1 = 0, dadx = 0, dady = 0, dadl_min = 0, dadl_max = 0;
float sz1 = 0.0, dszdx = 0, dszdy = 0, dszdl_min = 0.0, dszdl_max = 0.0;
float tz1 = 0.0, dtzdx = 0, dtzdy = 0, dtzdl_min = 0.0, dtzdl_max = 0.0;
byte fog_r = 0, fog_g = 0, fog_b = 0;
// we sort the vertex with increasing y
if (p1->y < p0->y) {
tp = p0;
p0 = p1;
p1 = tp;
}
if (p2->y < p0->y) {
tp = p2;
p2 = p1;
p1 = p0;
p0 = tp;
} else if (p2->y < p1->y) {
tp = p1;
p1 = p2;
p2 = tp;
}
// we compute dXdx and dXdy for all interpolated values
fdx1 = (float)(p1->x - p0->x);
fdy1 = (float)(p1->y - p0->y);
fdx2 = (float)(p2->x - p0->x);
fdy2 = (float)(p2->y - p0->y);
fz0 = fdx1 * fdy2 - fdx2 * fdy1;
if (fz0 == 0)
return;
fz0 = (float)(1.0 / fz0);
fdx1 *= fz0;
fdy1 *= fz0;
fdx2 *= fz0;
fdy2 *= fz0;
if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
fog_r = _fogColorR * 255;
fog_g = _fogColorG * 255;
fog_b = _fogColorB * 255;
d1 = (float)(p1->f - p0->f);
d2 = (float)(p2->f - p0->f);
dfdx = (int)(fdy2 * d1 - fdy1 * d2);
dfdy = (int)(fdx1 * d2 - fdx2 * d1);
}
if (kInterpZ) {
d1 = (float)(p1->z - p0->z);
d2 = (float)(p2->z - p0->z);
dzdx = (int)(fdy2 * d1 - fdy1 * d2);
dzdy = (int)(fdx1 * d2 - fdx2 * d1);
}
if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
d1 = (float)(p1->r - p0->r);
d2 = (float)(p2->r - p0->r);
drdx = (int)(fdy2 * d1 - fdy1 * d2);
drdy = (int)(fdx1 * d2 - fdx2 * d1);
d1 = (float)(p1->g - p0->g);
d2 = (float)(p2->g - p0->g);
dgdx = (int)(fdy2 * d1 - fdy1 * d2);
dgdy = (int)(fdx1 * d2 - fdx2 * d1);
d1 = (float)(p1->b - p0->b);
d2 = (float)(p2->b - p0->b);
dbdx = (int)(fdy2 * d1 - fdy1 * d2);
dbdy = (int)(fdx1 * d2 - fdx2 * d1);
d1 = (float)(p1->a - p0->a);
d2 = (float)(p2->a - p0->a);
dadx = (int)(fdy2 * d1 - fdy1 * d2);
dady = (int)(fdx1 * d2 - fdx2 * d1);
}
if (kInterpST || kInterpSTZ) {
if (kInterpSTZ) {
float zz;
zz = (float)p0->z;
p0->sz = (float)p0->s * zz;
p0->tz = (float)p0->t * zz;
zz = (float)p1->z;
p1->sz = (float)p1->s * zz;
p1->tz = (float)p1->t * zz;
zz = (float)p2->z;
p2->sz = (float)p2->s * zz;
p2->tz = (float)p2->t * zz;
} else {
p0->sz = (float)p0->s;
p0->tz = (float)p0->t;
p1->sz = (float)p1->s;
p1->tz = (float)p1->t;
p2->sz = (float)p2->s;
p2->tz = (float)p2->t;
}
d1 = p1->sz - p0->sz;
d2 = p2->sz - p0->sz;
dszdx = (fdy2 * d1 - fdy1 * d2);
dszdy = (fdx1 * d2 - fdx2 * d1);
d1 = p1->tz - p0->tz;
d2 = p2->tz - p0->tz;
dtzdx = (fdy2 * d1 - fdy1 * d2);
dtzdy = (fdx1 * d2 - fdx2 * d1);
}
int polyOffset = 0;
if (kInterpZ && kColorMode != ColorMode::NoInterpolation && (_offsetStates & TGL_OFFSET_FILL)) {
int m = MAX(ABS(dzdx), ABS(dzdy));
polyOffset = -m * _offsetFactor + -_offsetUnits * (1 << 6);
}
// screen coordinates
int pp1 = _pbufWidth * p0->y;
pz1 = _zbuf + p0->y * _pbufWidth;
if (kStencilEnabled) {
ps1 = _sbuf + p0->y * _pbufWidth;
}
if (kColorMode != ColorMode::NoInterpolation && !kSmoothMode) {
r1 = p2->r;
g1 = p2->g;
b1 = p2->b;
a1 = p2->a;
}
if (kColorMode != ColorMode::NoInterpolation && (kInterpST || kInterpSTZ)) {
texture = _currentTexture;
fdzdx = (float)dzdx;
fndzdx = NB_INTERP * fdzdx;
ndszdx = NB_INTERP * dszdx;
ndtzdx = NB_INTERP * dtzdx;
}
if (fz0 > 0) {
l1 = p0;
l2 = p2;
pr1 = p0;
pr2 = p1;
} else {
l1 = p0;
l2 = p1;
pr1 = p0;
pr2 = p2;
}
nb_lines = p1->y - p0->y;
y = p0->y;
for (part = 0; part < 2; part++) {
if (part == 1) {
// second part
if (fz0 > 0) {
update_left = 0;
pr1 = p1;
pr2 = p2;
} else {
update_right = 0;
l1 = p1;
l2 = p2;
}
nb_lines = p2->y - p1->y + 1;
}
// compute the values for the left edge
if (update_left) {
dy1 = l2->y - l1->y;
dx1 = l2->x - l1->x;
if (dy1 > 0) {
if (dx1 < 0) {
tmp = (-dx1 << 16) / dy1;
tmp = -tmp;
} else {
tmp = (dx1 << 16) / dy1;
}
} else {
tmp = 0;
}
x1 = l1->x;
error = 0;
derror = tmp & 0x0000ffff;
dxdy_min = tmp >> 16;
dxdy_max = dxdy_min + 1;
if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
f1 = l1->f;
dfdl_min = (dfdy + dfdx * dxdy_min);
dfdl_max = dfdl_min + dfdx;
}
if (kInterpZ) {
z1 = l1->z + polyOffset;
dzdl_min = (dzdy + dzdx * dxdy_min);
dzdl_max = dzdl_min + dzdx;
}
if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
r1 = l1->r;
drdl_min = (drdy + drdx * dxdy_min);
drdl_max = drdl_min + drdx;
g1 = l1->g;
dgdl_min = (dgdy + dgdx * dxdy_min);
dgdl_max = dgdl_min + dgdx;
b1 = l1->b;
dbdl_min = (dbdy + dbdx * dxdy_min);
dbdl_max = dbdl_min + dbdx;
a1 = l1->a;
dadl_min = (dady + dadx * dxdy_min);
dadl_max = dadl_min + dadx;
}
if (kInterpST || kInterpSTZ) {
sz1 = l1->sz;
dszdl_min = (dszdy + dszdx * dxdy_min);
dszdl_max = dszdl_min + dszdx;
tz1 = l1->tz;
dtzdl_min = (dtzdy + dtzdx * dxdy_min);
dtzdl_max = dtzdl_min + dtzdx;
}
}
// compute values for the right edge
if (update_right) {
dx2 = (pr2->x - pr1->x);
dy2 = (pr2->y - pr1->y);
if (dy2 > 0) {
if (dx2 < 0) {
dx2dy2 = (-dx2 << 16) / dy2;
dx2dy2 = -dx2dy2;
} else {
dx2dy2 = (dx2 << 16) / dy2;
}
} else {
dx2dy2 = 0;
}
x2 = pr1->x << 16;
}
// we draw all the scan line of the part
while (nb_lines > 0) {
int x = x1;
if (kColorMode == ColorMode::NoInterpolation) {
int n;
uint *pz;
byte *ps = nullptr;
uint z;
n = (x2 >> 16) - x1;
if (kInterpZ) {
pz = pz1 + x1;
z = z1;
}
if (kStencilEnabled) {
ps = ps1 + x1;
}
while (n >= 3) {
putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>(pz, ps, 0, x, y, z, dzdx);
putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>(pz, ps, 1, x, y, z, dzdx);
putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>(pz, ps, 2, x, y, z, dzdx);
putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>(pz, ps, 3, x, y, z, dzdx);
if (kInterpZ) {
pz += 4;
}
if (kStencilEnabled) {
ps += 4;
}
n -= 4;
x += 4;
}
while (n >= 0) {
putPixelDepth<kDepthWrite, kEnableScissor, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>(pz, ps, 0, x, y, z, dzdx);
if (kInterpZ) {
pz += 1;
}
if (kStencilEnabled) {
ps += 1;
}
n -= 1;
x += 1;
}
} else if (!(kInterpST || kInterpSTZ)) {
uint *pz;
byte *ps = nullptr;
int pp;
uint z, r, g, b, a, fog;
int n = (x2 >> 16) - x1;
pp = pp1 + x1;
r = r1;
g = g1;
b = b1;
a = a1;
if (kFogMode) {
fog = f1;
}
if (kInterpZ) {
pz = pz1 + x1;
z = z1;
}
if (kStencilEnabled) {
ps = ps1 + x1;
}
while (n >= 3) {
putPixelNoTexture<kDepthWrite, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>
(pp, pz, ps, 0, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx, fog, fog_r, fog_g, fog_b, dfdx);
putPixelNoTexture<kDepthWrite, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>
(pp, pz, ps, 1, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx, fog, fog_r, fog_g, fog_b, dfdx);
putPixelNoTexture<kDepthWrite, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>
(pp, pz, ps, 2, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx, fog, fog_r, fog_g, fog_b, dfdx);
putPixelNoTexture<kDepthWrite, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>
(pp, pz, ps, 3, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx, fog, fog_r, fog_g, fog_b, dfdx);
pp += 4;
if (kInterpZ) {
pz += 4;
}
if (kStencilEnabled) {
ps += 4;
}
n -= 4;
x += 4;
}
while (n >= 0) {
putPixelNoTexture<kDepthWrite, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kStippleEnabled, kDepthTestEnabled>
(pp, pz, ps, 0, x, y, z, r, g, b, a, dzdx, drdx, dgdx, dbdx, dadx, fog, fog_r, fog_g, fog_b, dfdx);
pp += 1;
if (kInterpZ) {
pz += 1;
}
if (kStencilEnabled) {
ps += 1;
}
n -= 1;
x += 1;
}
} else if (kInterpST || kInterpSTZ) {
uint *pz;
byte *ps = nullptr;
int s, t;
uint z, r, g, b, a, fog;
int n, pp;
float sz, tz, fz, zinv;
int dsdx, dtdx;
n = (x2 >> 16) - x1;
fz = (float)z1;
zinv = (float)(1.0 / fz);
pp = pp1 + x1;
if (kFogMode) {
fog = f1;
}
if (kInterpZ) {
pz = pz1 + x1;
z = z1;
}
if (kStencilEnabled) {
ps = ps1 + x1;
}
sz = sz1;
tz = tz1;
r = r1;
g = g1;
b = b1;
a = a1;
while (n >= (NB_INTERP - 1)) {
{
float ss, tt;
ss = sz * zinv;
tt = tz * zinv;
s = (int)ss;
t = (int)tt;
dsdx = (int)((dszdx - ss * fdzdx) * zinv);
dtdx = (int)((dtzdx - tt * fdzdx) * zinv);
fz += fndzdx;
zinv = (float)(1.0 / fz);
}
for (int _a = 0; _a < NB_INTERP; _a++) {
putPixelTexture<kDepthWrite, kColorMode, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kDepthTestEnabled>
(pp, texture, _wrapS, _wrapT, pz, ps, _a, x, y, z, t, s, r, g, b, a, dzdx, dsdx, dtdx, drdx, dgdx, dbdx, dadx, fog, fog_r, fog_g, fog_b, dfdx);
}
pp += NB_INTERP;
if (kInterpZ) {
pz += NB_INTERP;
}
if (kStencilEnabled) {
ps += NB_INTERP;
}
sz += ndszdx;
tz += ndtzdx;
n -= NB_INTERP;
x += NB_INTERP;
}
{
float ss, tt;
ss = sz * zinv;
tt = tz * zinv;
s = (int)ss;
t = (int)tt;
dsdx = (int)((dszdx - ss * fdzdx) * zinv);
dtdx = (int)((dtzdx - tt * fdzdx) * zinv);
}
while (n >= 0) {
putPixelTexture<kDepthWrite, kColorMode, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kDepthTestEnabled>
(pp, texture, _wrapS, _wrapT, pz, ps, 0, x, y, z, t, s, r, g, b, a, dzdx, dsdx, dtdx, drdx, dgdx, dbdx, dadx, fog, fog_r, fog_g, fog_b, dfdx);
pp += 1;
if (kInterpZ) {
pz += 1;
}
if (kStencilEnabled) {
ps += 1;
}
n -= 1;
x += 1;
}
}
// left edge
error += derror;
if (error > 0) {
error -= 0x10000;
x1 += dxdy_max;
if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
f1 += dfdl_max;
}
if (kInterpZ) {
z1 += dzdl_max;
}
if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
r1 += drdl_max;
g1 += dgdl_max;
b1 += dbdl_max;
a1 += dadl_max;
}
if (kInterpST || kInterpSTZ) {
sz1 += dszdl_max;
tz1 += dtzdl_max;
}
} else {
x1 += dxdy_min;
if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
f1 += dfdl_min;
}
if (kInterpZ) {
z1 += dzdl_min;
}
if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
r1 += drdl_min;
g1 += dgdl_min;
b1 += dbdl_min;
a1 += dadl_min;
}
if (kInterpST || kInterpSTZ) {
sz1 += dszdl_min;
tz1 += dtzdl_min;
}
}
// right edge
x2 += dx2dy2;
// screen coordinates
if (kColorMode != ColorMode::NoInterpolation) {
pp1 += _pbufWidth;
}
if (kInterpZ) {
pz1 += _pbufWidth;
}
if (kStencilEnabled) {
ps1 += _pbufWidth;
}
nb_lines--;
y++;
}
}
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kStippleEnabled>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
if (_depthTestEnabled) {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, kStippleEnabled, true>(p0, p1, p2);
} else {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, kStippleEnabled, false>(p0, p1, p2);
}
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
if (_polygonStippleEnabled) {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, true>(p0, p1, p2);
} else {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, false>(p0, p1, p2);
}
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
if (_sbuf && _stencilTestEnabled) {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, true>(p0, p1, p2);
} else {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, false>(p0, p1, p2);
}
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
if (_blendingEnabled) {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, true>(p0, p1, p2);
} else {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, false>(p0, p1, p2);
}
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
if (_clippingEnabled) {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, true>(p0, p1, p2);
} else {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, false>(p0, p1, p2);
}
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
if (_alphaTestEnabled) {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, true>(p0, p1, p2);
} else {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, false>(p0, p1, p2);
}
}
template <FrameBuffer::ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite>
void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
if (_fogEnabled) {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, true>(p0, p1, p2);
} else {
fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, false>(p0, p1, p2);
}
}
template <bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite>
void FrameBuffer::fillTriangleTextureMapping(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
// some color interpolation is implied by the texture mapping
if (_textureEnv->isDefault())
fillTriangle<ColorMode::Default, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite>(p0, p1, p2);
else
fillTriangle<ColorMode::CustomTexEnv, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite>(p0, p1, p2);
}
void FrameBuffer::fillTriangleDepthOnly(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const bool interpZ = true;
const ColorMode colorMode = ColorMode::NoInterpolation;
const bool interpST = false;
const bool interpSTZ = false;
const bool smoothMode = false;
if (_depthWrite && _depthTestEnabled)
fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
void FrameBuffer::fillTriangleFlat(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const bool interpZ = true;
const ColorMode colorMode = ColorMode::Default;
const bool interpST = false;
const bool interpSTZ = false;
const bool smoothMode = false;
if (_depthWrite && _depthTestEnabled)
fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
// Smooth filled triangle.
void FrameBuffer::fillTriangleSmooth(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const bool interpZ = true;
const ColorMode colorMode = ColorMode::Default;
const bool interpST = false;
const bool interpSTZ = false;
const bool smoothMode = true;
if (_depthWrite && _depthTestEnabled)
fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
void FrameBuffer::fillTriangleTextureMappingPerspectiveSmooth(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const bool interpZ = true;
const bool interpST = true;
const bool interpSTZ = true;
const bool smoothMode = true;
if (_depthWrite && _depthTestEnabled)
fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
void FrameBuffer::fillTriangleTextureMappingPerspectiveFlat(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const bool interpZ = true;
const bool interpST = false;
const bool interpSTZ = true;
const bool smoothMode = false;
if (_depthWrite && _depthTestEnabled)
fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
} // end of namespace TinyGL