[Scummvm-git-logs] scummvm master -> 27638abef614fd301e3e339947793a89594042f8
Helco
noreply at scummvm.org
Thu Sep 11 15:30:08 UTC 2025
This automated email contains information about 6 new commits which have been
pushed to the 'scummvm' repo located at https://api.github.com/repos/scummvm/scummvm .
Summary:
ebc33b9abb TINYGL: Add support for texture environments
f6cf576e03 TINYGL: Add fast-path for default texture environment
3567d57211 TESTBED: Add texenv comparisons between TinyGL and OpenGL
ee25b1f3f3 TINYGL: Add TGL_CONSTANT and TGL_BLEND
27f2b1b6d6 TESTBED: Add test for TGL_BLEND
27638abef6 TETRAEDGE: Enable texture environments on TinyGL
Commit: ebc33b9abbf00be524fa72bd2025acb8cf810c70
https://github.com/scummvm/scummvm/commit/ebc33b9abbf00be524fa72bd2025acb8cf810c70
Author: Helco (hermann.noll at hotmail.com)
Date: 2025-09-11T17:30:00+02:00
Commit Message:
TINYGL: Add support for texture environments
Changed paths:
A test/tgraphics/tinygl_texenv.h
graphics/tinygl/gl.h
graphics/tinygl/init.cpp
graphics/tinygl/texelbuffer.cpp
graphics/tinygl/texelbuffer.h
graphics/tinygl/texture.cpp
graphics/tinygl/zbuffer.cpp
graphics/tinygl/zbuffer.h
graphics/tinygl/zdirtyrect.cpp
graphics/tinygl/zdirtyrect.h
graphics/tinygl/zgl.h
graphics/tinygl/ztriangle.cpp
test/module.mk
diff --git a/graphics/tinygl/gl.h b/graphics/tinygl/gl.h
index 25aa10d8e2f..9e33c972c18 100644
--- a/graphics/tinygl/gl.h
+++ b/graphics/tinygl/gl.h
@@ -660,6 +660,23 @@ enum {
TGL_ALIASED_POINT_SIZE_RANGE = 0x846D,
+ // --- GL 1.3 --- selected
+
+ // Texture environments
+ TGL_COMBINE = 0x8570,
+ TGL_COMBINE_RGB = 0x8571,
+ TGL_COMBINE_ALPHA = 0x8572,
+ TGL_SOURCE0_RGB = 0x8580,
+ TGL_SOURCE1_RGB = 0x8581,
+ TGL_SOURCE0_ALPHA = 0x8588,
+ TGL_SOURCE1_ALPHA = 0x8589,
+ TGL_OPERAND0_RGB = 0x8590,
+ TGL_OPERAND1_RGB = 0x8591,
+ TGL_OPERAND0_ALPHA = 0x8598,
+ TGL_OPERAND1_ALPHA = 0x8599,
+ TGL_PRIMARY_COLOR = 0x8577,
+ TGL_PREVIOUS = 0x8578,
+
// --- GL 1.4 --- selected
// Texture mapping
diff --git a/graphics/tinygl/init.cpp b/graphics/tinygl/init.cpp
index 811b4b380d8..5ac8ef11fa3 100644
--- a/graphics/tinygl/init.cpp
+++ b/graphics/tinygl/init.cpp
@@ -161,6 +161,7 @@ void GLContext::init(int screenW, int screenH, Graphics::PixelFormat pixelFormat
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;
diff --git a/graphics/tinygl/texelbuffer.cpp b/graphics/tinygl/texelbuffer.cpp
index f9ab7daa5c0..a4c747c6a68 100644
--- a/graphics/tinygl/texelbuffer.cpp
+++ b/graphics/tinygl/texelbuffer.cpp
@@ -31,7 +31,7 @@ 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) {
+TexelBuffer::TexelBuffer(uint width, uint height, uint textureSize, int internalformat) {
assert(width);
assert(height);
assert(textureSize);
@@ -42,6 +42,7 @@ TexelBuffer::TexelBuffer(uint width, uint height, uint textureSize) {
_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) {
@@ -81,7 +82,7 @@ void TexelBuffer::getARGBAt(
// 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);
+ BaseNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat);
~BaseNearestTexelBuffer();
protected:
@@ -89,7 +90,8 @@ protected:
Graphics::PixelFormat _format;
};
-BaseNearestTexelBuffer::BaseNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize) : TexelBuffer(width, height, textureSize), _format(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);
@@ -102,8 +104,8 @@ BaseNearestTexelBuffer::~BaseNearestTexelBuffer() {
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)
- : BaseNearestTexelBuffer(buf, format, width, height, textureSize) {}
+ 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(
@@ -122,8 +124,8 @@ protected:
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)
- : BaseNearestTexelBuffer(buf, format, width, height, textureSize) {}
+ 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(
@@ -139,36 +141,41 @@ protected:
}
};
-TexelBuffer *createNearestTexelBuffer(const byte *buf, const Graphics::PixelFormat &pf, uint format, uint type, uint width, uint height, uint textureSize) {
+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
+ textureSize,
+ internalformat
);
} else if (format == TGL_RGB && type == TGL_UNSIGNED_BYTE) {
return new NearestTexelBuffer<TGL_RGB, TGL_UNSIGNED_BYTE>(
buf, pf,
width, height,
- textureSize
+ 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
+ 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
+ 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
+ textureSize,
+ internalformat
);
} else {
error("TinyGL texture: format 0x%04x and type 0x%04x combination not supported", format, type);
@@ -183,7 +190,7 @@ TexelBuffer *createNearestTexelBuffer(const byte *buf, const Graphics::PixelForm
// usage increase should be negligible.
class BilinearTexelBuffer : public TexelBuffer {
public:
- BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize);
+ BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSiz, int internalformat);
~BilinearTexelBuffer();
protected:
@@ -207,7 +214,8 @@ private:
#define P11_OFFSET 3
#define PIXEL_PER_TEXEL_SHIFT 2
-BilinearTexelBuffer::BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize) : TexelBuffer(width, height, textureSize) {
+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;
@@ -319,11 +327,12 @@ void BilinearTexelBuffer::getARGBAt(
);
}
-TexelBuffer *createBilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &pf, uint format, uint type, uint width, uint height, uint textureSize) {
+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
+ textureSize,
+ internalformat
);
}
diff --git a/graphics/tinygl/texelbuffer.h b/graphics/tinygl/texelbuffer.h
index 596e95c40a2..cde77458422 100644
--- a/graphics/tinygl/texelbuffer.h
+++ b/graphics/tinygl/texelbuffer.h
@@ -28,9 +28,11 @@ namespace TinyGL {
class TexelBuffer {
public:
- TexelBuffer(uint width, uint height, uint textureSize);
+ 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,
@@ -45,10 +47,11 @@ protected:
) 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);
-TexelBuffer *createBilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &pf, uint format, uint type, uint width, uint height, uint textureSize);
+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
diff --git a/graphics/tinygl/texture.cpp b/graphics/tinygl/texture.cpp
index ee5315c30b7..3be2cba08ca 100644
--- a/graphics/tinygl/texture.cpp
+++ b/graphics/tinygl/texture.cpp
@@ -165,7 +165,8 @@ void GLContext::glopTexImage2D(GLParam *p) {
pixels, pf,
format, type,
width, height,
- _textureSize
+ _textureSize,
+ internalformat
);
break;
default:
@@ -173,7 +174,8 @@ void GLContext::glopTexImage2D(GLParam *p) {
pixels, pf,
format, type,
width, height,
- _textureSize
+ _textureSize,
+ internalformat
);
break;
}
@@ -191,11 +193,82 @@ error:
error("tglTexParameter: unsupported option");
}
- if (pname != TGL_TEXTURE_ENV_MODE)
- goto error;
-
- if (param != TGL_DECAL)
+ switch (pname) {
+ case TGL_TEXTURE_ENV_MODE:
+ if (param == TGL_REPLACE ||
+ param == TGL_MODULATE ||
+ param == TGL_DECAL ||
+ //param == TGL_BLEND || // no tex env constants yet
+ 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)
+ 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)
+ 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;
+ }
+ default:
goto error;
+ }
}
// TODO: not all tests are done
diff --git a/graphics/tinygl/zbuffer.cpp b/graphics/tinygl/zbuffer.cpp
index b2fe9fff620..0fc653d284c 100644
--- a/graphics/tinygl/zbuffer.cpp
+++ b/graphics/tinygl/zbuffer.cpp
@@ -279,4 +279,184 @@ Graphics::Surface *copyFromFrameBuffer(const Graphics::PixelFormat &dstFormat) {
return c->fb->copyFromFrameBuffer(dstFormat);
}
+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
+
+ const auto satAdd = [](byte a, byte b) -> byte {
+ // from: https://web.archive.org/web/20190213215419/https://locklessinc.com/articles/sat_arithmetic/
+ byte r = a + b;
+ return (byte)(r | -(r < a));
+ };
+
+ const auto sat16_to_8 = [](uint32 x) -> byte {
+ x = (x + 128) >> 8; // rounding 16 to 8
+ return (byte)(x | -!!(x >> 8)); // branchfree saturation
+ };
+
+ const auto fpMul = [](byte a, byte b) -> byte {
+ // 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);
+ };
+
+ 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;
+ 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;
+ 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
+ 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);
+ 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_RGB: 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_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;
+ }
+}
+
} // end of namespace TinyGL
diff --git a/graphics/tinygl/zbuffer.h b/graphics/tinygl/zbuffer.h
index e44950377dc..109a6bff8e2 100644
--- a/graphics/tinygl/zbuffer.h
+++ b/graphics/tinygl/zbuffer.h
@@ -76,6 +76,8 @@ static const int DRAW_DEPTH_ONLY = 0;
static const int DRAW_FLAT = 1;
static const int DRAW_SMOOTH = 2;
+struct GLTextureEnv; // defined in zgl.h
+
struct Buffer {
byte *pbuf;
uint *zbuf;
@@ -673,6 +675,10 @@ public:
_wrapT = wrapt;
}
+ void setTextureEnvironment(const GLTextureEnv *texEnv) {
+ _textureEnv = texEnv;
+ }
+
void setTextureSizeAndMask(int textureSize, int textureSizeMask) {
_textureSize = textureSize;
_textureSizeMask = textureSizeMask;
@@ -773,6 +779,11 @@ private:
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);
+
Buffer _offscreenBuffer;
byte *_pbuf;
int _pbufWidth;
@@ -792,6 +803,7 @@ private:
bool _clippingEnabled;
const TexelBuffer *_currentTexture;
+ const GLTextureEnv *_textureEnv;
uint _wrapS, _wrapT;
bool _blendingEnabled;
int _sourceBlendingFactor;
diff --git a/graphics/tinygl/zdirtyrect.cpp b/graphics/tinygl/zdirtyrect.cpp
index 495f51994f1..7a14a426a0e 100644
--- a/graphics/tinygl/zdirtyrect.cpp
+++ b/graphics/tinygl/zdirtyrect.cpp
@@ -27,6 +27,21 @@
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) {}
+
+bool GLTextureEnv::isDefault() const {
+ return envMode == TGL_MODULATE;
+}
+
void GLContext::issueDrawCall(DrawCall *drawCall) {
if (_enableDirtyRectangles && drawCall->getDirtyRegion().isEmpty())
return;
@@ -469,6 +484,7 @@ RasterizationDrawCall::RasterizationState RasterizationDrawCall::captureState()
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;
@@ -543,6 +559,7 @@ void RasterizationDrawCall::applyState(const RasterizationDrawCall::Rasterizatio
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);
diff --git a/graphics/tinygl/zdirtyrect.h b/graphics/tinygl/zdirtyrect.h
index 07396ad3550..569049dd53a 100644
--- a/graphics/tinygl/zdirtyrect.h
+++ b/graphics/tinygl/zdirtyrect.h
@@ -38,6 +38,24 @@ struct GLContext;
struct GLVertex;
struct GLTexture;
+struct GLTextureEnvArgument {
+ GLTextureEnvArgument();
+
+ uint
+ sourceRGB,
+ operandRGB,
+ sourceAlpha,
+ operandAlpha;
+};
+
+struct GLTextureEnv {
+ GLTextureEnv();
+ bool isDefault() const;
+
+ uint envMode, combineRGB, combineAlpha;
+ GLTextureEnvArgument arg0, arg1;
+};
+
class DrawCall {
public:
@@ -159,6 +177,7 @@ private:
byte polygonStipplePattern[128];
GLTexture *texture;
uint wrapS, wrapT;
+ GLTextureEnv textureEnv;
bool fogEnabled;
float fogColorR;
float fogColorG;
diff --git a/graphics/tinygl/zgl.h b/graphics/tinygl/zgl.h
index e19934c9081..638e8a25431 100644
--- a/graphics/tinygl/zgl.h
+++ b/graphics/tinygl/zgl.h
@@ -324,6 +324,7 @@ struct GLContext {
int texture_min_filter;
uint texture_wrap_s;
uint texture_wrap_t;
+ GLTextureEnv _texEnv;
Common::Array<struct tglColorAssociation> colorAssociationList;
// shared state
diff --git a/graphics/tinygl/ztriangle.cpp b/graphics/tinygl/ztriangle.cpp
index 30741a82116..e4bb9216e61 100644
--- a/graphics/tinygl/ztriangle.cpp
+++ b/graphics/tinygl/ztriangle.cpp
@@ -123,14 +123,12 @@ void FrameBuffer::putPixelTexture(int fbOffset, const TexelBuffer *texture,
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);
if (kLightsMode) {
- uint l_a = (a >> (ZB_POINT_ALPHA_BITS - 8));
- uint l_r = (r >> (ZB_POINT_RED_BITS - 8));
- uint l_g = (g >> (ZB_POINT_GREEN_BITS - 8));
- uint l_b = (b >> (ZB_POINT_BLUE_BITS - 8));
- c_a = (c_a * l_a) >> (ZB_POINT_ALPHA_BITS - 8);
- c_r = (c_r * l_r) >> (ZB_POINT_RED_BITS - 8);
- c_g = (c_g * l_g) >> (ZB_POINT_GREEN_BITS - 8);
- c_b = (c_b * l_b) >> (ZB_POINT_BLUE_BITS - 8);
+ // the name kLightsMode might be misleading, it is only false for
+ // depth-only triangles, in which case: sure, no texture env needed
+ applyTextureEnvironment(
+ texture->internalformat(),
+ a, r, g, b,
+ c_a, c_r, c_g, c_b);
}
writePixel<kEnableAlphaTest, kEnableBlending, kDepthWrite, kFogMode>(fbOffset + _a, c_a, c_r, c_g, c_b, z, fog, fog_r, fog_g, fog_b);
}
diff --git a/test/module.mk b/test/module.mk
index 2c82c1c5216..48926845f2b 100644
--- a/test/module.mk
+++ b/test/module.mk
@@ -28,6 +28,10 @@ TEST_LIBS += test/null_osystem.o \
backends/platform/sdl/win32/win32_wrapper.o
endif
+ifdef USE_TINYGL
+TESTS += $(srcdir)/test/tgraphics/tinygl*.h
+endif
+
TEST_LIBS += audio/libaudio.a math/libmath.a common/formats/libformats.a common/compression/libcompression.a common/libcommon.a image/libimage.a graphics/libgraphics.a
ifeq ($(ENABLE_WINTERMUTE), STATIC_PLUGIN)
diff --git a/test/tgraphics/tinygl_texenv.h b/test/tgraphics/tinygl_texenv.h
new file mode 100644
index 00000000000..9d08bdd5c7f
--- /dev/null
+++ b/test/tgraphics/tinygl_texenv.h
@@ -0,0 +1,182 @@
+#include <cxxtest/TestSuite.h>
+
+#ifdef USE_TINYGL
+
+#include "graphics/tinygl/tinygl.h"
+
+// every test sets up some texture environment
+// then draws a single pixel and checks the resulting output pixel
+
+class TinyGLTexEnvTestSuite : public CxxTest::TestSuite {
+ TinyGL::ContextHandle *_context = nullptr;
+public:
+ void setUp() {
+ _context = TinyGL::createContext(2, 2, Graphics::PixelFormat::createFormatARGB32(), 2, false, false);
+ TinyGL::setContext(_context);
+
+ tglEnable(TGL_TEXTURE_2D);
+ tglDisable(TGL_BLEND);
+ tglDisable(TGL_DEPTH_TEST);
+ tglMatrixMode(TGL_PROJECTION);
+ tglLoadIdentity();
+ tglMatrixMode(TGL_MODELVIEW);
+ tglLoadIdentity();
+ tglViewport(0, 0, 2, 2);
+ }
+
+ void tearDown() {
+ if (_context != nullptr) {
+ TinyGL::destroyContext(_context);
+ _context = nullptr;
+ }
+ }
+
+ // these two functions use RGBA order instead of ARGB to make it consistent with tglColor4ub which we also call
+
+ void drawPixel(byte texR, byte texG, byte texB, byte texA) {
+ const float S = 10.0f;
+ const byte texData[] = { texR, texG, texB, texA };
+ TGLuint texture;
+ tglGenTextures(1, &texture);
+ tglBindTexture(TGL_TEXTURE_2D, texture);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_S, TGL_CLAMP);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_WRAP_T, TGL_CLAMP);
+ tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, 1, 1, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, &texData);
+ tglBegin(TGL_TRIANGLES);
+ tglTexCoord2f(0.0f, 0.0f); tglVertex2f(-S, -S);
+ tglTexCoord2f(1.0f, 0.0f); tglVertex2f(+S, -S);
+ tglTexCoord2f(0.5f, 1.0f); tglVertex2f(0, +S);
+ tglEnd();
+ }
+
+ void checkOutput(byte expR, byte expG, byte expB, byte expA) {
+ byte actA, actR, actG, actB;
+ Graphics::Surface surface;
+ TinyGL::presentBuffer();
+ TinyGL::getSurfaceRef(surface);
+ surface.format.colorToARGB(surface.getPixel(0, 0), actA, actR, actG, actB);
+
+ TS_ASSERT_EQUALS(expA, actA);
+ TS_ASSERT_EQUALS(expR, actR);
+ TS_ASSERT_EQUALS(expG, actG);
+ TS_ASSERT_EQUALS(expB, actB);
+ }
+
+ void testModulate() {
+ // no tglTexEnvi setup because TGL_MODULATE should be the default
+ tglColor4ub(255, 255, 255, 127);
+ drawPixel(255, 127, 0, 255);
+ checkOutput(255, 127, 0, 127);
+ }
+
+ void testReplace() {
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_REPLACE);
+ tglColor4ub(255, 255, 255, 127);
+ drawPixel(255, 127, 0, 255);
+ checkOutput(255, 127, 0, 255);
+ }
+
+ void testDecal() {
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL);
+ tglColor4ub(100, 200, 255, 123);
+ drawPixel(200, 100, 0, 192);
+ checkOutput(176, 125, 63, 123);
+ }
+
+ void testAdd() {
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_ADD);
+ tglColor4ub(50, 100, 150, 127);
+ drawPixel(25, 50, 150, 200);
+ checkOutput(75, 150, 255, 100);
+ // attention: TGL_ADD still modulates alpha
+ }
+
+ void setCombineMode(TGLuint combineRGB, TGLuint combineAlpha) {
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_COMBINE);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_RGB, combineRGB);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_ALPHA, combineAlpha);
+ }
+
+ void setCombineArg(int arg, TGLuint rgbSource, TGLuint rgbOperand, TGLuint alphaSource, TGLuint alphaOperand) {
+ assert(arg >= 0 && arg <= 1);
+ tglTexEnvi(TGL_TEXTURE_ENV, arg ? TGL_SOURCE1_RGB : TGL_SOURCE0_RGB, rgbSource);
+ tglTexEnvi(TGL_TEXTURE_ENV, arg ? TGL_SOURCE1_ALPHA : TGL_SOURCE0_ALPHA, alphaSource);
+ tglTexEnvi(TGL_TEXTURE_ENV, arg ? TGL_OPERAND1_RGB : TGL_OPERAND0_RGB, rgbOperand);
+ tglTexEnvi(TGL_TEXTURE_ENV, arg ? TGL_OPERAND1_ALPHA : TGL_OPERAND0_ALPHA, alphaOperand);
+ }
+
+ void testCombineArgTexture() {
+ setCombineMode(TGL_REPLACE, TGL_REPLACE);
+ setCombineArg(0, TGL_TEXTURE, TGL_SRC_COLOR, TGL_TEXTURE, TGL_SRC_ALPHA);
+ drawPixel(13, 37, 42, 24);
+ checkOutput(13, 37, 42, 24);
+ }
+
+ void testCombineArgPrimaryColor() {
+ setCombineMode(TGL_REPLACE, TGL_REPLACE);
+ setCombineArg(0, TGL_PRIMARY_COLOR, TGL_SRC_COLOR, TGL_PRIMARY_COLOR, TGL_SRC_ALPHA);
+ tglColor4ub(12, 34, 56, 78);
+ drawPixel(13, 37, 42, 24);
+ checkOutput(12, 34, 56, 78);
+ }
+
+ void testCombineArgMixed() {
+ setCombineMode(TGL_REPLACE, TGL_REPLACE);
+ setCombineArg(0, TGL_PRIMARY_COLOR, TGL_SRC_ALPHA, TGL_TEXTURE, TGL_SRC_ALPHA);
+ tglColor4ub(12, 34, 56, 78);
+ drawPixel(13, 37, 42, 24);
+ checkOutput(78, 78, 78, 24);
+ }
+
+ void testCombineOpOneMinus() {
+ setCombineMode(TGL_REPLACE, TGL_REPLACE);
+ setCombineArg(0, TGL_TEXTURE, TGL_ONE_MINUS_SRC_COLOR, TGL_TEXTURE, TGL_ONE_MINUS_SRC_ALPHA);
+ drawPixel(13, 37, 42, 24);
+ checkOutput(242, 218, 213, 231);
+ }
+
+ void testCombineOpSrcAlpha() {
+ setCombineMode(TGL_REPLACE, TGL_REPLACE);
+ setCombineArg(0, TGL_TEXTURE, TGL_SRC_ALPHA, TGL_TEXTURE, TGL_ONE_MINUS_SRC_ALPHA);
+ drawPixel(13, 37, 42, 24);
+ checkOutput(24, 24, 24, 231);
+ }
+
+ void testCombineReplace() {
+ setCombineMode(TGL_REPLACE, TGL_REPLACE);
+ setCombineArg(0, TGL_TEXTURE, TGL_SRC_COLOR, TGL_TEXTURE, TGL_SRC_ALPHA);
+ setCombineArg(1, TGL_PRIMARY_COLOR, TGL_SRC_COLOR, TGL_PRIMARY_COLOR, TGL_SRC_ALPHA);
+ tglColor4ub(12, 34, 56, 78); // just to confuse the implementation
+ drawPixel(13, 37, 42, 24);
+ checkOutput(13, 37, 42, 24);
+ }
+
+ void testCombineModulate() {
+ setCombineMode(TGL_MODULATE, TGL_MODULATE);
+ setCombineArg(0, TGL_TEXTURE, TGL_SRC_COLOR, TGL_TEXTURE, TGL_SRC_ALPHA);
+ setCombineArg(1, TGL_PRIMARY_COLOR, TGL_SRC_COLOR, TGL_PRIMARY_COLOR, TGL_SRC_ALPHA);
+ tglColor4ub(255, 255, 255, 127);
+ drawPixel(255, 127, 0, 255);
+ checkOutput(255, 127, 0, 127);
+ }
+
+ void testCombineAdd() {
+ setCombineMode(TGL_ADD, TGL_ADD);
+ setCombineArg(0, TGL_TEXTURE, TGL_SRC_COLOR, TGL_TEXTURE, TGL_SRC_ALPHA);
+ setCombineArg(1, TGL_PRIMARY_COLOR, TGL_SRC_COLOR, TGL_PRIMARY_COLOR, TGL_SRC_ALPHA);
+ tglColor4ub(50, 100, 150, 100);
+ drawPixel(25, 50, 150, 200);
+ checkOutput(75, 150, 255, 255);
+ // attention: TGL_COMBINE TGL_ADD does *not* modulate alpha
+ }
+
+ void testSaveTexEnvState() {
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_REPLACE);
+ tglColor4ub(0, 0, 0, 0);
+ drawPixel(255, 127, 0, 255);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE); // before executing the drawcall
+ checkOutput(255, 127, 0, 255);
+ }
+};
+
+#endif
Commit: f6cf576e033807c748038f3288e82bdc130dd9ea
https://github.com/scummvm/scummvm/commit/f6cf576e033807c748038f3288e82bdc130dd9ea
Author: Helco (hermann.noll at hotmail.com)
Date: 2025-09-11T17:30:00+02:00
Commit Message:
TINYGL: Add fast-path for default texture environment
Changed paths:
graphics/tinygl/texelbuffer.cpp
graphics/tinygl/zbuffer.cpp
graphics/tinygl/zbuffer.h
graphics/tinygl/ztriangle.cpp
diff --git a/graphics/tinygl/texelbuffer.cpp b/graphics/tinygl/texelbuffer.cpp
index a4c747c6a68..0c9ce69ed6b 100644
--- a/graphics/tinygl/texelbuffer.cpp
+++ b/graphics/tinygl/texelbuffer.cpp
@@ -190,7 +190,7 @@ TexelBuffer *createNearestTexelBuffer(const byte *buf, const Graphics::PixelForm
// usage increase should be negligible.
class BilinearTexelBuffer : public TexelBuffer {
public:
- BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSiz, int internalformat);
+ BilinearTexelBuffer(byte *buf, const Graphics::PixelFormat &format, uint width, uint height, uint textureSize, int internalformat);
~BilinearTexelBuffer();
protected:
diff --git a/graphics/tinygl/zbuffer.cpp b/graphics/tinygl/zbuffer.cpp
index 0fc653d284c..a6323669485 100644
--- a/graphics/tinygl/zbuffer.cpp
+++ b/graphics/tinygl/zbuffer.cpp
@@ -279,6 +279,25 @@ Graphics::Surface *copyFromFrameBuffer(const Graphics::PixelFormat &dstFormat) {
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,
@@ -289,25 +308,6 @@ void FrameBuffer::applyTextureEnvironment(
// texARGB is both input and output
// GL_RGB/GL_RGBA might be identical as TexelBuffer returns As=1
- const auto satAdd = [](byte a, byte b) -> byte {
- // from: https://web.archive.org/web/20190213215419/https://locklessinc.com/articles/sat_arithmetic/
- byte r = a + b;
- return (byte)(r | -(r < a));
- };
-
- const auto sat16_to_8 = [](uint32 x) -> byte {
- x = (x + 128) >> 8; // rounding 16 to 8
- return (byte)(x | -!!(x >> 8)); // branchfree saturation
- };
-
- const auto fpMul = [](byte a, byte b) -> byte {
- // 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);
- };
-
struct Arg {
byte a, r, g, b;
};
@@ -387,10 +387,7 @@ void FrameBuffer::applyTextureEnvironment(
{
// GL_RGB: CpCs | Ap
// GL_RGBA: CpCs | ApAs
- 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);
+ applyModulation(previousA, previousR, previousG, previousB, texA, texR, texG, texB);
break;
}
case TGL_DECAL:
@@ -459,4 +456,13 @@ void FrameBuffer::applyTextureEnvironment(
}
}
+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
diff --git a/graphics/tinygl/zbuffer.h b/graphics/tinygl/zbuffer.h
index 109a6bff8e2..37ef20711b0 100644
--- a/graphics/tinygl/zbuffer.h
+++ b/graphics/tinygl/zbuffer.h
@@ -364,7 +364,13 @@ private:
int &dzdx, 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 kLightsMode, bool kSmoothMode, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kDepthTestEnabled>
+ 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,
@@ -707,44 +713,47 @@ private:
void selectOffscreenBuffer(Buffer *buffer);
void clearOffscreenBuffer(Buffer *buffer);
- template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ 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 <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ 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 <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ 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 <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ 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 <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ 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 <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ 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 <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite, bool kFogMode>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
- template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+ template <ColorMode kColorMode, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
bool kDepthWrite>
void fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2);
- template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode>
+ 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);
@@ -784,6 +793,11 @@ private:
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;
diff --git a/graphics/tinygl/ztriangle.cpp b/graphics/tinygl/ztriangle.cpp
index e4bb9216e61..a8e0b5f47a5 100644
--- a/graphics/tinygl/ztriangle.cpp
+++ b/graphics/tinygl/ztriangle.cpp
@@ -92,7 +92,7 @@ end:
}
}
-template <bool kDepthWrite, bool kLightsMode, bool kSmoothMode, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kDepthTestEnabled>
+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,
@@ -122,13 +122,22 @@ void FrameBuffer::putPixelTexture(int fbOffset, const TexelBuffer *texture,
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);
- if (kLightsMode) {
- // the name kLightsMode might be misleading, it is only false for
- // depth-only triangles, in which case: sure, no texture env needed
+ 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);
}
@@ -180,7 +189,7 @@ end:
z += dzdx;
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode,
+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) {
@@ -248,7 +257,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
fdx2 *= fz0;
fdy2 *= fz0;
- if (kInterpRGB && kFogMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
fog_r = _fogColorR * 255;
fog_g = _fogColorG * 255;
fog_b = _fogColorB * 255;
@@ -265,7 +274,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
dzdy = (int)(fdx1 * d2 - fdx2 * d1);
}
- if (kInterpRGB && kSmoothMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
d1 = (float)(p1->r - p0->r);
d2 = (float)(p2->r - p0->r);
drdx = (int)(fdy2 * d1 - fdy1 * d2);
@@ -320,7 +329,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
}
int polyOffset = 0;
- if (kInterpZ && kInterpRGB && (_offsetStates & TGL_OFFSET_FILL)) {
+ if (kInterpZ && kColorMode != ColorMode::NoInterpolation && (_offsetStates & TGL_OFFSET_FILL)) {
int m = MAX(ABS(dzdx), ABS(dzdy));
polyOffset = -m * _offsetFactor + -_offsetUnits * (1 << 6);
}
@@ -333,14 +342,14 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
ps1 = _sbuf + p0->y * _pbufWidth;
}
- if (kInterpRGB && !kSmoothMode) {
+ if (kColorMode != ColorMode::NoInterpolation && !kSmoothMode) {
r1 = p2->r;
g1 = p2->g;
b1 = p2->b;
a1 = p2->a;
}
- if (kInterpRGB && (kInterpST || kInterpSTZ)) {
+ if (kColorMode != ColorMode::NoInterpolation && (kInterpST || kInterpSTZ)) {
texture = _currentTexture;
fdzdx = (float)dzdx;
fndzdx = NB_INTERP * fdzdx;
@@ -391,7 +400,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
dxdy_min = tmp >> 16;
dxdy_max = dxdy_min + 1;
- if (kInterpRGB && kFogMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
f1 = l1->f;
dfdl_min = (dfdy + dfdx * dxdy_min);
dfdl_max = dfdl_min + dfdx;
@@ -403,7 +412,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
dzdl_max = dzdl_min + dzdx;
}
- if (kInterpRGB && kSmoothMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
r1 = l1->r;
drdl_min = (drdy + drdx * dxdy_min);
drdl_max = drdl_min + drdx;
@@ -447,7 +456,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
// we draw all the scan line of the part
while (nb_lines > 0) {
int x = x1;
- if (!kInterpRGB) {
+ if (kColorMode == ColorMode::NoInterpolation) {
int n;
uint *pz;
byte *ps = nullptr;
@@ -581,7 +590,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
zinv = (float)(1.0 / fz);
}
for (int _a = 0; _a < NB_INTERP; _a++) {
- putPixelTexture<kDepthWrite, kInterpRGB, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kDepthTestEnabled>
+ 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;
@@ -608,7 +617,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
}
while (n >= 0) {
- putPixelTexture<kDepthWrite, kInterpRGB, kSmoothMode, kFogMode, kAlphaTestEnabled, kEnableScissor, kBlendingEnabled, kStencilEnabled, kDepthTestEnabled>
+ 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) {
@@ -627,13 +636,13 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
if (error > 0) {
error -= 0x10000;
x1 += dxdy_max;
- if (kInterpRGB && kFogMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
f1 += dfdl_max;
}
if (kInterpZ) {
z1 += dzdl_max;
}
- if (kInterpRGB && kSmoothMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
r1 += drdl_max;
g1 += dgdl_max;
b1 += dbdl_max;
@@ -645,13 +654,13 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
}
} else {
x1 += dxdy_min;
- if (kInterpRGB && kFogMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kFogMode) {
f1 += dfdl_min;
}
if (kInterpZ) {
z1 += dzdl_min;
}
- if (kInterpRGB && kSmoothMode) {
+ if (kColorMode != ColorMode::NoInterpolation && kSmoothMode) {
r1 += drdl_min;
g1 += dgdl_min;
b1 += dbdl_min;
@@ -667,7 +676,7 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
x2 += dx2dy2;
// screen coordinates
- if (kInterpRGB) {
+ if (kColorMode != ColorMode::NoInterpolation) {
pp1 += _pbufWidth;
}
if (kInterpZ) {
@@ -683,128 +692,135 @@ void FrameBuffer::fillTriangle(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint
}
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled, bool kStippleEnabled>
+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<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, kStippleEnabled, true>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, kStippleEnabled, true>(p0, p1, p2);
} else {
- fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, kStippleEnabled, false>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, kStippleEnabled, false>(p0, p1, p2);
}
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending, bool kStencilEnabled>
+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<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, true>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, true>(p0, p1, p2);
} else {
- fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, false>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, kStencilEnabled, false>(p0, p1, p2);
}
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor, bool kEnableBlending>
+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<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, true>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, true>(p0, p1, p2);
} else {
- fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, false>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, kEnableBlending, false>(p0, p1, p2);
}
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest, bool kEnableScissor>
+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<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, true>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, true>(p0, p1, p2);
} else {
- fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, false>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, kEnableScissor, false>(p0, p1, p2);
}
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode, bool kEnableAlphaTest>
+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<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, true>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, true>(p0, p1, p2);
} else {
- fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, false>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, kEnableAlphaTest, false>(p0, p1, p2);
}
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite, bool kFogMode>
+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<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, true>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, true>(p0, p1, p2);
} else {
- fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, false>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, kFogMode, false>(p0, p1, p2);
}
}
-template <bool kInterpRGB, bool kInterpZ, bool kInterpST, bool kInterpSTZ, bool kSmoothMode, bool kDepthWrite>
+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<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, true>(p0, p1, p2);
+ fillTriangle<kColorMode, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, true>(p0, p1, p2);
} else {
- fillTriangle<kInterpRGB, kInterpZ, kInterpST, kInterpSTZ, kSmoothMode, kDepthWrite, false>(p0, p1, p2);
+ 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 bool interpRGB = false;
+ const ColorMode colorMode = ColorMode::NoInterpolation;
const bool interpST = false;
const bool interpSTZ = false;
const bool smoothMode = false;
if (_depthWrite && _depthTestEnabled)
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
+ fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
+ fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
void FrameBuffer::fillTriangleFlat(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const bool interpZ = true;
- const bool interpRGB = true;
+ const ColorMode colorMode = ColorMode::Default;
const bool interpST = false;
const bool interpSTZ = false;
const bool smoothMode = false;
if (_depthWrite && _depthTestEnabled)
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
+ fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
+ 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 bool interpRGB = true;
+ const ColorMode colorMode = ColorMode::Default;
const bool interpST = false;
const bool interpSTZ = false;
const bool smoothMode = true;
if (_depthWrite && _depthTestEnabled)
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
+ fillTriangle<colorMode, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
+ 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 interpRGB = true;
const bool interpST = true;
const bool interpSTZ = true;
const bool smoothMode = true;
if (_depthWrite && _depthTestEnabled)
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
+ fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
+ fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
void FrameBuffer::fillTriangleTextureMappingPerspectiveFlat(ZBufferPoint *p0, ZBufferPoint *p1, ZBufferPoint *p2) {
const bool interpZ = true;
- const bool interpRGB = true;
const bool interpST = false;
const bool interpSTZ = true;
const bool smoothMode = false;
if (_depthWrite && _depthTestEnabled)
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
+ fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, true>(p0, p1, p2);
else
- fillTriangle<interpRGB, interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
+ fillTriangleTextureMapping<interpZ, interpST, interpSTZ, smoothMode, false>(p0, p1, p2);
}
} // end of namespace TinyGL
Commit: 3567d572119b2b9d3f1401866995bdb6c16087ac
https://github.com/scummvm/scummvm/commit/3567d572119b2b9d3f1401866995bdb6c16087ac
Author: Helco (hermann.noll at hotmail.com)
Date: 2025-09-11T17:30:00+02:00
Commit Message:
TESTBED: Add texenv comparisons between TinyGL and OpenGL
Changed paths:
A dists/engine-data/testbed-audiocd-files/image/pm5544-32bpp-grayalpha.png
A engines/testbed/tinygl.cpp
A engines/testbed/tinygl.h
dists/engine-data/testbed-audiocd-files/image/image-gen.sh
engines/testbed/configure.engine
engines/testbed/module.mk
engines/testbed/testbed.cpp
diff --git a/dists/engine-data/testbed-audiocd-files/image/image-gen.sh b/dists/engine-data/testbed-audiocd-files/image/image-gen.sh
index 9b72f92f07e..58c8f72007a 100755
--- a/dists/engine-data/testbed-audiocd-files/image/image-gen.sh
+++ b/dists/engine-data/testbed-audiocd-files/image/image-gen.sh
@@ -25,4 +25,5 @@ magick $1 -depth 8 -type Grayscale $base-8bpp-grey.png
magick $1 -depth 8 -type Grayscale $base-8bpp-grey.tga
magick $1 -depth 8 -type Grayscale -compress RLE $base-8bpp-grey-rle.tga
magick $1 -depth 1 -type Grayscale $base-1bpp.xbm
+magick $1 -channel-fx 'gray=>alpha' $base-32bpp-grayalpha.png
diff --git a/dists/engine-data/testbed-audiocd-files/image/pm5544-32bpp-grayalpha.png b/dists/engine-data/testbed-audiocd-files/image/pm5544-32bpp-grayalpha.png
new file mode 100644
index 00000000000..3edb90b9eec
Binary files /dev/null and b/dists/engine-data/testbed-audiocd-files/image/pm5544-32bpp-grayalpha.png differ
diff --git a/engines/testbed/configure.engine b/engines/testbed/configure.engine
index 4d6e7bfc63c..1fce680a777 100644
--- a/engines/testbed/configure.engine
+++ b/engines/testbed/configure.engine
@@ -1,3 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
-add_engine testbed "TestBed: the Testing framework" no "" "" "" "imgui midi universaltracker indeo3 indeo45 vpx mpc hnm mpeg2 qdm2 svq1"
+add_engine testbed "TestBed: the Testing framework" no "" "" "" "imgui midi universaltracker indeo3 indeo45 vpx mpc hnm mpeg2 qdm2 svq1 tinygl"
diff --git a/engines/testbed/module.mk b/engines/testbed/module.mk
index 76bbdcff655..2c002897d5b 100644
--- a/engines/testbed/module.mk
+++ b/engines/testbed/module.mk
@@ -37,6 +37,13 @@ MODULE_OBJS += \
imgui.o
endif
+ifdef USE_TINYGL
+ifdef USE_OPENGL_GAME # Currently only direct comparisons are implemented
+MODULE_OBJS += \
+ tinygl.o
+endif
+endif
+
MODULE_DIRS += \
engines/testbed
diff --git a/engines/testbed/testbed.cpp b/engines/testbed/testbed.cpp
index a149a120bd9..3d04993888f 100644
--- a/engines/testbed/testbed.cpp
+++ b/engines/testbed/testbed.cpp
@@ -57,6 +57,9 @@
#ifdef USE_IMGUI
#include "testbed/imgui.h"
#endif
+#if defined USE_TINYGL && defined USE_OPENGL_GAME
+#include "testbed/tinygl.h"
+#endif
namespace Testbed {
@@ -192,6 +195,11 @@ void TestbedEngine::pushTestsuites(Common::Array<Testsuite *> &testsuiteList) {
// Video decoder
ts = new VideoDecoderTestSuite();
testsuiteList.push_back(ts);
+#if defined USE_TINYGL && defined USE_OPENGL_GAME
+ // TinyGL
+ ts = new TinyGLTestSuite();
+ testsuiteList.push_back(ts);
+#endif
}
TestbedEngine::~TestbedEngine() {
diff --git a/engines/testbed/tinygl.cpp b/engines/testbed/tinygl.cpp
new file mode 100644
index 00000000000..0d24fab8994
--- /dev/null
+++ b/engines/testbed/tinygl.cpp
@@ -0,0 +1,385 @@
+/* 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/file.h"
+#include "engines/util.h"
+#include "image/png.h"
+#include "graphics/managed_surface.h"
+#include "graphics/opengl/system_headers.h"
+#include "graphics/opengl/debug.h"
+#include "graphics/tinygl/tinygl.h"
+
+#include "testbed/tinygl.h"
+
+namespace Testbed {
+
+namespace TinyGLTests {
+ struct TextureEnvironmentArg {
+ GLuint _sourceRGB, _operandRGB;
+ GLuint _sourceAlpha, _operandAlpha;
+
+ TextureEnvironmentArg(
+ GLuint sourceRGB = GL_TEXTURE,
+ GLuint operandRGB = GL_SRC_COLOR,
+ GLuint sourceAlpha = GL_TEXTURE,
+ GLuint operandAlpha = GL_SRC_ALPHA)
+ : _sourceRGB(sourceRGB)
+ , _operandRGB(operandRGB)
+ , _sourceAlpha(sourceAlpha)
+ , _operandAlpha(operandAlpha) { }
+ };
+
+ struct TextureEnvironment {
+ GLuint _mode, _combineRGB, _combineAlpha;
+ TextureEnvironmentArg _arg0, _arg1;
+
+ TextureEnvironment(
+ GLuint mode = GL_REPLACE,
+ GLuint combineRGB = GL_REPLACE,
+ GLuint combineAlpha = GL_REPLACE)
+ : _mode(mode)
+ , _combineRGB(combineRGB)
+ , _combineAlpha(combineAlpha) { }
+
+ template<typename TexEnvFunc>
+ void apply(TexEnvFunc func) const {
+ func(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, _mode);
+ func(GL_TEXTURE_ENV, GL_COMBINE_RGB, _combineRGB);
+ func(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, _combineAlpha);
+
+ func(GL_TEXTURE_ENV, GL_SOURCE0_RGB, _arg0._sourceRGB);
+ func(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, _arg0._sourceAlpha);
+ func(GL_TEXTURE_ENV, GL_SOURCE1_RGB, _arg1._sourceRGB);
+ func(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, _arg1._sourceAlpha);
+
+ func(GL_TEXTURE_ENV, GL_OPERAND0_RGB, _arg0._operandRGB);
+ func(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, _arg0._operandAlpha);
+ func(GL_TEXTURE_ENV, GL_OPERAND1_RGB, _arg1._operandRGB);
+ func(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, _arg1._operandAlpha);
+ }
+ };
+
+ TestExitStatus runTexEnvTest(
+ const char *testName,
+ const TextureEnvironment &env,
+ byte colorR, byte colorG, byte colorB, byte colorA);
+ TestExitStatus testTexEnvReplace();
+ TestExitStatus testTexEnvModulate();
+ TestExitStatus testTexEnvDecal();
+ TestExitStatus testTexEnvAdd();
+ TestExitStatus testTexEnvCombineOpNormal();
+ TestExitStatus testTexEnvCombineOpInverse();
+ TestExitStatus testTexEnvCombineOpAlphaToColor();
+ TestExitStatus testTexEnvCombineMixedArgs();
+ TestExitStatus testTexEnvCombineReplace();
+ TestExitStatus testTexEnvCombineModulate();
+ TestExitStatus testTexEnvCombineAdd();
+}
+
+TinyGLTestSuite::TinyGLTestSuite() {
+ addTest("Replace", &TinyGLTests::testTexEnvReplace);
+ addTest("Modulate", &TinyGLTests::testTexEnvModulate);
+ addTest("Decal", &TinyGLTests::testTexEnvDecal);
+ addTest("Add", &TinyGLTests::testTexEnvAdd);
+ addTest("CombineOpNormal", &TinyGLTests::testTexEnvCombineOpNormal);
+ addTest("CombineOpInverse", &TinyGLTests::testTexEnvCombineOpInverse);
+ addTest("CombineOpAlphaToColor", &TinyGLTests::testTexEnvCombineOpAlphaToColor);
+ addTest("CombineMixedArgs", &TinyGLTests::testTexEnvCombineMixedArgs);
+ addTest("CombineReplace", &TinyGLTests::testTexEnvCombineReplace);
+ addTest("CombineModulate", &TinyGLTests::testTexEnvCombineModulate);
+ addTest("CombineAdd", TinyGLTests::testTexEnvCombineAdd);
+}
+
+TestExitStatus TinyGLTests::testTexEnvModulate() {
+ TextureEnvironment env(GL_MODULATE);
+ return runTexEnvTest("Modulate", env, 50, 111, 222, 255);
+}
+
+TestExitStatus TinyGLTests::testTexEnvReplace() {
+ TextureEnvironment env(GL_REPLACE);
+ return runTexEnvTest("Replace", env, 255, 0, 255, 255);
+}
+
+TestExitStatus TinyGLTests::testTexEnvDecal() {
+ TextureEnvironment env(GL_DECAL);
+ return runTexEnvTest("Decal", env, 50, 111, 222, 255);
+}
+
+TestExitStatus TinyGLTests::testTexEnvAdd() {
+ TextureEnvironment env(GL_ADD);
+ return runTexEnvTest("Add", env, 50, 111, 222, 255);
+}
+
+TestExitStatus TinyGLTests::testTexEnvCombineOpNormal() {
+ TextureEnvironment env(GL_COMBINE, GL_REPLACE, GL_REPLACE);
+ env._arg0 = { GL_TEXTURE, GL_SRC_COLOR, GL_TEXTURE, GL_SRC_ALPHA };
+ return runTexEnvTest("CombineOpNormal", env, 50, 111, 222, 255);
+}
+
+TestExitStatus TinyGLTests::testTexEnvCombineOpInverse() {
+ TextureEnvironment env(GL_COMBINE, GL_REPLACE, GL_REPLACE);
+ env._arg0 = { GL_TEXTURE, GL_ONE_MINUS_SRC_COLOR, GL_TEXTURE, GL_ONE_MINUS_SRC_ALPHA };
+ return runTexEnvTest("CombineOpInverse", env, 50, 111, 222, 255);
+}
+
+TestExitStatus TinyGLTests::testTexEnvCombineOpAlphaToColor() {
+ TextureEnvironment env(GL_COMBINE, GL_REPLACE, GL_REPLACE);
+ env._arg0 = { GL_TEXTURE, GL_SRC_ALPHA, GL_TEXTURE, GL_ONE_MINUS_SRC_ALPHA };
+ return runTexEnvTest("CombineOpAlphaToColor", env, 50, 111, 222, 255);
+}
+
+TestExitStatus TinyGLTests::testTexEnvCombineMixedArgs() {
+ TextureEnvironment env(GL_COMBINE, GL_REPLACE, GL_REPLACE);
+ env._arg0 = { GL_PRIMARY_COLOR, GL_SRC_ALPHA, GL_TEXTURE, GL_SRC_ALPHA };
+ return runTexEnvTest("CombineMixedArgs", env, 255, 0, 255, 50);
+}
+
+TestExitStatus TinyGLTests::testTexEnvCombineReplace() {
+ TextureEnvironment env(GL_COMBINE, GL_REPLACE, GL_REPLACE);
+ env._arg0 = { GL_TEXTURE, GL_SRC_COLOR, GL_PRIMARY_COLOR, GL_SRC_ALPHA };
+ return runTexEnvTest("CombineReplace", env, 50, 11, 222, 127);
+}
+
+TestExitStatus TinyGLTests::testTexEnvCombineModulate() {
+ TextureEnvironment env(GL_COMBINE, GL_MODULATE, GL_MODULATE);
+ env._arg0 = { GL_TEXTURE, GL_SRC_COLOR, GL_TEXTURE, GL_SRC_ALPHA };
+ env._arg1 = { GL_PRIMARY_COLOR, GL_SRC_COLOR, GL_PRIMARY_COLOR, GL_SRC_ALPHA };
+ return runTexEnvTest("CombineModulate", env, 50, 11, 222, 127);
+}
+
+TestExitStatus TinyGLTests::testTexEnvCombineAdd() {
+ TextureEnvironment env(GL_COMBINE, GL_ADD, GL_ADD);
+ env._arg0 = { GL_TEXTURE, GL_SRC_COLOR, GL_TEXTURE, GL_SRC_ALPHA };
+ env._arg1 = { GL_PRIMARY_COLOR, GL_SRC_COLOR, GL_PRIMARY_COLOR, GL_SRC_ALPHA };
+ return runTexEnvTest("CombineAdd", env, 50, 11, 222, 127);
+}
+
+struct TinyGLContextDeleter {
+ inline void operator()(TinyGL::ContextHandle *handle) {
+ TinyGL::destroyContext(handle);
+ }
+};
+
+// using cdecl calling convention so we can use a function pointer
+static void classicGLTexEnvi(GLenum target, GLenum param, GLint value) {
+ glTexEnvi(target, param, value);
+}
+
+static void copyAlphaIntoColorChannels(Graphics::ManagedSurface &surface) {
+ byte a, r, g, b;
+ for (int y = 0; y < surface.h; y++) {
+ uint32 *pixel = (uint32*)surface.getBasePtr(0, y);
+ for (int x = 0; x < surface.w; x++, pixel++) {
+ surface.format.colorToARGB(*pixel, a, r, g, b);
+ r = g = b = a;
+ a = 255;
+ *pixel = surface.format.ARGBToColor(a, r, g, b);
+ }
+ }
+}
+
+static constexpr const int kScreenWidth = 800; // enough space for a 2x2 grid and some padding
+static constexpr const int kScreenHeight = 600;
+static constexpr const int kTextureSize = 256;
+
+static void renderClassicQuad(GLuint texture, float x, float y, bool flipV = false) {
+ const float tLow = flipV ? 1.0f : 0.0f;
+ const float tHigh = flipV ? 0.0f : 1.0f;
+ glEnable(GL_TEXTURE_2D);
+ glBindTexture(GL_TEXTURE_2D, texture);
+ glBegin(GL_TRIANGLE_STRIP);
+ glTexCoord2f(0.0f, tLow); glVertex2f(x, y);
+ glTexCoord2f(1.0f, tLow); glVertex2f(x + kTextureSize, y);
+ glTexCoord2f(0.0f, tHigh); glVertex2f(x, y + kTextureSize);
+ glTexCoord2f(1.0f, tHigh); glVertex2f(x + kTextureSize, y + kTextureSize);
+ glEnd();
+}
+
+static void renderClassicGridQuad(GLuint texture, int row, int column) {
+ renderClassicQuad(
+ texture,
+ kScreenWidth / 4 * (row * 2 + 1) - kTextureSize / 2,
+ kScreenHeight / 4 * (column * 2 + 1) - kTextureSize / 2,
+ true); // by rendering and reading back we have flipped the image
+}
+
+TestExitStatus TinyGLTests::runTexEnvTest(
+ const char *testName,
+ const TextureEnvironment &env,
+ byte colorR, byte colorG, byte colorB, byte colorA) {
+
+ int oldW = g_system->getWidth();
+ int oldH = g_system->getHeight();
+ auto oldFormat = g_system->getScreenFormat();
+ int oldGraphicsMode = g_system->getGraphicsMode();
+
+ // load test image (crop and scale to square power-of-two)
+ Graphics::ManagedSurface testImage;
+ {
+ constexpr const char *kImagePath = "image/pm5544-32bpp-grayalpha.png";
+ Image::PNGDecoder pngDecoder;
+ Common::File file;
+ if (!file.open(kImagePath) ||
+ !pngDecoder.loadStream(file)) {
+ Testsuite::logDetailedPrintf("Error! Could not load test image: %s\n", kImagePath);
+ return kTestFailed;
+ }
+ const auto *pngSurface = pngDecoder.getSurface();
+ if (pngSurface->w < 240 || pngSurface->h < 240) {
+ Testsuite::logDetailedPrintf("Error! Test image has unexpected size: %dx%d\n", pngSurface->w, pngSurface->h);
+ return kTestFailed;
+ }
+ int16 pngSize = MIN(pngSurface->w, pngSurface->h);
+ auto subRect = Common::Rect::center(pngSurface->w / 2, pngSurface->h / 2, pngSize, pngSize);
+
+ Graphics::ManagedSurface converted;
+ converted.convertFrom(*pngSurface, Graphics::PixelFormat::createFormatRGBA32());
+ testImage.create(kTextureSize, kTextureSize, Graphics::PixelFormat::createFormatRGBA32());
+ Graphics::scaleBlitBilinear(
+ (byte *)testImage.getBasePtr(0, 0),
+ (const byte *)converted.getBasePtr(subRect.left, subRect.top),
+ testImage.pitch,
+ converted.pitch,
+ testImage.w, testImage.h,
+ converted.w, converted.h,
+ converted.format);
+ }
+ Graphics::ManagedSurface readBack(testImage.w, testImage.h, testImage.format);
+
+ // Initialize classic opengl
+ initGraphics3d(kScreenWidth, kScreenHeight);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ glOrtho(0, kScreenWidth, 0, kScreenHeight, -1, 1);
+ glViewport(0, 0, kScreenWidth, kScreenHeight);
+ glDisable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glDisable(GL_CULL_FACE);
+ glEnable(GL_TEXTURE_2D);
+ glClearColor(0, 0, 0, 0);
+ GLuint classicTextures[5]; // test texture + 2*classic result textures + 2*TinyGL result textures
+ glGenTextures(5, classicTextures);
+ GLuint classicTestTexture = classicTextures[0];
+ glBindTexture(GL_TEXTURE_2D, classicTestTexture);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, testImage.getPixels());
+
+ // Initialize TinyGL
+ Common::ScopedPtr<TinyGL::ContextHandle, TinyGLContextDeleter> tglContext(
+ TinyGL::createContext(kTextureSize, kTextureSize, testImage.format, kTextureSize, false, false));
+ TinyGL::setContext(tglContext.get());
+ Graphics::Surface tinyglSurface;
+ TinyGL::getSurfaceRef(tinyglSurface);
+ tglMatrixMode(TGL_MODELVIEW);
+ tglLoadIdentity();
+ tglMatrixMode(TGL_PROJECTION);
+ tglLoadIdentity();
+ tglOrtho(0, kScreenWidth, 0, kScreenHeight, -1, 1);
+ tglViewport(0, 0, kScreenWidth, kScreenHeight);
+ tglDisable(TGL_BLEND);
+ tglDisable(TGL_DEPTH_TEST);
+ tglDisable(TGL_CULL_FACE);
+ tglEnable(TGL_TEXTURE_2D);
+ tglClearColor(0, 0, 0, 0);
+ TGLuint tinyglTestTexture;
+ tglGenTextures(1, &tinyglTestTexture);
+ tglBindTexture(TGL_TEXTURE_2D, classicTestTexture);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MIN_FILTER, TGL_NEAREST);
+ tglTexParameteri(TGL_TEXTURE_2D, TGL_TEXTURE_MAG_FILTER, TGL_NEAREST);
+ tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, kTextureSize, kTextureSize, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, testImage.getPixels());
+
+ // Render classic OpenGL
+ env.apply(classicGLTexEnvi);
+ glClear(GL_COLOR_BUFFER_BIT);
+ glColor4ub(colorR, colorG, colorB, colorA);
+ renderClassicQuad(classicTestTexture, 0, 0);
+ glFlush();
+ g_system->presentBuffer();
+ readBack.clear();
+ glReadPixels(0, 0, kTextureSize, kTextureSize, GL_RGBA, GL_UNSIGNED_BYTE, readBack.getPixels());
+ GLuint classicReadBackColorTex = classicTextures[1];
+ glBindTexture(GL_TEXTURE_2D, classicReadBackColorTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, readBack.getPixels());
+ copyAlphaIntoColorChannels(readBack);
+ GLuint classicReadBackAlphaTex = classicTextures[2];
+ glBindTexture(GL_TEXTURE_2D, classicReadBackAlphaTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, readBack.getPixels());
+
+ // Render TinyGL
+ env.apply(tglTexEnvi);
+ tglClear(TGL_COLOR_BUFFER_BIT);
+ tglColor4ub(colorR, colorG, colorB, colorA);
+ tglBegin(TGL_TRIANGLE_STRIP);
+ tglTexCoord2f(0.0f, 1.0f); tglVertex2f(0.0f, 0.0f);
+ tglTexCoord2f(1.0f, 1.0f); tglVertex2f(kTextureSize, 0.0f);
+ tglTexCoord2f(0.0f, 0.0f); tglVertex2f(0.0f, kTextureSize);
+ tglTexCoord2f(1.0f, 0.0f); tglVertex2f(kTextureSize, kTextureSize);
+ tglEnd();
+ TinyGL::presentBuffer();
+ readBack.copyFrom(tinyglSurface);
+ GLuint tinyglReadBackColorTex = classicTextures[3];
+ glBindTexture(GL_TEXTURE_2D, tinyglReadBackColorTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, readBack.getPixels());
+ copyAlphaIntoColorChannels(readBack);
+ GLuint tinyglReadBackAlphaTex = classicTextures[4];
+ glBindTexture(GL_TEXTURE_2D, tinyglReadBackAlphaTex);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, readBack.getPixels());
+
+ // Render comparison
+ glClear(GL_COLOR_BUFFER_BIT);
+ glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
+ glColor4f(1, 1, 1, 1);
+ renderClassicGridQuad(classicReadBackColorTex, 0, 0);
+ renderClassicGridQuad(classicReadBackAlphaTex, 1, 0);
+ renderClassicGridQuad(tinyglReadBackColorTex, 0, 1);
+ renderClassicGridQuad(tinyglReadBackAlphaTex, 1, 1);
+ glFlush();
+ g_system->updateScreen();
+ glDeleteTextures(5, classicTextures);
+
+ g_system->delayMillis(1000);
+ TestExitStatus status = kTestPassed;
+ Common::String info = Common::String::format("Does the top row of images look like the bottom row?\n(Testing %s)", testName);
+ if (Testsuite::handleInteractiveInput(info, "Yes", "No", kOptionRight)) {
+ Testsuite::logDetailedPrintf("Error! TinyGL and OpenGL have different texure environment behaviors for %s\n", testName);
+ status = kTestFailed;
+ }
+
+ // Return to previous state
+ g_system->beginGFXTransaction();
+ g_system->setGraphicsMode(oldGraphicsMode);
+ g_system->initSize(oldW, oldH, &oldFormat);
+ g_system->endGFXTransaction();
+ return status;
+}
+
+}
diff --git a/engines/testbed/tinygl.h b/engines/testbed/tinygl.h
new file mode 100644
index 00000000000..7b81b8dec57
--- /dev/null
+++ b/engines/testbed/tinygl.h
@@ -0,0 +1,53 @@
+/* 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 TESTBED_TINYGL_H
+#define TESTBED_TINYGL_H
+
+#include "testbed/testsuite.h"
+
+namespace Testbed {
+
+class TinyGLTestSuite : public Testsuite {
+public:
+ /**
+ * The constructor for the XXXTestSuite
+ * For every test to be executed one must:
+ * 1) Create a function that would invoke the test
+ * 2) Add that test to list by executing addTest()
+ *
+ * @see addTest()
+ */
+ TinyGLTestSuite();
+ ~TinyGLTestSuite() override = default;
+ const char *getName() const override {
+ return "TinyGL";
+ }
+
+ const char *getDescription() const override {
+ return "Comparing TinyGL output with classic OpenGL";
+ }
+
+};
+
+} // End of namespace Testbed
+
+#endif // TESTBED_TEMPLATE_H
Commit: ee25b1f3f389aac8b61bcf95c75bfa1de0f1ef63
https://github.com/scummvm/scummvm/commit/ee25b1f3f389aac8b61bcf95c75bfa1de0f1ef63
Author: Helco (hermann.noll at hotmail.com)
Date: 2025-09-11T17:30:00+02:00
Commit Message:
TINYGL: Add TGL_CONSTANT and TGL_BLEND
Changed paths:
graphics/tinygl/api.cpp
graphics/tinygl/gl.h
graphics/tinygl/texture.cpp
graphics/tinygl/zbuffer.cpp
graphics/tinygl/zbuffer.h
graphics/tinygl/zdirtyrect.cpp
graphics/tinygl/zdirtyrect.h
test/tgraphics/tinygl_texenv.h
diff --git a/graphics/tinygl/api.cpp b/graphics/tinygl/api.cpp
index 9e962e3904d..d82bdce6d16 100644
--- a/graphics/tinygl/api.cpp
+++ b/graphics/tinygl/api.cpp
@@ -742,6 +742,24 @@ void tglTexEnvi(TGLenum target, TGLenum pname, TGLint param) {
c->gl_add_op(p);
}
+void tglTexEnvfv(TGLenum target, TGLenum pname, const TGLfloat *params) {
+ TinyGL::GLContext *c = TinyGL::gl_get_context();
+ TinyGL::GLParam p[8];
+
+ p[0].op = TinyGL::OP_TexEnv;
+ p[1].i = target;
+ p[2].i = pname;
+ p[3].i = 0;
+
+ int n = 0;
+ if (target == TGL_TEXTURE_ENV && pname == TGL_TEXTURE_ENV_COLOR)
+ n = 4;
+ for (int i = 0; i < n; i++)
+ p[4 + i].f = params[i];
+
+ c->gl_add_op(p);
+}
+
void tglTexParameteri(TGLenum target, TGLenum pname, TGLint param) {
TinyGL::GLContext *c = TinyGL::gl_get_context();
TinyGL::GLParam p[8];
diff --git a/graphics/tinygl/gl.h b/graphics/tinygl/gl.h
index 9e33c972c18..37626998dc7 100644
--- a/graphics/tinygl/gl.h
+++ b/graphics/tinygl/gl.h
@@ -674,6 +674,7 @@ enum {
TGL_OPERAND1_RGB = 0x8591,
TGL_OPERAND0_ALPHA = 0x8598,
TGL_OPERAND1_ALPHA = 0x8599,
+ TGL_CONSTANT = 0x8576,
TGL_PRIMARY_COLOR = 0x8577,
TGL_PREVIOUS = 0x8578,
diff --git a/graphics/tinygl/texture.cpp b/graphics/tinygl/texture.cpp
index 3be2cba08ca..6172c66d36c 100644
--- a/graphics/tinygl/texture.cpp
+++ b/graphics/tinygl/texture.cpp
@@ -190,7 +190,7 @@ void GLContext::glopTexEnv(GLParam *p) {
if (target != TGL_TEXTURE_ENV) {
error:
- error("tglTexParameter: unsupported option");
+ error("tglTexEnv: unsupported option");
}
switch (pname) {
@@ -198,7 +198,7 @@ error:
if (param == TGL_REPLACE ||
param == TGL_MODULATE ||
param == TGL_DECAL ||
- //param == TGL_BLEND || // no tex env constants yet
+ param == TGL_BLEND ||
param == TGL_ADD ||
param == TGL_COMBINE)
_texEnv.envMode = param;
@@ -226,7 +226,8 @@ error:
{
GLTextureEnvArgument *op = pname == TGL_SOURCE0_RGB ? &_texEnv.arg0 : &_texEnv.arg1;
if (param == TGL_TEXTURE ||
- param == TGL_PRIMARY_COLOR)
+ param == TGL_PRIMARY_COLOR ||
+ param == TGL_CONSTANT)
op->sourceRGB = param;
else
goto error;
@@ -237,7 +238,8 @@ error:
{
GLTextureEnvArgument *op = pname == TGL_SOURCE0_ALPHA ? &_texEnv.arg0 : &_texEnv.arg1;
if (param == TGL_TEXTURE ||
- param == TGL_PRIMARY_COLOR)
+ param == TGL_PRIMARY_COLOR ||
+ param == TGL_CONSTANT)
op->sourceAlpha = param;
else
goto error;
@@ -266,6 +268,14 @@ error:
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;
}
diff --git a/graphics/tinygl/zbuffer.cpp b/graphics/tinygl/zbuffer.cpp
index a6323669485..6c8a080a7a8 100644
--- a/graphics/tinygl/zbuffer.cpp
+++ b/graphics/tinygl/zbuffer.cpp
@@ -328,6 +328,12 @@ void FrameBuffer::applyTextureEnvironment(
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;
@@ -339,6 +345,9 @@ void FrameBuffer::applyTextureEnvironment(
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;
@@ -402,14 +411,24 @@ void FrameBuffer::applyTextureEnvironment(
}
case TGL_ADD:
{
- // GL_RGB: Cp + Cs | Ap
- // GL_RGB: Cp + Cs | ApAs
+ // 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);
diff --git a/graphics/tinygl/zbuffer.h b/graphics/tinygl/zbuffer.h
index 37ef20711b0..6a73a28b337 100644
--- a/graphics/tinygl/zbuffer.h
+++ b/graphics/tinygl/zbuffer.h
@@ -76,7 +76,7 @@ static const int DRAW_DEPTH_ONLY = 0;
static const int DRAW_FLAT = 1;
static const int DRAW_SMOOTH = 2;
-struct GLTextureEnv; // defined in zgl.h
+struct GLTextureEnv; // defined in zdirtyrect.h
struct Buffer {
byte *pbuf;
diff --git a/graphics/tinygl/zdirtyrect.cpp b/graphics/tinygl/zdirtyrect.cpp
index 7a14a426a0e..b34947905fa 100644
--- a/graphics/tinygl/zdirtyrect.cpp
+++ b/graphics/tinygl/zdirtyrect.cpp
@@ -36,7 +36,8 @@ GLTextureEnvArgument::GLTextureEnvArgument()
GLTextureEnv::GLTextureEnv()
: envMode(TGL_MODULATE)
, combineRGB(TGL_REPLACE)
- , combineAlpha(TGL_REPLACE) {}
+ , combineAlpha(TGL_REPLACE)
+ , constA(255), constR(255), constG(255), constB(255) {}
bool GLTextureEnv::isDefault() const {
return envMode == TGL_MODULATE;
diff --git a/graphics/tinygl/zdirtyrect.h b/graphics/tinygl/zdirtyrect.h
index 569049dd53a..e0c614eac32 100644
--- a/graphics/tinygl/zdirtyrect.h
+++ b/graphics/tinygl/zdirtyrect.h
@@ -53,6 +53,7 @@ struct GLTextureEnv {
bool isDefault() const;
uint envMode, combineRGB, combineAlpha;
+ byte constA, constR, constG, constB;
GLTextureEnvArgument arg0, arg1;
};
diff --git a/test/tgraphics/tinygl_texenv.h b/test/tgraphics/tinygl_texenv.h
index 9d08bdd5c7f..e31b161b616 100644
--- a/test/tgraphics/tinygl_texenv.h
+++ b/test/tgraphics/tinygl_texenv.h
@@ -31,7 +31,12 @@ public:
}
}
- // these two functions use RGBA order instead of ARGB to make it consistent with tglColor4ub which we also call
+ // these three functions use RGBA order instead of ARGB to make it consistent with tglColor4ub which we also call
+
+ void setConstant(byte r, byte g, byte b, byte a) {
+ const float values[] = { r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f };
+ tglTexEnvfv(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_COLOR, values);
+ }
void drawPixel(byte texR, byte texG, byte texB, byte texA) {
const float S = 10.0f;
@@ -91,6 +96,15 @@ public:
// attention: TGL_ADD still modulates alpha
}
+ void testBlend() {
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_BLEND);
+ tglColor4ub(210, 210, 200, 127);
+ setConstant(123, 123, 100, 42);
+ drawPixel(0, 255, 128, 200);
+ checkOutput(211, 123, 150, 100);
+ // the 211 is an unfortunate rounding problem
+ }
+
void setCombineMode(TGLuint combineRGB, TGLuint combineAlpha) {
tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_COMBINE);
tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_RGB, combineRGB);
@@ -120,6 +134,14 @@ public:
checkOutput(12, 34, 56, 78);
}
+ void testCombineArgConstant() {
+ setCombineMode(TGL_REPLACE, TGL_REPLACE);
+ setCombineArg(0, TGL_CONSTANT, TGL_SRC_COLOR, TGL_CONSTANT, TGL_SRC_ALPHA);
+ setConstant(12, 34, 56, 78);
+ drawPixel(13, 37, 42, 24);
+ checkOutput(12, 34, 56, 78);
+ }
+
void testCombineArgMixed() {
setCombineMode(TGL_REPLACE, TGL_REPLACE);
setCombineArg(0, TGL_PRIMARY_COLOR, TGL_SRC_ALPHA, TGL_TEXTURE, TGL_SRC_ALPHA);
Commit: 27f2b1b6d6520e0130dc75592a315984bada2df9
https://github.com/scummvm/scummvm/commit/27f2b1b6d6520e0130dc75592a315984bada2df9
Author: Helco (hermann.noll at hotmail.com)
Date: 2025-09-11T17:30:00+02:00
Commit Message:
TESTBED: Add test for TGL_BLEND
Changed paths:
engines/testbed/tinygl.cpp
diff --git a/engines/testbed/tinygl.cpp b/engines/testbed/tinygl.cpp
index 0d24fab8994..4b824e6e0db 100644
--- a/engines/testbed/tinygl.cpp
+++ b/engines/testbed/tinygl.cpp
@@ -50,6 +50,7 @@ namespace TinyGLTests {
struct TextureEnvironment {
GLuint _mode, _combineRGB, _combineAlpha;
TextureEnvironmentArg _arg0, _arg1;
+ byte _constantR = 255, _constantG = 255, _constantB = 255, _constantA = 255;
TextureEnvironment(
GLuint mode = GL_REPLACE,
@@ -59,21 +60,29 @@ namespace TinyGLTests {
, _combineRGB(combineRGB)
, _combineAlpha(combineAlpha) { }
- template<typename TexEnvFunc>
- void apply(TexEnvFunc func) const {
- func(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, _mode);
- func(GL_TEXTURE_ENV, GL_COMBINE_RGB, _combineRGB);
- func(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, _combineAlpha);
-
- func(GL_TEXTURE_ENV, GL_SOURCE0_RGB, _arg0._sourceRGB);
- func(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, _arg0._sourceAlpha);
- func(GL_TEXTURE_ENV, GL_SOURCE1_RGB, _arg1._sourceRGB);
- func(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, _arg1._sourceAlpha);
-
- func(GL_TEXTURE_ENV, GL_OPERAND0_RGB, _arg0._operandRGB);
- func(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, _arg0._operandAlpha);
- func(GL_TEXTURE_ENV, GL_OPERAND1_RGB, _arg1._operandRGB);
- func(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, _arg1._operandAlpha);
+ template<typename TexEnvFunci, typename TexEnvFuncfv>
+ void apply(TexEnvFunci funci, TexEnvFuncfv funcfv) const {
+ funci(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, _mode);
+ funci(GL_TEXTURE_ENV, GL_COMBINE_RGB, _combineRGB);
+ funci(GL_TEXTURE_ENV, GL_COMBINE_ALPHA, _combineAlpha);
+
+ funci(GL_TEXTURE_ENV, GL_SOURCE0_RGB, _arg0._sourceRGB);
+ funci(GL_TEXTURE_ENV, GL_SOURCE0_ALPHA, _arg0._sourceAlpha);
+ funci(GL_TEXTURE_ENV, GL_SOURCE1_RGB, _arg1._sourceRGB);
+ funci(GL_TEXTURE_ENV, GL_SOURCE1_ALPHA, _arg1._sourceAlpha);
+
+ funci(GL_TEXTURE_ENV, GL_OPERAND0_RGB, _arg0._operandRGB);
+ funci(GL_TEXTURE_ENV, GL_OPERAND0_ALPHA, _arg0._operandAlpha);
+ funci(GL_TEXTURE_ENV, GL_OPERAND1_RGB, _arg1._operandRGB);
+ funci(GL_TEXTURE_ENV, GL_OPERAND1_ALPHA, _arg1._operandAlpha);
+
+ const float values[] = {
+ _constantR / 255.0f,
+ _constantG / 255.0f,
+ _constantB / 255.0f,
+ _constantA / 255.0f
+ };
+ funcfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, values);
}
};
@@ -85,6 +94,7 @@ namespace TinyGLTests {
TestExitStatus testTexEnvModulate();
TestExitStatus testTexEnvDecal();
TestExitStatus testTexEnvAdd();
+ TestExitStatus testTexEnvBlend();
TestExitStatus testTexEnvCombineOpNormal();
TestExitStatus testTexEnvCombineOpInverse();
TestExitStatus testTexEnvCombineOpAlphaToColor();
@@ -99,6 +109,7 @@ TinyGLTestSuite::TinyGLTestSuite() {
addTest("Modulate", &TinyGLTests::testTexEnvModulate);
addTest("Decal", &TinyGLTests::testTexEnvDecal);
addTest("Add", &TinyGLTests::testTexEnvAdd);
+ addTest("Blend", &TinyGLTests::testTexEnvBlend);
addTest("CombineOpNormal", &TinyGLTests::testTexEnvCombineOpNormal);
addTest("CombineOpInverse", &TinyGLTests::testTexEnvCombineOpInverse);
addTest("CombineOpAlphaToColor", &TinyGLTests::testTexEnvCombineOpAlphaToColor);
@@ -128,6 +139,15 @@ TestExitStatus TinyGLTests::testTexEnvAdd() {
return runTexEnvTest("Add", env, 50, 111, 222, 255);
}
+TestExitStatus TinyGLTests::testTexEnvBlend() {
+ TextureEnvironment env(GL_BLEND);
+ env._constantR = 250;
+ env._constantG = 150;
+ env._constantB = 100;
+ env._constantA = 50;
+ return runTexEnvTest("Blend", env, 50, 111, 222, 255);
+}
+
TestExitStatus TinyGLTests::testTexEnvCombineOpNormal() {
TextureEnvironment env(GL_COMBINE, GL_REPLACE, GL_REPLACE);
env._arg0 = { GL_TEXTURE, GL_SRC_COLOR, GL_TEXTURE, GL_SRC_ALPHA };
@@ -183,6 +203,10 @@ static void classicGLTexEnvi(GLenum target, GLenum param, GLint value) {
glTexEnvi(target, param, value);
}
+static void classicGLTexEnvfv(GLenum target, GLenum param, const GLfloat *values) {
+ glTexEnvfv(target, param, values);
+}
+
static void copyAlphaIntoColorChannels(Graphics::ManagedSurface &surface) {
byte a, r, g, b;
for (int y = 0; y < surface.h; y++) {
@@ -310,7 +334,7 @@ TestExitStatus TinyGLTests::runTexEnvTest(
tglTexImage2D(TGL_TEXTURE_2D, 0, TGL_RGBA, kTextureSize, kTextureSize, 0, TGL_RGBA, TGL_UNSIGNED_BYTE, testImage.getPixels());
// Render classic OpenGL
- env.apply(classicGLTexEnvi);
+ env.apply(classicGLTexEnvi, classicGLTexEnvfv);
glClear(GL_COLOR_BUFFER_BIT);
glColor4ub(colorR, colorG, colorB, colorA);
renderClassicQuad(classicTestTexture, 0, 0);
@@ -331,7 +355,7 @@ TestExitStatus TinyGLTests::runTexEnvTest(
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, readBack.getPixels());
// Render TinyGL
- env.apply(tglTexEnvi);
+ env.apply(tglTexEnvi, tglTexEnvfv);
tglClear(TGL_COLOR_BUFFER_BIT);
tglColor4ub(colorR, colorG, colorB, colorA);
tglBegin(TGL_TRIANGLE_STRIP);
Commit: 27638abef614fd301e3e339947793a89594042f8
https://github.com/scummvm/scummvm/commit/27638abef614fd301e3e339947793a89594042f8
Author: Helco (hermann.noll at hotmail.com)
Date: 2025-09-11T17:30:00+02:00
Commit Message:
TETRAEDGE: Enable texture environments on TinyGL
Changed paths:
engines/tetraedge/te/te_mesh_tinygl.cpp
engines/tetraedge/te/te_renderer_tinygl.cpp
diff --git a/engines/tetraedge/te/te_mesh_tinygl.cpp b/engines/tetraedge/te/te_mesh_tinygl.cpp
index f100363eebe..a2b93c3583e 100644
--- a/engines/tetraedge/te/te_mesh_tinygl.cpp
+++ b/engines/tetraedge/te/te_mesh_tinygl.cpp
@@ -28,7 +28,7 @@
namespace Tetraedge {
-TeMeshTinyGL::TeMeshTinyGL() : _glMeshMode(TGL_POINTS), _gltexEnvMode(TGL_DECAL) {
+TeMeshTinyGL::TeMeshTinyGL() : _glMeshMode(TGL_POINTS), _gltexEnvMode(TGL_MODULATE) {
}
void TeMeshTinyGL::draw() {
@@ -99,8 +99,7 @@ void TeMeshTinyGL::draw() {
if (!_colors.empty())
tglColorPointer(4, TGL_UNSIGNED_BYTE, sizeof(TeColor), _colors.data());
- // TODO: not supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, _gltexEnvMode);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, _gltexEnvMode);
if (renderer->scissorEnabled()) {
tglEnable(TGL_SCISSOR_TEST);
// TODO: Scissor not supported by TGL
@@ -141,8 +140,7 @@ void TeMeshTinyGL::draw() {
if (!renderer->scissorEnabled())
tglDisable(TGL_SCISSOR_TEST);
- // TODO: GL_MODULATE not supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
tglDisableClientState(TGL_VERTEX_ARRAY);
tglDisableClientState(TGL_NORMAL_ARRAY);
tglDisableClientState(TGL_COLOR_ARRAY);
diff --git a/engines/tetraedge/te/te_renderer_tinygl.cpp b/engines/tetraedge/te/te_renderer_tinygl.cpp
index eaa1dce076c..9d0077d21a6 100644
--- a/engines/tetraedge/te/te_renderer_tinygl.cpp
+++ b/engines/tetraedge/te/te_renderer_tinygl.cpp
@@ -208,8 +208,7 @@ void TeRendererTinyGL::renderTransparentMeshes() {
meshProperties._scissorWidth,
meshProperties._scissorHeight);*/
}
- // TODO: not supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL/*meshProperties._glTexEnvMode*/);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, meshProperties._glTexEnvMode);
tglDrawElements(TGL_TRIANGLES, meshProperties._vertexCount, TGL_UNSIGNED_SHORT,
_transparentMeshVertexNums.data() + vertsDrawn);
@@ -219,8 +218,7 @@ void TeRendererTinyGL::renderTransparentMeshes() {
tglEnableClientState(TGL_TEXTURE_COORD_ARRAY);
tglEnableClientState(TGL_COLOR_ARRAY);
}
- // TODO: not supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
if (meshProperties._scissorEnabled) {
tglDisable(TGL_SCISSOR_TEST);
}
@@ -306,7 +304,7 @@ void TeRendererTinyGL::shadowMode(enum ShadowMode mode) {
void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
//debug("TeMaterial::apply (%s)", dump().c_str());
- //static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
+ static const float constColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
if (_shadowMode == TeRenderer::ShadowModeNone) {
if (m._enableLights)
TeLightTinyGL::enableAll();
@@ -321,8 +319,6 @@ void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
tglDisable(TGL_ALPHA_TEST);
if (m._mode == TeMaterial::MaterialMode0) {
- /* TODO: Find TGL equivalents for this stuff*/
- /*
tglTexEnvfv(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_COLOR, constColor);
tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_COMBINE);
tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_RGB, TGL_MODULATE);
@@ -331,10 +327,9 @@ void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
tglTexEnvi(TGL_TEXTURE_ENV, TGL_COMBINE_ALPHA, TGL_REPLACE);
tglTexEnvi(TGL_TEXTURE_ENV, TGL_SOURCE0_ALPHA, TGL_CONSTANT);
tglTexEnvi(TGL_TEXTURE_ENV, TGL_OPERAND0_ALPHA, TGL_SRC_ALPHA);
- */
+
} else {
- // TODO: GL_MODULATE supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
if (m._mode != TeMaterial::MaterialMode1) {
tglEnable(TGL_ALPHA_TEST);
tglAlphaFunc(TGL_GREATER, 0.5);
@@ -365,8 +360,7 @@ void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
static const float fullColor[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
TeLightTinyGL::disableAll();
tglDisable(TGL_ALPHA_TEST);
- // TODO: GL_MODULATE not supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
tglMaterialfv(TGL_FRONT_AND_BACK, TGL_AMBIENT, fullColor);
tglMaterialfv(TGL_FRONT_AND_BACK, TGL_DIFFUSE, fullColor);
tglMaterialfv(TGL_FRONT_AND_BACK, TGL_SPECULAR, fullColor);
@@ -379,8 +373,7 @@ void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
tglDisable(TGL_TEXTURE_GEN_R);
tglDisable(TGL_TEXTURE_GEN_Q);
} else {
- // TODO: GL_MODULATE not supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
tglEnable(TGL_TEXTURE_GEN_S);
tglEnable(TGL_TEXTURE_GEN_T);
tglEnable(TGL_TEXTURE_GEN_R);
@@ -389,8 +382,7 @@ void TeRendererTinyGL::applyMaterial(const TeMaterial &m) {
TeLightTinyGL::disableAll();
tglDisable(TGL_ALPHA_TEST);
enableTexture();
- // TODO: GL_MODULATE not supported in TGL
- //tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_DECAL);
+ tglTexEnvi(TGL_TEXTURE_ENV, TGL_TEXTURE_ENV_MODE, TGL_MODULATE);
const float diffuse[4] = { m._diffuseColor.r() / 255.0f, m._diffuseColor.g() / 255.0f,
m._diffuseColor.b() / 255.0f, m._diffuseColor.a() / 255.0f };
More information about the Scummvm-git-logs
mailing list