#include "Rendering.h" #include "../Log.h" #include "../../engine/Shared.h" #include "../Global.h" #include "../Instance.h" #include "../Mesh.h" #include "bgfx/defines.h" #include "bx/timer.h" #include #include #include using namespace std::chrono_literals; namespace Game { namespace { static const bgfx::Memory* loadMem(bx::FileReaderI* _reader, const bx::FilePath& _filePath) { if (bx::open(_reader, _filePath)) { uint32_t size = (uint32_t)bx::getSize(_reader); const bgfx::Memory* mem = bgfx::alloc(size + 1); bx::read(_reader, mem->data, size, bx::ErrorAssert{}); bx::close(_reader); mem->data[mem->size - 1] = '\0'; return mem; } Log("Failed to load %s.", _filePath.getCPtr()); return NULL; } bgfx::ShaderHandle loadShader(const char* FILENAME) { const char* shaderPath = "???"; switch (bgfx::getRendererType()) { case bgfx::RendererType::Agc: case bgfx::RendererType::Nvn: case bgfx::RendererType::Count: case bgfx::RendererType::Noop: break; case bgfx::RendererType::Direct3D11: case bgfx::RendererType::Direct3D12: shaderPath = "game/compiled-shaders/dx11/"; break; case bgfx::RendererType::Gnm: shaderPath = "game/compiled-shaders/pssl/"; break; case bgfx::RendererType::Metal: shaderPath = "game/compiled-shaders/metal/"; break; case bgfx::RendererType::OpenGL: shaderPath = "game/compiled-shaders/glsl/"; break; case bgfx::RendererType::OpenGLES: shaderPath = "game/compiled-shaders/essl/"; break; case bgfx::RendererType::Vulkan: shaderPath = "game/compiled-shaders/spirv/"; break; } char buffer[512]{ 0 }; bx::strCopy(buffer, sizeof(buffer), shaderPath); bx::strCat(buffer, sizeof(buffer), FILENAME); bx::strCat(buffer, sizeof(buffer), ".bin"); Log("Loading shader at %s", buffer); FILE* file; for (int32_t i = 0; i < 3; ++i) { file = fopen(buffer, "rb"); if (file == nullptr) { std::this_thread::sleep_for(100ms); break; } fseek(file, 0, SEEK_END); long fileSize = ftell(file); fseek(file, 0, SEEK_SET); const bgfx::Memory* mem = bgfx::alloc(fileSize + 1); fread(mem->data, 1, fileSize, file); mem->data[mem->size - 1] = '\0'; fclose(file); return bgfx::createShader(mem); } Log("Failed to load shader %s", FILENAME); return {}; } } void GameRendering::Setup() { Log("Game rendering setup..."); SharedData& shared = GetShared(); bgfx::Init init; init.type = bgfx::RendererType::Direct3D12; init.debug = true; init.callback = &Callback; init.platformData.nwh = shared.Window.Handle; init.platformData.ndt = nullptr; init.platformData.type = bgfx::NativeWindowHandleType::Default; init.resolution.width = shared.Window.WindowWidth; init.resolution.height = shared.Window.WindowHeight; init.resolution.reset = BGFX_RESET_VSYNC; Log("%i by %i", init.resolution.width, init.resolution.height); if (!bgfx::init(init)) { Log("BGFX setup failed!"); } else { Log("BGFX setup succeded!"); } bgfx::setDebug(BGFX_DEBUG_TEXT); bgfx::setViewClear(0, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x303030ff, 1.0f, 0); LoadMesh(Models[0]); bgfx::ShaderHandle vertexShader = loadShader("vert"); bgfx::ShaderHandle fragmentShader = loadShader("frag"); Materials[0].Shader = bgfx::createProgram(vertexShader, fragmentShader, true); Materials[0].State = 0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS | BGFX_STATE_CULL_CW | BGFX_STATE_MSAA; if (!GetInstance().IsInitialized) { GetInstance().StartTime = bx::getHPCounter(); } } void GameRendering::Update() { SharedData& shared = GetShared(); // Reload shaders if necessary FileChangeNotification* shaderChange = nullptr; if (shared.Dev.ChangedShaderCount > 0) { shared.Dev.ChangedShaderCount = 0; // TODO: when to destroy shader? // bgfx::destroy(Shader); bgfx::ShaderHandle vertexShader = loadShader("vert"); bgfx::ShaderHandle fragmentShader = loadShader("frag"); if (isValid(vertexShader) && isValid(fragmentShader)) { bgfx::ProgramHandle newProgram = bgfx::createProgram(vertexShader, fragmentShader, true); if (isValid(newProgram)) { Materials[0].Shader = newProgram; } else { Log("Failed to load shader!"); } } } GetInstance().GameLevel.Update(); const bx::Vec3 at = { 0.0f, 0.0f, 0.0f }; const bx::Vec3 eye = { 0.0f, 0.0f, -35.0f }; // Set view and projection matrix for view 0. { float view[16]; bx::mtxLookAt(view, eye, at); float proj[16]; bx::mtxProj(proj, 60.0f, float(shared.Window.WindowWidth) / float(shared.Window.WindowHeight), 0.1f, 100.0f, bgfx::getCaps()->homogeneousDepth); bgfx::setViewTransform(0, view, proj); // Set view 0 default viewport. bgfx::setViewRect(0, 0, 0, shared.Window.WindowWidth, shared.Window.WindowHeight); } // This dummy draw call is here to make sure that view 0 is cleared // if no other draw calls are submitted to view 0. bgfx::touch(0); for (int32_t i = 0; i < GetInstance().GameLevel.Cubes.Count; ++i) { Cube* c = GetInstance().GameLevel.Cubes.Get(i); if (c) { bgfx::setTransform(c->Transform.M); Model& currentModel = Models[c->ModelIdx]; Material& currentMaterial = Materials[c->MaterialIdx]; bgfx::setVertexBuffer(0, currentModel.VertexBuffer); bgfx::setIndexBuffer(currentModel.IndexBuffer); bgfx::setState(currentMaterial.State); // Submit primitive for rendering to view 0. bgfx::submit(0, currentMaterial.Shader); } } bgfx::dbgTextPrintf(1, 1, 0x0F, "Time: %.1f", GetInstance().Now); bgfx::dbgTextPrintf(1, 2, 0x0f, "Frame: %u", GetInstance().FrameCounter); bgfx::frame(); } void GameRendering::Shutdown() { bgfx::shutdown(); } }