| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- // ----------------------------------------------------------------
- // From Game Programming in C++ by Sanjay Madhav
- // Copyright (C) 2017 Sanjay Madhav. All rights reserved.
- //
- // Released under the BSD License
- // See LICENSE in root directory for full details.
- // ----------------------------------------------------------------
- #include "Renderer.h"
- #include "Texture.h"
- #include "Mesh.h"
- #include <algorithm>
- #include "Shader.h"
- #include "VertexArray.h"
- #include "SpriteComponent.h"
- #include "MeshComponent.h"
- #include <GL/glew.h>
- Renderer::Renderer(Game* game)
- :mGame(game)
- ,mSpriteShader(nullptr)
- ,mMeshShader(nullptr)
- {
- }
- Renderer::~Renderer()
- {
- }
- bool Renderer::Initialize(float screenWidth, float screenHeight)
- {
- mScreenWidth = screenWidth;
- mScreenHeight = screenHeight;
- // Set OpenGL attributes
- // Use the core OpenGL profile
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
- // Specify version 3.3
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
- SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
- // Request a color buffer with 8-bits per RGBA channel
- SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
- SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
- // Enable double buffering
- SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
- // Force OpenGL to use hardware acceleration
- SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
- mWindow = SDL_CreateWindow("Game Programming in C++ (Chapter 9)", 100, 100,
- static_cast<int>(mScreenWidth), static_cast<int>(mScreenHeight), SDL_WINDOW_OPENGL);
- if (!mWindow)
- {
- SDL_Log("Failed to create window: %s", SDL_GetError());
- return false;
- }
- // Create an OpenGL context
- mContext = SDL_GL_CreateContext(mWindow);
- // Initialize GLEW
- glewExperimental = GL_TRUE;
- if (glewInit() != GLEW_OK)
- {
- SDL_Log("Failed to initialize GLEW.");
- return false;
- }
- // On some platforms, GLEW will emit a benign error code,
- // so clear it
- glGetError();
- // Make sure we can create/compile shaders
- if (!LoadShaders())
- {
- SDL_Log("Failed to load shaders.");
- return false;
- }
- // Create quad for drawing sprites
- CreateSpriteVerts();
- return true;
- }
- void Renderer::Shutdown()
- {
- delete mSpriteVerts;
- mSpriteShader->Unload();
- delete mSpriteShader;
- mMeshShader->Unload();
- delete mMeshShader;
- SDL_GL_DeleteContext(mContext);
- SDL_DestroyWindow(mWindow);
- }
- void Renderer::UnloadData()
- {
- // Destroy textures
- for (auto i : mTextures)
- {
- i.second->Unload();
- delete i.second;
- }
- mTextures.clear();
- // Destroy meshes
- for (auto i : mMeshes)
- {
- i.second->Unload();
- delete i.second;
- }
- mMeshes.clear();
- }
- void Renderer::Draw()
- {
- // Set the clear color to light grey
- glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
- // Clear the color buffer
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- // Draw mesh components
- // Enable depth buffering/disable alpha blend
- glEnable(GL_DEPTH_TEST);
- glDisable(GL_BLEND);
- // Set the mesh shader active
- mMeshShader->SetActive();
- // Update view-projection matrix
- mMeshShader->SetMatrixUniform("uViewProj", mView * mProjection);
- // Update lighting uniforms
- SetLightUniforms(mMeshShader);
- for (auto mc : mMeshComps)
- {
- if (mc->GetVisible())
- {
- mc->Draw(mMeshShader);
- }
- }
- // Draw all sprite components
- // Disable depth buffering
- glDisable(GL_DEPTH_TEST);
- // Enable alpha blending on the color buffer
- glEnable(GL_BLEND);
- glBlendEquationSeparate(GL_FUNC_ADD, GL_FUNC_ADD);
- glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ZERO);
- // Set shader/vao as active
- mSpriteShader->SetActive();
- mSpriteVerts->SetActive();
- for (auto sprite : mSprites)
- {
- if (sprite->GetVisible())
- {
- sprite->Draw(mSpriteShader);
- }
- }
- // Swap the buffers
- SDL_GL_SwapWindow(mWindow);
- }
- void Renderer::AddSprite(SpriteComponent* sprite)
- {
- // Find the insertion point in the sorted vector
- // (The first element with a higher draw order than me)
- int myDrawOrder = sprite->GetDrawOrder();
- auto iter = mSprites.begin();
- for (;
- iter != mSprites.end();
- ++iter)
- {
- if (myDrawOrder < (*iter)->GetDrawOrder())
- {
- break;
- }
- }
- // Inserts element before position of iterator
- mSprites.insert(iter, sprite);
- }
- void Renderer::RemoveSprite(SpriteComponent* sprite)
- {
- auto iter = std::find(mSprites.begin(), mSprites.end(), sprite);
- mSprites.erase(iter);
- }
- void Renderer::AddMeshComp(MeshComponent* mesh)
- {
- mMeshComps.emplace_back(mesh);
- }
- void Renderer::RemoveMeshComp(MeshComponent* mesh)
- {
- auto iter = std::find(mMeshComps.begin(), mMeshComps.end(), mesh);
- mMeshComps.erase(iter);
- }
- Texture* Renderer::GetTexture(const std::string& fileName)
- {
- Texture* tex = nullptr;
- auto iter = mTextures.find(fileName);
- if (iter != mTextures.end())
- {
- tex = iter->second;
- }
- else
- {
- tex = new Texture();
- if (tex->Load(fileName))
- {
- mTextures.emplace(fileName, tex);
- }
- else
- {
- delete tex;
- tex = nullptr;
- }
- }
- return tex;
- }
- Mesh* Renderer::GetMesh(const std::string & fileName)
- {
- Mesh* m = nullptr;
- auto iter = mMeshes.find(fileName);
- if (iter != mMeshes.end())
- {
- m = iter->second;
- }
- else
- {
- m = new Mesh();
- if (m->Load(fileName, this))
- {
- mMeshes.emplace(fileName, m);
- }
- else
- {
- delete m;
- m = nullptr;
- }
- }
- return m;
- }
- bool Renderer::LoadShaders()
- {
- // Create sprite shader
- mSpriteShader = new Shader();
- if (!mSpriteShader->Load("Shaders/Sprite.vert", "Shaders/Sprite.frag"))
- {
- return false;
- }
- mSpriteShader->SetActive();
- // Set the view-projection matrix
- Matrix4 viewProj = Matrix4::CreateSimpleViewProj(mScreenWidth, mScreenHeight);
- mSpriteShader->SetMatrixUniform("uViewProj", viewProj);
- // Create basic mesh shader
- mMeshShader = new Shader();
- if (!mMeshShader->Load("Shaders/Phong.vert", "Shaders/Phong.frag"))
- {
- return false;
- }
- mMeshShader->SetActive();
- // Set the view-projection matrix
- mView = Matrix4::CreateLookAt(Vector3::Zero, Vector3::UnitX, Vector3::UnitZ);
- mProjection = Matrix4::CreatePerspectiveFOV(Math::ToRadians(70.0f),
- mScreenWidth, mScreenHeight, 10.0f, 10000.0f);
- mMeshShader->SetMatrixUniform("uViewProj", mView * mProjection);
- return true;
- }
- void Renderer::CreateSpriteVerts()
- {
- float vertices[] = {
- -0.5f, 0.5f, 0.f, 0.f, 0.f, 0.0f, 0.f, 0.f, // top left
- 0.5f, 0.5f, 0.f, 0.f, 0.f, 0.0f, 1.f, 0.f, // top right
- 0.5f,-0.5f, 0.f, 0.f, 0.f, 0.0f, 1.f, 1.f, // bottom right
- -0.5f,-0.5f, 0.f, 0.f, 0.f, 0.0f, 0.f, 1.f // bottom left
- };
- unsigned int indices[] = {
- 0, 1, 2,
- 2, 3, 0
- };
- mSpriteVerts = new VertexArray(vertices, 4, indices, 6);
- }
- void Renderer::SetLightUniforms(Shader* shader)
- {
- // Camera position is from inverted view
- Matrix4 invView = mView;
- invView.Invert();
- shader->SetVectorUniform("uCameraPos", invView.GetTranslation());
- // Ambient light
- shader->SetVectorUniform("uAmbientLight", mAmbientLight);
- // Directional light
- shader->SetVectorUniform("uDirLight.mDirection",
- mDirLight.mDirection);
- shader->SetVectorUniform("uDirLight.mDiffuseColor",
- mDirLight.mDiffuseColor);
- shader->SetVectorUniform("uDirLight.mSpecColor",
- mDirLight.mSpecColor);
- }
- Vector3 Renderer::Unproject(const Vector3& screenPoint) const
- {
- // Convert screenPoint to device coordinates (between -1 and +1)
- Vector3 deviceCoord = screenPoint;
- deviceCoord.x /= (mScreenWidth) * 0.5f;
- deviceCoord.y /= (mScreenHeight) * 0.5f;
- // Transform vector by unprojection matrix
- Matrix4 unprojection = mView * mProjection;
- unprojection.Invert();
- return Vector3::TransformWithPerspDiv(deviceCoord, unprojection);
- }
- void Renderer::GetScreenDirection(Vector3& outStart, Vector3& outDir) const
- {
- // Get start point (in center of screen on near plane)
- Vector3 screenPoint(0.0f, 0.0f, 0.0f);
- outStart = Unproject(screenPoint);
- // Get end point (in center of screen, between near and far)
- screenPoint.z = 0.9f;
- Vector3 end = Unproject(screenPoint);
- // Get direction vector
- outDir = end - outStart;
- outDir.Normalize();
- }
|