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();

}