Add ktx
This commit is contained in:
@@ -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, ¶ms);
|
||||
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"
|
||||
"}";
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
Reference in New Issue
Block a user