Add ktx
This commit is contained in:
@@ -0,0 +1,313 @@
|
||||
/* -*- 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 Implementation of app for running a set of OpenGL load tests.
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include <sstream>
|
||||
#include <ktx.h>
|
||||
|
||||
#include "GLLoadTests.h"
|
||||
#include "ltexceptions.h"
|
||||
|
||||
GLLoadTests::GLLoadTests(const sampleInvocation samples[],
|
||||
const uint32_t numSamples,
|
||||
const char* const name,
|
||||
const SDL_GLProfile profile,
|
||||
const int majorVersion,
|
||||
const int minorVersion)
|
||||
: GLAppSDL(name, 640, 480, profile, majorVersion, minorVersion),
|
||||
siSamples(samples), sampleIndex(numSamples)
|
||||
{
|
||||
pCurSample = nullptr;
|
||||
}
|
||||
|
||||
GLLoadTests::~GLLoadTests()
|
||||
{
|
||||
delete pCurSample;
|
||||
}
|
||||
|
||||
bool
|
||||
GLLoadTests::initialize(Args& args)
|
||||
{
|
||||
bool explicitlyLoadOpenGL = false;
|
||||
for (uint32_t i = 1; i < args.size(); i++) {
|
||||
if (args[i].compare("--load-gl") == 0) {
|
||||
explicitlyLoadOpenGL = true;
|
||||
args.erase(args.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!GLAppSDL::initialize(args))
|
||||
return false;
|
||||
|
||||
// --load-gl allows testing of explicitly loading the OpenGL pointers.
|
||||
// Default is for ktxTexture_GLUpload to implicitly load them.
|
||||
if (explicitlyLoadOpenGL)
|
||||
ktxLoadOpenGL(reinterpret_cast<PFNGLGETPROCADDRESS>(SDL_GL_GetProcAddress));
|
||||
|
||||
for (auto it = args.begin() + 1; it != args.end(); it++) {
|
||||
infiles.push_back(*it);
|
||||
}
|
||||
if (infiles.size() > 0) {
|
||||
sampleIndex.setNumSamples((uint32_t)infiles.size());
|
||||
}
|
||||
// Launch the first sample.
|
||||
invokeSample(Direction::eForward);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GLLoadTests::finalize()
|
||||
{
|
||||
delete pCurSample;
|
||||
GLAppSDL::finalize();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
GLLoadTests::doEvent(SDL_Event* event)
|
||||
{
|
||||
bool result = false;
|
||||
switch (event->type) {
|
||||
case SDL_EVENT_KEY_UP:
|
||||
switch (event->key.key) {
|
||||
case 'q':
|
||||
quit = true;
|
||||
break;
|
||||
case 'n':
|
||||
++sampleIndex;
|
||||
invokeSample(Direction::eForward);
|
||||
break;
|
||||
case 'p':
|
||||
--sampleIndex;
|
||||
invokeSample(Direction::eBack);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_DOWN:
|
||||
// Forward to sample in case this is the start of motion.
|
||||
result = true;
|
||||
switch (event->button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
buttonDown.x = event->button.x;
|
||||
buttonDown.y = event->button.y;
|
||||
buttonDown.timestamp = event->button.timestamp;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_MOUSE_BUTTON_UP:
|
||||
// Forward to sample so it doesn't get stuck in button down state.
|
||||
result = true;
|
||||
switch (event->button.button) {
|
||||
case SDL_BUTTON_LEFT:
|
||||
if (SDL_fabs(event->button.x - buttonDown.x) < 5
|
||||
&& SDL_fabs(event->button.y - buttonDown.y) < 5
|
||||
&& (event->button.timestamp - buttonDown.timestamp) < 100) {
|
||||
// Advance to the next sample.
|
||||
++sampleIndex;
|
||||
invokeSample(Direction::eForward);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
// On macOS drop events come also when Launch Pad sends a file open event.
|
||||
case SDL_EVENT_DROP_BEGIN:
|
||||
// Opens of multiple selected files from Finder/LaunchPad come as
|
||||
// a BEGIN, COMPLETE sequence per file. Only clear infiles after a
|
||||
// suitable pause between COMPLETE and BEGIN.
|
||||
if (event->drop.timestamp - dropCompleteTime > 500) {
|
||||
infiles.clear();
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_DROP_FILE:
|
||||
infiles.push_back(event->drop.data);
|
||||
break;
|
||||
case SDL_EVENT_DROP_COMPLETE:
|
||||
if (!infiles.empty()) {
|
||||
// Guard against the drop being text.
|
||||
dropCompleteTime = event->drop.timestamp;
|
||||
sampleIndex.setNumSamples((uint32_t)infiles.size());
|
||||
invokeSample(Direction::eForward);
|
||||
}
|
||||
break;
|
||||
case SDL_EVENT_USER:
|
||||
if (event->user.code == SwipeDetector::swipeGesture) {
|
||||
// This is horrible.
|
||||
uint64_t udirection = reinterpret_cast<uint64_t>(event->user.data1);
|
||||
SwipeDetector::Direction direction =
|
||||
static_cast<SwipeDetector::Direction>(udirection);
|
||||
switch (direction) {
|
||||
case SwipeDetector::Direction::left:
|
||||
++sampleIndex;
|
||||
invokeSample(Direction::eForward);
|
||||
break;
|
||||
case SwipeDetector::Direction::right:
|
||||
--sampleIndex;
|
||||
invokeSample(Direction::eBack);
|
||||
break;
|
||||
default:
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
result = swipeDetector.doEvent(event);
|
||||
}
|
||||
|
||||
if (result) {
|
||||
// Further processing required.
|
||||
if (pCurSample != nullptr)
|
||||
result = pCurSample->doEvent(event); // Give sample a chance.
|
||||
if (result)
|
||||
return GLAppSDL::doEvent(event); // Pass to base class.
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GLLoadTests::windowResized()
|
||||
{
|
||||
if (pCurSample != nullptr)
|
||||
pCurSample->resize(w_width, w_height);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
GLLoadTests::drawFrame(uint32_t msTicks)
|
||||
{
|
||||
if (pCurSample != nullptr)
|
||||
pCurSample->run(msTicks);
|
||||
|
||||
GLAppSDL::drawFrame(msTicks);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void
|
||||
GLLoadTests::getOverlayText(GLTextOverlay * textOverlay)
|
||||
{
|
||||
if (enableTextOverlay && pCurSample != nullptr) {
|
||||
pCurSample->getOverlayText(textOverlay);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
GLLoadTests::invokeSample(Direction dir)
|
||||
{
|
||||
const sampleInvocation* sampleInv = &siSamples[sampleIndex];
|
||||
|
||||
delete pCurSample;
|
||||
// Certain events can be triggered during new sample initialization
|
||||
// while pCurSample is not valid, e.g. FOCUS_LOST. Protect against
|
||||
// problems from this by indicating there is no current sample.
|
||||
pCurSample = nullptr;
|
||||
|
||||
uint32_t unsupportedTypeExceptions = 0;
|
||||
std::string fileTitle;
|
||||
for (;;) {
|
||||
try {
|
||||
if (infiles.size() > 0) {
|
||||
fileTitle = "Viewing file ";
|
||||
fileTitle += infiles[sampleIndex];
|
||||
pCurSample = showFile(infiles[sampleIndex]);
|
||||
} else {
|
||||
sampleInv = &siSamples[sampleIndex];
|
||||
pCurSample = sampleInv->createSample(w_width, w_height,
|
||||
sampleInv->args,
|
||||
sBasePath);
|
||||
}
|
||||
break;
|
||||
} catch (unsupported_ctype& e) {
|
||||
(void)e; // To quiet unused variable warnings from some compilers.
|
||||
unsupportedTypeExceptions++;
|
||||
if (unsupportedTypeExceptions == sampleIndex.getNumSamples()) {
|
||||
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
|
||||
infiles.size() > 0 ? fileTitle.c_str() : sampleInv->title,
|
||||
"None of the specified samples or files use texture types "
|
||||
"supported on this platform.",
|
||||
NULL);
|
||||
exit(0);
|
||||
} else {
|
||||
dir == Direction::eForward ? ++sampleIndex : --sampleIndex;
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
const SDL_MessageBoxButtonData buttons[] = {
|
||||
/* .flags, .buttonid, .text */
|
||||
{ SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Continue" },
|
||||
{ SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 1, "Abort" },
|
||||
};
|
||||
#if 0
|
||||
const SDL_MessageBoxColorScheme colorScheme = {
|
||||
{ /* .colors (.r, .g, .b) */
|
||||
/* [SDL_MESSAGEBOX_COLOR_BACKGROUND] */
|
||||
{ 255, 0, 0 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_TEXT] */
|
||||
{ 0, 255, 0 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_BUTTON_BORDER] */
|
||||
{ 255, 255, 0 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_BUTTON_BACKGROUND] */
|
||||
{ 0, 0, 255 },
|
||||
/* [SDL_MESSAGEBOX_COLOR_BUTTON_SELECTED] */
|
||||
{ 255, 0, 255 }
|
||||
}
|
||||
};
|
||||
#endif
|
||||
const SDL_MessageBoxData messageboxdata = {
|
||||
SDL_MESSAGEBOX_ERROR, // .flags
|
||||
NULL, // .window
|
||||
infiles.size() > 0 ?
|
||||
fileTitle.c_str() : sampleInv->title, // .title
|
||||
e.what(), // .message
|
||||
SDL_arraysize(buttons), // .numbuttons
|
||||
buttons, // .buttons
|
||||
NULL //&colorScheme // .colorScheme
|
||||
};
|
||||
int buttonid;
|
||||
if (!SDL_ShowMessageBox(&messageboxdata, &buttonid)) {
|
||||
SDL_Log("error displaying error message box");
|
||||
exit(1);
|
||||
}
|
||||
if (buttonid == 0) {
|
||||
// Continue
|
||||
dir == Direction::eForward ? ++sampleIndex : --sampleIndex;
|
||||
} else {
|
||||
// We've been told to quit or no button was pressed.
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
setAppTitle(infiles.size() > 0 ? fileTitle.c_str() : sampleInv->title);
|
||||
pCurSample->resize(w_width, w_height);
|
||||
}
|
||||
|
||||
void
|
||||
GLLoadTests::onFPSUpdate()
|
||||
{
|
||||
// Using onFPSUpdate avoids rewriting the title every frame.
|
||||
//setWindowTitle(pCurSample->title);
|
||||
GLAppSDL::onFPSUpdate();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
#ifndef GL_LOAD_TESTS_H
|
||||
#define GL_LOAD_TESTS_H
|
||||
|
||||
/*
|
||||
* Copyright 2015-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* @file
|
||||
* @~English
|
||||
*
|
||||
* @brief Definition of app for running a set of OpenGL load tests.
|
||||
*/
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "GLAppSDL.h"
|
||||
#include "LoadTestSample.h"
|
||||
#include "SwipeDetector.h"
|
||||
|
||||
class GLLoadTests : public GLAppSDL {
|
||||
public:
|
||||
/** A table of samples and arguments */
|
||||
typedef struct sampleInvocation_ {
|
||||
const LoadTestSample::PFN_create createSample;
|
||||
const char* const args;
|
||||
const char* const title;
|
||||
} sampleInvocation;
|
||||
|
||||
GLLoadTests(const sampleInvocation samples[],
|
||||
const uint32_t numSamples,
|
||||
const char* const name,
|
||||
const SDL_GLProfile profile,
|
||||
const int majorVersion,
|
||||
const int minorVersion);
|
||||
virtual ~GLLoadTests();
|
||||
virtual bool doEvent(SDL_Event* event);
|
||||
virtual void drawFrame(uint32_t msTicks);
|
||||
virtual void finalize();
|
||||
//virtual void getOverlayText(TextOverlay* textOverlay, float yOffset);
|
||||
virtual bool initialize(Args& args);
|
||||
virtual void onFPSUpdate();
|
||||
virtual void windowResized();
|
||||
|
||||
protected:
|
||||
enum class Direction {
|
||||
eForward,
|
||||
eBack
|
||||
};
|
||||
void invokeSample(Direction dir);
|
||||
LoadTestSample* showFile(const std::string& filename);
|
||||
LoadTestSample* pCurSample;
|
||||
|
||||
bool quit = false;
|
||||
|
||||
const sampleInvocation* const siSamples;
|
||||
class sampleIndex {
|
||||
public:
|
||||
sampleIndex(const uint32_t numSamples) : numSamples(numSamples) {
|
||||
index = 0;
|
||||
}
|
||||
sampleIndex& operator++() {
|
||||
if (++index >= numSamples)
|
||||
index = 0;
|
||||
return *this;
|
||||
}
|
||||
sampleIndex& operator--() {
|
||||
if (--index > numSamples /* underflow */)
|
||||
index = numSamples-1;
|
||||
return *this;
|
||||
}
|
||||
operator int32_t() {
|
||||
return index;
|
||||
}
|
||||
uint32_t getNumSamples() { return numSamples; }
|
||||
void setNumSamples(uint32_t ns) { numSamples = ns; }
|
||||
protected:
|
||||
uint32_t numSamples;
|
||||
uint32_t index;
|
||||
} sampleIndex;
|
||||
|
||||
std::vector<std::string> infiles;
|
||||
|
||||
Sint64 dropCompleteTime = 0;
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
Sint64 timestamp;
|
||||
} buttonDown;
|
||||
|
||||
SwipeDetector swipeDetector;
|
||||
};
|
||||
|
||||
#endif /* GL_LOAD_TESTS_H */
|
||||
@@ -0,0 +1,250 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2018-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file DrawTexture.cpp
|
||||
* @brief Draw textures at actual size using the DrawTexture functions
|
||||
* from OES_draw_texture.
|
||||
*
|
||||
* @author Mark Callow
|
||||
*/
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <iomanip>
|
||||
#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 "frame.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
#if 0
|
||||
static int isPowerOfTwo (int x)
|
||||
{
|
||||
if (x < 0) x = -1;
|
||||
return ((x != 0) && !(x & (x - 1)));
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
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)
|
||||
: LoadTestSample(width, height, sBasePath)
|
||||
{
|
||||
GLint iCropRect[4] = {0, 0, 0, 0};
|
||||
const GLchar* szExtensions = (const GLchar*)glGetString(GL_EXTENSIONS);
|
||||
const char* filename;
|
||||
std::string pathname;
|
||||
GLenum target;
|
||||
GLboolean npotTexture;
|
||||
GLenum glerror;
|
||||
ktxTexture* kTexture;
|
||||
KTX_error_code ktxresult;
|
||||
GLint sign_s = 1, sign_t = 1;
|
||||
|
||||
bInitialized = false;
|
||||
gnTexture = 0;
|
||||
|
||||
|
||||
if (strstr(szExtensions, "OES_draw_texture") != NULL) {
|
||||
glDrawTexsOES = (PFNGLDRAWTEXSOESPROC)SDL_GL_GetProcAddress("glDrawTexsOES");
|
||||
glDrawTexiOES = (PFNGLDRAWTEXIOESPROC)SDL_GL_GetProcAddress("glDrawTexiOES");
|
||||
glDrawTexxOES = (PFNGLDRAWTEXXOESPROC)SDL_GL_GetProcAddress("glDrawTexxOES");
|
||||
glDrawTexfOES = (PFNGLDRAWTEXFOESPROC)SDL_GL_GetProcAddress("glDrawTexfOES");
|
||||
glDrawTexsvOES = (PFNGLDRAWTEXSVOESPROC)SDL_GL_GetProcAddress("glDrawTexsvOES");
|
||||
glDrawTexivOES = (PFNGLDRAWTEXIVOESPROC)SDL_GL_GetProcAddress("glDrawTexivOES");
|
||||
glDrawTexxvOES = (PFNGLDRAWTEXXVOESPROC)SDL_GL_GetProcAddress("glDrawTexxvOES");
|
||||
glDrawTexfvOES = (PFNGLDRAWTEXFVOESPROC)SDL_GL_GetProcAddress("glDrawTexfvOES");
|
||||
} else {
|
||||
/* Can't do anything */
|
||||
std::stringstream message;
|
||||
|
||||
message << "DrawTexture: this OpenGL ES implementation does not support"
|
||||
<< " OES_draw_texture. Can't Run Test";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
if (strstr(szExtensions, "OES_texture_npot") != NULL)
|
||||
bNpotSupported = GL_TRUE;
|
||||
else
|
||||
bNpotSupported = GL_FALSE;
|
||||
|
||||
|
||||
if ((filename = strchr(szArgs, ' ')) != NULL) {
|
||||
npotTexture = GL_FALSE;
|
||||
if (!strncmp(szArgs, "--npot ", 7)) {
|
||||
npotTexture = GL_TRUE;
|
||||
#if defined(DEBUG)
|
||||
} else {
|
||||
assert(0); /* Unknown argument in sampleInvocations */
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
filename = szArgs;
|
||||
npotTexture = GL_FALSE;
|
||||
}
|
||||
|
||||
if (npotTexture && !bNpotSupported) {
|
||||
/* Load error texture. */
|
||||
filename = "no-npot.ktx";
|
||||
}
|
||||
|
||||
pathname = getAssetPath() + filename;
|
||||
|
||||
ktxresult = ktxTexture_CreateFromNamedFile(pathname.c_str(),
|
||||
KTX_TEXTURE_CREATE_NO_FLAGS,
|
||||
&kTexture);
|
||||
if (KTX_SUCCESS != ktxresult) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Creation of ktxTexture from \"" << pathname
|
||||
<< "\" 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 */
|
||||
glDeleteTextures(1, &gnTexture);
|
||||
return;
|
||||
}
|
||||
|
||||
if (kTexture->orientation.x == KTX_ORIENT_X_LEFT)
|
||||
sign_s = -1;
|
||||
if (kTexture->orientation.y == KTX_ORIENT_Y_DOWN)
|
||||
sign_t = -1;
|
||||
|
||||
iCropRect[2] = uTexWidth = kTexture->baseWidth;
|
||||
iCropRect[3] = uTexHeight = kTexture->baseHeight;
|
||||
iCropRect[2] *= sign_s;
|
||||
iCropRect[3] *= sign_t;
|
||||
|
||||
glEnable(target);
|
||||
|
||||
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);
|
||||
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
||||
glTexParameteriv(target, GL_TEXTURE_CROP_RECT_OES, iCropRect);
|
||||
|
||||
/* Check for any errors */
|
||||
assert(GL_NO_ERROR == glGetError());
|
||||
|
||||
ktxTexture_Destroy(kTexture);
|
||||
} else {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Load of texture from \"" << pathname << "\" 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());
|
||||
}
|
||||
|
||||
glClearColor(0.4f, 0.4f, 0.5f, 1.0f);
|
||||
glColor4f(1.0f, 1.0f, 0.0f, 1.0f);
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glVertexPointer(3, GL_BYTE, 0, (GLvoid *)frame_position);
|
||||
|
||||
bInitialized = GL_TRUE;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
DrawTexture::~DrawTexture()
|
||||
{
|
||||
if (bInitialized) {
|
||||
glDeleteTextures(1, &gnTexture);
|
||||
}
|
||||
assert(GL_NO_ERROR == glGetError());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
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
|
||||
framePMatrix = glm::ortho(0.f, (float)uWidth, 0.f, (float)uHeight);
|
||||
// Move (0,0,0) to the center of the window.
|
||||
framePMatrix *= glm::translate(glm::mat4(),
|
||||
glm::vec3((float)uWidth/2, (float)uHeight/2, 0));
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadMatrixf(glm::value_ptr(framePMatrix));
|
||||
|
||||
glMatrixMode(GL_MODELVIEW);
|
||||
// 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.]
|
||||
glLoadIdentity();
|
||||
glScalef((float)(uWidth - 1) / 2, (float)(uHeight - 1) / 2, 1);
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
|
||||
void
|
||||
DrawTexture::run(uint32_t /*msTicks*/)
|
||||
{
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glDrawArrays(GL_LINE_LOOP, 0, 4);
|
||||
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
glDrawTexiOES(uWidth/2 - uTexWidth/2,
|
||||
(uHeight)/2 - uTexHeight/2,
|
||||
0,
|
||||
uTexWidth, uTexHeight);
|
||||
|
||||
assert(GL_NO_ERROR == glGetError());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
@@ -0,0 +1,66 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2018-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file DrawTexture.h
|
||||
* @brief Draw textures at actual size using the DrawTexture functions
|
||||
* from OES_draw_texture.
|
||||
*
|
||||
* @author Mark Callow
|
||||
*/
|
||||
|
||||
#ifndef DRAW_TEXTURE_H
|
||||
#define DRAW_TEXTURE_H
|
||||
|
||||
#include <GLES/gl.h>
|
||||
#include <GLES/glext.h>
|
||||
|
||||
#include "LoadTestSample.h"
|
||||
|
||||
class DrawTexture : public LoadTestSample {
|
||||
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:
|
||||
PFNGLDRAWTEXSOESPROC glDrawTexsOES;
|
||||
PFNGLDRAWTEXIOESPROC glDrawTexiOES;
|
||||
PFNGLDRAWTEXXOESPROC glDrawTexxOES;
|
||||
PFNGLDRAWTEXFOESPROC glDrawTexfOES;
|
||||
PFNGLDRAWTEXSVOESPROC glDrawTexsvOES;
|
||||
PFNGLDRAWTEXIVOESPROC glDrawTexivOES;
|
||||
PFNGLDRAWTEXXVOESPROC glDrawTexxvOES;
|
||||
PFNGLDRAWTEXFVOESPROC glDrawTexfvOES;
|
||||
|
||||
uint32_t uWidth;
|
||||
uint32_t uHeight;
|
||||
|
||||
uint32_t uTexWidth;
|
||||
uint32_t uTexHeight;
|
||||
|
||||
glm::mat4 framePMatrix;
|
||||
|
||||
GLuint gnTexture;
|
||||
|
||||
bool bNpotSupported;
|
||||
|
||||
bool bInitialized;
|
||||
};
|
||||
|
||||
#endif /* DRAW_TEXTURE_H */
|
||||
@@ -0,0 +1,109 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2018-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file ES1LoadTests.cpp
|
||||
* @brief List of tests of the KTX loader for OpenGL ES 1.1.
|
||||
*
|
||||
* The loader is tested by loading and drawing KTX textures in various formats
|
||||
* using the DrawTexture and TexturedCube samples.
|
||||
*/
|
||||
|
||||
#include <sstream>
|
||||
#include <ktx.h>
|
||||
|
||||
#include "GLLoadTests.h"
|
||||
#include "DrawTexture.h"
|
||||
#include "TexturedCube.h"
|
||||
|
||||
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 \"" << getAssetPath()
|
||||
<< filename << "\" failed: " << ktxErrorString(ktxresult);
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
LoadTestSample::PFN_create createViewer;
|
||||
LoadTestSample* pViewer;
|
||||
createViewer = DrawTexture::create;
|
||||
ktxTexture_Destroy(kTexture);
|
||||
pViewer = createViewer(w_width, w_height, filename.c_str(), "");
|
||||
return pViewer;
|
||||
}
|
||||
|
||||
const GLLoadTests::sampleInvocation siSamples[] = {
|
||||
{ DrawTexture::create,
|
||||
"--npot hi_mark.ktx",
|
||||
"RGB8 NPOT HI Logo"
|
||||
},
|
||||
{ DrawTexture::create,
|
||||
"--npot luminance-reference-metadata.ktx",
|
||||
"LUMINANCE8 NPOT"
|
||||
},
|
||||
{ DrawTexture::create,
|
||||
"orient-up-metadata.ktx",
|
||||
"RGB8 + KTXOrientation up"
|
||||
},
|
||||
{ DrawTexture::create,
|
||||
"orient-down-metadata.ktx",
|
||||
"RGB8 + KTXOrientation down"
|
||||
},
|
||||
{ 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,
|
||||
"rgba-reference.ktx",
|
||||
"RGBA8"
|
||||
},
|
||||
{ TexturedCube::create,
|
||||
"rgb-reference.ktx",
|
||||
"RGB8"
|
||||
},
|
||||
{ TexturedCube::create,
|
||||
"rgb-amg-reference.ktx",
|
||||
"RGB8 + Auto Mipmap"
|
||||
},
|
||||
{ TexturedCube::create,
|
||||
"rgb-mipmap-reference.ktx",
|
||||
"RGB8 Color/level mipmap"
|
||||
},
|
||||
{ TexturedCube::create,
|
||||
"--npot hi_mark_sq.ktx",
|
||||
"RGB8 NPOT HI Logo"
|
||||
},
|
||||
};
|
||||
|
||||
const int iNumSamples = sizeof(siSamples) / sizeof(GLLoadTests::sampleInvocation);
|
||||
|
||||
AppBaseSDL* theApp = new GLLoadTests(siSamples, iNumSamples,
|
||||
"KTX Loader Tests for OpenGL ES 1",
|
||||
SDL_GL_CONTEXT_PROFILE_ES, 1, 1);
|
||||
|
||||
|
||||
@@ -0,0 +1,202 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2018-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file TexturedCube.cpp
|
||||
* @brief Draw a textured cube.
|
||||
*/
|
||||
|
||||
#if defined(_WIN32)
|
||||
#if _MSC_VER < 1900
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#endif
|
||||
|
||||
#include <iomanip>
|
||||
#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"
|
||||
|
||||
|
||||
#if defined(_WIN32)
|
||||
#define snprintf _snprintf
|
||||
#endif
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
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)
|
||||
: LoadTestSample(width, height, sBasePath)
|
||||
{
|
||||
const GLchar* szExtensions = (const GLchar*)glGetString(GL_EXTENSIONS);
|
||||
const char* filename;
|
||||
std::string pathname;
|
||||
GLuint gnTexture = 0;
|
||||
GLenum target;
|
||||
GLenum glerror;
|
||||
GLboolean npotSupported, npotTexture;
|
||||
ktxTexture* kTexture;
|
||||
KTX_error_code ktxresult;
|
||||
|
||||
if (strstr(szExtensions, "OES_texture_npot") != NULL)
|
||||
npotSupported = GL_TRUE;
|
||||
else
|
||||
npotSupported = GL_FALSE;
|
||||
|
||||
if ((filename = strchr(szArgs, ' ')) != NULL) {
|
||||
npotTexture = GL_FALSE;
|
||||
if (!strncmp(szArgs, "--npot ", 7)) {
|
||||
npotTexture = GL_TRUE;
|
||||
#if defined(DEBUG)
|
||||
} else {
|
||||
assert(0); /* Unknown argument in sampleInvocations */
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
filename = szArgs;
|
||||
npotTexture = GL_FALSE;
|
||||
}
|
||||
|
||||
if (npotTexture && !npotSupported) {
|
||||
/* Load error texture. */
|
||||
filename = "no-npot.ktx";
|
||||
}
|
||||
pathname = getAssetPath() + filename;
|
||||
|
||||
ktxresult = ktxTexture_CreateFromNamedFile(pathname.c_str(),
|
||||
KTX_TEXTURE_CREATE_NO_FLAGS,
|
||||
&kTexture);
|
||||
if (KTX_SUCCESS != ktxresult) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Creation of ktxTexture from \"" << pathname
|
||||
<< "\" 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 */
|
||||
glDeleteTextures(1, &gnTexture);
|
||||
return;
|
||||
}
|
||||
glEnable(target);
|
||||
|
||||
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);
|
||||
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
|
||||
|
||||
ktxTexture_Destroy(kTexture);
|
||||
} else {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Load of texture from \"" << pathname << "\" 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);
|
||||
|
||||
glEnableClientState(GL_VERTEX_ARRAY);
|
||||
glEnableClientState(GL_COLOR_ARRAY);
|
||||
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
|
||||
glVertexPointer(3, GL_FLOAT, 0, cube_face);
|
||||
glColorPointer(4, GL_FLOAT, 0, cube_color);
|
||||
glTexCoordPointer(2, GL_FLOAT, 0, cube_texture);
|
||||
}
|
||||
|
||||
TexturedCube::~TexturedCube()
|
||||
{
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glEnable(GL_DITHER);
|
||||
glDisable(GL_CULL_FACE);
|
||||
glDisableClientState(GL_VERTEX_ARRAY);
|
||||
glDisableClientState(GL_COLOR_ARRAY);
|
||||
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
||||
assert(GL_NO_ERROR == glGetError());
|
||||
}
|
||||
|
||||
void
|
||||
TexturedCube::resize(uint32_t uWidth, uint32_t uHeight)
|
||||
{
|
||||
glm::mat4 matProj;
|
||||
glViewport(0, 0, uWidth, uHeight);
|
||||
|
||||
glMatrixMode( GL_PROJECTION );
|
||||
matProj = glm::perspective(glm::radians(45.f),
|
||||
uWidth / (float)uHeight,
|
||||
1.f, 100.f);
|
||||
glLoadIdentity();
|
||||
glLoadMatrixf(glm::value_ptr(matProj));
|
||||
|
||||
glMatrixMode( GL_MODELVIEW );
|
||||
assert(GL_NO_ERROR == glGetError());
|
||||
}
|
||||
|
||||
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 );
|
||||
|
||||
glLoadIdentity();
|
||||
glLoadMatrixf(glm::value_ptr(matView));
|
||||
|
||||
/* Draw */
|
||||
glClear( GL_COLOR_BUFFER_BIT );
|
||||
|
||||
glDrawElements(GL_TRIANGLES, CUBE_NUM_INDICES,
|
||||
GL_UNSIGNED_SHORT, cube_index_buffer);
|
||||
|
||||
assert(GL_NO_ERROR == glGetError());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2018-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file TexturedCube.cpp
|
||||
* @brief Draw a textured cube.
|
||||
*/
|
||||
|
||||
#ifndef TEXTURED_CUBE_H
|
||||
#define TEXTURED_CUBE_H
|
||||
|
||||
#include <GLES/gl.h>
|
||||
#include <GLES/glext.h>
|
||||
|
||||
#include "LoadTestSample.h"
|
||||
class TexturedCube : public LoadTestSample {
|
||||
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);
|
||||
};
|
||||
|
||||
#endif /* TEXTURED_CUBE_H */
|
||||
@@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2015-2024 Mark Callow
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
WARNING: Do not attempt to edit the Info.plist you see in the Xcode
|
||||
project. It is unavoidably configured from this by CMake. Edit here.
|
||||
-->
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>NSMainNibFile</key>
|
||||
<string></string>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<string>YES</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "iphone",
|
||||
"filename" : "launch-portrait-iphone@x2.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"extent" : "full-screen",
|
||||
"idiom" : "iphone",
|
||||
"subtype" : "retina4",
|
||||
"filename" : "launch-portrait-iphone@r4.png",
|
||||
"minimum-system-version" : "7.0",
|
||||
"orientation" : "portrait",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-portrait@x1.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-landscape@x1.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"orientation" : "portrait",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-portrait@2x.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"orientation" : "landscape",
|
||||
"idiom" : "ipad",
|
||||
"filename" : "launch-landscape@x2.png",
|
||||
"extent" : "full-screen",
|
||||
"minimum-system-version" : "7.0",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2017-2020 The Khronos Group Inc.
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="32700.99.1234" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
||||
<device id="retina6_12" orientation="portrait" appearance="light"/>
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="22685"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" misplaced="YES" textAlignment="center" lineBreakMode="middleTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
|
||||
<rect key="frame" x="20" y="300" width="560" height="88"/>
|
||||
<string key="text">KTX Texture
|
||||
OpenGL ${version} Loader Tests</string>
|
||||
<fontDescription key="fontDescription" name="HelveticaNeue" family="Helvetica Neue" pointSize="36"/>
|
||||
<color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<nil key="highlightedColor"/>
|
||||
<color key="shadowColor" red="0.66666668653488159" green="0.66666668653488159" blue="0.66666668653488159" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<size key="shadowOffset" width="1" height="2"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Ze5-6b-2t3" secondAttribute="leadingMargin" id="7go-Ai-m2p"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="trailing" secondItem="Ze5-6b-2t3" secondAttribute="trailingMargin" id="G9l-Nb-7mW"/>
|
||||
<constraint firstItem="GJd-Yh-RWb" firstAttribute="top" secondItem="Llm-lL-Icb" secondAttribute="bottom" constant="162" id="PSH-VH-8d0"/>
|
||||
<constraint firstItem="xb3-aO-Qok" firstAttribute="top" secondItem="GJd-Yh-RWb" secondAttribute="bottom" constant="325" id="U3h-5B-haO"/>
|
||||
</constraints>
|
||||
</view>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
@@ -0,0 +1,30 @@
|
||||
# Copyright 2023 Khronos Group, Inc.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
# If this file's extension is .desktop DO NOT edit it. It is generated from
|
||||
# glloadtests.desktop.in.
|
||||
#
|
||||
# If the extension is .in feel free to edit it. After editing you MUST
|
||||
# run cmake project configuration again to generate updated output files.
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Name=${version} Load Tests
|
||||
Name[ja]=${version} Load Tests
|
||||
Name[fr]=${version} Load Tests
|
||||
GenericName=KTX loader tests and file viewer
|
||||
GenericName[ja]=KTXローダーテストとファイルビューア
|
||||
GenericName[fr]=Tests de chargeur KTX et visionneuse de fichiers
|
||||
Comment=View KTX files using OpenGL or run GLUpload functionality tests.
|
||||
Comment[fr]=Affichez les fichiers KTX à l'aide d'OpenGL ou exécutez des tests de fonctionnalité GLUpload.
|
||||
Comment[ja]=OpenGLを使用してKTXファイルを表示するか、GLUpload機能テストを実行します。
|
||||
TryExec=${target}
|
||||
Exec=${target} %F
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Keywords=Ktx;Texture;Viewer
|
||||
Keywords[ja]=Ktx;Texture;Viewer
|
||||
Keywords[fr]=Ktx;Texture;Viewer
|
||||
Icon=${TARGET_INSTALL_RESOURCES_FULL_DIR}/ktx_app.svg
|
||||
Categories=Utility;
|
||||
StartupNotify=true
|
||||
MimeType=image/ktx;image/ktx2;
|
||||
@@ -0,0 +1,79 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
Copyright 2015-2024 Mark Callow
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
WARNING: Do not attempt to edit the Info.plist you see in the Xcode
|
||||
project. It is unavoidably configured from this by CMake. Edit here.
|
||||
-->
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
|
||||
<key>CFBundleGetInfoString</key>
|
||||
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
|
||||
<key>NSHumanReadableCopyright</key>
|
||||
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>en</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>LSIsAppleDefaultForType</key>
|
||||
<false/>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>CFBundleTypeIconFile</key>
|
||||
<string>${KTX_DOC_ICON}</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>public.ktx2</string>
|
||||
<string>public.ktx</string>
|
||||
</array>
|
||||
<key>CFBundleTypeExtensions</key>
|
||||
<array>
|
||||
<string>ktx</string>
|
||||
<string>ktx2</string>
|
||||
</array>
|
||||
<key>CFBundleTypeMIMETypes</key>
|
||||
<array>
|
||||
<string>image/ktx</string>
|
||||
<string>image/ktx2</string>
|
||||
</array>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>KTX texture file</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSTypeIsPackage</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</array>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.graphics-design</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
<key>NSMainNibFile</key>
|
||||
<string></string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -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"
|
||||
"}";
|
||||
|
||||
/* ----------------------------------------------------------------------------- */
|
||||
@@ -0,0 +1,516 @@
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#else
|
||||
#endif
|
||||
|
||||
// Emscripten assimp port not yet available.
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
#include <assimp/Importer.hpp>
|
||||
#include <assimp/scene.h>
|
||||
#include <assimp/postprocess.h>
|
||||
#endif
|
||||
|
||||
#include "disable_glm_warnings.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include "reenable_warnings.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <android/asset_manager.h>
|
||||
#endif
|
||||
|
||||
//#include <glad/glad.h>
|
||||
|
||||
namespace glMeshLoader
|
||||
{
|
||||
typedef enum VertexLayout {
|
||||
VERTEX_LAYOUT_POSITION = 0x0,
|
||||
VERTEX_LAYOUT_NORMAL = 0x1,
|
||||
VERTEX_LAYOUT_COLOR = 0x2,
|
||||
VERTEX_LAYOUT_UV = 0x3,
|
||||
VERTEX_LAYOUT_TANGENT = 0x4,
|
||||
VERTEX_LAYOUT_BITANGENT = 0x5,
|
||||
VERTEX_LAYOUT_DUMMY_FLOAT = 0x6,
|
||||
VERTEX_LAYOUT_DUMMY_VEC4 = 0x7
|
||||
} VertexLayout;
|
||||
|
||||
struct MeshBufferInfo
|
||||
{
|
||||
GLuint name = 0;
|
||||
size_t size = 0;
|
||||
};
|
||||
|
||||
struct MeshBuffer
|
||||
{
|
||||
GLuint vao = 0;
|
||||
MeshBufferInfo vertices;
|
||||
MeshBufferInfo indices;
|
||||
GLuint primitiveType;
|
||||
uint32_t indexCount = 0;
|
||||
glm::vec3 dim;
|
||||
glm::mat4 modelTransform; // To display the model correctly in the
|
||||
// GL coordinate system.
|
||||
|
||||
void FreeGLResources() {
|
||||
if (vertices.name)
|
||||
glDeleteBuffers(1, &vertices.name);
|
||||
if (indices.name)
|
||||
glDeleteBuffers(1, &indices.name);
|
||||
if (vao)
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
}
|
||||
|
||||
~MeshBuffer() {
|
||||
FreeGLResources();
|
||||
}
|
||||
|
||||
glm::mat4& getModelTransform() { return modelTransform; }
|
||||
|
||||
void Draw() {
|
||||
glBindVertexArray(vao);
|
||||
glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, 0);
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
};
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// Get vertex size from vertex layout
|
||||
static uint32_t vertexSize(std::vector<glMeshLoader::VertexLayout> layout)
|
||||
{
|
||||
uint32_t vSize = 0;
|
||||
for (auto& layoutDetail : layout)
|
||||
{
|
||||
switch (layoutDetail)
|
||||
{
|
||||
// UV only has two components
|
||||
case VERTEX_LAYOUT_UV:
|
||||
vSize += 2 * sizeof(float);
|
||||
break;
|
||||
default:
|
||||
vSize += 3 * sizeof(float);
|
||||
}
|
||||
}
|
||||
return vSize;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !defined(__EMSCRIPTEN__)
|
||||
// Simple mesh class for getting all the necessary stuff from models
|
||||
// loaded via ASSIMP.
|
||||
class GLMeshLoader {
|
||||
private:
|
||||
struct Vertex
|
||||
{
|
||||
glm::vec3 m_pos;
|
||||
glm::vec2 m_tex;
|
||||
glm::vec3 m_normal;
|
||||
glm::vec3 m_color;
|
||||
glm::vec3 m_tangent;
|
||||
glm::vec3 m_binormal;
|
||||
|
||||
Vertex() {}
|
||||
|
||||
Vertex(const glm::vec3& pos, const glm::vec2& tex, const glm::vec3& normal, const glm::vec3& tangent, const glm::vec3& bitangent, const glm::vec3& color)
|
||||
{
|
||||
m_pos = pos;
|
||||
m_tex = tex;
|
||||
m_normal = normal;
|
||||
m_color = color;
|
||||
m_tangent = tangent;
|
||||
m_binormal = bitangent;
|
||||
}
|
||||
};
|
||||
|
||||
struct MeshEntry {
|
||||
uint32_t NumIndices;
|
||||
uint32_t MaterialIndex;
|
||||
uint32_t vertexBase;
|
||||
uint32_t primitiveType;
|
||||
std::vector<Vertex> Vertices;
|
||||
std::vector<unsigned int> Indices;
|
||||
};
|
||||
|
||||
aiMatrix4x4 rootTransform;
|
||||
|
||||
public:
|
||||
#if defined(__ANDROID__)
|
||||
AAssetManager* assetManager = nullptr;
|
||||
#endif
|
||||
|
||||
std::vector<MeshEntry> m_Entries;
|
||||
|
||||
struct Dimension
|
||||
{
|
||||
glm::vec3 min = glm::vec3(FLT_MAX);
|
||||
glm::vec3 max = glm::vec3(-FLT_MAX);
|
||||
glm::vec3 size;
|
||||
} dim;
|
||||
|
||||
uint32_t numVertices = 0;
|
||||
|
||||
Assimp::Importer importer;
|
||||
const aiScene* pScene;
|
||||
|
||||
~GLMeshLoader()
|
||||
{
|
||||
m_Entries.clear();
|
||||
}
|
||||
|
||||
// Load a mesh with some default flags
|
||||
void LoadMesh(const std::string& filename)
|
||||
{
|
||||
int flags = aiProcess_Triangulate | aiProcess_PreTransformVertices
|
||||
| aiProcess_JoinIdenticalVertices
|
||||
| aiProcess_CalcTangentSpace
|
||||
| aiProcess_GenSmoothNormals;
|
||||
|
||||
LoadMesh(filename, flags);
|
||||
}
|
||||
|
||||
// Load the mesh with custom flags
|
||||
void LoadMesh(const std::string& filename, int flags)
|
||||
{
|
||||
#if defined(__ANDROID__)
|
||||
// Meshes are stored inside the apk on Android (compressed)
|
||||
// So they need to be loaded via the asset manager
|
||||
|
||||
AAsset* asset = AAssetManager_open(assetManager, filename.c_str(),
|
||||
AASSET_MODE_STREAMING);
|
||||
assert(asset);
|
||||
size_t size = AAsset_getLength(asset);
|
||||
|
||||
assert(size > 0);
|
||||
|
||||
void *meshData = malloc(size);
|
||||
AAsset_read(asset, meshData, size);
|
||||
AAsset_close(asset);
|
||||
|
||||
pScene = importer.ReadFileFromMemory(meshData, size, flags);
|
||||
|
||||
free(meshData);
|
||||
#else
|
||||
pScene = importer.ReadFile(filename.c_str(), flags);
|
||||
#endif
|
||||
if(!pScene || pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE
|
||||
|| !pScene->mRootNode)
|
||||
{
|
||||
std::stringstream message;
|
||||
|
||||
message << " Import via ASSIMP from\"" << filename << "\" failed. "
|
||||
<< importer.GetErrorString() << std::endl;
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
|
||||
InitFromScene(pScene, filename);
|
||||
#if 0
|
||||
// retrieve the directory path of the filepath
|
||||
directory = path.substr(0, path.find_last_of('/'));
|
||||
|
||||
// process ASSIMP's root node recursively
|
||||
processNode(scene->mRootNode, scene);
|
||||
#endif
|
||||
rootTransform = pScene->mRootNode->mTransformation;
|
||||
}
|
||||
|
||||
void InitFromScene(const aiScene* pSrcScene, const std::string& /*filename*/)
|
||||
{
|
||||
m_Entries.resize(pSrcScene->mNumMeshes);
|
||||
|
||||
// Counters
|
||||
for (unsigned int i = 0; i < m_Entries.size(); i++)
|
||||
{
|
||||
m_Entries[i].vertexBase = numVertices;
|
||||
numVertices += pSrcScene->mMeshes[i]->mNumVertices;
|
||||
}
|
||||
|
||||
// Initialize the meshes in the scene one by one
|
||||
for (unsigned int i = 0; i < m_Entries.size(); i++)
|
||||
{
|
||||
const aiMesh* paiMesh = pSrcScene->mMeshes[i];
|
||||
InitMesh(i, paiMesh, pSrcScene);
|
||||
}
|
||||
}
|
||||
|
||||
void InitMesh(unsigned int index, const aiMesh* paiMesh, const aiScene* pSrcScene)
|
||||
{
|
||||
m_Entries[index].MaterialIndex = paiMesh->mMaterialIndex;
|
||||
|
||||
aiColor3D pColor(0.f, 0.f, 0.f);
|
||||
pSrcScene->mMaterials[paiMesh->mMaterialIndex]->Get(AI_MATKEY_COLOR_DIFFUSE, pColor);
|
||||
|
||||
aiVector3D Zero3D(0.0f, 0.0f, 0.0f);
|
||||
|
||||
for (unsigned int i = 0; i < paiMesh->mNumVertices; i++) {
|
||||
aiVector3D* pPos = &(paiMesh->mVertices[i]);
|
||||
aiVector3D* pNormal = &(paiMesh->mNormals[i]);
|
||||
aiVector3D *pTexCoord;
|
||||
if (paiMesh->HasTextureCoords(0))
|
||||
{
|
||||
pTexCoord = &(paiMesh->mTextureCoords[0][i]);
|
||||
}
|
||||
else {
|
||||
pTexCoord = &Zero3D;
|
||||
}
|
||||
aiVector3D* pTangent = (paiMesh->HasTangentsAndBitangents()) ?
|
||||
&(paiMesh->mTangents[i]) : &Zero3D;
|
||||
aiVector3D* pBiTangent = (paiMesh->HasTangentsAndBitangents()) ?
|
||||
&(paiMesh->mBitangents[i]) : &Zero3D;
|
||||
|
||||
Vertex v(glm::vec3(pPos->x, -pPos->y, pPos->z),
|
||||
glm::vec2(pTexCoord->x , pTexCoord->y),
|
||||
glm::vec3(pNormal->x, pNormal->y, pNormal->z),
|
||||
glm::vec3(pTangent->x, pTangent->y, pTangent->z),
|
||||
glm::vec3(pBiTangent->x, pBiTangent->y, pBiTangent->z),
|
||||
glm::vec3(pColor.r, pColor.g, pColor.b)
|
||||
);
|
||||
|
||||
dim.max.x = fmax(pPos->x, dim.max.x);
|
||||
dim.max.y = fmax(pPos->y, dim.max.y);
|
||||
dim.max.z = fmax(pPos->z, dim.max.z);
|
||||
|
||||
dim.min.x = fmin(pPos->x, dim.min.x);
|
||||
dim.min.y = fmin(pPos->y, dim.min.y);
|
||||
dim.min.z = fmin(pPos->z, dim.min.z);
|
||||
|
||||
m_Entries[index].Vertices.push_back(v);
|
||||
}
|
||||
|
||||
dim.size = dim.max - dim.min;
|
||||
|
||||
for (unsigned int i = 0; i < paiMesh->mNumFaces; i++)
|
||||
{
|
||||
const aiFace& Face = paiMesh->mFaces[i];
|
||||
if (Face.mNumIndices != 3)
|
||||
continue;
|
||||
m_Entries[index].Indices.push_back(Face.mIndices[0]);
|
||||
m_Entries[index].Indices.push_back(Face.mIndices[1]);
|
||||
m_Entries[index].Indices.push_back(Face.mIndices[2]);
|
||||
}
|
||||
|
||||
switch (paiMesh->mPrimitiveTypes) {
|
||||
case aiPrimitiveType_POINT:
|
||||
m_Entries[index].primitiveType = GL_POINTS ;
|
||||
break;
|
||||
case aiPrimitiveType_LINE:
|
||||
m_Entries[index].primitiveType = GL_LINES;
|
||||
break;
|
||||
case aiPrimitiveType_TRIANGLE:
|
||||
m_Entries[index].primitiveType = GL_TRIANGLES;
|
||||
break;
|
||||
default: assert(false); // Shouldn't happen because of triangulate.
|
||||
}
|
||||
}
|
||||
|
||||
void CreateBuffers(glMeshLoader::MeshBuffer& meshBuffer,
|
||||
std::vector<glMeshLoader::VertexLayout> layout,
|
||||
float scale)
|
||||
{
|
||||
std::vector<float> vertexBuffer;
|
||||
for (uint32_t m = 0; m < m_Entries.size(); m++)
|
||||
{
|
||||
for (uint32_t i = 0; i < m_Entries[m].Vertices.size(); i++)
|
||||
{
|
||||
// Push vertex data depending on layout
|
||||
for (auto& layoutDetail : layout)
|
||||
{
|
||||
// Position
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_POSITION)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.x * scale);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.y * scale);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_pos.z * scale);
|
||||
}
|
||||
// Normal
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_NORMAL)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_normal.x);
|
||||
vertexBuffer.push_back(-m_Entries[m].Vertices[i].m_normal.y);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_normal.z);
|
||||
}
|
||||
// Texture coordinates
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_UV)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tex.s);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tex.t);
|
||||
}
|
||||
// Color
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_COLOR)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.r);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.g);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_color.b);
|
||||
}
|
||||
// Tangent
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_TANGENT)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.x);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.y);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_tangent.z);
|
||||
}
|
||||
// Bitangent
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_BITANGENT)
|
||||
{
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.x);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.y);
|
||||
vertexBuffer.push_back(m_Entries[m].Vertices[i].m_binormal.z);
|
||||
}
|
||||
// Dummy layout components for padding
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_DUMMY_FLOAT)
|
||||
{
|
||||
vertexBuffer.push_back(0.0f);
|
||||
}
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_DUMMY_VEC4)
|
||||
{
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
meshBuffer.vertices.size = vertexBuffer.size() * sizeof(float);
|
||||
|
||||
dim.min *= scale;
|
||||
dim.max *= scale;
|
||||
dim.size *= scale;
|
||||
|
||||
meshBuffer.modelTransform[0][0] = rootTransform.a1;
|
||||
meshBuffer.modelTransform[0][1] = rootTransform.b1;
|
||||
meshBuffer.modelTransform[0][2] = rootTransform.c1;
|
||||
meshBuffer.modelTransform[0][3] = rootTransform.d1;
|
||||
meshBuffer.modelTransform[1][0] = rootTransform.a2;
|
||||
meshBuffer.modelTransform[1][1] = rootTransform.b2;
|
||||
meshBuffer.modelTransform[1][2] = rootTransform.c2;
|
||||
meshBuffer.modelTransform[1][3] = rootTransform.d2;
|
||||
meshBuffer.modelTransform[2][0] = rootTransform.a3;
|
||||
meshBuffer.modelTransform[2][1] = rootTransform.b3;
|
||||
meshBuffer.modelTransform[2][2] = rootTransform.c3;
|
||||
meshBuffer.modelTransform[2][3] = rootTransform.d3;
|
||||
meshBuffer.modelTransform[3][0] = rootTransform.a4;
|
||||
meshBuffer.modelTransform[3][1] = rootTransform.b4;
|
||||
meshBuffer.modelTransform[3][2] = rootTransform.c4;
|
||||
meshBuffer.modelTransform[3][3] = rootTransform.d4;
|
||||
|
||||
std::vector<uint32_t> indexBuffer;
|
||||
for (uint32_t m = 0; m < m_Entries.size(); m++)
|
||||
{
|
||||
uint32_t indexBase = (uint32_t)indexBuffer.size();
|
||||
for (uint32_t i = 0; i < m_Entries[m].Indices.size(); i++)
|
||||
{
|
||||
indexBuffer.push_back(m_Entries[m].Indices[i] + indexBase);
|
||||
}
|
||||
}
|
||||
meshBuffer.indices.size = indexBuffer.size() * sizeof(uint32_t);
|
||||
meshBuffer.indexCount = (uint32_t)indexBuffer.size();
|
||||
meshBuffer.primitiveType = m_Entries[0].primitiveType;
|
||||
|
||||
// Make the GL buffers
|
||||
|
||||
glGenVertexArrays(1, &meshBuffer.vao);
|
||||
glBindVertexArray(meshBuffer.vao);
|
||||
|
||||
// Setup vertices.
|
||||
glGenBuffers(1, &meshBuffer.vertices.name);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, meshBuffer.vertices.name);
|
||||
|
||||
// Create the buffer data store and upload the vertices.
|
||||
glBufferData(GL_ARRAY_BUFFER, meshBuffer.vertices.size,
|
||||
vertexBuffer.data(), GL_STATIC_DRAW);
|
||||
|
||||
GLuint attribNumber = 0;
|
||||
GLsizeiptr attribOffset = 0;
|
||||
GLsizei stride = vertexSize(layout);
|
||||
for (auto& layoutDetail : layout)
|
||||
{
|
||||
// Position
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_POSITION) {
|
||||
glEnableVertexAttribArray(attribNumber);
|
||||
glVertexAttribPointer(attribNumber, 3, GL_FLOAT, GL_FALSE,
|
||||
stride, (void *)attribOffset);
|
||||
attribOffset += sizeof(float) * 3;
|
||||
attribNumber++;
|
||||
}
|
||||
// Normal
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_NORMAL) {
|
||||
glEnableVertexAttribArray(attribNumber);
|
||||
glVertexAttribPointer(attribNumber, 3, GL_FLOAT, GL_FALSE,
|
||||
stride, (void *)attribOffset);
|
||||
attribOffset += sizeof(float) * 3;
|
||||
attribNumber++;
|
||||
}
|
||||
// Texture coordinates
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_UV) {
|
||||
glEnableVertexAttribArray(attribNumber);
|
||||
glVertexAttribPointer(attribNumber, 2, GL_FLOAT, GL_FALSE,
|
||||
stride, (void *)attribOffset);
|
||||
attribOffset += sizeof(float) * 2;
|
||||
attribNumber++;
|
||||
}
|
||||
// Color
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_COLOR) {
|
||||
glEnableVertexAttribArray(attribNumber);
|
||||
glVertexAttribPointer(attribNumber, 3, GL_FLOAT, GL_FALSE,
|
||||
stride, (void *)attribOffset);
|
||||
attribOffset += sizeof(float) * 3;
|
||||
attribNumber++;
|
||||
}
|
||||
// Tangent
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_TANGENT) {
|
||||
glEnableVertexAttribArray(attribNumber);
|
||||
glVertexAttribPointer(attribNumber, 3, GL_FLOAT, GL_FALSE,
|
||||
stride, (void *)attribOffset);
|
||||
attribOffset += sizeof(float) * 3;
|
||||
attribNumber++;
|
||||
}
|
||||
// Bitangent
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_BITANGENT) {
|
||||
glEnableVertexAttribArray(attribNumber);
|
||||
glVertexAttribPointer(attribNumber, 3, GL_FLOAT, GL_FALSE,
|
||||
stride, (void *)attribOffset);
|
||||
attribOffset += sizeof(float) * 3;
|
||||
attribNumber++;
|
||||
}
|
||||
// Dummy layout components for padding
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_DUMMY_FLOAT) {
|
||||
vertexBuffer.push_back(0.0f);
|
||||
}
|
||||
if (layoutDetail == glMeshLoader::VERTEX_LAYOUT_DUMMY_VEC4) {
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
vertexBuffer.push_back(0.0f);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup indices
|
||||
glGenBuffers(1, &meshBuffer.indices.name);
|
||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, meshBuffer.indices.name);
|
||||
// Create the buffer data store and upload the elements.
|
||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, meshBuffer.indices.size,
|
||||
indexBuffer.data(), GL_STATIC_DRAW);
|
||||
|
||||
glBindVertexArray(0);
|
||||
}
|
||||
};
|
||||
#endif // !defined(__EMSCRIPTEN__)
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
/* -*- tab-width: 4; -*- */
|
||||
/* vi: set sw=2 ts=4 expandtab: */
|
||||
|
||||
/*
|
||||
* Copyright 2017-2020 Mark Callow.
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <ktx.h>
|
||||
|
||||
#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
|
||||
|
||||
class TextureTranscoder {
|
||||
public:
|
||||
TextureTranscoder() {
|
||||
determineCompressedTexFeatures(deviceFeatures);
|
||||
if (deviceFeatures.astc_ldr)
|
||||
defaultTf = KTX_TTF_ASTC_4x4_RGBA;
|
||||
else if (deviceFeatures.bc3)
|
||||
defaultTf = KTX_TTF_BC1_OR_3;
|
||||
else if (deviceFeatures.etc2)
|
||||
defaultTf = KTX_TTF_ETC; // Let transcoder decide RGB or RGBA
|
||||
else if (deviceFeatures.pvrtc1)
|
||||
defaultTf = KTX_TTF_PVRTC1_4_RGBA;
|
||||
else if (deviceFeatures.etc1)
|
||||
defaultTf = KTX_TTF_ETC1_RGB;
|
||||
else {
|
||||
std::stringstream message;
|
||||
|
||||
message << "OpenGL implementation does not support any available transcode target.";
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
void transcode(ktxTexture2* kTexture,
|
||||
ktx_transcode_fmt_e otf = KTX_TTF_NOSELECTION) {
|
||||
KTX_error_code ktxresult;
|
||||
ktx_transcode_fmt_e tf;
|
||||
if (otf != KTX_TTF_NOSELECTION) {
|
||||
tf = otf;
|
||||
} else {
|
||||
khr_df_model_e colorModel = ktxTexture2_GetColorModel_e(kTexture);
|
||||
if (colorModel == KHR_DF_MODEL_UASTC && deviceFeatures.astc_ldr) {
|
||||
tf = KTX_TTF_ASTC_4x4_RGBA;
|
||||
} else if (colorModel == KHR_DF_MODEL_ETC1S && deviceFeatures.etc2) {
|
||||
tf = KTX_TTF_ETC;
|
||||
} else {
|
||||
tf = defaultTf;
|
||||
}
|
||||
}
|
||||
ktxresult = ktxTexture2_TranscodeBasis(kTexture, tf, 0);
|
||||
if (KTX_SUCCESS != ktxresult) {
|
||||
std::stringstream message;
|
||||
|
||||
message << "Transcoding of ktxTexture2 to "
|
||||
<< ktxTranscodeFormatString(tf) << " failed: "
|
||||
<< ktxErrorString(ktxresult);
|
||||
throw std::runtime_error(message.str());
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
ktx_transcode_fmt_e defaultTf;
|
||||
|
||||
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;
|
||||
} deviceFeatures;
|
||||
|
||||
void 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;
|
||||
}
|
||||
|
||||
};
|
||||
Reference in New Issue
Block a user