Tutorial 07: Multiple Models
Complete Source Code:
Scene.h
#pragma once
#include "Math.h"
#include "Orientation.h"
#include "Model.h"
#include <vector>
struct Object : public Orientation
{
Object(const vec3& position, unsigned int modelIndex)
: Orientation(position), ModelIndex(modelIndex)
{
m_ID = GetUniqueID();
}
unsigned int ModelIndex{ 0 };
unsigned int ID() const { return m_ID; }
// ...
private:
unsigned int m_ID;
};
struct Scene
{
struct : public Orientation {
float FoV{ radians(45.0f) };
float ZNear{ 0.1f }, ZFar{ 100.0f };
} Camera;
struct {
vec3 Ambient{ 0.2f, 0.2f, 0.2f };
std::vector<DirectionalLight> DirectionalLights;
std::vector<PointLight> PointLights;
std::vector<SpotLight> SpotLights;
} Light;
std::vector<Object> Objects;
Scene();
void AnimateNextFrame(float timestep, int tracked_ID);
};
Scene.cpp
#include "Scene.h"
#include "Main.h"
Scene::Scene()
{
// put the camera back a little ...
Camera.SetPosition(vec3(0, 1, 5));
// create some lights
Light.DirectionalLights.push_back(DirectionalLight(vec3(5, -10, 0), vec3(1, 1, 1)));
Light.PointLights.push_back(PointLight(vec3(0, 5, 0), vec3(1, 1, 1), 0.1f));
//Light.SpotLights.push_back(SpotLight(vec3(0, 5, 0), vec3(0, -1, 0), vec3(0.7f, 0.7f, 0.7f), 5));
// create some objects ...
Objects.push_back(Object(vec3(-2, 0, -2), 0));
Objects.push_back(Object(vec3(+2, 0, -2), 0));
Objects.push_back(Object(vec3(+2, 0, +2), 1));
Objects.push_back(Object(vec3(-2, 0, +2), 1));
// ...
}
void Scene::AnimateNextFrame(float timestep, int tracked_ID)
{
// camera control
// hold right mouse button to rotate the camera
// scroll up/down to move the camera
// ----------------------------------------------------------------------------------------------------------
if (Framework->IsClicked(MOUSE_BUTTON_RIGHT))
{
Camera.Yaw(Framework->EventCursorMoved().x * -0.01f);
Camera.Pitch(Framework->EventCursorMoved().y * +0.01f);
}
if (Framework->ScrolledUp())
Camera.Move(Camera.Forward() * +0.5f);
if (Framework->ScrolledDown())
Camera.Move(Camera.Forward() * -0.5f);
// ----------------------------------------------------------------------------------------------------------
// animate all objects
// ----------------------------------------------------------------------------------------------------------
for (auto& object : Objects)
object.Yaw(radians(30.0f) * timestep);
// ----------------------------------------------------------------------------------------------------------
// animate lights
// ----------------------------------------------------------------------------------------------------------
double totaltime = Framework->Time();
// change intensity
//for (auto& directionallight : Light.DirectionalLights)
// directionallight.Intensity = vec3(abs2(std::cos(totaltime * 2));
// change Y-coordinate (height)
for (auto& pointlight : Light.PointLights)
pointlight.Position.y = 5 * std::cos(totaltime * 0.5f);
// rotate arount Y-axis
for (auto& spotlight : Light.SpotLights)
spotlight.Direction = vec3(std::cos(totaltime), -5, std::sin(totaltime));
// ----------------------------------------------------------------------------------------------------------
// just print the ID at cursor position:
// ----------------------------------------------------------------------------------------------------------
if (Framework->EventClicked(MOUSE_BUTTON_LEFT))
if (tracked_ID != -1)
std::cout << "clicked at object: \t" << tracked_ID << std::endl;
// ----------------------------------------------------------------------------------------------------------
// ...
}
Model.h
#pragma once
#include "Math.h"
#include <vector>
struct Vertex
{
vec3 Position;
vec2 TexCoord;
vec3 Normal;
int MaterialIndex;
};
struct ModelInstance
{
mat4 ModelMatrix;
int ObjectID;
};
// parameters of "glDrawArrays(...)"
struct DrawCall
{
unsigned int Primitive, Offset, Count;
};
struct Model : public DrawCall
{
// model size
struct {
vec3 Min{ 0, 0, 0 }, Max{ 0, 0, 0 };
float RMax{ 0 };
} Size;
// stores temporarily all the instances, needed for the instancebuffer
std::vector<ModelInstance> Instances;
// ...
};
// those padding variables are there to match the required memory layout for "std140":
// arrays of structs must be rounded up to a multiple of 16byte (or sizeof(vec4))
// vec3 are actually consuming sizeof(vec4) in GLSL
struct Material
{
vec3 Ka;
float PADDING1;
vec3 Kd;
float PADDING2;
vec3 Ks;
float PADDING3;
float Ns{ 1 };
float d{ 1 };
int map_Kd{ 0 };
float PADDING5;
Material();
Material(const vec3& ambient, const vec3& diffuse, const vec3& specular, float shininess, float transparency, int map_kd);
};
struct DirectionalLight
{
vec3 Direction;
float PADDING1;
vec3 Intensity;
float PADDING2;
DirectionalLight(const vec3& direction, const vec3& intensity);
};
struct PointLight
{
vec3 Position;
float PADDING1;
vec3 Intensity;
float PADDING2;
float AttenuationLinear{ 0.5 };
float PADDING3;
float PADDING4;
float PADDING5;
PointLight(const vec3& position, const vec3& intensity, float attenuationlinear);
};
struct SpotLight
{
vec3 Position;
float PADDING1;
vec3 Direction;
float PADDING2;
vec3 Intensity;
float PADDING3;
int Exponent{ 1 };
float PADDING4;
float PADDING5;
float PADDING6;
SpotLight(const vec3& position, const vec3& direction, const vec3& intensity, int exponent);
};
void LoadModel(
const std::string& filename, const std::string& base_path,
std::vector<Vertex>& vertexarray,
std::vector<Material>& materialarray,
std::vector<std::string>& textureKdarray);
Renderer_OpenGL.h
#pragma once
#include "Renderer.h"
#include <GL/glew.h>
class Renderer_OpenGL : public Renderer
{
public:
Renderer_OpenGL(unsigned int width, unsigned int height);
virtual ~Renderer_OpenGL();
virtual void Render(const Scene& scene, int& tracked_ID);
protected:
unsigned int m_width{ 0 }, m_height{ 0 };
unsigned int m_framebuffer{ 0 };
unsigned int m_framebuffercolortexture{ 0 };
unsigned int m_framebufferintegertexture{ 0 };
unsigned int m_framebufferdepthtexture{ 0 };
unsigned int m_program{ 0 };
unsigned int m_vertexarray{ 0 };
unsigned int m_vertexbuffer{ 0 };
unsigned int m_instancebuffer{ 0 };
unsigned int m_materialbuffer{ 0 };
unsigned int m_directionallightbuffer{ 0 };
unsigned int m_pointlightbuffer{ 0 };
unsigned int m_spotlightbuffer{ 0 };
unsigned int m_textureKd{ 0 };
std::vector<Model> m_models;
};
Renderer_OpenGL.cpp
#include "Renderer_OpenGL.h"
#include "Shader.h"
#include "Model.h"
#include "BMP.h"
#include "Main.h"
#include <iostream>
#define MAX_MODEL_INSTANCES 1000
#define MAX_MATERIALS 1000
#define MAX_POINTLIGHTS 10
#define MAX_DIRECTIONALLIGHTS 10
#define MAX_SPOTLIGHTS 10
void CheckForGLError()
{
GLenum error;
while ((error = glGetError()) != GL_NO_ERROR)
{
std::cout << "ERROR: (Renderer_OpenGL) \t";
if (error == GL_INVALID_ENUM)
std::cout << "GL_INVALID_ENUM";
if (error == GL_INVALID_VALUE)
std::cout << "GL_INVALID_VALUE";
if (error == GL_INVALID_OPERATION)
std::cout << "GL_INVALID_OPERATION";
if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
std::cout << "GL_INVALID_FRAMEBUFFER_OPERATION";
if (error == GL_OUT_OF_MEMORY)
std::cout << "GL_OUT_OF_MEMORY";
if (error == GL_STACK_UNDERFLOW)
std::cout << "GL_STACK_UNDERFLOW";
if (error == GL_STACK_OVERFLOW)
std::cout << "GL_STACK_OVERFLOW";
std::cout << (char)7 << std::endl; /*play sound*/
std::cin.get();
}
}
Renderer_OpenGL::Renderer_OpenGL(unsigned int width, unsigned int height)
{
// settings
// ----------------------------------------------------------------------------------------------------------
glEnable(GL_DEPTH_TEST); // 3D
glEnable(GL_CULL_FACE); // dont render backward faces
// ----------------------------------------------------------------------------------------------------------
// setup framebuffer:
// ----------------------------------------------------------------------------------------------------------
// make sure that the framebuffer isnt too small
m_width = max2(100, width);
m_height = max2(100, height);
glViewport(0, 0, m_width, m_height);
glGenTextures(1, &m_framebuffercolortexture);
glBindTexture(GL_TEXTURE_2D, m_framebuffercolortexture);
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_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_width, m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferintegertexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferintegertexture);
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_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// special texture to store only integer values (32 bit)
glTexImage2D(GL_TEXTURE_2D, 0, GL_R32I, m_width, m_height, 0, GL_RED_INTEGER, GL_INT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
glGenTextures(1, &m_framebufferdepthtexture);
glBindTexture(GL_TEXTURE_2D, m_framebufferdepthtexture);
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_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, m_width, m_height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
glBindTexture(GL_TEXTURE_2D, 0);
// create framebuffer
glGenFramebuffers(1, &m_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_framebuffercolortexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, m_framebufferintegertexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, m_framebufferdepthtexture, 0);
std::vector<GLenum> drawbuffers = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(drawbuffers.size(), drawbuffers.data());
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// ERROR CHECK:
GLenum status = glCheckNamedFramebufferStatus(m_framebuffer, GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE)
{
if (status == GL_FRAMEBUFFER_UNDEFINED)
std::cout << "ERROR: undefined framebuffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
std::cout << "ERROR: a necessary attachment is uninitialized" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
std::cout << "ERROR: no attachments" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER)
std::cout << "ERROR: incomplete draw buffer" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER)
std::cout << "ERROR: incomplete read buffer" << std::endl;
if (status == GL_FRAMEBUFFER_UNSUPPORTED)
std::cout << "ERROR: combination of attachments is not supported" << std::endl;
if (status == GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE)
std::cout << "ERROR: number if samples for all attachments does not match" << std::endl;
}
// ----------------------------------------------------------------------------------------------------------
// setup program
// ----------------------------------------------------------------------------------------------------------
m_program = glCreateProgram();
unsigned int vertexshader = glCreateShader(GL_VERTEX_SHADER);
unsigned int fragmentshader = glCreateShader(GL_FRAGMENT_SHADER);
CompileShader(vertexshader, LoadTextFile("shader.vs"));
CompileShader(fragmentshader, LoadTextFile("shader.fs"));
LinkProgram(m_program, { vertexshader , fragmentshader });
glDeleteShader(vertexshader);
glDeleteShader(fragmentshader);
// ----------------------------------------------------------------------------------------------------------
// setup vertexarray
// ----------------------------------------------------------------------------------------------------------
glGenVertexArrays(1, &m_vertexarray);
glGenBuffers(1, &m_vertexbuffer);
glGenBuffers(1, &m_instancebuffer);
glBindVertexArray(m_vertexarray);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glVertexAttribPointer(0, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 0));
glVertexAttribPointer(1, 2, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 3));
glVertexAttribPointer(2, 3, GL_FLOAT, false, sizeof(Vertex), (void*)(sizeof(float) * 5));
glVertexAttribIPointer(3, 1, GL_INT, sizeof(Vertex), (void*)(sizeof(float) * 8));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_instancebuffer);
glVertexAttribPointer(4, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 0));
glVertexAttribPointer(5, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 4));
glVertexAttribPointer(6, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 8));
glVertexAttribPointer(7, 4, GL_FLOAT, false, sizeof(ModelInstance), (void*)(sizeof(float) * 12));
glVertexAttribIPointer(8, 1, GL_INT, sizeof(ModelInstance), (void*)(sizeof(float) * 16));
glBindBuffer(GL_ARRAY_BUFFER, 0);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glEnableVertexAttribArray(4);
glEnableVertexAttribArray(5);
glEnableVertexAttribArray(6);
glEnableVertexAttribArray(7);
glEnableVertexAttribArray(8);
// sent these attributes only once per instance to the program:
glVertexAttribDivisor(4, 1);
glVertexAttribDivisor(5, 1);
glVertexAttribDivisor(6, 1);
glVertexAttribDivisor(7, 1);
glVertexAttribDivisor(8, 1);
glBindVertexArray(0);
// ----------------------------------------------------------------------------------------------------------
// setup vertex buffer
// ----------------------------------------------------------------------------------------------------------
std::vector<Vertex> vertices;
std::vector<Material> materials;
std::vector<std::string> texturesdiffuse = { "" };
// list of all model filenames + base paths
std::list<std::pair<std::string, std::string>> listmodelfiles;
listmodelfiles.push_back({ { "sphere.obj" },{ "" } });
listmodelfiles.push_back({ { "monkey.obj" },{ "" } });
// create models:
for (auto& modelfile : listmodelfiles)
{
Model model;
model.Primitive = GL_TRIANGLES;
model.Offset = vertices.size();
LoadModel(modelfile.first, modelfile.second, vertices, materials, texturesdiffuse);
model.Count = vertices.size() - model.Offset;
// check if model has any vertex data
if (model.Count < 3)
continue;
// now set the size information of this model:
float rmax_sq = 0;
for (unsigned int i = model.Offset; i < model.Offset + model.Count; i++)
{
model.Size.Min.x = min2(model.Size.Min.x, vertices[i].Position.x);
model.Size.Min.y = min2(model.Size.Min.y, vertices[i].Position.y);
model.Size.Min.z = min2(model.Size.Min.z, vertices[i].Position.z);
model.Size.Max.x = max2(model.Size.Max.x, vertices[i].Position.x);
model.Size.Max.y = max2(model.Size.Max.y, vertices[i].Position.y);
model.Size.Max.z = max2(model.Size.Max.z, vertices[i].Position.z);
rmax_sq = max2(rmax_sq, dot(vertices[i].Position, vertices[i].Position));
}
model.Size.RMax = std::sqrt(rmax_sq);
// append model
m_models.push_back(model);
}
// show all model infos:
for (auto& model : m_models)
{
static int index = 0;
std::cout << "model " << index++ << std::endl;
std::cout << "x: " << model.Size.Min.x << " ... " << model.Size.Max.x << std::endl;
std::cout << "y: " << model.Size.Min.y << " ... " << model.Size.Max.y << std::endl;
std::cout << "z: " << model.Size.Min.z << " ... " << model.Size.Max.z << std::endl;
std::cout << "r max: " << model.Size.RMax << std::endl;
std::cout << "vertex count: " << model.Count << std::endl << std::endl;
}
// make sure that there is at least 1 model available:
if (vertices.size() == 0)
{
vertices.push_back({ { 0, 0, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });
vertices.push_back({ { 1, 0, 0 },{ 0, 1 },{ 0, 0, 1 },{ 0 } });
vertices.push_back({ { 0, 1, 0 },{ 0, 0 },{ 0, 0, 1 },{ 0 } });
Model model;
model.Primitive = GL_TRIANGLES;
model.Offset = 0;
model.Count = 3;
m_models.push_back(model);
}
glBindBuffer(GL_ARRAY_BUFFER, m_vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * vertices.size(), vertices.data(), GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// setup instance buffer
// ----------------------------------------------------------------------------------------------------------
glBindBuffer(GL_UNIFORM_BUFFER, m_instancebuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(ModelInstance) * MAX_MODEL_INSTANCES, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// ----------------------------------------------------------------------------------------------------------
// setup material buffer
// ----------------------------------------------------------------------------------------------------------
if (materials.size() == 0)
materials.push_back(Material(vec3(1, 1, 1), vec3(1, 1, 1), vec3(1, 1, 1), 1.0f, 1.0f, 0));
materials.resize(MAX_MATERIALS);
glGenBuffers(1, &m_materialbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_materialbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(Material) * materials.size(), materials.data(), GL_STATIC_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, 1, m_materialbuffer);
// ----------------------------------------------------------------------------------------------------------
// setup light buffer
// ----------------------------------------------------------------------------------------------------------
glGenBuffers(1, &m_directionallightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_directionallightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(DirectionalLight) * MAX_DIRECTIONALLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glGenBuffers(1, &m_pointlightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_pointlightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(PointLight) * MAX_POINTLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
glGenBuffers(1, &m_spotlightbuffer);
glBindBuffer(GL_UNIFORM_BUFFER, m_spotlightbuffer);
glBufferData(GL_UNIFORM_BUFFER, sizeof(SpotLight) * MAX_SPOTLIGHTS, NULL, GL_STREAM_DRAW);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// bind buffers to uniform blocks:
glBindBufferBase(GL_UNIFORM_BUFFER, 2, m_directionallightbuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, 3, m_pointlightbuffer);
glBindBufferBase(GL_UNIFORM_BUFFER, 4, m_spotlightbuffer);
// ----------------------------------------------------------------------------------------------------------
// setup textures
// ----------------------------------------------------------------------------------------------------------
unsigned int mipmapcount = 1;
unsigned int texturewidth = 1024;
unsigned int textureheight = 1024;
unsigned int numberoflayers = texturesdiffuse.size();
struct Pixel { unsigned char r{ 255 }, g{ 255 }, b{ 255 }, a{ 255 }; };
std::vector<Pixel> whitetexture(texturewidth * textureheight);
glGenTextures(1, &m_textureKd);
glBindTexture(GL_TEXTURE_2D_ARRAY, m_textureKd);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// allocate texture memory:
glTexStorage3D(GL_TEXTURE_2D_ARRAY, mipmapcount, GL_RGBA8, texturewidth, textureheight, numberoflayers);
// load all textures Kd:
// there is 1 restriction: all textures MUST be of resolution "texturewidth" x "textureheight"
for (unsigned int i = 0; i < texturesdiffuse.size(); i++)
{
// this c++ class to load images was written by Benjamin Kalytta, http://www.kalytta.com/bitmap.h
CBitmap bmp;
void* texturedata = whitetexture.data();
if (bmp.Load(texturesdiffuse[i].c_str()))
if (bmp.GetWidth() == texturewidth && bmp.GetHeight() == textureheight)
texturedata = bmp.GetBits();
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, texturewidth, textureheight, 1, GL_RGBA, GL_UNSIGNED_BYTE, texturedata);
}
glBindTexture(GL_TEXTURE_2D_ARRAY, 0);
// bind diffuse texture to textureunit 1:
glBindTextureUnit(1, m_textureKd);
glProgramUniform1i(m_program, glGetUniformLocation(m_program, "textureKd"), 1);
// ----------------------------------------------------------------------------------------------------------
}
Renderer_OpenGL::~Renderer_OpenGL()
{
glDeleteProgram(m_program);
glDeleteFramebuffers(1, &m_framebuffer);
glDeleteTextures(1, &m_framebuffercolortexture);
glDeleteTextures(1, &m_framebufferintegertexture);
glDeleteTextures(1, &m_framebufferdepthtexture);
glDeleteVertexArrays(1, &m_vertexarray);
glDeleteBuffers(1, &m_vertexbuffer);
glDeleteBuffers(1, &m_instancebuffer);
glDeleteBuffers(1, &m_materialbuffer);
glDeleteBuffers(1, &m_directionallightbuffer);
glDeleteBuffers(1, &m_pointlightbuffer);
glDeleteBuffers(1, &m_spotlightbuffer);
glDeleteTextures(1, &m_textureKd);
}
void Renderer_OpenGL::Render(const Scene & scene, int& tracked_ID)
{
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
// explicitly clear each buffer:
float clearvalue0[] = { 0.1f, 0.2f, 0.7f, 0.0f }; // background color
int clearvalue1[] = { -1 }; // background integer values
glClearBufferfv(GL_COLOR, 0, clearvalue0);
glClearBufferiv(GL_COLOR, 1, clearvalue1);
glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glUseProgram(m_program);
glBindVertexArray(m_vertexarray);
// set camera
// ----------------------------------------------------------------------------------------------------------
glUniformMatrix4fv(glGetUniformLocation(m_program, "View"), 1, false, value_ptr(scene.Camera.View()));
glUniformMatrix4fv(glGetUniformLocation(m_program, "Projection"), 1, false, value_ptr(perspective(scene.Camera.FoV, 1.33f, scene.Camera.ZNear, scene.Camera.ZFar)));
// ----------------------------------------------------------------------------------------------------------
// set light sources:
// ----------------------------------------------------------------------------------------------------------
glUniform3fv(glGetUniformLocation(m_program, "CameraPosition"), 1, value_ptr(scene.Camera.Position()));
glUniform3fv(glGetUniformLocation(m_program, "Ambient"), 1, value_ptr(scene.Light.Ambient));
unsigned int directionallightcount = min2(MAX_DIRECTIONALLIGHTS, scene.Light.DirectionalLights.size());
unsigned int pointlightcount = min2(MAX_POINTLIGHTS, scene.Light.PointLights.size());
unsigned int spotlightcount = min2(MAX_SPOTLIGHTS, scene.Light.SpotLights.size());
glUniform1i(glGetUniformLocation(m_program, "DirectionalLightCount"), directionallightcount);
glUniform1i(glGetUniformLocation(m_program, "PointLightCount"), pointlightcount);
glUniform1i(glGetUniformLocation(m_program, "SpotLightCount"), spotlightcount);
if (directionallightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_directionallightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(DirectionalLight) * directionallightcount, scene.Light.DirectionalLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
if (pointlightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_pointlightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(PointLight) * pointlightcount, scene.Light.PointLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
if (spotlightcount > 0)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_spotlightbuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SpotLight) * spotlightcount, scene.Light.SpotLights.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
// ----------------------------------------------------------------------------------------------------------
// create instance buffer data for all models:
for (auto& object : scene.Objects)
if (object.ModelIndex < m_models.size())
m_models[object.ModelIndex].Instances.push_back({ object.ModelMatrix(), (int)object.ID() });
// for each model:
for (auto& model : m_models)
{
// determine instance count:
unsigned int instancecount = model.Instances.size();
if (instancecount >= MAX_MODEL_INSTANCES)
instancecount = MAX_MODEL_INSTANCES;
// upload instance buffer data:
glBindBuffer(GL_UNIFORM_BUFFER, m_instancebuffer);
glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(ModelInstance) * instancecount, model.Instances.data());
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// render objects in scene
glDrawArraysInstanced(model.Primitive, model.Offset, model.Count, instancecount);
}
// dont forget to clear the instance data for each model:
for (auto& model : m_models)
{
model.Instances.clear();
model.Instances.reserve(MAX_MODEL_INSTANCES);
}
glBindVertexArray(0);
glUseProgram(0);
// read ID at cursor position:
ivec2 cursorposition = Framework->CursorPosition();
ivec2 windowsize = Framework->WindowSize();
int x = cursorposition.x * (float)m_width / windowsize.x;
int y = cursorposition.y * (float)m_height / windowsize.y;
glReadBuffer(GL_COLOR_ATTACHMENT1);
glReadPixels(x, y, 1, 1, GL_RED_INTEGER, GL_INT, &tracked_ID);
// copy to default framebuffer:
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glBlitFramebuffer(
0, 0, m_width, m_height,
0, 0, windowsize.x, windowsize.y,
GL_COLOR_BUFFER_BIT,
GL_NEAREST);
// check for errors
CheckForGLError();
}