This commit is contained in:
2026-06-14 19:09:18 +01:00
parent 14bd1a9271
commit 13fa90a0e9
3958 changed files with 999286 additions and 4 deletions
@@ -0,0 +1,424 @@
/* -*- tab-iWidth: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Tests texture loading by using glDrawTexture to draw the texture.
*
* @author Mark Callow
*/
#if defined(_WIN32)
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <sstream>
#include <ktx.h>
#include "disable_glm_warnings.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "reenable_warnings.h"
#include "DrawTexture.h"
#include "GLTextureTranscoder.hpp"
#include "TranscodeTargetStrToFmt.h"
#include "frame.h"
#include "quad.h"
#include "argparser.h"
#include "ltexceptions.h"
#if !defined(GL_TEXTURE_1D)
#define GL_TEXTURE_1D 0x0DE0
#endif
/* ------------------------------------------------------------------------- */
extern const GLchar* pszVs;
extern const GLchar *pszDecalFs, *pszDecalSrgbEncodeFs;
extern const GLchar *pszColorFs, *pszColorSrgbEncodeFs;
/* ------------------------------------------------------------------------- */
LoadTestSample*
DrawTexture::create(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
{
return new DrawTexture(width, height, szArgs, sBasePath);
}
DrawTexture::DrawTexture(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
: GL3LoadTestSample(width, height, szArgs, sBasePath)
{
GLfloat* pfQuadTexCoords = quad_texture;
GLfloat fTmpTexCoords[sizeof(quad_texture)/sizeof(GLfloat)];
GLenum target;
GLenum glerror;
GLint sign_s = 1, sign_t = 1;
GLint i;
GLuint gnColorFs, gnDecalFs, gnVs;
GLsizeiptr offset;
ktxTexture* kTexture;
KTX_error_code ktxresult;
bInitialized = false;
transcodeTarget = KTX_TTF_NOSELECTION;
gnTexture = 0;
processArgs(szArgs);
std::string ktxfilepath = externalFile ? ktxfilename
: getAssetPath() + ktxfilename;
ktxresult = ktxTexture_CreateFromNamedFile(ktxfilepath.c_str(),
preloadImages ? KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT
: KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Creation of ktxTexture from \"" << ktxfilepath
<< "\" failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
if (ktxTexture_NeedsTranscoding(kTexture)) {
TextureTranscoder tc;
tc.transcode((ktxTexture2*)kTexture, transcodeTarget);
}
ktxresult = ktxTexture_GLUpload(kTexture, &gnTexture, &target, &glerror);
if (KTX_SUCCESS == ktxresult) {
// GLUpload won't set target to GL_TEXTURE_1D not supported by context.
if (target != GL_TEXTURE_1D && target != GL_TEXTURE_2D) {
/* Can only draw 1D & 2D textures */
std::stringstream message;
glDeleteTextures(1, &gnTexture);
message << "DrawTexture supports only 1D & 2D textures. \""
<< ktxfilename << "\" is not one of these.";
throw std::runtime_error(message.str());
}
if (kTexture->orientation.x == KTX_ORIENT_X_LEFT)
sign_s = -1;
if (kTexture->orientation.y == KTX_ORIENT_Y_DOWN)
sign_t = -1;
if (sign_s < 0 || sign_t < 0) {
// Transform the texture coordinates to get correct image
// orientation.
int iNumCoords = sizeof(quad_texture) / sizeof(float);
for (i = 0; i < iNumCoords; i++) {
fTmpTexCoords[i] = quad_texture[i];
if (i & 1) { // odd, i.e. a y coordinate
if (sign_t < 1) {
fTmpTexCoords[i] = fTmpTexCoords[i] * -1 + 1;
}
} else { // an x coordinate
if (sign_s < 1) {
fTmpTexCoords[i] = fTmpTexCoords[i] * -1 + 1;
}
}
}
pfQuadTexCoords = fTmpTexCoords;
}
uTexWidth = kTexture->baseWidth;
uTexHeight = kTexture->baseHeight;
if (kTexture->numLevels > 1)
// Enable bilinear mipmapping.
// TO DO: application can consider inserting a key,value pair in
// the KTX file that indicates what type of filtering to use.
glTexParameteri(target,
GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
else
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
ktx_uint32_t swizzleLen;
char* swizzleStr;
ktxresult = ktxHashList_FindValue(&kTexture->kvDataHead, KTX_SWIZZLE_KEY,
&swizzleLen, (void**)&swizzleStr);
if (ktxresult == KTX_SUCCESS && swizzleLen == 5) {
if (contextSupportsSwizzle()) {
GLint swizzle[4];
swizzle[0] = swizzleStr[0] == 'r' ? GL_RED
: swizzleStr[0] == 'g' ? GL_GREEN
: swizzleStr[0] == 'b' ? GL_BLUE
: swizzleStr[0] == 'a' ? GL_ALPHA
: swizzleStr[0] == '0' ? GL_ZERO
: GL_ONE;
swizzle[1] = swizzleStr[1] == 'r' ? GL_RED
: swizzleStr[1] == 'g' ? GL_GREEN
: swizzleStr[1] == 'b' ? GL_BLUE
: swizzleStr[1] == 'a' ? GL_ALPHA
: swizzleStr[1] == '0' ? GL_ZERO
: GL_ONE;
swizzle[2] = swizzleStr[2] == 'r' ? GL_RED
: swizzleStr[2] == 'g' ? GL_GREEN
: swizzleStr[2] == 'b' ? GL_BLUE
: swizzleStr[2] == 'a' ? GL_ALPHA
: swizzleStr[2] == '2' ? GL_ZERO
: GL_ONE;
swizzle[3] = swizzleStr[3] == 'r' ? GL_RED
: swizzleStr[3] == 'g' ? GL_GREEN
: swizzleStr[3] == 'b' ? GL_BLUE
: swizzleStr[3] == 'a' ? GL_ALPHA
: swizzleStr[3] == '3' ? GL_ZERO
: GL_ONE;
glTexParameteri(target, GL_TEXTURE_SWIZZLE_R, swizzle[0]);
glTexParameteri(target, GL_TEXTURE_SWIZZLE_G, swizzle[1]);
glTexParameteri(target, GL_TEXTURE_SWIZZLE_B, swizzle[2]);
glTexParameteri(target, GL_TEXTURE_SWIZZLE_A, swizzle[3]);
} else {
std::stringstream message;
message << "Input file has swizzle metadata but the "
<< "GL context does not support swizzling.";
// I have absolutely no idea why the following line makes clang
// raise an error about no matching conversion from
// std::__1::basic_stringstream to unsupported_ttype
// so I've resorted to using a temporary variable.
//throw(unsupported_ttype(message.str());
std::string msg = message.str();
throw(unsupported_ttype(msg));
}
}
assert(GL_NO_ERROR == glGetError());
ktxTexture_Destroy(kTexture);
} else {
std::stringstream message;
message << "ktxTexture_GLUpload failed: ";
if (ktxresult != KTX_GL_ERROR) {
message << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
} else if (kTexture->isCompressed
// Emscripten/WebGL returns INVALID_VALUE for unsupported
// ETC formats.
&& (glerror == GL_INVALID_ENUM || glerror == GL_INVALID_VALUE)) {
throw unsupported_ctype();
} else {
message << std::showbase << "GL error " << std::hex << glerror
<< " occurred.";
throw std::runtime_error(message.str());
}
}
glClearColor(0.4f, 0.4f, 0.5f, 1.0f);
// Must have vertex data in buffer objects to use VAO's on ES3/GL Core
glGenBuffers(1, &gnVbo);
glBindBuffer(GL_ARRAY_BUFFER, gnVbo);
// Create the buffer data store
glBufferData(GL_ARRAY_BUFFER,
sizeof(frame_position) + sizeof(frame_color)
+ sizeof(quad_position) + sizeof(quad_color)
+ sizeof(quad_texture),
NULL, GL_STATIC_DRAW);
glGenVertexArrays(2, gnVaos);
// Interleave data copying and attrib pointer setup so offset is only
// computed once.
// Setup VAO and buffer the data for frame
glBindVertexArray(gnVaos[FRAME]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
offset = 0;
glBufferSubData(GL_ARRAY_BUFFER, offset,
sizeof(frame_position), frame_position);
glVertexAttribPointer(0, 3, GL_BYTE, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(frame_position);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(frame_color), frame_color);
glVertexAttribPointer(1, 3, GL_BYTE, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(frame_color);
// Setup VAO for quad
glBindVertexArray(gnVaos[QUAD]);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glBufferSubData(GL_ARRAY_BUFFER, offset,
sizeof(quad_position), quad_position);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(quad_position);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(quad_color), quad_color);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(quad_color);
glBufferSubData(GL_ARRAY_BUFFER, offset,
sizeof(quad_texture), pfQuadTexCoords);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
glBindVertexArray(0);
const GLchar *actualColorFs, *actualDecalFs;
if (framebufferColorEncoding() == GL_LINEAR) {
actualColorFs = pszColorSrgbEncodeFs;
actualDecalFs = pszDecalSrgbEncodeFs;
} else {
actualColorFs = pszColorFs;
actualDecalFs = pszDecalFs;
}
try {
makeShader(GL_VERTEX_SHADER, pszVs, &gnVs);
makeShader(GL_FRAGMENT_SHADER, actualColorFs, &gnColorFs);
makeProgram(gnVs, gnColorFs, &gnColProg);
gulMvMatrixLocCP = glGetUniformLocation(gnColProg, "mvmatrix");
gulPMatrixLocCP = glGetUniformLocation(gnColProg, "pmatrix");
makeShader(GL_FRAGMENT_SHADER, actualDecalFs, &gnDecalFs);
makeProgram(gnVs, gnDecalFs, &gnTexProg);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
throw;
}
gulMvMatrixLocTP = glGetUniformLocation(gnTexProg, "mvmatrix");
gulPMatrixLocTP = glGetUniformLocation(gnTexProg, "pmatrix");
gulSamplerLocTP = glGetUniformLocation(gnTexProg, "sampler");
glUseProgram(gnTexProg);
// We're using the default texture unit 0
glUniform1i(gulSamplerLocTP, 0);
glDeleteShader(gnVs);
glDeleteShader(gnColorFs);
glDeleteShader(gnDecalFs);
// Set the quad's mv matrix to scale by the texture size.
// With the pixel-mapping ortho projection set below, the texture will
// be rendered at actual size just like DrawTex*OES.
quadMvMatrix = glm::scale(glm::mat4(),
glm::vec3((float)uTexWidth / 2,
(float)uTexHeight / 2,
1));
assert(GL_NO_ERROR == glGetError());
bInitialized = true;
}
/* ------------------------------------------------------------------------- */
DrawTexture::~DrawTexture()
{
if (bInitialized) {
// A bug in the PVR SDK 3.1 emulator causes the
// glDeleteProgram(gnColProg) below to raise an INVALID_VALUE error
// if the following glUseProgram(0) has been executed. Strangely the
// equivalent line in TexturedCube.cpp, where only 1 program is used,
// does not raise an error.
glUseProgram(0);
glDeleteTextures(1, &gnTexture);
glDeleteProgram(gnTexProg);
glDeleteProgram(gnColProg);
glDeleteBuffers(1, &gnVbo);
glDeleteVertexArrays(2, gnVaos);
}
assert(GL_NO_ERROR == glGetError());
}
/* ------------------------------------------------------------------------- */
void
DrawTexture::processArgs(std::string sArgs)
{
// Options descriptor
struct argparser::option longopts[] = {
{"external", argparser::option::no_argument, &externalFile, 1},
{"preload", argparser::option::no_argument, &preloadImages, 1},
{"transcode-target", argparser::option::required_argument, nullptr, 2},
{NULL, argparser::option::no_argument, nullptr, 0}
};
argvector argv(sArgs);
argparser ap(argv);
int ch;
while ((ch = ap.getopt(nullptr, longopts, nullptr)) != -1) {
switch (ch) {
case 0: break;
case 2:
transcodeTarget = TranscodeTargetStrToFmt(ap.optarg);
break;
default: assert(false); // Error in args in sample table.
}
}
assert(ap.optind < argv.size());
ktxfilename = argv[ap.optind];
}
/* ------------------------------------------------------------------------- */
void
DrawTexture::resize(uint32_t uNewWidth, uint32_t uNewHeight)
{
glViewport(0, 0, uNewWidth, uNewHeight);
this->uWidth = uNewWidth;
this->uHeight = uNewHeight;
// Set up an orthographic projection where 1 = 1 pixel, and 0,0,0
// is at the center of the window.
//atSetOrthoZeroAtCenterMatrix(fPMatrix,
// 0.0f, (float)iWidth,
// 0.0f, (float)iHeight,
// -1.0f, 1.0f);
// Set up an orthographic projection where 1 = 1 pixel
pMatrix = glm::ortho(0.f, (float)uWidth, 0.f, (float)uHeight);
// Move (0,0,0) to the center of the window.
pMatrix *= glm::translate(glm::mat4(),
glm::vec3((float)uWidth/2, (float)uHeight/2, 0));
// Scale the frame to fill the viewport. To guarantee its lines
// appear we need to inset them by half-a-pixel hence the -1.
// [Lines at the edges of the clip volume may or may not appear
// depending on the OpenGL ES implementation. This is because
// (a) the edges are on the points of the diamonds of the diamond
// exit rule and slight precision errors can easily push the
// lines outside the diamonds.
// (b) the specification allows lines to be up to 1 pixel either
// side of the exact position.]
frameMvMatrix = glm::scale(glm::mat4(),
glm::vec3((float)(uWidth - 1)/2,
(float)(uHeight - 1)/2,
1));
}
/* ------------------------------------------------------------------------- */
void
DrawTexture::run(uint32_t /*msTicks*/)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glBindVertexArray(gnVaos[FRAME]);
glUseProgram(gnColProg);
glUniformMatrix4fv(gulMvMatrixLocCP, 1, GL_FALSE,
glm::value_ptr(frameMvMatrix));
glUniformMatrix4fv(gulPMatrixLocCP, 1, GL_FALSE, glm::value_ptr(pMatrix));
glDrawArrays(GL_LINE_LOOP, 0, 4);
glBindVertexArray(gnVaos[QUAD]);
glUseProgram(gnTexProg);
glUniformMatrix4fv(gulMvMatrixLocTP, 1, GL_FALSE,
glm::value_ptr(quadMvMatrix));
glUniformMatrix4fv(gulPMatrixLocTP, 1, GL_FALSE, glm::value_ptr(pMatrix));
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
assert(GL_NO_ERROR == glGetError());
}
/* ------------------------------------------------------------------------- */
@@ -0,0 +1,71 @@
/* -*- tab-iWidth: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Definition of texture loading test using glDrawTexture.
*
* @author Mark Callow
*/
#ifndef DRAW_TEXTURE_H
#define DRAW_TEXTURE_H
#include "GL3LoadTestSample.h"
class DrawTexture : public GL3LoadTestSample {
public:
DrawTexture(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
~DrawTexture();
virtual void resize(uint32_t width, uint32_t height);
virtual void run(uint32_t msTicks);
//virtual void getOverlayText(GLTextOverlay *textOverlay);
static LoadTestSample*
create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath);
protected:
void processArgs(std::string sArgs);
int preloadImages = 0;
uint32_t uWidth;
uint32_t uHeight;
uint32_t uTexWidth;
uint32_t uTexHeight;
glm::mat4 frameMvMatrix;
glm::mat4 quadMvMatrix;
glm::mat4 pMatrix;
GLuint gnTexture;
GLuint gnTexProg;
GLuint gnColProg;
#define FRAME 0
#define QUAD 1
GLuint gnVaos[2];
GLuint gnVbo;
GLint gulMvMatrixLocTP;
GLint gulPMatrixLocTP;
GLint gulSamplerLocTP;
GLint gulMvMatrixLocCP;
GLint gulPMatrixLocCP;
bool bInitialized;
ktx_transcode_fmt_e transcodeTarget;
};
#endif /* DRAW_TEXTURE_H */
@@ -0,0 +1,326 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 sts expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Encode a texture then texture a cube with it, transcoding if necessary.
*
* This is used principally to check the encoders are properly linked on platforms where the ktx tools are
* unavailable and libktx is a static library.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#if defined(_WIN32)
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <sstream>
#include <ktx.h>
#include "disable_glm_warnings.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "reenable_warnings.h"
#include "EncodeTexture.h"
#include "GLTextureTranscoder.hpp"
#include "TranscodeTargetStrToFmt.h"
#include "cube.h"
#include "argparser.h"
#include "ltexceptions.h"
/* ------------------------------------------------------------------------- */
extern const GLchar* pszVs;
extern const GLchar *pszDecalFs, *pszDecalSrgbEncodeFs;
/* ------------------------------------------------------------------------- */
LoadTestSample*
EncodeTexture::create(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
{
return new EncodeTexture(width, height, szArgs, sBasePath);
}
EncodeTexture::EncodeTexture(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
: GL3LoadTestSample(width, height, szArgs, sBasePath)
{
std::string filename;
GLenum target;
GLenum glerror;
GLuint gnDecalFs, gnVs;
GLsizeiptr offset;
ktxTexture2* kTexture;
KTX_error_code ktxresult;
bInitialized = GL_FALSE;
gnTexture = 0;
encodeTarget = EF_ETC1S;
transcodeTarget = KTX_TTF_NOSELECTION;
processArgs(szArgs);
filename = getAssetPath() + ktxfilename;
ktxresult = ktxTexture_CreateFromNamedFile(filename.c_str(),
KTX_TEXTURE_CREATE_NO_FLAGS,
(ktxTexture**)&kTexture);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Creation of ktxTexture from \"" << filename
<< "\" failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
if (!kTexture->isCompressed) {
switch (encodeTarget) {
case EF_ASTC:
ktxresult = ktxTexture2_CompressAstc(kTexture, 0);
break;
case EF_ETC1S:
ktxresult = ktxTexture2_CompressBasis(kTexture, 0);
break;
case EF_UASTC:
{
ktxBasisParams params = { };
params.structSize = sizeof(params);
params.uastc = KTX_TRUE;
params.threadCount = 1;
ktxresult = ktxTexture2_CompressBasisEx(kTexture, &params);
break;
}
}
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Encoding of ktxTexture2 to " << encodeTarget
<< " failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
}
if (ktxTexture2_NeedsTranscoding(kTexture)) {
TextureTranscoder tc;
tc.transcode((ktxTexture2*)kTexture);
}
ktxresult = ktxTexture_GLUpload(ktxTexture(kTexture), &gnTexture, &target,
&glerror);
if (KTX_SUCCESS == ktxresult) {
if (target != GL_TEXTURE_2D) {
/* Can only draw 2D textures */
std::stringstream message;
glDeleteTextures(1, &gnTexture);
message << "App can only draw 2D textures.";
throw std::runtime_error(message.str());
}
if (kTexture->numLevels > 1)
// Enable bilinear mipmapping.
// TO DO: application can consider inserting a key,value pair in
// the KTX file that indicates what type of filtering to use.
glTexParameteri(target,
GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
else
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
ktxTexture_Destroy(ktxTexture(kTexture));
assert(GL_NO_ERROR == glGetError());
} else {
std::stringstream message;
message << "Load of texture from \"" << filename << "\" failed: ";
if (ktxresult == KTX_GL_ERROR && glerror == GL_INVALID_ENUM) {
throw unsupported_ctype();
} else {
if (ktxresult == KTX_GL_ERROR) {
message << std::showbase << "GL error " << std::hex << glerror
<< " occurred.";
} else {
message << ktxErrorString(ktxresult);
}
throw std::runtime_error(message.str());
}
}
// By default dithering is enabled. Dithering does not provide visual
// improvement in this sample so disable it to improve performance.
glDisable(GL_DITHER);
glEnable(GL_CULL_FACE);
glClearColor(0.2f,0.3f,0.4f,1.0f);
// Create a VAO and bind it.
glGenVertexArrays(1, &gnVao);
glBindVertexArray(gnVao);
// Must have vertex data in buffer objects to use VAO's on ES3/GL Core
glGenBuffers(2, gnVbo);
glBindBuffer(GL_ARRAY_BUFFER, gnVbo[0]);
// Must be done after the VAO is bound
// WebGL requires different buffers for data and indices.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gnVbo[1]);
// Create the buffer data store.
glBufferData(GL_ARRAY_BUFFER,
sizeof(cube_face) + sizeof(cube_color) + sizeof(cube_texture)
+ sizeof(cube_normal), NULL, GL_STATIC_DRAW);
// Interleave data copying and attrib pointer setup so offset is only
// computed once.
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
offset = 0;
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_face), cube_face);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_face);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_color), cube_color);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_color);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_texture), cube_texture);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_texture);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_normal), cube_normal);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_normal);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_index_buffer),
cube_index_buffer, GL_STATIC_DRAW);
const GLchar* actualDecalFs;
if (framebufferColorEncoding() == GL_LINEAR) {
actualDecalFs = pszDecalSrgbEncodeFs;
} else {
actualDecalFs = pszDecalFs;
}
try {
makeShader(GL_VERTEX_SHADER, pszVs, &gnVs);
makeShader(GL_FRAGMENT_SHADER, actualDecalFs, &gnDecalFs);
makeProgram(gnVs, gnDecalFs, &gnTexProg);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
throw;
}
gulMvMatrixLocTP = glGetUniformLocation(gnTexProg, "mvmatrix");
gulPMatrixLocTP = glGetUniformLocation(gnTexProg, "pmatrix");
gulSamplerLocTP = glGetUniformLocation(gnTexProg, "sampler");
glUseProgram(gnTexProg);
// We're using the default texture unit 0
glUniform1i(gulSamplerLocTP, 0);
glDeleteShader(gnVs);
glDeleteShader(gnDecalFs);
assert(GL_NO_ERROR == glGetError());
bInitialized = GL_TRUE;
}
EncodeTexture::~EncodeTexture()
{
glEnable(GL_DITHER);
glDisable(GL_CULL_FACE);
if (bInitialized) {
glUseProgram(0);
glDeleteTextures(1, &gnTexture);
glDeleteProgram(gnTexProg);
glDeleteBuffers(2, gnVbo);
glDeleteVertexArrays(1, &gnVao);
}
assert(GL_NO_ERROR == glGetError());
}
/* ------------------------------------------------------------------------- */
void
EncodeTexture::processArgs(std::string sArgs)
{
// Options descriptor
struct argparser::option longopts[] = {
{"encode", argparser::option::required_argument, nullptr, 1},
{"transcode-target", argparser::option::required_argument, nullptr, 2},
{NULL, argparser::option::no_argument, nullptr, 0}
};
argvector argv(sArgs);
argparser ap(argv);
int ch;
while ((ch = ap.getopt(nullptr, longopts, nullptr)) != -1) {
switch (ch) {
case 0: break;
case 1:
if (!ap.optarg.compare("astc"))
encodeTarget = EF_ASTC;
else if (!ap.optarg.compare("etc1s"))
encodeTarget = EF_ETC1S;
else if (!ap.optarg.compare("uastc"))
encodeTarget = EF_UASTC;
else
assert(false && "Error in args in sample table");
break;
case 2:
transcodeTarget = TranscodeTargetStrToFmt(ap.optarg);
break;
default: assert(false && "Error in args in sample table");
}
}
assert(ap.optind < argv.size());
ktxfilename = argv[ap.optind];
}
/* ------------------------------------------------------------------------- */
void
EncodeTexture::resize(uint32_t uWidth, uint32_t uHeight)
{
glm::mat4 matProj;
glViewport(0, 0, uWidth, uHeight);
matProj = glm::perspective(glm::radians(45.f),
uWidth / (float)uHeight,
1.f, 100.f);
glUniformMatrix4fv(gulPMatrixLocTP, 1, GL_FALSE, glm::value_ptr(matProj));
}
void
EncodeTexture::run(uint32_t msTicks)
{
// Setup the view matrix : just turn around the cube.
const float fDistance = 5.0f;
glm::vec3 eye((float)cos( msTicks*0.001f ) * fDistance,
(float)sin( msTicks*0.0007f ) * fDistance,
(float)sin( msTicks*0.001f ) * fDistance);
glm::vec3 look(0.,0.,0.);
glm::vec3 up(0.,1.,0.);
glm::mat4 matView = glm::lookAt( eye, look, up );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(gulMvMatrixLocTP, 1, GL_FALSE, glm::value_ptr(matView));
glDrawElements(GL_TRIANGLES, CUBE_NUM_INDICES, GL_UNSIGNED_SHORT, 0);
assert(GL_NO_ERROR == glGetError());
}
/* ------------------------------------------------------------------------- */
@@ -0,0 +1,74 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2022 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief GLLoadTestSample derived class for encoding a texture and texturing a cube with it.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#ifndef ENCODE_TEXTURE_H
#define ENCODE_TEXTURE_H
#include "GL3LoadTestSample.h"
#include <iostream>
class EncodeTexture : public GL3LoadTestSample {
public:
EncodeTexture(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
~EncodeTexture();
virtual void resize(uint32_t width, uint32_t height);
virtual void run(uint32_t msTicks);
//virtual void getOverlayText(GLTextOverlay *textOverlay);
static LoadTestSample*
create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath);
protected:
void processArgs(std::string sArgs);
GLuint gnTexture;
GLuint gnTexProg;
GLuint gnVao;
GLuint gnVbo[2];
GLint gulMvMatrixLocTP;
GLint gulPMatrixLocTP;
GLint gulSamplerLocTP;
bool bInitialized;
ktx_transcode_fmt_e transcodeTarget;
enum encode_fmt_e { EF_ASTC = 1, EF_ETC1S = 2, EF_UASTC = 3 };
friend std::ostream& operator<<(std::ostream& os, encode_fmt_e format);
encode_fmt_e encodeTarget;
};
inline std::ostream& operator<<(std::ostream& os, EncodeTexture::encode_fmt_e format)
{
switch (format) {
case EncodeTexture::EF_ASTC:
os << "astc";
break;
case EncodeTexture::EF_ETC1S:
os << "etc1s";
break;
case EncodeTexture::EF_UASTC:
os << "uastc";
break;
}
return os;
}
#endif /* ENCODE_TEXTURE_H */
@@ -0,0 +1,281 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2017-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @class GLLoadTestSample
* @~English
*
* @brief Definition of a base class for OpenGL texture loading test samples.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <string.h>
#include <sstream>
#include <ktx.h>
#include <SDL3/SDL_platform.h>
#include "GL3LoadTestSample.h"
/* ------------------------------------------------------------------------- */
static const GLchar* pszESLangVer = "#version 300 es\n";
// location layout qualifier did not appear until version 330.
static const GLchar* pszGLLangVer = "#version 330 core\n";
/* ------------------------------------------------------------------------- */
void
GL3LoadTestSample::makeShader(GLenum type,
ShaderSource& source,
GLuint* shader)
{
GLint sh = glCreateShader(type);
GLint shaderCompiled;
if (strstr((const char*)glGetString(GL_VERSION), "GL ES") == NULL)
source.insert(source.cbegin(), pszGLLangVer);
else
source.insert(source.cbegin(), pszESLangVer);
glShaderSource(sh, (GLsizei)source.size(), source.data(), NULL);
glCompileShader(sh);
// Check if compilation succeeded
glGetShaderiv(sh, GL_COMPILE_STATUS, &shaderCompiled);
if (!shaderCompiled) {
// An error happened, first retrieve the length of the log message
int logLength, charsWritten;
char* infoLog;
std::stringstream message;
glGetShaderiv(sh, GL_INFO_LOG_LENGTH, &logLength);
// Allocate enough space for the message and retrieve it
infoLog = new char[logLength];
glGetShaderInfoLog(sh, logLength, &charsWritten, infoLog);
message << "makeShader compilation error" << std::endl << infoLog;
delete[] infoLog;
glDeleteShader(sh);
throw std::runtime_error(message.str());
} else {
*shader = sh;
}
}
void
GL3LoadTestSample::makeShader(GLenum type, const GLchar* const source,
GLuint* shader)
{
ShaderSource ss;
ss.push_back(source);
makeShader(type, ss, shader);
}
void
GL3LoadTestSample::makeProgram(GLuint vs, GLuint fs, GLuint* program)
{
GLint linked;
GLint fsCompiled, vsCompiled;
glGetShaderiv(vs, GL_COMPILE_STATUS, &fsCompiled);
glGetShaderiv(fs, GL_COMPILE_STATUS, &vsCompiled);
if (fsCompiled && vsCompiled) {
GLuint prog = glCreateProgram();
glAttachShader(prog, vs);
glAttachShader(prog, fs);
glLinkProgram(prog);
// Check if linking succeeded in the same way we checked for compilation success
glGetProgramiv(prog, GL_LINK_STATUS, &linked);
if (!linked) {
int logLength, charsWritten;
char* infoLog;
std::stringstream message;
glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &logLength);
infoLog = new char[logLength];
glGetProgramInfoLog(prog, logLength, &charsWritten, infoLog);
message << "makeProgram link error" << std::endl << infoLog;
delete[] infoLog;
glDeleteProgram(prog);
throw std::runtime_error(message.str());
}
*program = prog;
} else {
std::stringstream message;
message << "makeProgram: either vertex or fragment shader is not compiled.";
throw std::runtime_error(message.str());
}
}
#if !defined(GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT)
#define GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT 0x8A54
#define GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG 0x8C01
#define GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG 0x9137
#endif
#if !defined(GL_COMPRESSED_RG_RGTC2)
#define GL_COMPRESSED_RG_RGTC2 0x8DBD
#endif
#if !defined(GL_COMPRESSED_RGBA_BPTC_UNORM)
#define GL_COMPRESSED_RGBA_BPTC_UNORM 0x8E8C
#endif
#if !defined(GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT)
#define GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT 0x8E8E
#endif
void
GL3LoadTestSample::determineCompressedTexFeatures(compressedTexFeatures& features)
{
ktx_int32_t numCompressedFormats;
memset(&features, false, sizeof(features));
glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &numCompressedFormats);
GLint* formats = new GLint[numCompressedFormats];
glGetIntegerv(GL_COMPRESSED_TEXTURE_FORMATS, formats);
for (ktx_int32_t i = 0; i < numCompressedFormats; i++) {
if (formats[i] == GL_COMPRESSED_RGBA8_ETC2_EAC)
features.etc2 = true;
if (formats[i] == GL_ETC1_RGB8_OES)
features.etc1 = true;
if (formats[i] == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
features.bc3 = true;
if (formats[i] == GL_COMPRESSED_RG_RGTC2)
features.rgtc = true;
if (formats[i] == GL_COMPRESSED_SRGB_PVRTC_2BPPV1_EXT)
features.pvrtc_srgb = true;
if (formats[i] == GL_COMPRESSED_RGB_PVRTC_2BPPV1_IMG)
features.pvrtc1 = true;
if (formats[i] == GL_COMPRESSED_RGBA_PVRTC_2BPPV2_IMG)
features.pvrtc2 = true;
if (formats[i] == GL_COMPRESSED_RGBA_ASTC_4x4_KHR)
features.astc_ldr = true;
if (formats[i] == GL_COMPRESSED_RGBA_BPTC_UNORM)
features.bc7 = true;
if (formats[i] == GL_COMPRESSED_RGB_BPTC_SIGNED_FLOAT)
features.bc6h = true;
}
delete[] formats;
// Just in case COMPRESSED_TEXTURE_FORMATS didn't return anything.
// There is no ETC2 extension. It went into core in OpenGL ES 2.0.
// ARB_es_compatibility is not a good indicator. ETC2 could be supported
// by software decompression. Better to report unsupported.
if (!features.etc1 && SDL_GL_ExtensionSupported("GL_OES_compressed_ETC1_RGB8_texture"))
features.etc1 = true;;
if (!features.bc3 && SDL_GL_ExtensionSupported("GL_EXT_texture_compression_s3tc"))
features.bc3 = true;
if (!features.rgtc && SDL_GL_ExtensionSupported("GL_ARB_texture_compression_rgtc"))
features.rgtc = true;
if (!features.pvrtc1 && SDL_GL_ExtensionSupported("GL_IMG_texture_compression_pvrtc"))
features.pvrtc1 = true;
if (!features.pvrtc2 && SDL_GL_ExtensionSupported("GL_IMG_texture_compression_pvrtc2"))
features.pvrtc2 = true;
if (!features.pvrtc_srgb && SDL_GL_ExtensionSupported("GL_EXT_pvrtc_sRGB"))
features.pvrtc_srgb = true;
if (!(features.bc7 && features.bc6h) && SDL_GL_ExtensionSupported("GL_ARB_texture_compression_bptc"))
features.bc6h = features.bc7 = true;
if (!features.astc_ldr && SDL_GL_ExtensionSupported("GL_KHR_texture_compression_astc_ldr"))
features.astc_ldr = true;
// The only way to identify this support is the extension string.
// The format name is the same.
if (SDL_GL_ExtensionSupported("GL_KHR_texture_compression_astc_hdr"))
features.astc_hdr = true;
}
bool
GL3LoadTestSample::contextSupportsSwizzle()
{
bool esProfile = false;
GLint majorVersion, minorVersion;
if (strstr((const char*)glGetString(GL_VERSION), "GL ES") != NULL) {
esProfile = true;
}
// MAJOR & MINOR only introduced in GL {,ES} 3.0
glGetIntegerv(GL_MAJOR_VERSION, &majorVersion);
glGetIntegerv(GL_MINOR_VERSION, &minorVersion);
if (glGetError() != GL_NO_ERROR) {
// This is not a GL {,ES} 3.0 context...
assert(false);
return false;
}
if (esProfile)
return true; // ES 3.0+ has swizzle
else if (majorVersion == 3 && minorVersion < 3)
return false; // Swizzle was introduced in OpenGL 3.3.
else
return true;
}
#if !defined(SDL_PLATFORM_IOS)
// SDL only defines this on IOS and on Windows undefined != 0
#define SDL_PLATFORM_IOS 0
#endif
GLint
GL3LoadTestSample::framebufferColorEncoding()
{
GLint encoding = GL_SRGB;
GLenum attachment;
#if !defined(GL_BACK_LEFT)
#define GL_BACK_LEFT 0x0402
#endif
if (strstr((const char*)glGetString(GL_VERSION), "GL ES") == NULL)
attachment = GL_BACK_LEFT;
else if (SDL_PLATFORM_IOS)
// iOS does not use the default framebuffer.
attachment = GL_COLOR_ATTACHMENT0;
else
attachment = GL_BACK;
glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, attachment,
GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING,
&encoding);
return encoding;
}
// Emscripten assimp port not yet available.
#if !defined(__EMSCRIPTEN__)
void
GL3LoadTestSample::loadMesh(std::string filename,
glMeshLoader::MeshBuffer& meshBuffer,
std::vector<glMeshLoader::VertexLayout> vertexLayout,
float scale)
{
GLMeshLoader *mesh = new GLMeshLoader();
mesh->LoadMesh(filename);
assert(mesh->m_Entries.size() > 0);
mesh->CreateBuffers(
meshBuffer,
vertexLayout,
scale);
meshBuffer.dim = mesh->dim.size;
delete(mesh);
}
#else
void
GL3LoadTestSample::loadMesh(std::string,
glMeshLoader::MeshBuffer&,
std::vector<glMeshLoader::VertexLayout>,
float)
{
}
#endif
@@ -0,0 +1,76 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2017-2020 Mark Callow, <khronos at callow dot im>.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef GL3_LOAD_TEST_SAMPLE_H
#define GL3_LOAD_TEST_SAMPLE_H
#include <ktx.h>
#include "LoadTestSample.h"
#include "mygl.h"
#include "utils/GLMeshLoader.hpp"
#define ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
class GL3LoadTestSample : public LoadTestSample {
public:
typedef uint64_t ticks_t;
GL3LoadTestSample(uint32_t width, uint32_t height,
const char* const /*szArgs*/,
const std::string sBasePath)
: LoadTestSample(width, height, sBasePath)
{
}
virtual ~GL3LoadTestSample() { }
virtual int doEvent(SDL_Event* event) { return LoadTestSample::doEvent(event); };
virtual void resize(uint32_t width, uint32_t height) = 0;
virtual void run(uint32_t msTicks) = 0;
//virtual void getOverlayText(TextOverlay *textOverlay) { };
typedef LoadTestSample* (*PFN_create)(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
protected:
using ShaderSource = std::vector<const GLchar*>;
virtual void keyPressed(uint32_t /*keyCode*/) { }
virtual void viewChanged() { }
static bool contextSupportsSwizzle();
std::string ktxfilename;
int externalFile = 0;
struct compressedTexFeatures {
bool astc_ldr;
bool astc_hdr;
bool bc6h;
bool bc7;
bool etc1;
bool etc2;
bool bc3;
bool pvrtc1;
bool pvrtc_srgb;
bool pvrtc2;
bool rgtc;
};
static void determineCompressedTexFeatures(compressedTexFeatures& features);
static GLint framebufferColorEncoding();
void loadMesh(std::string filename, glMeshLoader::MeshBuffer& meshBuffer,
std::vector<glMeshLoader::VertexLayout> vertexLayout,
float scale);
static void makeShader(GLenum type, ShaderSource& source, GLuint* shader);
static void makeShader(GLenum type, const GLchar* const source,
GLuint* shader);
static void makeProgram(GLuint vs, GLuint fs, GLuint* program);
};
#endif /* GL3_LOAD_TEST_SAMPLE_H */
@@ -0,0 +1,278 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2015-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Instantiate GLLoadTests app with set of tests for OpenGL 3.3+ and
* OpenGL ES 3.x
*/
#include <string>
#include <regex>
#include "GLLoadTests.h"
#include "EncodeTexture.h"
#include "DrawTexture.h"
#include "TexturedCube.h"
#include "Texture3d.h"
#include "TextureCubemap.h"
#include "TextureArray.h"
#include "TextureMipmap.h"
#if !defined TEST_COMPRESSION
#define TEST_COMPRESSION 1
#endif
LoadTestSample*
GLLoadTests::showFile(const std::string& filename)
{
KTX_error_code ktxresult;
ktxTexture* kTexture;
ktxresult = ktxTexture_CreateFromNamedFile(filename.c_str(),
KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Creation of ktxTexture from \"" << filename
<< "\" failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
LoadTestSample::PFN_create createViewer;
LoadTestSample* pViewer;
if (kTexture->numDimensions == 3)
createViewer = Texture3d::create;
else if (kTexture->isArray && kTexture->isCubemap) {
// TODO: Add cubemap array app.
std::stringstream message;
message << "Display of cubemap array textures not yet implemented.";
throw std::runtime_error(message.str());
} else if (kTexture->isArray) {
createViewer = TextureArray::create;
} else if (kTexture->isCubemap) {
#if !defined(__EMSCRIPTEN__)
createViewer = TextureCubemap::create;
#else
throw std::runtime_error("Emscripten viewer can't display cube maps"
" because there is no libassimp support.");
#endif
} else if (kTexture->numLevels > 1 || kTexture->generateMipmaps) {
// TODO: Add option to choose to display showing the individual
// mipmaps vs. DrawTexture that displays a single rect using the
// mipmaps, if present.
createViewer = TextureMipmap::create;
} else {
createViewer = DrawTexture::create;
}
ktxTexture_Destroy(kTexture);
// Escape any spaces in filename.
std::string args = "--external " + std::regex_replace( filename, std::regex(" "), "\\ " );
pViewer = createViewer(w_width, w_height, args.c_str(), sBasePath);
return pViewer;
}
const GLLoadTests::sampleInvocation siSamples[] = {
{ DrawTexture::create,
"etc1s_Iron_Bars_001_normal.ktx2",
"Transcode of ETC1S+BasisLZ Compressed KTX2 XY normal map mipmapped"
},
{ DrawTexture::create,
"uastc_Iron_Bars_001_normal.ktx2",
"Transcode of UASTC+zstd Compressed KTX2 XY normal map mipmapped"
},
{ DrawTexture::create,
"color_grid_uastc_zstd.ktx2",
"Transcode of UASTC+Zstd Compressed KTX2 RGB not mipmapped "
},
{ DrawTexture::create,
"color_grid_zstd.ktx2",
"Zstd Compressed KTX2 RGB not mipmapped"
},
{ DrawTexture::create,
"color_grid_uastc.ktx2",
"Transcode of UASTC Compressed KTX2 RGB not mipmapped"
},
{ DrawTexture::create,
"color_grid_basis.ktx2",
"Transcode of ETC1S+BasisLZ Compressed KTX2 RGB not mipmapped"
},
{ DrawTexture::create,
"kodim17_basis.ktx2",
"Transcode of ETC1S+BasisLZ Compressed KTX2 RGB not mipmapped"
},
{ DrawTexture::create,
"--transcode-target RGBA4444 kodim17_basis.ktx2",
"Transcode of ETC1S+BasisLZ Compressed KTX2 RGB not mipmapped to RGBA4444"
},
{ EncodeTexture::create,
"FlightHelmet_baseColor_basis.ktx2",
"Transcode of ETC1S+BasisLZ Compressed KTX2 RGBA not mipmapped"
},
#if TEST_COMPRESSION
{ EncodeTexture::create,
"--encode etc1s rgba-reference-u.ktx2",
"Encode to ETC1S+BasisLZ then Transcode of Compressed KTX2 RGBA not mipmapped"
},
{ EncodeTexture::create,
"--encode uastc rgba-reference-u.ktx2",
"Encode to UASTC then Transcode of Compressed KTX2 RGBA not mipmapped"
},
{ EncodeTexture::create,
"--encode astc rgba-reference-u.ktx2",
"Encode to ASTC then display RGBA not mipmapped"
},
#endif
#if !defined(__EMSCRIPTEN__)
{ TextureCubemap::create,
"cubemap_goldengate_uastc_rdo4_zstd5_rd.ktx2",
"Transcode of UASTC+rdo+zstd Compressed KTX2 Cube Map Transcoded"
},
{ TextureCubemap::create,
"cubemap_yokohama_basis_rd.ktx2",
"Transcode of ETC1S/BasisLZ Compressed KTX2 mipmapped cube map",
},
#endif
{ DrawTexture::create,
"orient-down-metadata-u.ktx2",
"KTX2: RGB8 + KTXOrientation down"
},
{ DrawTexture::create,
"--preload orient-down-metadata-u.ktx2",
"KTX2: RGB8 + KTXOrientation down with pre-loaded images"
},
{ TextureArray::create,
"texturearray_bc3_unorm.ktx2",
"KTX2: BC3 (S3TC DXT5) Compressed Texture Array"
},
{ TextureArray::create,
"texturearray_astc_8x8_unorm.ktx2",
"KTX2: ASTC 8x8 Compressed Texture Array"
},
{ TextureArray::create,
"texturearray_etc2_unorm.ktx2",
"KTX2: ETC2 Compressed Texture Array"
},
{ Texture3d::create,
"3dtex_7_reference_u.ktx2",
"RGBA8 3d Texture, Depth == 7"
},
{ TexturedCube::create,
"rgb-mipmap-reference-u.ktx2",
"KTX2: RGB8 Color/level mipmap"
},
{ DrawTexture::create,
"hi_mark.ktx",
"RGB8 NPOT HI Logo"
},
{ DrawTexture::create,
"orient-up-metadata.ktx",
"RGB8 + KTXOrientation up"
},
{ DrawTexture::create,
"orient-down-metadata.ktx",
"RGB8 + KTXOrientation down"
},
{ DrawTexture::create,
"not4_rgb888_srgb.ktx",
"RGB8 2D, Row length not Multiple of 4"
},
{ DrawTexture::create,
"etc1.ktx",
"ETC1 RGB8"
},
{ DrawTexture::create,
"etc2-rgb.ktx",
"ETC2 RGB8"
},
{ DrawTexture::create,
"etc2-rgba1.ktx",
"ETC2 RGB8A1"
},
{ DrawTexture::create,
"etc2-rgba8.ktx",
"ETC2 RGB8A8"
},
{ DrawTexture::create,
"etc2-sRGB.ktx",
"ETC2 sRGB8"
},
{ DrawTexture::create,
"etc2-sRGBa1.ktx",
"ETC2 sRGB8A1"
},
{ DrawTexture::create,
"etc2-sRGBa8.ktx",
"ETC2 sRGB8A8"
},
{ DrawTexture::create,
"rgba-reference.ktx",
"RGBA8"
},
{ DrawTexture::create,
"rgb-reference.ktx",
"RGB8"
},
{ DrawTexture::create,
"conftestimage_R11_EAC.ktx",
"ETC2 R11"
},
{ DrawTexture::create,
"conftestimage_SIGNED_R11_EAC.ktx",
"ETC2 Signed R11"
},
{ DrawTexture::create,
"conftestimage_RG11_EAC.ktx",
"ETC2 RG11"
},
{ DrawTexture::create,
"conftestimage_SIGNED_RG11_EAC.ktx",
"ETC2 Signed RG11"
},
{ TextureArray::create,
"texturearray_bc3_unorm.ktx",
"BC3 (S3TC DXT5) Compressed Texture Array"
},
{ TextureArray::create,
"texturearray_astc_8x8_unorm.ktx",
"ASTC 8x8 Compressed Texture Array"
},
{ TextureArray::create,
"texturearray_etc2_unorm.ktx",
"ETC2 Compressed Texture Array"
},
{ TexturedCube::create,
"rgb-amg-reference.ktx",
"RGB8 + Auto Mipmap"
},
{ TexturedCube::create,
"rgb-mipmap-reference.ktx",
"RGB8 Color/level mipmap"
},
{ TexturedCube::create,
"hi_mark_sq.ktx",
"RGB8 NPOT HI Logo"
},
};
const uint32_t uNumSamples = sizeof(siSamples) / sizeof(GLLoadTests::sampleInvocation);
#if !(defined(GL_CONTEXT_PROFILE) && defined(GL_CONTEXT_MAJOR_VERSION) && defined(GL_CONTEXT_MINOR_VERSION))
#error GL_CONTEXT_PROFILE, GL_CONTEXT_MAJOR_VERSION & GL_CONTEXT_MINOR_VERSION must be defined.
#endif
AppBaseSDL* theApp = new GLLoadTests(siSamples, uNumSamples,
"KTX Loader Tests for GL3 & ES3",
GL_CONTEXT_PROFILE,
GL_CONTEXT_MAJOR_VERSION,
GL_CONTEXT_MINOR_VERSION);
@@ -0,0 +1,476 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2021 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Base for samplesusing instancing such as array texture display.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include <time.h>
#include <sstream>
#include <vector>
#include <ktx.h>
#include "argparser.h"
#include "InstancedSampleBase.h"
#include "TranscodeTargetStrToFmt.h"
#include "GLTextureTranscoder.hpp"
#include "ltexceptions.h"
#define member_size(type, member) sizeof(((type *)0)->member)
using namespace std;
const GLchar* InstancedSampleBase::pszInstancingFsDeclarations =
"precision mediump float;\n"
"in vec3 UVW;\n\n"
"layout (location = 0) out vec4 outFragColor;\n\n";
const GLchar* InstancedSampleBase::pszSrgbEncodeFunc =
"vec3 srgb_encode(vec3 color) {\n"
" float r = color.r < 0.0031308 ? 12.92 * color.r : 1.055 * pow(color.r, 1.0/2.4) - 0.055;\n"
" float g = color.g < 0.0031308 ? 12.92 * color.g : 1.055 * pow(color.g, 1.0/2.4) - 0.055;\n"
" float b = color.b < 0.0031308 ? 12.92 * color.b : 1.055 * pow(color.b, 1.0/2.4) - 0.055;\n"
" return vec3(r, g, b);\n"
"}\n\n";
const GLchar* InstancedSampleBase::pszInstancingFsMain =
"void main()\n"
"{\n"
" outFragColor = texture(uSampler, UVW);\n"
"}";
const GLchar* InstancedSampleBase::pszInstancingSrgbEncodeFsMain =
"void main()\n"
"{\n"
" vec4 t_color = texture(uSampler, UVW);\n"
" outFragColor.rgb = srgb_encode(t_color.rgb);\n"
" outFragColor.a = t_color.a;\n"
"}";
const GLchar* InstancedSampleBase::pszInstancingVsDeclarations =
"layout (location = 0) in vec4 inPos;\n"
"layout (location = 1) in vec2 inUV;\n\n"
"struct Instance\n"
"{\n"
" mat4 model;\n"
"};\n\n"
"//layout (binding = 0) uniform UBO\n"
"layout(std140) uniform UBO\n"
"{\n"
" mat4 projection;\n"
" mat4 view;\n"
" Instance instance[INSTANCE_COUNT];\n"
"} ubo;\n\n"
"out vec3 UVW;\n\n";
/* ------------------------------------------------------------------------- */
/**
* @internal
* @class InstancedSampleBase
* @~English
*
* @brief Test loading of 2D texture arrays.
*/
InstancedSampleBase::InstancedSampleBase(uint32_t width, uint32_t height,
const char* const szArgs,
const string sBasePath)
: GL3LoadTestSample(width, height, szArgs, sBasePath),
texUnit(GL_TEXTURE0), uniformBufferBindId(0),
bInitialized(false)
{
zoom = -15.0f;
rotationSpeed = 0.25f;
//rotation = glm::vec3(-15.0f, 35.0f, 0.0f);
rotation = glm::vec3(15.0f, 35.0f, 0.0f);
gnTexture = 0;
// Ensure we're using the desired unit
glActiveTexture(texUnit);
processArgs(szArgs);
KTX_error_code ktxresult;
ktxTexture* kTexture;
GLenum glerror;
string ktxfilepath = externalFile ? ktxfilename
: getAssetPath() + ktxfilename;
ktxresult =
ktxTexture_CreateFromNamedFile(ktxfilepath.c_str(),
KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Creation of ktxTexture from \"" << ktxfilepath
<< "\" failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
if (ktxTexture_NeedsTranscoding(kTexture)) {
transcodeTarget = KTX_TTF_NOSELECTION;
TextureTranscoder tc;
tc.transcode((ktxTexture2*)kTexture, transcodeTarget);
}
ktxresult = ktxTexture_GLUpload(kTexture, &gnTexture, &texTarget,
&glerror);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "ktxTexture_GLUpload failed: ";
if (ktxresult != KTX_GL_ERROR) {
message << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
} else if (kTexture->isCompressed && glerror == GL_INVALID_ENUM) {
throw unsupported_ctype();
} else {
message << std::showbase << "GL error " << std::hex << glerror
<< " occurred.";
throw std::runtime_error(message.str());
}
}
if (kTexture->generateMipmaps) {
// GLUpload will have generated the mipmaps already.
uint32_t maxDim = (std::max)(
(std::max)(kTexture->baseWidth, kTexture->baseHeight),
kTexture->baseDepth);
textureInfo.numLevels = (uint32_t)floor(log2(maxDim)) + 1;
} else {
textureInfo.numLevels = kTexture->numLevels;
}
textureInfo.numLayers = kTexture->numLayers;
textureInfo.baseDepth = kTexture->baseDepth;
if (textureInfo.numLevels > 1)
bIsMipmapped = true;
else
bIsMipmapped = false;
// Checking if KVData contains keys of interest would go here.
ktxTexture_Destroy(kTexture);
}
InstancedSampleBase::~InstancedSampleBase()
{
cleanup();
}
void
InstancedSampleBase::resize(uint32_t width, uint32_t height)
{
this->w_width = width;
this->w_height = height;
glViewport(0, 0, width, height);
updateUniformBufferMatrices();
}
void
InstancedSampleBase::run(uint32_t /*msTicks*/)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Keep these permanently bound
//glBindVertexArray(gnVao);
// Must be done after the VAO is bound
//glBindBuffer(GL_ARRAY_BUFFER, gnVbo[0]);
//glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gnVbo[1]);
glDrawElementsInstanced(GL_TRIANGLES, quad.indexCount,
GL_UNSIGNED_INT, (GLvoid*)quad.indicesOffset,
instanceCount);
assert(GL_NO_ERROR == glGetError());
}
//===================================================================
void
InstancedSampleBase::processArgs(string sArgs)
{
// Options descriptor
struct argparser::option longopts[] = {
{"external", argparser::option::no_argument, &externalFile, 1},
{"transcode-target", argparser::option::required_argument, nullptr, 2},
{NULL, argparser::option::no_argument, NULL, 0}
};
argvector argv(sArgs);
argparser ap(argv);
int ch;
while ((ch = ap.getopt(nullptr, longopts, nullptr)) != -1) {
switch (ch) {
case 0: break;
case 2:
transcodeTarget = TranscodeTargetStrToFmt(ap.optarg);
break;
default: assert(false); // Error in args in sample table.
}
}
assert(ap.optind < argv.size());
ktxfilename = argv[ap.optind];
}
/* ------------------------------------------------------------------------- */
void
InstancedSampleBase::cleanup()
{
glEnable(GL_DITHER);
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glDisable(GL_DEPTH_TEST);
if (bInitialized) {
glUseProgram(0);
glDeleteTextures(1, &gnTexture);
glDeleteProgram(gnInstancingProg);
glDeleteBuffers(2, quad.gnVbo);
glDeleteVertexArrays(1, &quad.gnVao);
delete uboVS.instance;
}
assert(GL_NO_ERROR == glGetError());
}
// Setup vertices for a single uv-mapped quad
void
InstancedSampleBase::generateQuad()
{
#define dim 2.5f
//std::vector<TAVertex> vertices =
TAVertex vertices[] =
{
{ { dim, dim, 0.0f }, { 1.0f, 1.0f } },
{ { -dim, dim, 0.0f }, { 0.0f, 1.0f } },
{ { -dim, -dim, 0.0f }, { 0.0f, 0.0f } },
{ { dim, -dim, 0.0f }, { 1.0f, 0.0f } }
};
#undef dim
// Setup indices
uint32_t indices[] = { 0,1,2, 2,3,0 };
quad.indexCount = static_cast<uint32_t>(ARRAY_LEN(indices));
// Create a VAO and bind it.
glGenVertexArrays(1, &quad.gnVao);
glBindVertexArray(quad.gnVao);
// Must have vertex data in buffer objects to use VAO's on ES3/GL Core
glGenBuffers(2, quad.gnVbo);
glBindBuffer(GL_ARRAY_BUFFER, quad.gnVbo[0]);
// Must be done after the VAO is bound
// WebGL requires different buffers for data and indices.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad.gnVbo[1]);
// Create the buffer data store.
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), NULL, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
GLsizeiptr offset = 0;
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(vertices), vertices);
quad.verticesOffset = offset;
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(TAVertex),
(GLvoid*)offset);
offset += member_size(TAVertex, pos);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
sizeof(TAVertex), (GLvoid*)offset);
offset = sizeof(vertices);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices),
indices, GL_STATIC_DRAW);
quad.indicesOffset = 0;
}
#define _PAD16(nbytes) (ktx_uint32_t)(16 * ceilf((float)(nbytes) / 16))
void
InstancedSampleBase::prepareUniformBuffers()
{
uProgramUniforms = glGetUniformBlockIndex(gnInstancingProg, "UBO");
if (uProgramUniforms == -1) {
std::stringstream message;
message << "prepareUniformBuffers: UBO not found in program";
throw std::runtime_error(message.str());
}
// INSTANCE_COUNT is set in GLSL code via define set in prepareProgram.
//
// Elements of the array of UboInstanceData will be aligned on 16-byte
// boundaries per the std140 rule for mat4/vec4. _PAD16 is unnecessary
// right now but will become so if anything is added to the ubo before
// the UboInstanceData. _PAD16 is put here as a warning.
uint32_t uboSize = _PAD16(sizeof(uboVS.matrices))
+ instanceCount * sizeof(UboInstanceData);
uboVS.instance = new UboInstanceData[instanceCount];
glGenBuffers(1, &gnUbo);
glBindBuffer(GL_UNIFORM_BUFFER, gnUbo);
// Create the data store.
glBufferData(GL_UNIFORM_BUFFER, uboSize, 0, GL_DYNAMIC_DRAW);
float offset = 1.5f;
float center = (instanceCount * offset) / 2;
for (uint32_t i = 0; i < instanceCount; i++)
{
// Instance model matrix
uboVS.instance[i].model
= glm::translate(glm::mat4(), glm::vec3(0.0f,
i * offset - center, 0.0f));
uboVS.instance[i].model = glm::rotate(uboVS.instance[i].model,
glm::radians(120.0f),
glm::vec3(1.0f, 0.0f, 0.0f));
}
// Update instanced part of the uniform buffer
// N.B. See comment re _PAD16 before uboSize above.
uint32_t dataOffset = _PAD16(sizeof(uboVS.matrices));
uint32_t dataSize = instanceCount * sizeof(UboInstanceData);
glBufferSubData(GL_UNIFORM_BUFFER, dataOffset, dataSize, uboVS.instance);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, uniformBufferBindId, gnUbo);
glUseProgram(gnInstancingProg);
glUniformBlockBinding(gnInstancingProg, uProgramUniforms,
uniformBufferBindId);
updateUniformBufferMatrices();
glUseProgram(0);
assert(glGetError() == GL_NO_ERROR);
}
void
InstancedSampleBase::updateUniformBufferMatrices()
{
// Only updates the uniform buffer block part containing the global matrices
// Projection
uboVS.matrices.projection = glm::perspective(glm::radians(60.0f),
(float)w_width / w_height,
.01f, 256.f);
// View
uboVS.matrices.view = glm::translate(glm::mat4(),
glm::vec3(0.0f, 1.0f, zoom));
uboVS.matrices.view *= glm::translate(glm::mat4(), cameraPos);
uboVS.matrices.view = glm::rotate(uboVS.matrices.view,
glm::radians(rotation.x),
glm::vec3(1.0f, 0.0f, 0.0f));
uboVS.matrices.view = glm::rotate(uboVS.matrices.view,
glm::radians(rotation.y),
glm::vec3(0.0f, 1.0f, 0.0f));
uboVS.matrices.view = glm::rotate(uboVS.matrices.view,
glm::radians(rotation.z),
glm::vec3(0.0f, 0.0f, 1.0f));
#if !defined(EMSCRIPTEN)
// Only update the matrices part of the uniform buffer
uint8_t *pData = (uint8_t*)glMapBufferRange(GL_UNIFORM_BUFFER, 0,
sizeof(uboVS.matrices),
GL_MAP_WRITE_BIT);
memcpy(pData, &uboVS.matrices, sizeof(uboVS.matrices));
glUnmapBuffer(GL_UNIFORM_BUFFER);
#else
glBufferSubData(GL_UNIFORM_BUFFER, 0,
sizeof(uboVS.matrices), &uboVS.matrices);
#endif
}
void
InstancedSampleBase::prepareSampler()
{
glBindTexture(texTarget, gnTexture);
if (bIsMipmapped)
glTexParameteri(texTarget,
GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
else
glTexParameteri(texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(texTarget, 0);
glUseProgram(gnInstancingProg);
if ((uSampler = glGetUniformLocation(gnInstancingProg,
"uSampler")) == -1) {
std::stringstream message;
message << "prepareSampler: uSampler not found in program";
throw std::runtime_error(message.str());
}
glUniform1i(uSampler, texUnit - GL_TEXTURE0);
glUseProgram(0);
}
void
InstancedSampleBase::prepareProgram(ShaderSource& fs, ShaderSource& vs)
{
GLuint gnInstancingFs, gnInstancingVs;
try {
std::stringstream ssDefine;
ssDefine << "#define INSTANCE_COUNT " << instanceCount << "U" << endl;
// str().c_str() doesn't work because str goes outof scope immediately.
// Hence this 2 step process.
string sDefine = ssDefine.str();
vs.insert(vs.begin(), sDefine.c_str());
makeShader(GL_VERTEX_SHADER, vs, &gnInstancingVs);
makeShader(GL_FRAGMENT_SHADER, fs, &gnInstancingFs);
makeProgram(gnInstancingVs, gnInstancingFs, &gnInstancingProg);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
throw;
}
glDeleteShader(gnInstancingVs);
glDeleteShader(gnInstancingFs);
}
void
InstancedSampleBase::prepare(ShaderSource& fs, ShaderSource& vs)
{
// By default dithering is enabled. Dithering does not provide visual
// improvement in this sample so disable it to improve performance.
glDisable(GL_DITHER);
glFrontFace(GL_CW);
glDisable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
glClearColor(0.2f,0.3f,0.4f,1.0f);
//prepareSamplerAndView();
//setupVertexDescriptions();
generateQuad();
prepareProgram(fs, vs);
prepareUniformBuffers();
prepareSampler();
glUseProgram(gnInstancingProg);
glBindTexture(texTarget, gnTexture);
}
@@ -0,0 +1,121 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _INSTANCE_SAMPLE_BASE_H_
#define _INSTANCE_SAMPLE_BASE_H_
#include <vector>
#include "GL3LoadTestSample.h"
#include <glm/gtc/matrix_transform.hpp>
#define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false
class InstancedSampleBase : public GL3LoadTestSample
{
public:
InstancedSampleBase(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
~InstancedSampleBase();
virtual void resize(uint32_t width, uint32_t height);
virtual void run(uint32_t msTicks);
//virtual void getOverlayText(VulkanTextOverlay *textOverlay);
protected:
using ShaderSource = GL3LoadTestSample::ShaderSource;
const GLuint texUnit;
const GLuint uniformBufferBindId;
GLenum texTarget;
GLuint gnTexture;
GLuint gnInstancingProg;
GLuint gnUbo;
ktx_transcode_fmt_e transcodeTarget;
bool bInitialized;
bool bIsMipmapped;
struct textureInfo {
uint32_t numLayers;
uint32_t numLevels;
uint32_t baseDepth;
} textureInfo;
uint32_t instanceCount;
// Vertex layout for this example
struct TAVertex {
float pos[3];
float uv[2];
};
struct MeshBuffer {
uint32_t indexCount;
glm::vec3 dim;
GLuint gnVao;
GLuint gnVbo[2];
GLsizeiptr verticesOffset;
GLsizeiptr indicesOffset;
};
MeshBuffer quad;
struct UboInstanceData {
// Model matrix
glm::mat4 model;
};
struct {
// Global matrices
struct {
glm::mat4 projection;
glm::mat4 view;
} matrices;
// N.B. The UBO structure declared in the shader has the array of
// instance data inside the structure rather than pointed at from the
// structure. The start of the array will be aligned on a 16-byte
// boundary as it starts with a matrix.
//
// Separate data for each instance
UboInstanceData *instance;
} uboVS;
GLint uProgramUniforms;
GLint uSampler;
static const GLchar* pszInstancingFsDeclarations;
static const GLchar* pszSrgbEncodeFunc;
static const GLchar* pszInstancingFsMain;
static const GLchar* pszInstancingSrgbEncodeFsMain;
static const GLchar* pszInstancingVsDeclarations;
void cleanup();
// Setup vertices for a single uv-mapped quad
void generateQuad();
void prepareUniformBuffers();
void updateUniformBufferMatrices();
void prepareSampler();
void prepareProgram(ShaderSource& fs, ShaderSource& vs);
void prepare(ShaderSource& fs, ShaderSource& vs);
void processArgs(std::string sArgs);
virtual void viewChanged()
{
updateUniformBufferMatrices();
}
};
#endif /* _INSTANCE_SAMPLE_BASE_H_ */
@@ -0,0 +1,107 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2021 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Definition of test sample for loading and displaying the slices of a 3d texture.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include <time.h>
#include <sstream>
#include <vector>
#include <ktx.h>
#include "argparser.h"
#include "Texture3d.h"
#include "ltexceptions.h"
#define member_size(type, member) sizeof(((type *)0)->member)
const GLchar* pszFsSampler3dDeclaration =
"uniform mediump sampler3D uSampler;\n\n";
const GLchar* psz3dVsMain =
"void main()\n"
"{\n"
" UVW = vec3(inUV, float(gl_InstanceID) / float(INSTANCE_COUNT - 1U));\n"
" mat4 modelView = ubo.view * ubo.instance[gl_InstanceID].model;\n"
" gl_Position = ubo.projection * modelView * inPos;\n"
"}";
/* ------------------------------------------------------------------------- */
LoadTestSample*
Texture3d::create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath)
{
return new Texture3d(width, height, szArgs, sBasePath);
}
/**
* @internal
* @class Texture3d
* @~English
*
* @brief Test loading of 2D texture arrays.
*/
Texture3d::Texture3d(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
: InstancedSampleBase(width, height, szArgs, sBasePath)
{
zoom = -15.0f;
if (texTarget != GL_TEXTURE_3D) {
std::stringstream message;
message << "Texture3d requires an 3d texture.";
throw std::runtime_error(message.str());
}
// Checking if KVData contains keys of interest would go here.
instanceCount = textureInfo.baseDepth;
InstancedSampleBase::ShaderSource fs;
InstancedSampleBase::ShaderSource vs;
fs.push_back(pszInstancingFsDeclarations);
fs.push_back(pszFsSampler3dDeclaration);
if (framebufferColorEncoding() == GL_LINEAR) {
fs.push_back(pszSrgbEncodeFunc);
fs.push_back(pszInstancingSrgbEncodeFsMain);
} else {
fs.push_back(pszInstancingFsMain);;
}
vs.push_back(pszInstancingVsDeclarations);
vs.push_back(psz3dVsMain);
try {
prepare(fs, vs);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
cleanup();
throw;
}
// Texture was bound by prepare()
// Set this so it is easier to recognize that the texture has the expected
// slices.
glTexParameteri(texTarget, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
bInitialized = true;
}
@@ -0,0 +1,41 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2021 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declaration of test sample for loading and displaying the slices of a 3d texture..
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <vector>
#include "InstancedSampleBase.h"
#include <glm/gtc/matrix_transform.hpp>
#define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false
class Texture3d : public InstancedSampleBase
{
public:
Texture3d(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
static LoadTestSample*
create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath);
protected:
using ShaderSource = GL3LoadTestSample::ShaderSource;
};
@@ -0,0 +1,100 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Definition of test sample for loading and displaying the layers of a 2D array texture.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include <time.h>
#include <sstream>
#include <vector>
#include <ktx.h>
#include "argparser.h"
#include "TextureArray.h"
#include "ltexceptions.h"
#define member_size(type, member) sizeof(((type *)0)->member)
const GLchar* pszFsArraySamplerDeclaration =
"uniform mediump sampler2DArray uSampler;\n\n";
const GLchar* pszArrayVsMain =
"void main()\n"
"{\n"
" UVW = vec3(inUV, gl_InstanceID);\n"
" mat4 modelView = ubo.view * ubo.instance[gl_InstanceID].model;\n"
" gl_Position = ubo.projection * modelView * inPos;\n"
"}";
/* ------------------------------------------------------------------------- */
LoadTestSample*
TextureArray::create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath)
{
return new TextureArray(width, height, szArgs, sBasePath);
}
/**
* @internal
* @class TextureArray
* @~English
*
* @brief Test loading of 2D texture arrays.
*/
TextureArray::TextureArray(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
: InstancedSampleBase(width, height, szArgs, sBasePath)
{
zoom = -15.0f;
if (texTarget != GL_TEXTURE_2D_ARRAY) {
std::stringstream message;
message << "TextureArray requires an array texture.";
throw std::runtime_error(message.str());
}
// Checking if KVData contains keys of interest would go here.
instanceCount = textureInfo.numLayers;
InstancedSampleBase::ShaderSource fs;
InstancedSampleBase::ShaderSource vs;
fs.push_back(pszInstancingFsDeclarations);
fs.push_back(pszFsArraySamplerDeclaration);
if (framebufferColorEncoding() == GL_LINEAR) {
fs.push_back(pszSrgbEncodeFunc);
fs.push_back(pszInstancingSrgbEncodeFsMain);
} else {
fs.push_back(pszInstancingFsMain);;
}
vs.push_back(pszInstancingVsDeclarations);
vs.push_back(pszArrayVsMain);
try {
prepare(fs, vs);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
cleanup();
throw;
}
bInitialized = true;
}
@@ -0,0 +1,41 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2021 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declaration of test sample for loading and displaying the layers of a 2D array texture.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <vector>
#include "InstancedSampleBase.h"
#include <glm/gtc/matrix_transform.hpp>
#define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false
class TextureArray : public InstancedSampleBase
{
public:
TextureArray(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
static LoadTestSample*
create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath);
protected:
using ShaderSource = GL3LoadTestSample::ShaderSource;
};
@@ -0,0 +1,710 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Test loading of 2D texture arrays.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include <time.h>
#include <sstream>
#include <vector>
#include <ktx.h>
#include "argparser.h"
#include "TextureCubemap.h"
#include "GLTextureTranscoder.hpp"
#include "SwipeDetector.h"
#include "ltexceptions.h"
#define member_size(type, member) sizeof(((type *)0)->member)
const GLchar* pszReflectFs =
"precision highp float;"
"uniform UBO\n"
"{\n"
" mat4 projection;\n"
" mat4 modelView;\n"
" mat4 skyboxView;\n"
" mat4 invModelView;\n"
" mat4 uvwTransform;\n"
" float lodBias;\n"
"} ubo;\n\n"
"uniform samplerCube uSamplerColor;\n\n"
"in vec3 vPos;\n"
"in vec3 vNormal;\n"
"in float vLodBias;\n"
"in vec3 vViewVec;\n"
"in vec3 vLightVec;\n\n"
"layout (location = 0) out vec4 outFragColor;\n\n"
"void main()\n"
"{\n"
" vec3 cI = normalize (vPos);\n"
" vec3 cR = reflect (cI, normalize(vNormal));\n\n"
" cR = vec3(ubo.uvwTransform * ubo.invModelView * vec4(cR, 0.0));\n\n"
" vec4 color = texture(uSamplerColor, cR, vLodBias);\n\n"
" vec3 N = normalize(vNormal);\n"
" vec3 L = normalize(vLightVec);\n"
" vec3 V = normalize(vViewVec);\n"
" vec3 R = reflect(-L, N);\n"
" vec3 ambient = vec3(0.5) * color.rgb;\n"
" vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);\n"
" vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.5);\n"
" outFragColor = vec4(ambient + diffuse * color.rgb + specular, 1.0);\n"
"}\n";
const GLchar* pszReflectSrgbEncodeFs =
"precision highp float;"
"uniform UBO\n"
"{\n"
" mat4 projection;\n"
" mat4 modelView;\n"
" mat4 skyboxView;\n"
" mat4 invModelView;\n"
" mat4 uvwTransform;\n"
" float lodBias;\n"
"} ubo;\n\n"
"uniform samplerCube uSamplerColor;\n\n"
"in vec3 vPos;\n"
"in vec3 vNormal;\n"
"in float vLodBias;\n"
"in vec3 vViewVec;\n"
"in vec3 vLightVec;\n\n"
"layout (location = 0) out vec4 outFragColor;\n\n"
"vec3 srgb_encode(vec3 color) {\n"
" float r = color.r < 0.0031308 ? 12.92 * color.r : 1.055 * pow(color.r, 1.0/2.4) - 0.055;\n"
" float g = color.g < 0.0031308 ? 12.92 * color.g : 1.055 * pow(color.g, 1.0/2.4) - 0.055;\n"
" float b = color.b < 0.0031308 ? 12.92 * color.b : 1.055 * pow(color.b, 1.0/2.4) - 0.055;\n"
" return vec3(r, g, b);\n"
"}\n\n"
"void main()\n"
"{\n"
" vec3 cI = normalize (vPos);\n"
" vec3 cR = reflect (cI, normalize(vNormal));\n\n"
" cR = vec3(ubo.uvwTransform * ubo.invModelView * vec4(cR, 0.0));\n\n"
" vec4 color = texture(uSamplerColor, cR, vLodBias);\n\n"
" vec3 N = normalize(vNormal);\n"
" vec3 L = normalize(vLightVec);\n"
" vec3 V = normalize(vViewVec);\n"
" vec3 R = reflect(-L, N);\n"
" vec3 ambient = vec3(0.5) * color.rgb;\n"
" vec3 diffuse = max(dot(N, L), 0.0) * vec3(1.0);\n"
" vec3 specular = pow(max(dot(R, V), 0.0), 16.0) * vec3(0.5);\n"
" color.rgb = srgb_encode(ambient + diffuse * color.rgb + specular);\n"
" outFragColor = vec4(color.rgb, 1.0);\n"
"}\n";
const GLchar* pszReflectVs =
"precision highp float;"
"layout (location = 0) in vec3 inPos;\n"
"layout (location = 1) in vec3 inNormal;\n\n"
"uniform UBO\n"
"{\n"
" mat4 projection;\n"
" mat4 modelView;\n"
" mat4 skyboxView;\n"
" mat4 invModelView;\n"
" mat4 uvwTransform;\n"
" float lodBias;\n"
"} ubo;\n"
"\n"
"out vec3 vPos;\n"
"out vec3 vNormal;\n"
"out float vLodBias;\n"
"out vec3 vViewVec;\n"
"out vec3 vLightVec;\n\n"
"void main()\n"
"{\n"
" gl_Position = ubo.projection * ubo.modelView * vec4(inPos, 1.0);\n\n"
" vPos = vec3(ubo.modelView * vec4(inPos, 1.0));\n"
" vNormal = mat3(ubo.modelView) * inNormal;\n"
" vLodBias = ubo.lodBias;\n\n"
" vec3 lightPos = vec3(0.0f, -5.0f, 5.0f);\n"
" vLightVec = lightPos.xyz - vPos.xyz;\n"
" vViewVec = -vPos.xyz;\n"
"}\n";
const GLchar* pszSkyboxFs =
"precision highp float;"
"uniform samplerCube uSamplerColor;\n\n"
"in vec3 vUVW;\n\n"
"layout (location = 0) out vec4 outFragColor;\n\n"
"void main()\n"
"{\n"
" outFragColor = texture(uSamplerColor, vUVW);\n"
"}\n";
const GLchar* pszSkyboxSrgbEncodeFs =
"precision highp float;"
"uniform samplerCube uSamplerColor;\n\n"
"in vec3 vUVW;\n\n"
"layout (location = 0) out vec4 outFragColor;\n\n"
"vec3 srgb_encode(vec3 color) {\n"
" float r = color.r < 0.0031308 ? 12.92 * color.r : 1.055 * pow(color.r, 1.0/2.4) - 0.055;\n"
" float g = color.g < 0.0031308 ? 12.92 * color.g : 1.055 * pow(color.g, 1.0/2.4) - 0.055;\n"
" float b = color.b < 0.0031308 ? 12.92 * color.b : 1.055 * pow(color.b, 1.0/2.4) - 0.055;\n"
" return vec3(r, g, b);\n"
"}\n\n"
"void main()\n"
"{\n"
" vec4 color = texture(uSamplerColor, vUVW);\n"
" outFragColor.rgb = srgb_encode(color.rgb);\n"
" outFragColor.a = color.a;\n"
"}\n";
const GLchar* pszSkyboxVs =
"precision highp float;"
"layout (location = 0) in vec3 inPos;\n\n"
"uniform UBO\n"
"{\n"
" mat4 projection;\n"
" mat4 modelView;\n"
" mat4 skyboxView;\n"
" mat4 invModelView;\n"
" mat4 uvwTransform;\n"
"} ubo;\n\n"
"out vec3 vUVW;\n\n"
"void main()\n"
"{\n"
" vUVW = (ubo.uvwTransform * vec4(inPos.xyz, 1.0)).xyz;\n"
" //vUVW = inPos.xyz;\n"
" gl_Position = (ubo.projection * ubo.skyboxView * vec4(inPos.xyz, 1.0)).xyww;\n"
"}\n";
/* ------------------------------------------------------------------------- */
// Vertex layout for this example
std::vector<glMeshLoader::VertexLayout> vertexLayout =
{
glMeshLoader::VERTEX_LAYOUT_POSITION,
glMeshLoader::VERTEX_LAYOUT_NORMAL,
glMeshLoader::VERTEX_LAYOUT_UV
};
LoadTestSample*
TextureCubemap::create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath)
{
return new TextureCubemap(width, height, szArgs, sBasePath);
}
/**
* @internal
* @class TextureCubemap
* @~English
*
* @brief Test loading of 2D texture arrays.
*/
TextureCubemap::TextureCubemap(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
: GL3LoadTestSample(width, height, szArgs, sBasePath),
cubemapTexUnit(GL_TEXTURE0), uniformBufferBindId(0),
bInitialized(false)
{
zoom = -4.0f;
rotationSpeed = 0.25f;
rotation = { -7.25f, 120.0f, 0.0f };
gnCubemapTexture = 0;
// Ensure we're using the desired unit
glActiveTexture(cubemapTexUnit);
processArgs(szArgs);
KTX_error_code ktxresult;
ktxTexture* kTexture;
GLenum glerror;
std::string ktxfilepath = externalFile ? ktxfilename
: getAssetPath() + ktxfilename;
ktxresult = ktxTexture_CreateFromNamedFile(ktxfilepath.c_str(),
KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Creation of ktxTexture from \"" << ktxfilepath
<< "\" failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
if (ktxTexture_NeedsTranscoding(kTexture)) {
TextureTranscoder tc;
tc.transcode((ktxTexture2*)kTexture);
//transcoded = true;
}
ktxresult = ktxTexture_GLUpload(kTexture,
&gnCubemapTexture, &cubemapTexTarget,
&glerror);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "ktxTexture_GLUpload failed: ";
if (ktxresult != KTX_GL_ERROR) {
message << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
} else if (kTexture->isCompressed && glerror == GL_INVALID_ENUM) {
throw unsupported_ctype();
} else {
message << std::showbase << "GL error " << std::hex << glerror
<< " occurred.";
throw std::runtime_error(message.str());
}
}
if (cubemapTexTarget != GL_TEXTURE_CUBE_MAP) {
std::stringstream message;
message << "Loaded texture is not a cubemap texture.";
throw std::runtime_error(message.str());
}
numLayers = kTexture->numLayers;
if (numLayers > 1 || kTexture->generateMipmaps)
// GLUpload will have generated the mipmaps already.
bIsMipmapped = true;
else
bIsMipmapped = false;
levelCount = kTexture->numLevels;
if (kTexture->orientation.y == KTX_ORIENT_Y_DOWN) {
// Assume a KTX-compliant cube map. That means the faces are in a
// LH coord system with +y up, +z forward and +x on the right.
// Scale the skybox cube's z by -1 to convert it to LH coords to
// match the cube map while placing the +z face in the -z direction
// so it will be in front of the view. Alternatively we could multiply
// the cube's x by -1 which will place the +z face in the +z direction
// placing it behind the viewer.
ubo.uvwTransform = glm::scale(glm::mat4(1.0f), glm::vec3(1, 1, -1));
} else {
std::stringstream message;
message << "Cubemap faces have unsupported KTXorientation value.";
throw std::runtime_error(message.str());
}
ktxTexture_Destroy(kTexture);
try {
prepare();
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
cleanup();
throw;
}
bInitialized = true;
}
TextureCubemap::~TextureCubemap()
{
cleanup();
}
int
TextureCubemap::doEvent(SDL_Event* event)
{
int result = 0;
switch(event->type) {
case SDL_EVENT_USER:
if (event->user.code == SwipeDetector::swipeGesture) {
SwipeDetector::Direction direction
= SwipeDetector::pointerToDirection(event->user.data1);
switch (direction) {
case SwipeDetector::Direction::up:
toggleObject(+1);
break;
case SwipeDetector::Direction::down:
toggleObject(-1);
break;
default:
result = 1;
}
} else {
result = 1;
}
break;
default:
result = 1;
}
if (result == 1)
result = GL3LoadTestSample::doEvent(event);
return result;
}
void
TextureCubemap::resize(uint32_t width, uint32_t height)
{
this->w_width = width;
this->w_height = height;
glViewport(0, 0, width, height);
updateUniformBuffers();
}
void
TextureCubemap::run(uint32_t /*msTicks*/)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw object.
glFrontFace(GL_CW); // Why is everything CW?
glCullFace(GL_BACK);
glUseProgram(gnReflectProg);
meshes.objects[meshes.objectIndex].Draw();
if (bDisplaySkybox) {
// Change so depth test passes when values are equal to the
// depth buffer's content. This works in conjunction with the
// gl_Position = inPos.xyww trick in the shader.
glDepthFunc(GL_LEQUAL);
// The cube is a regular mesh with the front faces on the outside.
// We're inside the cube so want to see the back faces.
glCullFace(GL_FRONT);
glUseProgram(gnSkyboxProg);
meshes.skybox.Draw();
// Revert to defaults for 3D object.
glDepthFunc(GL_LESS);
}
assert(GL_NO_ERROR == glGetError());
}
//===================================================================
void
TextureCubemap::processArgs(std::string sArgs)
{
// Options descriptor
struct argparser::option longopts[] = {
{"external", argparser::option::no_argument, &externalFile, 1},
{NULL, argparser::option::no_argument, NULL, 0}
};
argvector argv(sArgs);
argparser ap(argv);
int ch;
while ((ch = ap.getopt(nullptr, longopts, nullptr)) != -1) {
switch (ch) {
case 0: break;
default: assert(false); // Error in args in sample table.
}
}
assert(ap.optind < argv.size());
ktxfilename = argv[ap.optind];
}
/* ------------------------------------------------------------------------- */
void
TextureCubemap::cleanup()
{
glEnable(GL_DITHER);
glDisable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glDisable(GL_DEPTH_TEST);
if (bInitialized) {
glUseProgram(0);
glDeleteTextures(1, &gnCubemapTexture);
glDeleteProgram(gnReflectProg);
glDeleteProgram(gnSkyboxProg);
meshes.skybox.FreeGLResources();
for (int i = 0; i < 3; i++) {
meshes.objects[i].FreeGLResources();
}
}
assert(GL_NO_ERROR == glGetError());
}
void
TextureCubemap::loadMeshes()
{
std::string filepath = getAssetPath();
// Skybox
loadMesh(filepath + "cube.obj", meshes.skybox, vertexLayout, 0.05f);
// Objects
meshes.objects.resize(3);
loadMesh(filepath + "sphere.obj", meshes.objects[0], vertexLayout, 0.05f);
loadMesh(filepath + "teapot.dae", meshes.objects[1], vertexLayout, 0.05f);
loadMesh(filepath + "torusknot.obj", meshes.objects[2], vertexLayout, 0.05f);
}
void
TextureCubemap::prepareUniformBuffers()
{
uReflectProgramUniforms = glGetUniformBlockIndex(gnReflectProg, "UBO");
if (uReflectProgramUniforms == -1) {
std::stringstream message;
message << "prepareUniformBuffers: UBO not found in reflect program";
throw std::runtime_error(message.str());
}
uSkyboxProgramUniforms = glGetUniformBlockIndex(gnSkyboxProg, "UBO");
if (uSkyboxProgramUniforms == -1) {
std::stringstream message;
message << "prepareUniformBuffers: UBO not found in skybox program";
throw std::runtime_error(message.str());
}
glGenBuffers(1, &gnUbo);
glBindBuffer(GL_UNIFORM_BUFFER, gnUbo);
// Create the data store.
glBufferData(GL_UNIFORM_BUFFER, sizeof(ubo), 0, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, uniformBufferBindId, gnUbo);
glUseProgram(gnReflectProg);
glUniformBlockBinding(gnReflectProg, uReflectProgramUniforms,
uniformBufferBindId);
glUseProgram(gnSkyboxProg);
glUniformBlockBinding(gnSkyboxProg, uSkyboxProgramUniforms,
uniformBufferBindId);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
updateUniformBuffers();
glUseProgram(0);
assert(glGetError() == GL_NO_ERROR);
}
void
TextureCubemap::updateUniformBuffers()
{
// Reflect / 3D object
glm::mat4 viewMatrix = glm::mat4(1.0f);
ubo.projection = glm::perspective(glm::radians(60.0f),
(float)w_width / (float)w_height,
0.001f, 256.0f);
viewMatrix = glm::translate(viewMatrix, glm::vec3(0.0f, 0.0f, zoom));
// Transform the z axis to what the viewer is perceiving as the z axis to make
// the behaviour of 2-finger rotation (the value in rotation.z) understandable.
glm::mat4 zAxisRotMatrix;
zAxisRotMatrix = glm::rotate(zAxisRotMatrix, glm::radians(180.0f - rotation.x),
glm::vec3(1.0f, 0.0f, 0.0f));
zAxisRotMatrix = glm::rotate(zAxisRotMatrix, glm::radians(rotation.y),
glm::vec3(0.0f, 1.0f, 0.0f));
zRotationAxis = normalize(zAxisRotMatrix * glm::vec4(0.0f, 0.0f, 1.0f, 1.0f));
// I do not understand why this is necessary. Assimp is supposed to
// put models in the GL coordinate system by default but the teapot is
// upside down. Since the other objects are symmetrical it is not possible
// to say if they are upside down.
glm::mat4 object;
object = glm::rotate(object, glm::radians(180.0f),
glm::vec3(1.0f, 0.0f, 0.0f));
ubo.modelView = viewMatrix * glm::translate(glm::mat4(), cameraPos);
ubo.modelView = glm::rotate(ubo.modelView, glm::radians(rotation.x),
glm::vec3(1.0f, 0.0f, 0.0f));
ubo.modelView = glm::rotate(ubo.modelView, glm::radians(rotation.y),
glm::vec3(0.0f, 1.0f, 0.0f));
ubo.modelView = glm::rotate(ubo.modelView, glm::radians(rotation.z),
zRotationAxis);
// Remove translation from modelView so the skybox doesn't move.
ubo.skyboxView = glm::mat4(glm::mat3(ubo.modelView));
// Do the inverse here because doing it in every fragment is a bit much.
ubo.invModelView = glm::inverse(ubo.modelView);
// Now add the object rotation.
ubo.modelView = ubo.modelView * object;
glBindBuffer(GL_UNIFORM_BUFFER, gnUbo);
#if !defined(EMSCRIPTEN)
uint8_t* pData = (uint8_t*)glMapBufferRange(GL_UNIFORM_BUFFER,
0, sizeof(ubo),
GL_MAP_WRITE_BIT);
memcpy(pData, &ubo, sizeof(ubo));
glUnmapBuffer(GL_UNIFORM_BUFFER);
#else
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ubo), &ubo);
#endif
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
void
TextureCubemap::prepareSampler()
{
glBindTexture(cubemapTexTarget, gnCubemapTexture);
if (bIsMipmapped)
glTexParameteri(cubemapTexTarget,
GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
else
glTexParameteri(cubemapTexTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(cubemapTexTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glUseProgram(gnReflectProg);
if ((uReflectCubemap = glGetUniformLocation(gnReflectProg,
"uSamplerColor")) == -1) {
std::stringstream message;
message << "prepareSampler: uSamplerColor not found in reflect program";
throw std::runtime_error(message.str());
}
glUniform1i(uReflectCubemap, cubemapTexUnit - GL_TEXTURE0);
glUseProgram(gnSkyboxProg);
if ((uSkyboxCubemap = glGetUniformLocation(gnSkyboxProg,
"uSamplerColor")) == -1) {
std::stringstream message;
message << "prepareSampler: uSamplerColor not found in skybox program";
throw std::runtime_error(message.str());
}
glUniform1i(uSkyboxCubemap, cubemapTexUnit - GL_TEXTURE0);
glUseProgram(0);
}
void
TextureCubemap::preparePrograms()
{
GLuint gnReflectFs, gnReflectVs, gnSkyboxFs, gnSkyboxVs;
const GLchar* actualReflectFs;
const GLchar* actualSkyboxFs;
if (framebufferColorEncoding() == GL_LINEAR) {
actualReflectFs = pszReflectSrgbEncodeFs;
actualSkyboxFs = pszSkyboxSrgbEncodeFs;
} else {
actualReflectFs = pszReflectFs;
actualSkyboxFs = pszSkyboxFs;
}
try {
makeShader(GL_VERTEX_SHADER, pszReflectVs, &gnReflectVs);
makeShader(GL_FRAGMENT_SHADER, actualReflectFs, &gnReflectFs);
makeProgram(gnReflectVs, gnReflectFs, &gnReflectProg);
makeShader(GL_VERTEX_SHADER, pszSkyboxVs, &gnSkyboxVs);
makeShader(GL_FRAGMENT_SHADER, actualSkyboxFs, &gnSkyboxFs);
makeProgram(gnSkyboxVs, gnSkyboxFs, &gnSkyboxProg);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
throw;
}
glDeleteShader(gnReflectVs);
glDeleteShader(gnReflectFs);
glDeleteShader(gnSkyboxVs);
glDeleteShader(gnSkyboxFs);
}
void
TextureCubemap::prepare()
{
// By default dithering is enabled. Dithering does not provide visual
// improvement in this sample so disable it to improve performance.
glDisable(GL_DITHER);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glClearColor(0.2f,0.3f,0.4f,1.0f);
loadMeshes();
preparePrograms();
prepareUniformBuffers();
prepareSampler();
}
void
TextureCubemap::toggleSkyBox()
{
bDisplaySkybox = !bDisplaySkybox;
}
void
TextureCubemap::toggleObject(int direction)
{
meshes.objectIndex += direction;
if (meshes.objectIndex >= static_cast<int32_t>(meshes.objects.size())) {
meshes.objectIndex = 0;
} else if (meshes.objectIndex < 0) {
meshes.objectIndex = static_cast<int32_t>(meshes.objects.size()) - 1;
}
}
void
TextureCubemap::changeLodBias(float delta)
{
ubo.lodBias += delta;
if (ubo.lodBias < 0.0f)
{
ubo.lodBias = 0.0f;
}
if (ubo.lodBias > levelCount)
{
ubo.lodBias = (float)levelCount;
}
updateUniformBuffers();
}
void
TextureCubemap::keyPressed(uint32_t keyCode)
{
switch (keyCode)
{
case 's':
toggleSkyBox();
break;
case ' ':
toggleObject(+1);
break;
case SDLK_KP_PLUS:
changeLodBias(0.1f);
break;
case SDLK_KP_MINUS:
changeLodBias(-0.1f);
break;
}
}
@@ -0,0 +1,114 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Test loading of a cubemap.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <vector>
#include "GL3LoadTestSample.h"
#include <glm/gtc/matrix_transform.hpp>
#define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false
class TextureCubemap : public GL3LoadTestSample
{
public:
TextureCubemap(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
~TextureCubemap();
virtual int doEvent(SDL_Event* event);
virtual void resize(uint32_t width, uint32_t height);
virtual void run(uint32_t msTicks);
//virtual void getOverlayText(VulkanTextOverlay *textOverlay);
static LoadTestSample*
create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath);
protected:
const GLuint cubemapTexUnit;
const GLuint uniformBufferBindId;
GLuint levelCount = 0;
GLenum cubemapTexTarget;
GLuint gnCubemapTexture;
GLuint gnReflectProg;
GLuint gnSkyboxProg;
GLuint gnUbo;
bool bInitialized;
bool bIsMipmapped;
bool bDisplaySkybox = true;
uint32_t numLayers;
// Vertex layout for this example
struct TAVertex {
float pos[3];
float uv[2];
};
struct {
glMeshLoader::MeshBuffer skybox;
std::vector<glMeshLoader::MeshBuffer> objects;
int32_t objectIndex = 0;
} meshes;
struct {
// Global matrices
glm::mat4 projection;
glm::mat4 modelView;
glm::mat4 skyboxView;
glm::mat4 invModelView;
glm::mat4 uvwTransform;
float lodBias = 0.0f;
} ubo;
GLint uReflectProgramUniforms;
GLint uSkyboxProgramUniforms;
GLint uReflectCubemap;
GLint uSkyboxCubemap;
glm::vec3 zRotationAxis;
void cleanup();
void loadMeshes();
void prepareUniformBuffers();
void updateUniformBuffers();
void prepareSampler();
void preparePrograms();
void prepare();
void toggleSkyBox();
void toggleObject(int direction);
void changeLodBias(float delta);
void processArgs(std::string sArgs);
virtual void keyPressed(uint32_t keyCode);
virtual void viewChanged()
{
updateUniformBuffers();
}
GLuint skyboxVAO, skyboxVBO;
};
@@ -0,0 +1,125 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Definition of test sample for loading and displaying all the levels of a 2D mipmapped texture.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <algorithm>
#include <time.h>
#include <sstream>
#include <vector>
#include <ktx.h>
#include "argparser.h"
#include "TextureMipmap.h"
#include "ltexceptions.h"
#define member_size(type, member) sizeof(((type *)0)->member)
const GLchar* pszLodFsDeclarations =
"precision mediump float;\n"
"in vec2 UV;\n"
"flat in float lambda;\n\n"
"layout (location = 0) out vec4 outFragColor;\n\n"
"uniform mediump sampler2D uSampler;\n\n";
const GLchar* pszLodFsMain =
"void main()\n"
"{\n"
" outFragColor = textureLod(uSampler, UV, lambda);\n"
"}";
const GLchar* pszLodSrgbEncodeFsMain =
"void main()\n"
"{\n"
" vec4 t_color = textureLod(uSampler, UV, lambda);\n"
" outFragColor.rgb = srgb_encode(t_color.rgb);\n"
" outFragColor.a = t_color.a;\n"
"}";
const GLchar* pszLodVsMain =
"out vec2 UV;\n"
"flat out float lambda;\n\n"
"void main()\n"
"{\n"
" UV = inUV;\n"
" lambda = gl_InstanceID + 0.5;\n"
" mat4 modelView = ubo.view * ubo.instance[gl_InstanceID].model;\n"
" gl_Position = ubo.projection * modelView * inPos;\n"
"}";
/* ------------------------------------------------------------------------- */
LoadTestSample*
TextureMipmap::create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath)
{
return new TextureMipmap(width, height, szArgs, sBasePath);
}
/**
* @internal
* @class TextureMipmap
* @~English
*
* @brief Test loading of 2D texture arrays.
*/
TextureMipmap::TextureMipmap(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
: InstancedSampleBase(width, height, szArgs, sBasePath)
{
zoom = -15.0f;
if (texTarget != GL_TEXTURE_2D || textureInfo.numLevels == 1) {
std::stringstream message;
message << "TextureMipmap requires a 2D mipmapped texture.";
throw std::runtime_error(message.str());
}
// Checking if KVData contains keys of interest would go here.
instanceCount = textureInfo.numLevels;
InstancedSampleBase::ShaderSource fs;
InstancedSampleBase::ShaderSource vs;
fs.push_back(pszLodFsDeclarations);
if (framebufferColorEncoding() == GL_LINEAR) {
fs.push_back(pszSrgbEncodeFunc);
fs.push_back(pszLodSrgbEncodeFsMain);
} else {
fs.push_back(pszLodFsMain);;
}
vs.push_back(pszInstancingVsDeclarations);
vs.push_back(pszLodVsMain);
try {
prepare(fs, vs);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
cleanup();
throw;
}
bInitialized = true;
}
@@ -0,0 +1,41 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2021 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @internal
* @file
* @~English
*
* @brief Declaration of test sample for loading and displaying all the levels of a 2D mipmapped texture.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#include <vector>
#include "InstancedSampleBase.h"
#include <glm/gtc/matrix_transform.hpp>
#define VERTEX_BUFFER_BIND_ID 0
#define ENABLE_VALIDATION false
class TextureMipmap : public InstancedSampleBase
{
public:
TextureMipmap(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
static LoadTestSample*
create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath);
protected:
using ShaderSource = GL3LoadTestSample::ShaderSource;
};
@@ -0,0 +1,245 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 sts expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Draw a textured cube.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#if defined(_WIN32)
#if _MSC_VER < 1900
#define snprintf _snprintf
#endif
#define _CRT_SECURE_NO_WARNINGS
#endif
#include <assert.h>
#include <sstream>
#include <ktx.h>
#include "disable_glm_warnings.h"
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "reenable_warnings.h"
#include "TexturedCube.h"
#include "cube.h"
/* ------------------------------------------------------------------------- */
extern const GLchar* pszVs;
extern const GLchar *pszDecalFs, *pszDecalSrgbEncodeFs;
/* ------------------------------------------------------------------------- */
LoadTestSample*
TexturedCube::create(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
{
return new TexturedCube(width, height, szArgs, sBasePath);
}
TexturedCube::TexturedCube(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath)
: GL3LoadTestSample(width, height, szArgs, sBasePath)
{
std::string filename;
GLenum target;
GLenum glerror;
GLuint gnDecalFs, gnVs;
GLsizeiptr offset;
ktxTexture* kTexture;
KTX_error_code ktxresult;
bInitialized = GL_FALSE;
gnTexture = 0;
filename = getAssetPath() + szArgs;
ktxresult = ktxTexture_CreateFromNamedFile(filename.c_str(),
KTX_TEXTURE_CREATE_NO_FLAGS,
&kTexture);
if (KTX_SUCCESS != ktxresult) {
std::stringstream message;
message << "Creation of ktxTexture from \"" << filename
<< "\" failed: " << ktxErrorString(ktxresult);
throw std::runtime_error(message.str());
}
ktxresult = ktxTexture_GLUpload(kTexture, &gnTexture, &target, &glerror);
if (KTX_SUCCESS == ktxresult) {
if (target != GL_TEXTURE_2D) {
/* Can only draw 2D textures */
std::stringstream message;
glDeleteTextures(1, &gnTexture);
message << "App can only draw 2D textures.";
throw std::runtime_error(message.str());
}
if (kTexture->numLevels > 1)
// Enable bilinear mipmapping.
// TO DO: application can consider inserting a key,value pair in
// the KTX file that indicates what type of filtering to use.
glTexParameteri(target,
GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
else
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
ktxTexture_Destroy(kTexture);
assert(GL_NO_ERROR == glGetError());
} else {
std::stringstream message;
message << "Load of texture from \"" << filename << "\" failed: ";
if (ktxresult == KTX_GL_ERROR) {
message << std::showbase << "GL error " << std::hex << glerror
<< " occurred.";
} else {
message << ktxErrorString(ktxresult);
}
throw std::runtime_error(message.str());
}
// By default dithering is enabled. Dithering does not provide visual
// improvement in this sample so disable it to improve performance.
glDisable(GL_DITHER);
glEnable(GL_CULL_FACE);
glClearColor(0.2f,0.3f,0.4f,1.0f);
// Create a VAO and bind it.
glGenVertexArrays(1, &gnVao);
glBindVertexArray(gnVao);
// Must have vertex data in buffer objects to use VAO's on ES3/GL Core
glGenBuffers(2, gnVbo);
glBindBuffer(GL_ARRAY_BUFFER, gnVbo[0]);
// Must be done after the VAO is bound
// WebGL requires different buffers for data and indices.
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gnVbo[1]);
// Create the buffer data store.
glBufferData(GL_ARRAY_BUFFER,
sizeof(cube_face) + sizeof(cube_color) + sizeof(cube_texture)
+ sizeof(cube_normal), NULL, GL_STATIC_DRAW);
// Interleave data copying and attrib pointer setup so offset is only
// computed once.
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
offset = 0;
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_face), cube_face);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_face);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_color), cube_color);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_color);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_texture), cube_texture);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_texture);
glBufferSubData(GL_ARRAY_BUFFER, offset, sizeof(cube_normal), cube_normal);
glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, 0, (GLvoid*)offset);
offset += sizeof(cube_normal);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_index_buffer),
cube_index_buffer, GL_STATIC_DRAW);
const GLchar* actualDecalFs;
if (framebufferColorEncoding() == GL_LINEAR) {
actualDecalFs = pszDecalSrgbEncodeFs;
} else {
actualDecalFs = pszDecalFs;
}
try {
makeShader(GL_VERTEX_SHADER, pszVs, &gnVs);
makeShader(GL_FRAGMENT_SHADER, actualDecalFs, &gnDecalFs);
makeProgram(gnVs, gnDecalFs, &gnTexProg);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
throw;
}
try {
makeShader(GL_VERTEX_SHADER, pszVs, &gnVs);
makeShader(GL_FRAGMENT_SHADER, pszDecalFs, &gnDecalFs);
makeProgram(gnVs, gnDecalFs, &gnTexProg);
} catch (std::exception& e) {
(void)e; // To quiet unused variable warnings from some compilers.
throw;
}
gulMvMatrixLocTP = glGetUniformLocation(gnTexProg, "mvmatrix");
gulPMatrixLocTP = glGetUniformLocation(gnTexProg, "pmatrix");
gulSamplerLocTP = glGetUniformLocation(gnTexProg, "sampler");
glUseProgram(gnTexProg);
// We're using the default texture unit 0
glUniform1i(gulSamplerLocTP, 0);
glDeleteShader(gnVs);
glDeleteShader(gnDecalFs);
assert(GL_NO_ERROR == glGetError());
bInitialized = GL_TRUE;
}
TexturedCube::~TexturedCube()
{
glEnable(GL_DITHER);
glEnable(GL_CULL_FACE);
if (bInitialized) {
glUseProgram(0);
glDeleteTextures(1, &gnTexture);
glDeleteProgram(gnTexProg);
glDeleteBuffers(2, gnVbo);
glDeleteVertexArrays(1, &gnVao);
}
assert(GL_NO_ERROR == glGetError());
}
void
TexturedCube::resize(uint32_t uWidth, uint32_t uHeight)
{
glm::mat4 matProj;
glViewport(0, 0, uWidth, uHeight);
matProj = glm::perspective(glm::radians(45.f),
uWidth / (float)uHeight,
1.f, 100.f);
glUniformMatrix4fv(gulPMatrixLocTP, 1, GL_FALSE, glm::value_ptr(matProj));
}
void
TexturedCube::run(uint32_t msTicks)
{
// Setup the view matrix : just turn around the cube.
const float fDistance = 5.0f;
glm::vec3 eye((float)cos( msTicks*0.001f ) * fDistance,
(float)sin( msTicks*0.0007f ) * fDistance,
(float)sin( msTicks*0.001f ) * fDistance);
glm::vec3 look(0.,0.,0.);
glm::vec3 up(0.,1.,0.);
glm::mat4 matView = glm::lookAt( eye, look, up );
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glUniformMatrix4fv(gulMvMatrixLocTP, 1, GL_FALSE, glm::value_ptr(matView));
glDrawElements(GL_TRIANGLES, CUBE_NUM_INDICES, GL_UNSIGNED_SHORT, 0);
assert(GL_NO_ERROR == glGetError());
}
/* ------------------------------------------------------------------------- */
@@ -0,0 +1,51 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2018-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief GLLoadTestSample derived class for drawing a textured cube.
*
* @author Mark Callow, github.com/MarkCallow.
*/
#ifndef TEXTURED_CUBE_H
#define TEXTURED_CUBE_H
#include "GL3LoadTestSample.h"
class TexturedCube : public GL3LoadTestSample {
public:
TexturedCube(uint32_t width, uint32_t height,
const char* const szArgs,
const std::string sBasePath);
~TexturedCube();
virtual void resize(uint32_t width, uint32_t height);
virtual void run(uint32_t msTicks);
//virtual void getOverlayText(GLTextOverlay *textOverlay);
static LoadTestSample*
create(uint32_t width, uint32_t height,
const char* const szArgs, const std::string sBasePath);
protected:
GLuint gnTexture;
GLuint gnTexProg;
GLuint gnVao;
GLuint gnVbo[2];
GLint gulMvMatrixLocTP;
GLint gulPMatrixLocTP;
GLint gulSamplerLocTP;
bool bInitialized;
};
#endif /* TEXTURED_CUBE_H */
@@ -0,0 +1,81 @@
/* -*- tab-width: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2016-2020 Mark Callow.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Include appropriate version of gl.h for the shader-based tests.
*
* This is a separate header to avoid repetition of these conditionals.
*/
#ifndef MYGL_H
#define MYGL_H
#include <SDL3/SDL_video.h> // For the SDL_GL_CONTEXT_PROFILE macros
#if GL_CONTEXT_PROFILE == SDL_GL_CONTEXT_PROFILE_CORE
#ifdef _WIN32
#include <windows.h>
#undef KTX_USE_GETPROC /* Must use GETPROC on Windows */
#define KTX_USE_GETPROC 1
#else
#if !defined(KTX_USE_GETPROC)
#define KTX_USE_GETPROC 0
#endif
#endif
#if KTX_USE_GETPROC
#include <GL/glew.h>
#else
#define GL_GLEXT_PROTOTYPES
#include <GL/glcorearb.h>
#endif
//#define GL_APIENTRY APIENTRY
#elif GL_CONTEXT_PROFILE == SDL_GL_CONTEXT_PROFILE_COMPATIBILITY
#error This application is not intended to run in compatibility mode.
#elif GL_CONTEXT_PROFILE == SDL_GL_CONTEXT_PROFILE_ES
#if GL_CONTEXT_MAJOR_VERSION == 1
#error This application cannot run on OpenGL ES 1.
#elif GL_CONTEXT_MAJOR_VERSION == 2
#define GL_GLEXT_PROTOTYPES
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
#elif GL_CONTEXT_MAJOR_VERSION == 3
#define GL_GLEXT_PROTOTYPES
#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>
#else
#error Unexpected GL_CONTEXT_MAJOR_VERSION
#endif
#endif
/* To help find supported transcode targets */
#if !defined(GL_ETC1_RGB8_OES)
#define GL_ETC1_RGB8_OES 0x8D64
#endif
#if !defined(GL_COMPRESSED_RGB8_ETC2)
#define GL_COMPRESSED_RGB8_ETC2 0x9274
#endif
#if !defined(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
#define GL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3
#endif
#endif /* MYGL_H */
@@ -0,0 +1,115 @@
/* -*- tab-iWidth: 4; -*- */
/* vi: set sw=2 ts=4 expandtab: */
/*
* Copyright 2008 HI Corporation.
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief Shaders used by the DrawTexture and TexturedCube samples.
*/
#include "GL/glcorearb.h"
#include "ktx.h"
const GLchar* pszVs =
"layout(location = 0) in vec4 position;\n"
"layout(location = 1) in vec4 color;\n"
"layout(location = 2) in vec4 texcoord;\n\n"
"out vec4 v_color;\n"
"out vec2 v_texcoord;\n\n"
"uniform highp mat4 mvmatrix;\n"
"uniform highp mat4 pmatrix;\n\n"
"void main(void)\n"
"{\n"
" mat4 mvpmatrix = pmatrix * mvmatrix;\n"
" v_color = color;\n"
" v_texcoord = texcoord.xy;\n"
" gl_Position = mvpmatrix * position;\n"
"}";
const GLchar* pszDecalFs =
"precision mediump float;\n\n"
"uniform sampler2D sampler;\n\n"
"in vec4 v_color;\n"
"in vec2 v_texcoord;\n"
"out vec4 fragcolor;\n\n"
"void main(void)\n"
"{\n"
" vec4 color = texture(sampler, v_texcoord);\n"
" // DECAL\n"
" fragcolor.rgb = v_color.rgb * (1.0f - color.a) + color.rgb * color.a;\n"
" // No need for blending with page or screen background.\n"
" fragcolor.a = 1.0f;\n"
"}";
const GLchar* pszColorFs =
"precision mediump float;\n\n"
"in vec4 v_color;\n"
"out vec4 fragcolor;\n\n"
"void main(void)\n"
"{\n"
" fragcolor = v_color;\n"
"}";
// For use when sRGB rendering is not available. Note this is really a
// workaround for broken GL implementations (most) where "linear" framebuffers
// are really sRGB framebuffers but since they are "linear" GL does not encode
// the output of the fs to sRGB so we do it ourselves.
const GLchar* pszDecalSrgbEncodeFs =
"precision mediump float;\n\n"
"uniform sampler2D sampler;\n\n"
"in vec4 v_color;\n"
"in vec2 v_texcoord;\n"
"out vec4 fragcolor;\n\n"
"vec3 srgb_encode(vec3 color) {\n"
" float r = color.r < 0.0031308 ? 12.92 * color.r : 1.055 * pow(color.r, 1.0/2.4) - 0.055;\n"
" float g = color.g < 0.0031308 ? 12.92 * color.g : 1.055 * pow(color.g, 1.0/2.4) - 0.055;\n"
" float b = color.b < 0.0031308 ? 12.92 * color.b : 1.055 * pow(color.b, 1.0/2.4) - 0.055;\n"
" return vec3(r, g, b);\n"
"}\n\n"
"void main(void)\n"
"{\n"
" vec4 t_color = texture(sampler, v_texcoord);\n"
" vec3 lin_fragcolor;\n"
" // DECAL\n"
" lin_fragcolor = v_color.rgb * (1.0f - t_color.a) + t_color.rgb * t_color.a;\n"
" fragcolor.rgb = srgb_encode(lin_fragcolor);\n"
" // No need for blending with page or screen background.\n"
" fragcolor.a = 1.0f;\n"
"}";
const GLchar* pszColorSrgbEncodeFs =
"precision mediump float;\n\n"
"in vec4 v_color;\n"
"out vec4 fragcolor;\n\n"
"vec3 srgb_encode(vec3 color) {\n"
" float r = color.r < 0.0031308 ? 12.92 * color.r : 1.055 * pow(color.r, 1.0/2.4) - 0.055;\n"
" float g = color.g < 0.0031308 ? 12.92 * color.g : 1.055 * pow(color.g, 1.0/2.4) - 0.055;\n"
" float b = color.b < 0.0031308 ? 12.92 * color.b : 1.055 * pow(color.b, 1.0/2.4) - 0.055;\n"
" return vec3(r, g, b);\n"
"}\n\n"
"void main(void)\n"
"{\n"
" fragcolor.rgb = srgb_encode(v_color.rgb);\n"
" fragcolor.a = v_color.a;\n"
"}";
/* ----------------------------------------------------------------------------- */