#include "Renderer.hpp" #include "GameObject.hpp" #include #include #include #include #include #include #include using namespace std::literals; RenderObject::RenderObject() : mVao{ GL_NONE } { } RenderObject::~RenderObject() { DeleteGLObjects(); } GLuint RenderObject::GetGLVao() const { return mVao; } void RenderObject::RebuildIfNecessary() { if (mVao != GL_NONE) { return; } assert(mIndexBuf != nullptr); assert(mVertexFormat != nullptr); glGenVertexArrays(1, &mVao); glBindVertexArray(mVao); auto& vBindings = mVertexBufBinding.bindings; auto& shaderInfo = mMaterial->GetShader()->GetInfo(); // Setup vertex buffers for (auto& elm : mVertexFormat->elements) { assert(elm.bindingIndex < vBindings.size()); auto& buffer = vBindings[elm.bindingIndex]; int index = shaderInfo.FindInputLocation(elm.semantic); if (index == -1) { continue; } glBindBuffer(GL_ARRAY_BUFFER, buffer->handle); glEnableVertexAttribArray(index); glVertexAttribPointer( index, Tags::VectorLenOf(elm.type), Tags::FindGLType(elm.type), Tags::IsNormalized(elm.type), mVertexFormat->vertexSize, (void*)(uintptr_t)elm.offset); } // Setup index buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBuf->handle); glBindVertexArray(GL_NONE); } void RenderObject::SetMaterial(Material* material) { mMaterial.Attach(material); DeleteGLObjects(); } void RenderObject::UpdateIndexBuffer(GpuIndexBuffer* indexBuffer) { mIndexBuf.Attach(indexBuffer); DeleteGLObjects(); } void RenderObject::UpdateVertexFormat(VertexFormat* vertexFormat) { mVertexFormat.Attach(vertexFormat); DeleteGLObjects(); } void RenderObject::UpdateVertexBufferBindings(BufferBindings** bindingsOut) { *bindingsOut = &mVertexBufBinding; DeleteGLObjects(); } void RenderObject::SetFormat(VertexFormat* vertexFormat, Tags::IndexType indexFormat) { mIndexBuf.Attach(new GpuIndexBuffer()); mIndexBuf->indexType = indexFormat; mIndexBuf->count = 0; mVertexFormat.Attach(vertexFormat); mVertexBufBinding.Clear(); for (auto& element : vertexFormat->elements) { if (mVertexBufBinding.GetBinding(element.bindingIndex) == nullptr) { mVertexBufBinding.SetBinding(element.bindingIndex, new GpuVertexBuffer()); } } } void RenderObject::DeleteGLObjects() { if (mVao != GL_NONE) { glDeleteVertexArrays(1, &mVao); mVao = GL_NONE; } } Renderer::Renderer() : binding_WireframeMaterial{ gDefaultMaterial } // { mRenderOptions[RO_Shading] = true; mRenderOptions[RO_Wireframe] = false; } void Renderer::LoadBindings(const rapidjson::Value& bindings) { if (auto rvWireframe = rapidjson::GetProperty(bindings, "WireframeMaterial"sv)) { Uid uidWireframe; uidWireframe.Read(*rvWireframe); // TODO don't assume binding_WireframeMaterial.Attach(((IresMaterial*)IresManager::instance->FindIres(uidWireframe))->GetInstance()); } } void Renderer::SaveBindings(rapidjson::Value& into, rapidjson::Document& root) const { if (auto ires = binding_WireframeMaterial->GetIres()) { into.AddMember("WireframeMaterial", ires->GetUid().Write(root), root.GetAllocator()); } } void Renderer::BeginFrame(Camera& camera, float currentTime, float deltaTime) { assert(mInsideFrame == false); mInsideFrame = true; mFrame.camera = &camera; mFrame.matrixView = camera.CalcViewMatrix(); mFrame.matrixProj = camera.CalcProjectionMatrix(); mFrame.time = currentTime; mFrame.deltaTime = deltaTime; } void Renderer::EndFrame() { assert(mInsideFrame == true); mInsideFrame = false; } void Renderer::Draw(const RenderObject* objects, const GameObject* gameObject, size_t count) { using namespace Tags; assert(mInsideFrame); // Desired order: proj * view * (translate * rotate * scale) * vec // <----- order of application <----- ^^^ input glm::mat4 objectMatrix(1.0f); objectMatrix = glm::translate(objectMatrix, gameObject->GetPos()); objectMatrix *= glm::toMat4(gameObject->GetRotation()); objectMatrix = glm::scale(objectMatrix, gameObject->GetScale()); auto mvpMatrix = mFrame.matrixProj * mFrame.matrixView * objectMatrix; if (GetRenderOption(RO_Shading)) { // TODO shader grouping // TODO material grouping for (size_t i = 0; i < count; ++i) { auto& object = objects[i]; auto indexBuffer = object.GetIndexBuffer(); auto mat = object.GetMaterial(); auto shader = mat->GetShader(); glUseProgram(shader->GetProgram()); // Material uniforms mat->UseUniforms(); // Next available texture unit ID after all material textures int texIdx = mat->GetTextures().size(); // Autofill uniforms if (shader->autofill_Transform != kInvalidLocation) { glUniformMatrix4fv(shader->autofill_Transform, 1, GL_FALSE, &mvpMatrix[0][0]); } if (shader->autofill_Time != kInvalidLocation) { glUniform1f(shader->autofill_Time, mFrame.time); } if (shader->autofill_DeltaTime != kInvalidLocation) { glUniform1f(shader->autofill_DeltaTime, mFrame.deltaTime); } if (shader->autofill_TextureAtlas != kInvalidLocation && object.autofill_TextureAtlas != nullptr) { glActiveTexture(GL_TEXTURE0 + texIdx); glBindTexture(GL_TEXTURE_2D, object.autofill_TextureAtlas->GetHandle()); glUniform1i(shader->autofill_TextureAtlas, texIdx); ++texIdx; } glBindVertexArray(object.GetGLVao()); glDrawElements(GL_TRIANGLES, indexBuffer->count, indexBuffer->GetIndexTypeGL(), 0); } } if (GetRenderOption(RO_Wireframe)) { auto& mat = *binding_WireframeMaterial; auto& shader = *mat.GetShader(); auto& shaderInfo = shader.GetInfo(); glUseProgram(shader.GetProgram()); mat.UseUniforms(); // TODO reduce calls with consecutive wireframe setting glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); for (size_t i = 0; i < count; ++i) { auto& object = objects[i]; auto& vBindings = object.GetVertexBufferBindings().bindings; auto vf = object.GetVertexFormat(); // Setup vertex buffers for (auto& elm : vf->elements) { assert(elm.bindingIndex < vBindings.size()); auto& buffer = vBindings[elm.bindingIndex]; int index = shaderInfo.FindInputLocation(elm.semantic); if (index == -1) { continue; } glBindBuffer(GL_ARRAY_BUFFER, buffer->handle); glEnableVertexAttribArray(index); glVertexAttribPointer( index, Tags::VectorLenOf(elm.type), Tags::FindGLType(elm.type), Tags::IsNormalized(elm.type), vf->vertexSize, (void*)(uintptr_t)elm.offset); } // Setup index buffer auto indexBuffer = object.GetIndexBuffer(); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer->handle); glDrawElements(GL_TRIANGLES, indexBuffer->count, indexBuffer->GetIndexTypeGL(), 0); } glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); return; } } bool Renderer::GetRenderOption(RenderOption option) const { return mRenderOptions[option]; } void Renderer::SetRenderOption(RenderOption option, bool flag) { mRenderOptions[option] = flag; }