#include "Graphics.h"
#include "SystemInformation.h"
#include "Shaders.h"
#include "Log.h"
#include "Debug.h"
#include <cstdlib>
#include <cstring>
#include <functional>
//#include "Font6.h"
//#include "Font7.h"
#include "PngImage.h"
#include "ResourceLoader.h"
#include "ObjModel.h"
#ifdef USE_OPENGLES2
#include <OpenGLES/ES2/gl.h>
#include <OpenGLES/ES2/glext.h>
#include <GLKit/GLKMatrix4.h>
#include <GLKit/GLKMatrix3.h>
#include <GLKit/GLKMathUtils.h>
#endif
#define TIME_BASED 1
#define SHADOW_DIM 2048 // 1024 // 2048 // 512
/*
Potential ways to slice the code here:
Program class abstraction around a glProgram
Model class which abstracts vertex data
Camera class
Clipping
HUD
Some concepts:
Models
Scene - composition of models (varying formats etc)
Camera - players perspective (is it part of the scene?)
Lights - lighting data (also, is it part of the scene?)
Effects - last with alpha in sorted order with z-test and no z-writes
Shaders
Program
HUD
Submit scene to be rendered by shaders from camera, also from lights
Then submit HUD over the top
For lights, need to know the camera to work out the frustrum to find a light frustum that fits
*/
bool toggleDebugCameraView = false;
bool debugCameraView = false;
bool debugCollisions = false;
bool showDebugging = false;
bool modelEditing = true;
#ifdef OLD_CAMERA
#if 0
const int cameraTransitionFrames = 30; // Fast for quicker debugging
const int cameraWaitFrames = 50; // Fast for quicker debugging
#else
const int cameraTransitionFrames = 150; // Nice value
const int cameraWaitFrames = 250;
#endif
int m_frame = 0;
#endif
const int vertSize = sizeof(PolyVertex);
const int fltsPerVert = vertSize / sizeof(GLfloat);
const int trisPerCube = 12;
const int vertsPerCube = 3*trisPerCube;
size_t m_hudVertexBufferSize = 0;
std::map<std::string,std::string> gDebugVariables;
void setDebugValue(const char* variableName, int val)
{
gDebugVariables[variableName] = std::to_string(val);
}
void setDebugValue(const char* variableName, float val)
{
gDebugVariables[variableName] = std::to_string(val);
}
void setDebugValue(const char* variableName, const char* val)
{
gDebugVariables[variableName] = std::string(val);
}
inline void addDebugString(GameUi::DrawItems& items, int line, int column, const char* string)
{
items.addContrastString(column*75, 90 + 19*line, string, 1);
items.addString( column*75, 90 + 19*line, string, 0xff00ff00, 1);
}
// One big triangle that covers the entire screen, each vertex is 2d (x,y)
GLfloat gOverlayVertexData[6] = { 1.0f, 1.0f, -3.0f, 1.0f, 1.0f, -3.0f };
GLfloat gBaseCubeVertexData[fltsPerVert * vertsPerCube];
uint32_t colorForObjectType(Game::ObjectType a_type)
{
uint32_t color = 0;
switch (a_type)
{
// Terrain
case Game::Ground: color = 0x47bd37; break;
case Game::Road: color = 0x6a6963; break;
case Game::Rail: color = 0x464441; break;
case Game::Water: color = 0x3a31f5; break;
case Game::WaterLane: color = 0x3a31f5; break;
// Obstacles
case Game::Tree: color = 0x508c23; break;
case Game::Shrub: color = 0x508c23; break;
case Game::Building: color = 0xd38b37; break;
case Game::Rocks: color = 0x5c4932; break;
// Vehicles
case Game::Bike: color = 0xdf1f24; break;
case Game::Car: color = 0xf52542; break;
case Game::Van: color = 0x3a4b88; break;
case Game::Truck: color = 0xdfd41f; break;
// Trains
case Game::Train1: color = 0x8bbb38; break;
case Game::Train2: color = 0xa2d051; break;
case Game::Train3: color = 0x6ca50a; break;
case Game::Train4: color = 0x0a53a5; break;
// Vessels
case Game::Log: color = 0x6b5331; break;
case Game::Boat: color = 0xd5d5d5; break;
case Game::Crocodile: color = 0x597e3e; break;
case Game::LilyPad: color = 0x71df1f; break;
case Game::SteppingStone: color = 0x71df1f; break;
case Game::Player: color = 0xffffff; break;
// Pickups
case Game::Coin: color = 0xffffff; break;
case Game::Food: color = 0xffffff; break;
case Game::Life: color = 0xffffff; break;
//case Bonus: color = 0xffffff; break;
default:
break;
}
return color;
}
void initCubeVerticesFromObject(PolyVertex* cubePtr, const Game::GameObject& obj, float scale)
{
uint32_t color = (obj.m_type == Game::Car) ? obj.m_color : colorForObjectType(obj.m_type);
for (int v = 0; v < 36; v++)
{
cubePtr->m_animParams.y = obj.m_w;
cubePtr->m_animParams.x = cubePtr->m_position.x - 0.5;
cubePtr->m_normal.x += cubePtr->m_position.x;
cubePtr->m_normal.y += cubePtr->m_position.y;
cubePtr->m_normal.z += cubePtr->m_position.z;
cubePtr->m_position.x *= obj.m_w*scale;
cubePtr->m_position.y *= obj.m_h*scale;
cubePtr->m_position.z *= (obj.m_d < 0.0) ? -obj.m_d*scale : obj.m_d*scale;
cubePtr->m_position.x += 0.5*obj.m_w*scale;
cubePtr->m_position.y += 0.5*obj.m_h*scale;
cubePtr->m_position.z += (obj.m_d < 0.0) ? -0.5*obj.m_d*scale : 0.5*obj.m_d*scale;
#if TIME_BASED
cubePtr->m_position.x += obj.m_x*scale;
cubePtr->m_position.y += obj.m_y*scale;
cubePtr->m_position.z += obj.m_z*scale;
#endif
cubePtr->m_delta.x = obj.m_dx;
cubePtr->m_delta.y = obj.m_dy;
cubePtr->m_delta.z = 0.0f;
cubePtr->m_color.r = GLfloat((color >> 16) & 0xff) / 255.0;
cubePtr->m_color.g = GLfloat((color >> 8) & 0xff) / 255.0;
cubePtr->m_color.b = GLfloat((color >> 0) & 0xff) / 255.0;
cubePtr->m_color.a = 1.0f;
cubePtr++;
}
}
void initCubeVerticesFromVoxel(PolyVertex* cubePtr, const VoxelBox& obj, float scale)
{
uint32_t color = obj.m_color;
for (int v = 0; v < 36; v++)
{
cubePtr->m_animParams.y = obj.m_w;
cubePtr->m_animParams.x = cubePtr->m_position.x - 0.5;
cubePtr->m_position.x *= obj.m_w*scale;
cubePtr->m_position.y *= obj.m_h*scale;
cubePtr->m_position.z *= (obj.m_d < 0.0) ? -obj.m_d*scale : obj.m_d*scale;
cubePtr->m_position.x += 0.5*obj.m_w*scale;
cubePtr->m_position.y += 0.5*obj.m_h*scale;
cubePtr->m_position.z += (obj.m_d < 0.0) ? -0.5*obj.m_d*scale : 0.5*obj.m_d*scale;
cubePtr->m_position.x += obj.m_x*scale;
cubePtr->m_position.y += obj.m_y*scale;
cubePtr->m_position.z += obj.m_z*scale;
cubePtr->m_delta.x = 0.0f;
cubePtr->m_delta.y = 0.0f;
cubePtr->m_delta.z = 0.0f;
cubePtr->m_color.r = GLfloat((color >> 16) & 0xff) / 255.0;
cubePtr->m_color.g = GLfloat((color >> 8) & 0xff) / 255.0;
cubePtr->m_color.b = GLfloat((color >> 0) & 0xff) / 255.0;
cubePtr->m_color.a = 1.0f;
cubePtr++;
}
}
void GameGraphics::resetVertexBuffers(const std::vector<Game::GameObjectList*>& a_objectLists /*,
const Game::GameObjectList& a_carModels*/ )
{
m_scene.reset(a_objectLists);
#ifdef OLD_CAMERA
m_frame = 0;
#endif
size_t m_cubeCount = 0;
m_cubeCount = m_scene.m_rest.size();
// for (Game::GameObjectList* objList : a_objectLists)
// if (objList->back().m_type != Game::Player)
// m_cubeCount += objList->size();
size_t m_triCount = 12 * m_cubeCount;
size_t vertCount = 3 * m_triCount;
m_staticPolyVertexArray.m_vertexData.clear();
m_staticPolyVertexArray.m_vertexData.resize(vertCount);
// A cube instance
// Probably don't need to do this every reset
Cube cube;
memcpy(gBaseCubeVertexData, cube.m_baseCubeData.data(), vertSize*vertsPerCube);
size_t index = 0;
/*
for (Game::GameObjectList* objList : a_objectLists)
{
if (objList->back().m_type != Game::Player)
for (unsigned i = 0; i < objList->size(); i++)
{
const Game::GameObject& obj = objList->at(i);
*/
{
for (const Game::GameObject& obj : m_scene.m_rest)
{
PolyVertex* cubePtr = &m_staticPolyVertexArray.m_vertexData[index*vertsPerCube];
memcpy(cubePtr, gBaseCubeVertexData, vertSize*vertsPerCube);
initCubeVerticesFromObject(cubePtr, obj, 1.0);
index++;
}
}
}
GameGraphics::GameGraphics()
{
}
GameGraphics::~GameGraphics()
{
}
// Uniform index.
enum
{
UNIFORM_HUD_TEXTURE,
UNIFORM_HUD_INV_SCREEN,
NUM_UNIFORMS
};
GLint uniforms[NUM_UNIFORMS];
// Attribute index.
enum
{
ATTRIB_VERTEX,
ATTRIB_COLOR,
ATTRIB_CHAR,
ATTRIB_TEXTURE_IDX,
NUM_ATTRIBUTES
};
struct Program
{
GLProgram m_program;
// ... uniforms ....
};
ObjScene frogModel;
//bool frogModelLoaded = false;
uint8_t hudTextureIdx = 0;
uint8_t frogModelTextureIdx = 0;
uint8_t vehiclesTextureIdx1 = 0;
uint8_t vehiclesTextureIdx2 = 0;
uint8_t vehiclesTextureIdx3 = 0;
uint8_t rampTextureIdx1 = 0;
uint8_t rampTextureIdx2 = 0;
uint8_t playersTextureIdx1 = 0;
uint8_t playersTextureIdx2 = 0;
uint8_t playersTextureIdx3 = 0;
void GameGraphics::reloadShaders()
{
// Setup main program
auto bindUniformsFunc = [this](GLuint a_program) { m_polyUniforms.getLocations(a_program); };
m_program.deleteProgram();
m_program.loadShaders(s_vertexShader, s_fragmentShader, Medium, m_options, m_staticPolyVertexArray.bindAttributesFunction(), bindUniformsFunc);
// Setup hud program
m_hudProgram.deleteProgram();
m_hudProgram.loadShaders(s_hudVertexShader, s_hudFragmentShader, Medium, m_options,
[](GLuint a_program) {
auto bindAttribFunc = [a_program](const char* name, GLuint index, GLsizei, GLenum, GLboolean, GLsizei, const void*) {
glBindAttribLocation(a_program, index, name);
};
GameUi::UiVertex::visit(bindAttribFunc);
}, [](GLuint a_program) {
uniforms[UNIFORM_HUD_TEXTURE] = glGetUniformLocation(a_program, "texture");
uniforms[UNIFORM_HUD_INV_SCREEN] = glGetUniformLocation(a_program, "invScreen");
});
// Setup shadow program
auto bindShadowUniformsFunc = [this](GLuint a_program) { m_shadowUniforms.getLocations(a_program); };
m_shadowProgram.deleteProgram();
m_shadowProgram.loadShaders(s_shadowVertexShader, s_shadowFragmentShader, Medium, m_options,
m_staticPolyVertexArray.bindAttributesFunction(), bindShadowUniformsFunc);
}
void objToVertexBuffer(std::vector<PolyVertex> &objVerts, const std::vector<Object>& obj)
{
size_t vertCount = 0;
for (const Object& o : obj)
vertCount += o.faces.size() * 3;
objVerts.resize(vertCount);
int x = 0;
for (const Object& o : frogModel.objects) {
for (const Face& f : o.faces) {
for (int i = 0; i < 3; i++) {
Position v = { 0.0, 0.0, 0.0 };
size_t vIdx = f.indices[i].vertexIndex;
if (vIdx && o.vertices.size() >= vIdx)
v = o.vertices[vIdx-1];
Normal n = { 0.0, 1.0, 0.0 };
size_t nIdx = f.indices[i].normalIndex;
if (nIdx && o.normals.size() >= nIdx)
n = o.normals[nIdx-1];
UV uv = { 0.0, 0.0 };
size_t uIdx = f.indices[i].uvIndex;
if (uIdx && o.uvs.size() >= uIdx)
uv = o.uvs[uIdx-1];
objVerts[x].m_animParams.x = 0.0;
objVerts[x].m_animParams.y = 0.0;
objVerts[x].m_animParams.z = 0.0;
// X,Y,Z
objVerts[x].m_position.x = -v.y * 1.5 + 3.0;
objVerts[x].m_position.y = v.x * 1.5 + 0.3;
objVerts[x].m_position.z = v.z * 1.5 + 6.0;
// DX,DY,DZ
objVerts[x].m_delta.x = 0.0;
objVerts[x].m_delta.y = 0.0;
objVerts[x].m_delta.z = 0.0;
// Normal
objVerts[x].m_normal.x = -n.y;
objVerts[x].m_normal.y = n.x;
objVerts[x].m_normal.z = n.z;
// RGB
objVerts[x].m_color.r = uv.u;
objVerts[x].m_color.g = 1.0 - uv.v;
objVerts[x].m_color.b = float(frogModelTextureIdx - 0);
objVerts[x].m_color.a = 0.0f;
// Barycentric
objVerts[x].m_barycentric.x = (i==0) ? 1.0 : 0.0;
objVerts[x].m_barycentric.y = (i==1) ? 1.0 : 0.0;
objVerts[x].m_barycentric.z = (i==2) ? 1.0 : 0.0;
x++;
}
}
}
}
void GameGraphics::initialize()
{
// Setup default options
m_options["debugTriangles"] = false;
m_options["enableShadows"] = true;//true;// false;
m_options["enableShadowAttenuation"] = true;//false;//true;
m_options["enableDistanceFog"] = false;//true;//false;//true;
m_options["enableRampShading"] = false;//true;//false;//true;
m_options["enableDarkenedSides"] = false;//true;//false;//true;
m_options["enableTextures"] = true;// true;//false;//true;
m_options["enableBevels"] = false;// true;//false;//true;
m_options["enableLighting"] = true;// true;//false;//true;
glEnable(GL_DEPTH_TEST);
//glClearDepthf(1.0f);
glClearColor(0.65f, 0.65f, 0.65f, 1.0f);
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&m_defaultFramebufferId);
//glGenFramebuffers(1, &m_defaultFramebufferId);
//glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebufferId);
// glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_shadowTexId, 0);
//glBindFramebuffer(GL_FRAMEBUFFER, 0);
//m_vertBufferSize = vertSize * m_staticPolyVertexArray.m_vertexData.size();
reloadShaders();
m_cuboidVertexList.createVertexBuffer();
m_staticPolyVertexArray.createVertexBuffer();
m_dynamicPolyVertexArray.createVertexBuffer();
for (int i = 0; i < MODEL_COUNT; i++)
m_modelsVertexArrays[i].createVertexBuffer();
for (int i = 0; i < PLAYER_MODEL_COUNT; i++)
m_playerModelsVertexArrays[i].createVertexBuffer();
bool okay = ReadObjFile(frogModel, "froggy.obj", [this](bool okay){
Log(LL_Info, "OBJ", "Loaded test model called back with %s", okay ? "okay" : "not okay");
if (!okay)
// Might want to set a flag to retry loading later
return;
// The conversion of the frog model from obj to triangle soup can be done once off
// Perhaps a uniform to position the frog as player
objToVertexBuffer(m_dynamicPolyVertexArray.m_vertexData, frogModel.objects);
m_dynamicPolyVertexArray.sync();
});
Log(LL_Info, "OBJ", "Loading test model started %s", okay ? "okay" : "not okay");
if (m_hudEnabled)
{
glGenVertexArraysOES(1, &m_auxVertexArray);
glBindVertexArrayOES(m_auxVertexArray);
glGenBuffers(1, &m_auxVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_auxVertexBuffer);
GameUi::UiVertex::visit(AttribPointerVisitor);
glGenVertexArraysOES(1, &m_hudVertexArray);
glBindVertexArrayOES(m_hudVertexArray);
glGenBuffers(1, &m_hudVertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_hudVertexBuffer);
GameUi::UiVertex::visit(AttribPointerVisitor);
glBindVertexArrayOES(0);
glGenTextures(1, &m_hudTexId);
glBindTexture(GL_TEXTURE_2D, m_hudTexId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
/*
std::vector<unsigned char> fontData;
unsigned long fontW, fontH;
int error = ::decodePNG(fontData, fontW, fontH, Font7_png, Font7_png_len);
if (error != 0 || fontW != 256 || fontH != 256)
printf("error: %d\n", error);
*/
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2048, 2048, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
//glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)&fontData[0]);
}
if (m_shadowsEnabled)
{
// Depth texture. Slower than a depth buffer, but you can sample it later in your shader
glGenTextures(1, &m_shadowTexId);
glBindTexture(GL_TEXTURE_2D, m_shadowTexId);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//GL_NEAREST);
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);//GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_EXT, GL_LEQUAL);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_EXT, GL_COMPARE_REF_TO_TEXTURE_EXT);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT16, 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_DIM, SHADOW_DIM, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
// glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, SHADOW_DIM, SHADOW_DIM, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1024, 1024, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glBindTexture(GL_TEXTURE_2D, 0);
/*
// create a renderbuffer object to store depth info
GLuint rboId;
glGenRenderbuffers(1, &rboId);
glBindRenderbuffer(GL_RENDERBUFFER, rboId);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, SHADOW_DIM, SHADOW_DIM);
*/
// The framebuffer, which regroups 0, 1, or more textures, and 0 or 1 depth buffer.
glGenFramebuffers(1, &m_shadowFramebufferId);
glBindFramebuffer(GL_FRAMEBUFFER, m_shadowFramebufferId);
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rboId);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, m_shadowTexId, 0);
//glBindRenderbuffer(GL_RENDERBUFFER, 0);
//glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, 1024, 1024);
//glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_shadowFramebufferId);
//GLenum bufs[1] = {GL_NONE};
//glDrawBuffers(1, bufs); // No color buffer is drawn to.
/*
#ifndef USE_OPENGLES2
// gllsBuffer ?
glDrawBuffer(GL_NONE); // No color buffer is drawn to.
glReadBuffer(GL_NONE); // No color buffer is read from.
#endif
*/
// Always check that our framebuffer is ok
GLenum res = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (res != GL_FRAMEBUFFER_COMPLETE)
{
Log(LL_Error, "blah", "Error setting up FBO, error: %i\n", res);
}
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// This finds the texture of the frog model, loads it from file, then loads it to the GPU in a texture slot
GameUi::DrawItems items;
//frogModelTextureIdx = items.getTextureIdForAsset(frogModel.materialLibrary[frogModel.objects[0].materialName].map_Kd.c_str());
//vehiclesTextureIdx = items.getTextureIdForAsset("models-texture.png");
hudTextureIdx = items.getTextureIdForAsset("ui.png");
vehiclesTextureIdx1 = items.getTextureIdForAsset("models.png");
rampTextureIdx1 = items.getTextureIdForAsset("ramp.png");
//rampTextureIdx2 = items.getTextureIdForAsset("ramp2.png");
rampTextureIdx2 = items.getTextureIdForAsset("tile-map.png");
frogModelTextureIdx = items.getTextureIdForAsset("froggy-spots.png");
frogModelTextureIdx = items.getTextureIdForAsset("froggy-strips.png");
frogModelTextureIdx = items.getTextureIdForAsset("froggy-common.png");
for (auto job : items.loadTexturesQueue)
LoadPendingTexture(job);
// Just reserve these texture slots, don't actually load anything in to them
vehiclesTextureIdx2 = items.getTextureIdForAsset("models-x-axis");
vehiclesTextureIdx3 = items.getTextureIdForAsset("models-z-axis");
playersTextureIdx1 = items.getTextureIdForAsset("player-models.png");
playersTextureIdx2 = items.getTextureIdForAsset("player-models-x-axis");
playersTextureIdx3 = items.getTextureIdForAsset("player-models-z-axis");
m_models.reloadDataFromFile("models.png", true);
m_playerModels.reloadDataFromFile("player-models.png", true);
}
void GameGraphics::shutdown()
{
m_staticPolyVertexArray.destroyVertexBuffer();
m_dynamicPolyVertexArray.destroyVertexBuffer();
m_cuboidVertexList.destroyVertexBuffer();
// TODO: car models
glDeleteVertexArraysOES(1, &m_hudVertexArray);
m_program.deleteProgram();
}
// May need a way to collect unused textures to free up space
void GameGraphics::LoadPendingTexture(GameUi::TextureLoadJob& job)
{
int idx = job.texIdx;
//Resource* res = LoadFile(job.assetName.c_str(), true);
LoadFileAsync(job.assetName.c_str(), true, [idx, this](Resource* res) {
if (res && res->data.size() > 1) {
glBindTexture(GL_TEXTURE_2D, m_hudTexId);
std::vector<unsigned char> texData;
unsigned long texW, texH;
int error = ::decodePNG(texData, texW, texH, res->data.data(), res->data.size());
if (error != 0 || texW != 256 || texH != 256)
printf("error: %d\n", error);
// job.texIdx;
glTexSubImage2D(GL_TEXTURE_2D, 0, (idx%8)*256, (idx/8)*256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)&texData[0]);
// glTexSubImage2D(GL_TEXTURE_2D, 0, 256, 0, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)&texData[0]);
}
});
}
void GameGraphics::loadVoxelModel(VertexArray<PolyVertex>& vertexArray, const char* name, int x, int y, int w, int h, int d, bool direction, int scaling, bool asCubes,
int yClamp0, int yClamp1)
{
float modelScale = 1.0f;
// int scaling = 8;
// loading asCubes is inefficient, but could have benefits if voxel editing just one model
if (asCubes)
{
VoxelBoxList& voxelModel = m_models.getModel(name, x, y, w, h, d, direction, m_model_x_axis, m_model_z_axis);
vertexArray.m_vertexData.clear();
int size = 0;
for (unsigned i = 0; i < voxelModel.size(); i++)
if (voxelModel.at(i).m_z >= yClamp0 && voxelModel.at(i).m_z < yClamp1)
size++;
vertexArray.m_vertexData.resize(size * 12 * 3);
int cube = 0;
for (unsigned i = 0; i < voxelModel.size(); i++)
{
if (voxelModel.at(i).m_z >= yClamp0 && voxelModel.at(i).m_z < yClamp1)
{
PolyVertex* cubePtr = &vertexArray.m_vertexData[cube*vertsPerCube];
memcpy(cubePtr, gBaseCubeVertexData, vertSize*vertsPerCube);
initCubeVerticesFromVoxel(cubePtr, voxelModel.at(i), modelScale);
cube++;
}
}
return;
}
if (scaling == 2)
{
modelScale = 2.0f;
x /= 2;
x += 128;
y /= 2;
y += 128;
w /= 2;
h /= 2;
d /= 2;
}
else if (scaling == 4)
{
modelScale = 4.0f;
x /= 4;
x += 128 + 64;
y /= 4;
y += 128 + 64;
w /= 4;
h /= 4;
d /= 4;
}
else if (scaling == 8)
{
modelScale = 8.0f;
x /= 8;
x += 128 + 64 + 32;
y /= 8;
y += 128 + 64 + 32;
w /= 8;
h /= 8;
d /= 8;
}
// TODO: is this needed?
if (m_model_x_axis == nullptr) {
m_model_x_axis = new uint32_t[256*256];
for (int j = 0; j < 256; j++)
for (int i = 0; i < 256; i++)
m_model_x_axis[j*256+i] = (0xff<<24)|(i<<16)|(j<<8);
glBindTexture(GL_TEXTURE_2D, m_hudTexId);
glTexSubImage2D(GL_TEXTURE_2D, 0, (vehiclesTextureIdx2%8)*256, (vehiclesTextureIdx2/8)*256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)m_model_x_axis);
}
if (m_model_z_axis == nullptr) {
m_model_z_axis = new uint32_t[256*256];
for (int j = 0; j < 256; j++)
for (int i = 0; i < 256; i++)
m_model_x_axis[j*256+i] = (0xff<<24)|(i<<8)|j;
glBindTexture(GL_TEXTURE_2D, m_hudTexId);
glTexSubImage2D(GL_TEXTURE_2D, 0, (vehiclesTextureIdx3%8)*256, (vehiclesTextureIdx3/8)*256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)m_model_z_axis);
}
VoxelTriangleList& voxelModel = m_models.getModelTriangles(name, x, y, w, h, d, direction, m_model_x_axis, m_model_z_axis);
vertexArray.m_vertexData.clear();
vertexArray.m_vertexData.resize(voxelModel.size() * 3);
unsigned i = 0;
for (unsigned t = 0; t < voxelModel.size(); t++)
{
const VoxelTriangle& tri = voxelModel.at(t);
if (tri.m_verts[0].m_z >= yClamp0 && tri.m_verts[0].m_z < yClamp1) {
// lookup the normal based on the axis
float normals[6][4] = {
{ 0.0,0.0,-1.0,0.0 },
{ -1.0,0.0,0.0,0.0 },
{ 0.0,-1.0,0.0,0.0 },
{ 0.0,0.0,1.0,0.0 },
{ 1.0,0.0,0.0,0.0 },
{ 0.0,1.0,0.0,0.0 },
};
int texIdxs[6] = {
vehiclesTextureIdx3,
vehiclesTextureIdx2,
vehiclesTextureIdx1,
vehiclesTextureIdx3,
vehiclesTextureIdx2,
vehiclesTextureIdx1,
};
float normal[4];
memcpy(normal, normals[tri.m_axis], sizeof(float)*4);
int texIdx = vehiclesTextureIdx1;
texIdx = texIdxs[tri.m_axis];
/*
// calculate the normal
float vt[2][4];
vt[0][0] = tri.m_verts[1].m_x - tri.m_verts[0].m_x;
vt[0][1] = tri.m_verts[1].m_y - tri.m_verts[0].m_y;
vt[0][2] = tri.m_verts[1].m_z - tri.m_verts[0].m_z;
vt[1][0] = tri.m_verts[1].m_x - tri.m_verts[2].m_x;
vt[1][1] = tri.m_verts[1].m_y - tri.m_verts[2].m_y;
vt[1][2] = tri.m_verts[1].m_z - tri.m_verts[2].m_z;
Math::normalizeVector(vt[0]);
Math::normalizeVector(vt[1]);
float normal[4];
Math::crossProduct(normal, vt[1], vt[0]);
*/
for (int v = 0; v < 3; v++)
{
PolyVertex& vert = vertexArray.m_vertexData[i*3 + v];
vert.m_animParams.y = 0.0;//obj.m_w;
vert.m_animParams.x = 0.0;//cubePtr->m_position.x - 0.5;
vert.m_position.x = tri.m_verts[v].m_x * modelScale;
vert.m_position.y = tri.m_verts[v].m_y * modelScale;
vert.m_position.z = tri.m_verts[v].m_z * modelScale;
vert.m_barycentric.x = (v == 0) ? 1.0 : 0.0;
vert.m_barycentric.y = (v == 1) ? 1.0 : 0.1;
vert.m_barycentric.z = (v == 2) ? 1.0 : 0.0;
if ( y == 128 || (tri.m_axis%3) == 0) // roads and stuff
{
vert.m_normal.x = normal[0];
vert.m_normal.y = normal[1];
vert.m_normal.z = normal[2];
}
else
{
vert.m_normal.x = normal[0]*0.5 + 0.5;
vert.m_normal.y = normal[1]*0.5 + 0.5;
vert.m_normal.z = normal[2]*0.5 + 0.5;
vert.m_normal.x += 1.2*(vert.m_position.x - w/2)/w;
vert.m_normal.y += 1.2*(vert.m_position.y - d/2)/d;
vert.m_normal.z += 1.2*(vert.m_position.z - h/2)/h;
/*
vert.m_normal.x = normal[0];
vert.m_normal.y = normal[1];
vert.m_normal.z = normal[2];
vert.m_normal.x = 2.0*(vert.m_position.x - w/2)/w;
vert.m_normal.y = 2.0*(vert.m_position.y - d/2)/d;
vert.m_normal.z = 2.0*(vert.m_position.z - h/2)/h;
*/
/*
vert.m_normal.x = vert.m_position.x - w/2;
vert.m_normal.y = vert.m_position.y - d/2;
vert.m_normal.z = vert.m_position.z - h/2;
*/
}
// Math::normalizeVector(&vert.m_normal.x);
vert.m_delta.x = 0.0f;
vert.m_delta.y = 0.0f;
vert.m_delta.z = 0.0f;
vert.m_color.r = (GLfloat(tri.m_uvs[v].m_u)-0.0) / 256.0;
vert.m_color.g = (GLfloat(tri.m_uvs[v].m_v)+0.0) / 256.0;
vert.m_color.b = GLfloat(texIdx);
vert.m_color.a = 0.0f;
}
}
i++;
}
}
struct ModelInfo {
const char* name;
int x, y;
int w, h, d;
bool direction;
int scaling;
};
ModelInfo g_modelInfos[MODEL_COUNT] = {
{ "car-left", 0, 0, 16, 16, 8, false , 1 },
{ "car-right", 0, 0, 16, 16, 8, true , 1 },
{ "bus-left", 16, 0, 32, 16, 8, false , 1 },
{ "bus-right", 16, 0, 32, 16, 8, true , 1 },
{ "bike-left", 48, 0, 16, 16, 8, false , 1 },
{ "bike-right", 48, 0, 16, 16, 8, true , 1 },
{ "van-left", 64, 0, 16, 16, 8, false , 1 },
{ "van-right", 64, 0, 16, 16, 8, true , 1 },
{ "trk-left", 80, 0, 48, 16, 8, false , 1 },
{ "trk-right", 80, 0, 48, 16, 8, true , 1 },
{ "trn-left", 128, 0, 128, 16, 8, false , 1 },
{ "trn-right", 128, 0, 128, 16, 8, true , 1 },
{ "road", 0, 128, 16, 8,16, true , 1 },
{ "water", 16, 128, 16, 8,16, true , 1 },
{ "tracks", 32, 128, 16, 8,16, true , 1 },
{ "road-2", 48, 128, 16, 8,16, true , 1 },
{ "tree", 64, 128, 16, 16, 8, true , 1 },
{ "shrub", 64+16*2, 128, 16, 16, 8, true , 1 },
{ "building", 64+16*3, 128, 16, 16, 8, true , 1 },
{ "rock", 64+16*4, 128, 16, 16, 8, true , 1 },
{ "logs", 64+16*5, 128, 16, 16, 8, true , 1 },
{ "boats", 64+16*6, 128, 16, 16, 8, true , 1 },
{ "crocs", 64+16*7, 128, 16, 16, 8, true , 1 },
{ "edit-slot", 80, 128, 16, 16, 8, true , 1 },
};
/*
void GetBool(const GameUi::DrawItems& a_uiItems, const char* name)
{
if (a_uiItems.m_variables.count(name))
if (a_uiItems.m_variables.at(name) == "on")
return true;
return false;
}
*/
// Doesn't change the value if it is not in a_uiItems, otherwise sets it to what it is set as
bool MapBool(bool& var, const GameUi::DrawItems& a_uiItems, const char* name)
{
bool ret = false;
if (a_uiItems.m_variables.count(name)) {
std::string str = a_uiItems.m_variables.at(name);
ret = true;
if (str == "on" && !var)
var = true;
else if (str == "off" && var)
var = false;
else
ret = false;
}
return ret;
}
void GameGraphics::update(float a_elapsed, float a_aspect, const GameUi::DrawItems& a_uiItems, float a_playerPos[3]
, float a_cameraStyle[8], float a_light[8], float a_debugCamera[8])
{
MapBool(debugCameraView, a_uiItems, "DEBUG_CAMERA");
MapBool(debugCollisions, a_uiItems, "DEBUG_COLLISIONS");
MapBool(showDebugging, a_uiItems, "DEBUG");
MapBool(modelEditing, a_uiItems, "MODEL_EDITING");
bool needToReloadShaders = false;
needToReloadShaders |= MapBool(m_options["debugTriangles"], a_uiItems, "DEBUG_TRIANGLES");
needToReloadShaders |= MapBool(m_options["enableShadows"], a_uiItems, "ENABLE_SHADOWS");
needToReloadShaders |= MapBool(m_options["enableShadowAttenuation"], a_uiItems, "ENABLE_SHADOW_ATTN");
needToReloadShaders |= MapBool(m_options["enableDistanceFog"], a_uiItems, "ENABLE_DISTANCE_FOG");
needToReloadShaders |= MapBool(m_options["enableRampShading"], a_uiItems, "ENABLE_RAMP_SHADING");
needToReloadShaders |= MapBool(m_options["enableDarkenedSides"], a_uiItems, "ENABLE_DARKENED_SIDES");
needToReloadShaders |= MapBool(m_options["enableTextures"], a_uiItems, "ENABLE_TEXTURES");
needToReloadShaders |= MapBool(m_options["enableBevels"], a_uiItems, "ENABLE_BEVELS");
needToReloadShaders |= MapBool(m_options["enableLighting"], a_uiItems, "ENABLE_LIGHTING");
if (needToReloadShaders)
reloadShaders();
//fprintf(stderr, "debug = %s\n", debugStr.c_str());
// allow it to be changed by key strokes too
if (toggleDebugCameraView)
debugCameraView = !debugCameraView;
const bool reloadModels = true;
if (reloadModels && m_models.dataUpdated())
{
m_models.ackDataUpdated();
for (int i = 0; i < MODEL_COUNT; i++) {
ModelInfo &mi = g_modelInfos[i];
loadVoxelModel(m_modelsVertexArrays[i], mi.name, mi.x, mi.y, mi.w, mi.h, mi.d, mi.direction, mi.scaling, (i == (MODEL_COUNT-1)));
}
glBindTexture(GL_TEXTURE_2D, m_hudTexId);
glTexSubImage2D(GL_TEXTURE_2D, 0, (vehiclesTextureIdx1%8)*256, (vehiclesTextureIdx1/8)*256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)m_models.y_axis());
glTexSubImage2D(GL_TEXTURE_2D, 0, (vehiclesTextureIdx2%8)*256, (vehiclesTextureIdx2/8)*256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)m_model_x_axis);
glTexSubImage2D(GL_TEXTURE_2D, 0, (vehiclesTextureIdx3%8)*256, (vehiclesTextureIdx3/8)*256, 256, 256, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid *)m_model_z_axis);
}
m_staticPolyVertexArray.sync();
for (int i = 0; i < MODEL_COUNT; i++)
m_modelsVertexArrays[i].sync();
modelIdx = int(a_playerPos[0]);
float currentPlayerX = a_playerPos[0];
float currentPlayerY = a_playerPos[1];
float currentPlayerZ = a_playerPos[2];
static float prevPlayerX = currentPlayerX;
static float prevPlayerY = currentPlayerY;
static float lastPlayerX = currentPlayerX;
static float lastPlayerY = currentPlayerY;
static float lastPlayerZ = currentPlayerZ;
static float jumpTime = 0.0;
static bool inJump = false;
//const float playerLerpSpeed = 0.5;
// lerp frog position - avoids too jumpy movement
float playerX = currentPlayerX; // Math::lerp(lastPlayerX, currentPlayerX, a_elapsed / playerLerpSpeed);
float playerY = currentPlayerY; // Math::lerp(lastPlayerY, currentPlayerY, a_elapsed / playerLerpSpeed);
float playerZ = 0.0;
if (currentPlayerX != prevPlayerX || currentPlayerY != prevPlayerY || inJump) {
jumpTime += a_elapsed;
float ratio = jumpTime * 2.0;
if (ratio >= 1.0) {
inJump = false;
ratio = 1.0;
} else {
playerX = Math::lerp(lastPlayerX, currentPlayerX, easeInBackCurve(ratio));
playerY = Math::lerp(lastPlayerY, currentPlayerY, easeInBackCurve(ratio));
inJump = true;
}
playerZ = Math::lerp(lastPlayerZ, currentPlayerZ, ratio) + 2.0;
if (ratio < 0.5f)
playerZ += easeInOutBackCurve(ratio * 2.0) * 5.0;
else
playerZ += easeInBackCurve(1.0 - (ratio-0.5)*2.0) * 5.0;
} else {
inJump = false;
jumpTime = 0.0;
lastPlayerZ = currentPlayerZ;
playerZ = currentPlayerZ;
}
lastPlayerX = playerX;
lastPlayerY = playerY;
prevPlayerX = currentPlayerX;
prevPlayerY = currentPlayerY;
m_playerX = playerX;
m_playerY = playerY;
m_playerZ = playerZ;
// setDebugValue("triangles:", int(m_triCount));
// setDebugValue("vertices:", int(m_triCount * 3));
m_cameraX = -a_playerPos[0] + 20;
m_cameraY = -a_playerPos[1] - 100;
#ifdef OLD_CAMERA
//const Game::GameObject& player = a_objectLists.back()->front();
float xOff = -300.0;
float yOff = -100.0;
float zOff = 20.0; // height of camera, smaller is higher
xOff = m_cameraX;//-player.m_x + 20;
yOff = m_cameraY;//-player.m_y - 100;
// lerp camera pos towards the player's position (avoids jumpy camera movements, feels nicer)
static float lastXOff = xOff;
static float lastYOff = yOff;
const float cameraLerpSpeed = 10.0;
#define LERP(A, B, Ratio) ((A) + (((B) - (A)) * (Ratio)))
xOff = Math::lerp(lastXOff, xOff, a_elapsed / cameraLerpSpeed);
yOff = Math::lerp(lastYOff, yOff, a_elapsed / cameraLerpSpeed);
lastXOff = xOff;
lastYOff = yOff;
// Layout of camera style parameters:
// float cameraStyle[8] = { fov, rx, ry, rz, s, tx, ty, tz };
// Frogger mode (perspective devolved to ortho and with top down view)
float debugCameraStyle[8] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0035f, -20.0f, 20.0f, 80.0f }; // Debug Style - 2D top down mode
// float topDownCameraStyle[8] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.02f, -20.0f, 20.0f, 80.0f }; // Frogger Style - 2D top down mode
float topDownCameraStyle[8] = { 0.5f, 0.0f, 0.0f, 0.0f, 0.015f, -20.0f, 40.0f, 80.0f }; // Frogger Style - 2D top down mode
float isoCameraStyle[8] = { 0.5f, -45.0f, 0.0f, -20.0f, 0.015f, 0.0f, 0.0f, 0.0f }; // Crossy Road Style - nearly isometric / orthographic
float perspectiveCameraStyle[8] = { 65.0f, -45.0f, 0.0f, -20.0f, 1.0f, 0.0f, 0.0f, 0.0f }; // Normal as designed style with perspective
float* defaultCameraStyle = perspectiveCameraStyle;
bool useIso = true;
if (useIso)
defaultCameraStyle = isoCameraStyle;
const int cwf = cameraWaitFrames;
const int ctf = cameraTransitionFrames;
float cameraStyle[8];
if (m_frame < cwf)
for (int i = 0; i < 8; i++)
cameraStyle[i] = topDownCameraStyle[i];
else if (m_frame < cwf+ctf)
for (int i = 0; i < 8; i++)
cameraStyle[i] = Math::lerp(topDownCameraStyle[i], isoCameraStyle[i], (m_frame-cwf) / float(ctf));
else if (m_frame < 2*cwf+ctf)
for (int i = 0; i < 8; i++)
cameraStyle[i] = isoCameraStyle[i];
else if (m_frame < 2*(cwf+ctf))
for (int i = 0; i < 8; i++)
cameraStyle[i] = Math::lerp(isoCameraStyle[i], defaultCameraStyle[i], (m_frame-(2*cwf+ctf)) / float(ctf));
else
for (int i = 0; i < 8; i++)
cameraStyle[i] = defaultCameraStyle[i];
/*
if (debugCameraView)
for (int i = 0; i < 8; i++)
cameraStyle[i] = debugCameraStyle[i];
*/
m_polyUniforms.m_shadowStrength = float(m_frame) / (3*cwf+2*ctf);
if (float(m_frame) >= (3*cwf+2*ctf))
m_polyUniforms.m_shadowStrength = 1.0f;
m_frame++;
m_camera.build(cameraStyle[0], a_aspect, &cameraStyle[1], cameraStyle[4] * 0.01f, xOff + cameraStyle[5], yOff + cameraStyle[6], zOff + cameraStyle[7]);
/*
fprintf(stderr, "mvp[] = {\n");
fprintf(stderr, " %f %f %f %f\n", m_modelViewProjectionMatrix[0], m_modelViewProjectionMatrix[1], m_modelViewProjectionMatrix[2], m_modelViewProjectionMatrix[3]);
fprintf(stderr, " %f %f %f %f\n", m_modelViewProjectionMatrix[4], m_modelViewProjectionMatrix[5], m_modelViewProjectionMatrix[6], m_modelViewProjectionMatrix[7]);
fprintf(stderr, " %f %f %f %f\n", m_modelViewProjectionMatrix[8], m_modelViewProjectionMatrix[9], m_modelViewProjectionMatrix[10], m_modelViewProjectionMatrix[11]);
fprintf(stderr, " %f %f %f %f\n", m_modelViewProjectionMatrix[12], m_modelViewProjectionMatrix[13], m_modelViewProjectionMatrix[14], m_modelViewProjectionMatrix[15]);
fprintf(stderr, "};\n");
*/
if (debugCameraView)
{
for (int i = 0; i < 8; i++)
cameraStyle[i] = debugCameraStyle[i];
m_debugCamera.build(cameraStyle[0], a_aspect, &cameraStyle[1], cameraStyle[4] * 0.01f, xOff + cameraStyle[5], yOff + cameraStyle[6], zOff + cameraStyle[7]);
}
#else
m_polyUniforms.m_shadowStrength = 1.0f;
m_polyUniforms.m_mixNoise = false;
m_camera.build(a_cameraStyle[0], a_aspect, &a_cameraStyle[1], a_cameraStyle[4], a_cameraStyle[5], a_cameraStyle[6], a_cameraStyle[7]);
float hackShadowScale = 0.7f; // hack value to make the shadows not dissappear on edges of the screen
m_light.build(a_light[0], 1.0f, &a_light[1], hackShadowScale*a_light[4], a_light[5], a_light[6], a_light[7], true);
if (debugCameraView)
{
m_debugCamera.build(a_debugCamera[0], a_aspect, &a_debugCamera[1], a_debugCamera[4], a_debugCamera[5], a_debugCamera[6], a_debugCamera[7]);
}
#endif
if (m_hudEnabled)
{
// probably want this to be more of a backgrounded task
for (auto job : a_uiItems.loadTexturesQueue)
LoadPendingTexture(job);
const GameUi::DrawItems *hudItems = &a_uiItems;
GameUi::DrawItems debugTextItems;
debugTextItems = a_uiItems;
hudItems = &debugTextItems;
std::string fps = gDebugVariables["Average FPS:"];
int fpsi = int(atof(fps.c_str()));
fps = std::to_string(fpsi);
debugTextItems.addContrastString(7*75, 90, fps.c_str(), 2);
debugTextItems.addString( 7*75, 90, fps.c_str(), (fpsi>58)?0xff00ff00:0xff0000ff, 2);
if (showDebugging)
{
addDebugString(debugTextItems, 2, 4, "Debugging variables:");
int line = 3;
for (auto var : gDebugVariables) {
addDebugString(debugTextItems, line, 5, var.first.c_str());
addDebugString(debugTextItems, line, 7, var.second.c_str());
line++;
}
}
//printf("Doing update with %ld items\n", items.m_items.size());
glBindBuffer(GL_ARRAY_BUFFER, m_hudVertexBuffer);
m_hudItemCount = hudItems->m_items.size();
//if (m_hudItemCount > m_hudVertexBufferSize) {
glBufferData(GL_ARRAY_BUFFER, sizeof(GameUi::UiVertex)*m_hudItemCount, (void*)&hudItems->m_items[0], GL_DYNAMIC_DRAW);
m_hudVertexBufferSize = m_hudItemCount;
//} else {
// glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GameUi::UiVertex)*m_hudItemCount, (void*)&hudItems->m_items[0]);
//}
}
if (!modelEditing)
{
// This continually refreshes the model data from file - probably only want to do this quite periodically
const int fastRefresh = 50;
const int normalRefresh = 1000; // @60fps this is every 16s
static int tick = normalRefresh;
int refresh = (debugCameraView) ? fastRefresh : normalRefresh;
if (tick++ >= refresh) {
// etag checking would make this faster / better
m_models.reloadDataFromFile("models.png", true);
tick = 0;
}
}
}
void GameGraphics::drawCube(float x, float y, float z, float w, float h, float d, uint32_t /*color*/)
{
float pnts[8][4];
for (int i = 0; i < 8; i++) {
pnts[i][0] = x + ((i&1) ? w : 0);
pnts[i][1] = y + ((i&2) ? h : 0);
pnts[i][2] = z + ((i&4) ? d : 0);
}
drawCuboid(pnts);
}
bool shadowPass = false;
void GameGraphics::drawObjectTypeList(const Game::GameObjectList& a_objs, int a_voxelModelIndex)
{
GLint deltaIndex = VERTEX_ATTRIB_INDEX(PolyVertex, delta);
glDisableVertexAttribArray(deltaIndex);
Camera& camera = (shadowPass) ? m_light : m_camera;
if (!shadowPass) {
m_polyUniforms.setUniforms();
}
glVertexAttrib4f(deltaIndex, 0.0, 0.0, 0.0, 0.0);
// part of the optimization
GLsizei siz = GLsizei(m_modelsVertexArrays[a_voxelModelIndex].m_vertexDataSize);
if (!siz)
return;
glBindVertexArrayOES(m_modelsVertexArrays[a_voxelModelIndex].m_vertexArray);
for (const Game::GameObject& obj : a_objs) {
if (obj.m_dx <= 0.0) {
float x = Math::modulus(obj.m_x + obj.m_dx * m_time, 1000.0f + obj.m_w);
//if (!isClipped(fabs(fmod(obj.m_x + obj.m_dx * m_time, 1000.0)), obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d))
//if (!isClipped(Math::modulus(obj.m_x + obj.m_dx * m_time, 1000.0), obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d))
if (!camera.isClipped(x, obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d))
{
//glVertexAttrib4f(deltaIndex, obj.m_dx, obj.m_dy, 0.0, 0.0);
//glVertexAttrib4f(deltaIndex, 0.0, 0.0, 0.0, 0.0);
float pos[4] = { x, obj.m_y, obj.m_z, 0.0f };
if (shadowPass) {
glUniform4fv(m_shadowUniforms.m_translation_index, 1, (GLfloat*)pos);
} else {
glUniform4fv(m_polyUniforms.m_translation_index, 1, (GLfloat*)pos);
}
glDrawArrays(GL_TRIANGLES, 0, siz); // tri-strips are better
//m_modelsVertexArrays[obj.m_dx > 0.0 ? a_voxelModelIndex+1 : a_voxelModelIndex].draw();
}
}
}
// part of the optimization
siz = GLsizei(m_modelsVertexArrays[a_voxelModelIndex+1].m_vertexDataSize);
if (!siz)
return;
glBindVertexArrayOES(m_modelsVertexArrays[a_voxelModelIndex+1].m_vertexArray);
for (const Game::GameObject& obj : a_objs) {
if (obj.m_dx > 0.0) {
float x = Math::modulus(obj.m_x + obj.m_dx * m_time, 1000.0f + obj.m_w);
//if (!isClipped(fabs(fmod(obj.m_x + obj.m_dx * m_time, 1000.0)), obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d))
//if (!isClipped(Math::modulus(obj.m_x + obj.m_dx * m_time, 1000.0), obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d))
if (!camera.isClipped(x, obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d))
{
//glVertexAttrib4f(deltaIndex, obj.m_dx, obj.m_dy, 0.0, 0.0);
//glVertexAttrib4f(deltaIndex, 0.0, 0.0, 0.0, 0.0);
float pos[4] = { x, obj.m_y, obj.m_z, 0.0f };
if (shadowPass) {
glUniform4fv(m_shadowUniforms.m_translation_index, 1, (GLfloat*)pos);
} else {
glUniform4fv(m_polyUniforms.m_translation_index, 1, (GLfloat*)pos);
}
glDrawArrays(GL_TRIANGLES, 0, siz); // tri-strips are better
//m_modelsVertexArrays[obj.m_dx > 0.0 ? a_voxelModelIndex+1 : a_voxelModelIndex].draw();
}
}
}
if (debugCollisions || debugCameraView)
{
for (const Game::GameObject& obj : a_objs) {
float x = Math::modulus(obj.m_x + obj.m_dx * m_time, 1000.0f + obj.m_w);
bool clipped = camera.isClipped(x, obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d);
if ((clipped && debugCameraView) || (debugCollisions && x > obj.m_w && x < (1000-obj.m_w)))
drawCube(x, obj.m_y, obj.m_z, obj.m_w, obj.m_h, obj.m_d, 0x00ff00);
}
}
//glEnableVertexAttribArray(deltaIndex);
glVertexAttrib4f(deltaIndex, 0.0, 0.0, 0.0, 0.0);
}
void GameGraphics::drawObjectTypeListTiled(const Game::GameObjectList& a_objs, int a_voxelModelIndex, int zoff, int wid)
{
GLint deltaIndex = VERTEX_ATTRIB_INDEX(PolyVertex, delta);
glDisableVertexAttribArray(deltaIndex);
Camera& camera = (shadowPass) ? m_light : m_camera;
if (!shadowPass) {
m_polyUniforms.m_mixNoise = (a_voxelModelIndex==13); // if water, mix noise
m_polyUniforms.setUniforms();
}
glVertexAttrib4f(deltaIndex, 0.0, 0.0, 0.0, 0.0);
// part of the optimization
GLsizei siz = GLsizei(m_modelsVertexArrays[a_voxelModelIndex].m_vertexDataSize);
if (!siz)
return;
glBindVertexArrayOES(m_modelsVertexArrays[a_voxelModelIndex].m_vertexArray);
for (const Game::GameObject& obj : a_objs) {
for (int j = 0; j < int(obj.m_h/10.0); j++)
{
for (int i = 0; i < 1000/wid; i++)
{
//float x = Math::modulus(obj.m_x + 650.0f + obj.m_dx * m_time, 1000.0 + obj.m_w);
float x = obj.m_x + i*wid;// obj.m_x + 650.0f;
if (!camera.isClipped(x, obj.m_y + j*10.0f + 1.0f, obj.m_z + zoff, wid, obj.m_h, obj.m_d))
{
/*
m_polyUniforms.m_translation = vec4attrib{ obj.m_x + i*10.0f, obj.m_y + j*10.0f, obj.m_z + (zoff/3) - 6.0f, 0.0f };
glVertexAttrib4f(deltaIndex, obj.m_dx, obj.m_dy, 0.0f, 0.0f);
m_polyUniforms.setUniforms();
m_modelsVertexArrays[obj.m_dx > 0.0f ? a_voxelModelIndex+1 : a_voxelModelIndex].draw();
*/
// optimized version which moves invariant out of the loop
float pos[4] = { x, obj.m_y + j*10.0f + 0.0f, obj.m_z + zoff, 0.0f };
if (shadowPass) {
glUniform4fv(m_shadowUniforms.m_translation_index, 1, (GLfloat*)pos);
} else {
glUniform4fv(m_polyUniforms.m_translation_index, 1, (GLfloat*)pos);
}
glDrawArrays(GL_TRIANGLES, 0, siz); // tri-strips are better
}
}
// float m_w, m_h, m_d;
}
}
//glEnableVertexAttribArray(deltaIndex);
glVertexAttrib4f(deltaIndex, 0.0, 0.0, 0.0, 0.0);
}
#define setProfileLocation(name) \
thisTime = GameTime::now(); \
setDebugValue(name ":", std::string(std::to_string(GameTime::Duration(thisTime - lastTime).count() * 0.000001f) + " ms").c_str()); \
lastTime = thisTime
void GameGraphics::preDraw(float a_time)
{
m_time = a_time;
glDisable(GL_BLEND);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glDepthMask(GL_TRUE);
// Get some parameters
GLint vp[4];
glGetIntegerv(GL_VIEWPORT, vp);
m_w = vp[2];
m_h = vp[3];
}
void GameGraphics::drawScene()
{
m_staticPolyVertexArray.draw();
setDebugValue("Tri count static:", int(m_staticPolyVertexArray.m_vertexDataSize));
if (shadowPass)
{
m_shadowUniforms.m_translation = vec4attrib{ m_playerX, m_playerY, m_playerZ, 0.0f };
m_shadowUniforms.setUniforms();
} else {
m_polyUniforms.m_translation = vec4attrib{ m_playerX, m_playerY, m_playerZ, 0.0f };
m_polyUniforms.setUniforms();
}
m_dynamicPolyVertexArray.draw();
setDebugValue("Tri count dynamic:", int(m_dynamicPolyVertexArray.m_vertexDataSize));
/*
// instancing test
glUniform4fv(m_polyUniforms.m_translation_index, 100, (GLfloat*)trans);
glBindVertexArrayOES(m_carRightVertexArray.m_vertexArray);
glDrawArraysInstancedEXT(GL_TRIANGLES, 0, 100, GLsizei(m_carRightVertexArray.m_vertexDataSize)); // tri-strips are better
*/
drawObjectTypeList(m_scene.m_cars, 0);
// TODO: add buses
drawObjectTypeList(m_scene.m_bikes, 4);
drawObjectTypeList(m_scene.m_vans, 6);
drawObjectTypeList(m_scene.m_trucks, 8);
drawObjectTypeList(m_scene.m_trains, 10);
drawObjectTypeList(m_scene.m_trees, 16);
drawObjectTypeList(m_scene.m_shrubs, 17);
drawObjectTypeList(m_scene.m_buildings, 18);
drawObjectTypeList(m_scene.m_rocks, 19);
drawObjectTypeList(m_scene.m_logs, 20);
drawObjectTypeList(m_scene.m_boats, 21);
drawObjectTypeList(m_scene.m_crocs, 22);
// TODO: below takes time (3ms)
drawObjectTypeListTiled(m_scene.m_road, 12, 0, 16);
drawObjectTypeListTiled(m_scene.m_water, 13, 0, 16);
drawObjectTypeListTiled(m_scene.m_tracks, 14, 0, 15);
}
void GameGraphics::prepareShadowContext()
{
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*)&m_defaultFramebufferId);
// Render the objects from light POV to make shadow map
#ifndef USE_OPENGLES2
glDrawBuffer(GL_NONE); // No color buffer is drawn to.
glReadBuffer(GL_NONE); // No color buffer is read from.
#endif
//setProfileLocation("Draw Point B0");
glBindFramebuffer(GL_FRAMEBUFFER, m_shadowFramebufferId);
glViewport(0, 0, SHADOW_DIM, SHADOW_DIM);
//setProfileLocation("Draw Point B1");
glClear(GL_DEPTH_BUFFER_BIT);
//setProfileLocation("Draw Point B2");
//glCullFace(GL_FRONT);
// Hack because front culling isn't working properly because of a bug in the vectorization - some backface polys aren't in the mesh properly
glCullFace(GL_BACK);
//glBindVertexArrayOES(m_dynamicPolyVertexArray.m_vertexArray);
m_shadowProgram.useProgram();
//GLKVector3 lightInvDir = GLKVector3Make(0.5f,2,2);
// Compute the MVP matrix from the light's point of view
//const Game::GameObjec-75.0ft& player = m_sim.objectLists().back()->front();
//setProfileLocation("Draw Point B3");
#ifdef OLD_CAMERA
float rotations[3] = { -75.0f, 20.0f, -30.0f };
// TODO: the light position in the shader(s) and here should line up for better realism
m_light.build(1.0f, 1.0f, rotations, 0.0175f, m_cameraX-20.0, m_cameraY+45.0, 20.0, true);
#endif
memcpy(m_shadowUniforms.m_modelViewProjectionMatrix.m, m_light.m_modelViewProjectionMatrix, sizeof(float)*16);
m_shadowUniforms.m_time = m_time;
m_shadowUniforms.m_translation = vec4attrib{ 0.0f, 0.0f, 0.0f, 0.0f };
m_shadowUniforms.setUniforms();
}
void GameGraphics::renderShadows()
{
if (m_options["enableShadows"])
{
prepareShadowContext();
shadowPass = true;
drawScene();
shadowPass = false;
// restore original framebuffer and viewport
glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebufferId);
}
}
void GameGraphics::prepareScreenContext()
{
//glViewport(vp[0], vp[1], vp[2], vp[3]);
glViewport(0, 0, m_w, m_h);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#ifndef USE_OPENGLES2
glDrawBuffer(GL_BACK);
glReadBuffer(GL_NONE);
#endif
// Render the object again with ES2
m_program.useProgram();
if (debugCameraView)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
else
glClear(GL_DEPTH_BUFFER_BIT);
// setProfileLocation("Draw Point C2");
glCullFace(GL_BACK);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_shadowTexId);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_hudTexId);
float biasedShadowMVP[16] = { 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.0, 0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.5, 1.0 };
Math::multiplyMatrix4x4(m_polyUniforms.m_shadowMapMVP.m, biasedShadowMVP, m_light.m_modelViewProjectionMatrix);
if (debugCameraView)
m_polyUniforms.m_modelViewProjectionMatrix = m_debugCamera.m_modelViewProjectionMatrix;
else
m_polyUniforms.m_modelViewProjectionMatrix = m_camera.m_modelViewProjectionMatrix;
m_polyUniforms.m_shadowMap = 0;
m_polyUniforms.m_texture = 1;
m_polyUniforms.m_time = m_time;
m_polyUniforms.m_translation = vec4attrib{ 0.0f, 0.0f, 0.0f, 0.0f };
m_polyUniforms.setUniforms();
/*
*/
}
void GameGraphics::renderScreen()
{
prepareScreenContext();
//setProfileLocation("Draw Point C3");
//glEnable(GL_BLEND);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
drawScene();
//glDisable(GL_ALPHA_TEST);
//setProfileLocation("Draw Point C43");
}
void GameGraphics::draw()
{
GameTime::TimePoint startOfDraw = GameTime::now();
GameTime::TimePoint lastTime = startOfDraw;
GameTime::TimePoint thisTime = startOfDraw;
setProfileLocation("Draw Point A1");
renderShadows();
setProfileLocation("Draw Point C0");
renderScreen();
/*
// seems to just clip in screen space
int p = 0;
GLdouble plane[4] = { m_clipPlanes[p][0], m_clipPlanes[p][1], m_clipPlanes[p][2], m_clipPlanes[p][3] };
glClipPlane(GL_CLIP_PLANE0, plane);
glEnable(GL_CLIP_PLANE0);
*/
if (debugCameraView)
{
float frustumPts[8][4];
m_camera.calcFrustum(frustumPts);
drawCuboid(frustumPts);
m_light.calcFrustum(frustumPts);
drawCuboid(frustumPts);
}
if (m_options["enableShadows"])
{
glBindFramebuffer(GL_FRAMEBUFFER, m_shadowFramebufferId);
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//glClear(GL_DEPTH_BUFFER_BIT);
const GLenum discards[] = {GL_DEPTH_ATTACHMENT};
#ifndef USE_OPENGLES2
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, discards);
#else
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER_APPLE, 1, discards);
#endif
// restore original framebuffer and viewport
glBindFramebuffer(GL_FRAMEBUFFER, m_defaultFramebufferId);
//glViewport(vp[0], vp[1], vp[2], vp[3]);
}
//glFlush();
//glFinish();
}
void GameGraphics::prepareHUDContext()
{
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_DST_COLOR);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_hudTexId);
m_hudProgram.useProgram();
glUniform1i(uniforms[UNIFORM_HUD_TEXTURE], 0);
//glUniform2f(uniforms[UNIFORM_HUD_INV_SCREEN], 2.0f / m_w, -2.0f / m_h);
glUniform2f(uniforms[UNIFORM_HUD_INV_SCREEN], 2.0f / 640, -2.0f / 960);
}
void GameGraphics::drawHUD()
{
if (m_hudEnabled)
{
prepareHUDContext();
glBindVertexArrayOES(m_hudVertexArray);
glDrawArrays(GL_TRIANGLES, 0, GLsizei(m_hudItemCount));
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
}
}
void GameGraphics::drawAuxItems(const GameUi::DrawItems& a_uiItems)
{
prepareHUDContext();
glBindVertexArrayOES(m_auxVertexArray);
glBindBuffer(GL_ARRAY_BUFFER, m_auxVertexBuffer);
size_t itemCount = a_uiItems.m_items.size();
glBufferData(GL_ARRAY_BUFFER, sizeof(GameUi::UiVertex)*itemCount, (void*)&a_uiItems.m_items[0], GL_DYNAMIC_DRAW);
glDrawArrays(GL_TRIANGLES, 0, GLsizei(itemCount));
glDisable(GL_BLEND);
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glEnable(GL_DEPTH_TEST);
}
#include <functional>
void GameGraphics::drawViews(const std::vector<GameUi::View*>& a_views)
{
std::map<std::string, std::function<void()> > viewMap;
viewMap["RotatingFrog"] = std::bind(&GameGraphics::drawRotatingFrog, this);
viewMap["ModelEditView"] = std::bind(&GameGraphics::drawModelEditLayer, this);
viewMap["RotatingModelPreview"] = std::bind(&GameGraphics::drawModelRotatingPreview, this);
viewMap["CaroselTest"] = std::bind(&GameGraphics::drawCaroselTest, this);
bool prepared = false;
for (GameUi::View* view : a_views)
{
if (viewMap.count(view->m_view)) {
if (!prepared) {
prepareScreenContext();
prepared = true;
}
//fprintf(stderr, "drawing view: %s\n", view->m_view.c_str());
GLint vp[4];
glGetIntegerv(GL_VIEWPORT, vp);
glViewport(view->m_geometry.x, vp[3]-(view->m_geometry.y+view->m_geometry.h), view->m_geometry.w, view->m_geometry.h);
m_w = view->m_geometry.w;
m_h = view->m_geometry.h;
viewMap[view->m_view]();
glViewport(vp[0], vp[1], vp[2], vp[3]);
m_w = vp[2];
m_h = vp[3];
}
}
}
void GameGraphics::drawRotatingFrog()
{
// Rotating model in center
static float zr = 0.0f;
float modelScale = 8.0f;
float aspectRatio = (m_h) ? (float(m_w)/float(m_h)) : 1.0;
float isoCameraStyle[8] = { 25.0f, -60.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f }; // Crossy Road Style - nearly isometric / orthographic
Camera camera;
camera.build(25.0f, aspectRatio, &isoCameraStyle[1], 0.005f, 0.0f, 0.0f, 0.0f);
float baseModelViewMatrix[16];
Math::makeIdentityMatrix4x4(baseModelViewMatrix);
Math::translateMatrix4x4(baseModelViewMatrix, 0.0f, 0.0f, -3.5f);
float modelViewMatrix[16];
Math::multiplyMatrix4x4(modelViewMatrix, baseModelViewMatrix, camera.m_cameraMatrix);
Math::translateMatrix4x4(modelViewMatrix, 0, 35, -35);
Math::rotateMatrix4x4(modelViewMatrix, zr, 0.0f, 0.0f, 1.0f);
Math::translateMatrix4x4(modelViewMatrix, -350.0f, -28.0f, 0);
Math::scaleMatrix4x4(modelViewMatrix, modelScale, modelScale, modelScale);
Math::multiplyMatrix4x4(m_polyUniforms.m_modelViewProjectionMatrix.m, camera.m_projectionMatrix, modelViewMatrix);
m_polyUniforms.m_translation = vec4attrib{ 40, 0, 0, 0.0f };
m_polyUniforms.setUniforms();
// draw the frog model
m_dynamicPolyVertexArray.draw();
zr += 0.03f;
}
void GameGraphics::drawModelEditLayer()
{
// Rotating model in center
static float zr = 10.0f;//-0.5f;
float modelScale = 4.0f;
float aspectRatio = (m_h) ? (float(m_w)/float(m_h)) : 1.0;
float isoCameraStyle[8] = { 25.0f, -60.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f }; // Crossy Road Style - nearly isometric / orthographic
int idx = Math::modulus(m_editModel, MODEL_COUNT);
ModelInfo &mi = g_modelInfos[idx];
Camera camera;
camera.build(25.0f, aspectRatio, &isoCameraStyle[1], 0.005f, 0.0f, 0.0f, 0.0f);
Math::translateMatrix4x4(camera.m_cameraMatrix, 0, 200+154+35, -55-55-55-55-5 );
Math::rotateMatrix4x4(camera.m_cameraMatrix, zr, 0.0f, 0.0f, 1.0f);
Math::translateMatrix4x4(camera.m_cameraMatrix, -mi.w * 0.5*modelScale, -mi.d * 0.5*modelScale, -mi.h * 0.5*modelScale);
Math::scaleMatrix4x4(camera.m_cameraMatrix, modelScale, modelScale, modelScale);
Math::multiplyMatrix4x4(m_polyUniforms.m_modelViewProjectionMatrix.m, camera.m_projectionMatrix, camera.m_cameraMatrix);
m_polyUniforms.m_translation = vec4attrib{ 0, 0, 0, 0 };
m_polyUniforms.setUniforms();
int editLayer = (mi.h-1) - int((mi.h-1) * m_editLayer + 0.0);
//if (m_editModelDirty)
{
loadVoxelModel(m_editModelVertexArray1, mi.name, mi.x, mi.y, mi.w, mi.h, mi.d, mi.direction, mi.scaling, true, 0, editLayer+1);
m_editModelVertexArray1.update();
}
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
//m_editModelVertexArray1.sync();
//m_modelsVertexArrays[idx].update();
m_editModelVertexArray1.draw();
if (editLayer <= mi.h-1)
{
//if (m_editModelDirty)
{
loadVoxelModel(m_editModelVertexArray2, mi.name, mi.x, mi.y, mi.w, mi.h, mi.d, mi.direction, mi.scaling, true, editLayer+1, 256);
//m_editModelVertexArray2.sync();
}
glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE);
glDisable(GL_CULL_FACE);
glEnable(GL_BLEND);
/*
glBlendFunc(GL_ONE_MINUS_CONSTANT_ALPHA, GL_CONSTANT_ALPHA);
glBlendColor(0.7, 0.7, 0.7, 0.95);
*/
/*
#define GL_SRC_COLOR 0x0300
#define GL_ONE_MINUS_SRC_COLOR 0x0301
#define GL_SRC_ALPHA 0x0302
#define GL_ONE_MINUS_SRC_ALPHA 0x0303
#define GL_DST_ALPHA 0x0304
#define GL_ONE_MINUS_DST_ALPHA 0x0305
#define GL_DST_COLOR 0x0306
#define GL_ONE_MINUS_DST_COLOR 0x0307
#define GL_SRC_ALPHA_SATURATE 0x0308
#define GL_CONSTANT_COLOR 0x8001
#define GL_ONE_MINUS_CONSTANT_COLOR 0x8002
#define GL_CONSTANT_ALPHA 0x8003
#define GL_ONE_MINUS_CONSTANT_ALPHA 0x8004
*/
glBlendFuncSeparate(GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_CONSTANT_ALPHA);
glBlendColor(0.7, 0.7, 0.7, 0.05);
//m_editModelVertexArray2.sync();
m_editModelVertexArray2.update();
m_editModelVertexArray2.draw();
glBlendColor(0.7, 0.7, 0.7, 0.15);
drawSolidCuboid( 0, 0, editLayer+1, mi.w, mi.d, 0.001, 0x3f3fff, 1.0);
glDisable(GL_BLEND);
glEnable(GL_CULL_FACE);
}
m_editModelDirty = false;
/*
//m_models.editPixel(mi.x, mi.y, 0);
//m_models.invalidateCache();
//loadVoxelModel(m_modelsVertexArrays[idx], mi.name, mi.x, mi.y, mi.w, mi.h, mi.d, mi.direction, mi.scaling, true);
*/
/*
float cuboid[8][4] = {
{ float( 0), float( 0), float(editLayer ), 0.0 },
{ float( 0), float(mi.d), float(editLayer ), 0.0 },
{ float(mi.w), float( 0), float(editLayer ), 0.0 },
{ float(mi.w), float(mi.d), float(editLayer ), 0.0 },
{ float( 0), float( 0), float(editLayer+1), 0.0 },
{ float( 0), float(mi.d), float(editLayer+1), 0.0 },
{ float(mi.w), float( 0), float(editLayer+1), 0.0 },
{ float(mi.w), float(mi.d), float(editLayer+1), 0.0 },
};
drawCuboid(cuboid);
*/
}
void GameGraphics::drawCaroselTest()
{
// Rotating model in center
static float zr = 0.0f;
float modelScale = 0.2f;
float aspectRatio = (m_h) ? (float(m_w)/float(m_h)) : 1.0;
float isoCameraStyle[8] = { 25.0f, -60.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f }; // Crossy Road Style - nearly isometric / orthographic
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
//int mIdx = Math::modulus(m_editModel, MODEL_COUNT);
int mIdx = m_editModel;
static int lastEditModel = mIdx;
static int savedLastEditModel = mIdx;
static int frame = 0;
if (mIdx != lastEditModel) {
savedLastEditModel = lastEditModel;
lastEditModel = mIdx;
frame = 0;
} else {
frame++;
if (frame > 50) {
frame = 50;
}
}
int f = frame;
if (savedLastEditModel < lastEditModel) {
f = -f + 25;
} else {
f = f - 75;
}
for (int i = 0; i < MODEL_COUNT; i++)
{
const float radius = 250.0f;
float radian = (float(i) + 0.5 + (f/50.0)) * 2 * 3.14 / MODEL_COUNT - 0.5*3.14;
int idx = Math::modulus(i + m_editModel, MODEL_COUNT);
float x = cos(radian) * radius;
float y = (1.0 + sin(radian)) * radius;
int curIdx = Math::modulus(mIdx, MODEL_COUNT);
int lastIdx = Math::modulus(savedLastEditModel, MODEL_COUNT);
int f2 = 0;
if ( idx == curIdx ) {
f2 = frame;
} else if ( idx == lastIdx ) {
f2 = 50 - frame;
}
float zRot = Math::modulus(zr,3.14f*2) * (f2/50.0);
y = y * ((50-f2)/50.0) - f2;
modelScale = 0.2 + 0.8 * (f2/50.0);
//fprintf(stderr, "model idx: %i\n", idx);
ModelInfo &mi = g_modelInfos[idx];
Camera camera;
camera.build(28.0f, aspectRatio, &isoCameraStyle[1], 0.005f, 0.0f, 0.0f, 0.0f);
Math::rotateMatrix4x4(camera.m_cameraMatrix, -0.25, 1.0f, 0.0f, 0.0f);
Math::translateMatrix4x4(camera.m_cameraMatrix, x*0.2, 100+200+154+35 + y*modelScale, -105-15 );
Math::rotateMatrix4x4(camera.m_cameraMatrix, zRot, 0.0f, 0.0f, 1.0f);
Math::translateMatrix4x4(camera.m_cameraMatrix, (-100*modelScale) -mi.w * 0.5*modelScale, -mi.d * 0.5*modelScale, -mi.h * 0.5*modelScale);
Math::scaleMatrix4x4(camera.m_cameraMatrix, modelScale, modelScale, modelScale);
Math::multiplyMatrix4x4(m_polyUniforms.m_modelViewProjectionMatrix.m, camera.m_projectionMatrix, camera.m_cameraMatrix);
m_polyUniforms.m_translation = vec4attrib{ 100, 0, -5, 0 };
m_polyUniforms.setUniforms();
//loadVoxelModel(m_modelsVertexArrays[idx], mi.name, mi.x, mi.y, mi.w, mi.h, mi.d, mi.direction, mi.scaling, false);
//m_modelsVertexArrays[idx].update();
m_modelsVertexArrays[idx].draw();
}
zr += 0.03f;
}
void GameGraphics::drawModelRotatingPreview()
{
// Rotating model in center
static float zr = 0.0f;
float modelScale = 4.0f;
float aspectRatio = (m_h) ? (float(m_w)/float(m_h)) : 1.0;
float isoCameraStyle[8] = { 25.0f, -60.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f }; // Crossy Road Style - nearly isometric / orthographic
int idx = Math::modulus(m_editModel, MODEL_COUNT);
ModelInfo &mi = g_modelInfos[idx];
Camera camera;
camera.build(25.0f, aspectRatio, &isoCameraStyle[1], 0.005f, 0.0f, 0.0f, 0.0f);
Math::translateMatrix4x4(camera.m_cameraMatrix, 0, 200+154+35, -55-55-55-55 );
Math::rotateMatrix4x4(camera.m_cameraMatrix, zr, 0.0f, 0.0f, 1.0f);
Math::translateMatrix4x4(camera.m_cameraMatrix, -mi.w * 0.5*modelScale, -mi.d * 0.5*modelScale, -mi.h * 0.5*modelScale);
Math::scaleMatrix4x4(camera.m_cameraMatrix, modelScale, modelScale, modelScale);
Math::multiplyMatrix4x4(m_polyUniforms.m_modelViewProjectionMatrix.m, camera.m_projectionMatrix, camera.m_cameraMatrix);
m_polyUniforms.m_translation = vec4attrib{ 0, 0, 0, 0 };
m_polyUniforms.setUniforms();
//loadVoxelModel(m_modelsVertexArrays[idx], mi.name, mi.x, mi.y, mi.w, mi.h, mi.d, mi.direction, mi.scaling, false);
glClear(GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);
//m_modelsVertexArrays[idx].sync();
//m_modelsVertexArrays[idx].update();
m_modelsVertexArrays[idx].draw();
setDebugValue("ModelTris:", int(m_modelsVertexArrays[idx].m_vertexDataSize/3));
setDebugValue("ModelVerts:", int(m_modelsVertexArrays[idx].m_vertexDataSize));
zr += 0.03f;
}
void GameGraphics::getEditObjectPixels(float a_editLayer, std::vector<uint32_t>& pixels, int& w, int& h, int& d)
{
int idx = Math::modulus(m_editModel, MODEL_COUNT);
ModelInfo &mi = g_modelInfos[idx];
int editLayer = (mi.h-1) - int((mi.h-1) * a_editLayer + 0.0);
w = mi.w;
h = mi.d;
d = mi.h;
pixels.clear();
pixels.resize(w*h);
for (int i = 0; i < w*h; i++)
pixels[i] = 0x3f000000;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
int z = editLayer;
uint32_t col = m_models.getPixel( mi.x+x, mi.y+(y*mi.h)+(15-z));
pixels[y*w+x] = col;
}
/*
for (unsigned i = 0; i < voxelModel.size(); i++)
if (voxelModel.at(i).m_z == editLayer)
{
uint32_t col = voxelModel.at(i).m_color;
pixels[voxelModel.at(i).m_y * w + voxelModel.at(i).m_x] = ((col&0xff)<<16)|(col&0xff00)|((col>>16)&0xff)|0xff000000;
}
*/
}
void GameGraphics::setEditObjectPixels(float a_editLayer, const std::vector<uint32_t>& pixels, int w, int h)
{
int idx = Math::modulus(m_editModel, MODEL_COUNT);
ModelInfo &mi = g_modelInfos[idx];
int editLayer = (mi.h-1) - int((mi.h-1) * a_editLayer + 0.0);
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
int z = editLayer;
uint32_t col = pixels[y*w+x];
if (col == 0x3f000000)
col = 0;
m_models.editPixel( mi.x+x, mi.y+(y*mi.h)+(15-z), col);
}
m_models.invalidateCache();
}
void GameGraphics::getPalettePixels(std::vector<uint32_t>& pixels)
{
pixels.clear();
pixels.resize(256);
for (int i = 0; i < 256; i++)
pixels[i] = m_models.getPixel(255-(i%16), 255-15+(i/16)) | 0xff000000;
}
void GameGraphics::drawCuboid(float cuboid[8][4])
{
m_polyUniforms.m_translation = vec4attrib{ 0, 0, 0, 0.0f };
m_polyUniforms.setUniforms();
const int lineSegments[12][2] = {{0,1},{1,3},{3,2},{2,0}, {4,5},{5,7},{7,6},{6,4}, {0,4},{1,5},{3,7},{2,6}};
std::vector<PolyVertex> &objVerts = m_cuboidVertexList.m_vertexData;
objVerts.resize(24);
for (int i = 0; i < 12; i++) {
for (int p = 0; p < 2; p++) {
memset(&objVerts[i*2+p], 0, sizeof(PolyVertex));
objVerts[i*2+p].m_color.r = 1.0;
objVerts[i*2+p].m_color.g = 0.5;
objVerts[i*2+p].m_color.b = 0.5;
objVerts[i*2+p].m_color.a = 1.0;
objVerts[i*2+p].m_position.x = cuboid[lineSegments[i][p]][0];
objVerts[i*2+p].m_position.y = cuboid[lineSegments[i][p]][1];
objVerts[i*2+p].m_position.z = cuboid[lineSegments[i][p]][2];
}
}
m_cuboidVertexList.update();
glBindVertexArrayOES(m_cuboidVertexList.m_vertexArray);
glDrawArrays(GL_LINES, 0, 24);
}
void GameGraphics::drawSolidCuboid(float x, float y, float z, float w, float h, float d, uint32_t col, float scale)
{
m_polyUniforms.m_translation = vec4attrib{ 0, 0, 0, 0.0f };
m_polyUniforms.setUniforms();
std::vector<PolyVertex> &objVerts = m_cuboidVertexList.m_vertexData;
objVerts.resize(12 * 3);
PolyVertex* cubePtr = &objVerts[0];
memcpy(cubePtr, gBaseCubeVertexData, vertSize*vertsPerCube);
VoxelBox obj = { x, y, z, 0.0, w, d, h, col };
initCubeVerticesFromVoxel(cubePtr, obj, scale);
for (int v = 0; v < 36; v++)
cubePtr->m_color.a = 0.9;
m_cuboidVertexList.update();
glBindVertexArrayOES(m_cuboidVertexList.m_vertexArray);
//glDrawArrays(GL_LINES, 0, 24);
glDrawArrays(GL_TRIANGLES, 0, 36);
}