627 lines
22 KiB
C++
627 lines
22 KiB
C++
#include "../../engine/Shared.h"
|
|
#include "../Global.h"
|
|
#include "../Instance.h"
|
|
#include "../Log.h"
|
|
#include "../Mesh.h"
|
|
#include "../Tools.h"
|
|
#include "Rendering.h"
|
|
|
|
#include "Dither.h"
|
|
#include "SDL3/SDL_events.h" // IWYU pragma: keep
|
|
#include "backends/imgui_impl_sdl3.h"
|
|
#include "bgfx/defines.h"
|
|
#include "bx/bx.h"
|
|
#include "bx/debug.h"
|
|
#include "bx/filepath.h"
|
|
#include "bx/string.h"
|
|
#include "bx/timer.h"
|
|
#include <bgfx/bgfx.h>
|
|
#include <bimg/bimg.h>
|
|
#include <bx/file.h>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <thread>
|
|
#include <tracy/Tracy.hpp>
|
|
|
|
#include "imgui-helper.h"
|
|
#include "imgui-style.h"
|
|
#include "imgui.h"
|
|
|
|
using namespace std::chrono_literals;
|
|
|
|
namespace Game
|
|
{
|
|
namespace
|
|
{
|
|
constexpr size_t ChunkSize = 1024;
|
|
constexpr size_t MaxFileSize = ChunkSize * 1024 * 1024;
|
|
constexpr size_t MaxChunkCount = MaxFileSize / ChunkSize;
|
|
|
|
bool BufferedFileRead(FILE* const file, uint8_t* const writePtrIn, size_t& outTotalReadCount)
|
|
{
|
|
uint8_t* writePtr = writePtrIn;
|
|
for (int32_t i = 0; i < MaxChunkCount; ++i)
|
|
{
|
|
size_t readCount = std::fread(writePtr, 1, ChunkSize, file);
|
|
writePtr += readCount;
|
|
outTotalReadCount += readCount;
|
|
|
|
if (readCount != ChunkSize)
|
|
{
|
|
if (std::feof(file))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
int err = std::ferror(file);
|
|
if (err != 0)
|
|
{
|
|
LOG_ERROR("Error reading file: %i", err);
|
|
return false;
|
|
}
|
|
|
|
LOG_ERROR("This should never happen!");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!std::feof(file))
|
|
{
|
|
LOG_ERROR("File too big to be read!");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
const bgfx::Memory* LoadBinaryFile(const char* path, int32_t retryCount = 1)
|
|
{
|
|
FILE* file = nullptr;
|
|
for (int32_t i = 0; i < retryCount; ++i)
|
|
{
|
|
file = std::fopen(path, "rb");
|
|
if (file == nullptr)
|
|
{
|
|
if (i < retryCount - 1)
|
|
{
|
|
std::this_thread::sleep_for(100ms);
|
|
LOG_WARN("Failed to open file, retrying...");
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
LOG_ERROR("Failed to open file!");
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
uint8_t* dataPtr = AllocateScratch(MaxFileSize);
|
|
if (dataPtr == nullptr)
|
|
{
|
|
LOG_ERROR("Failed to load file, exceeded scratch memory! %s", path);
|
|
return nullptr;
|
|
}
|
|
|
|
uint64_t totalReadCount = 0;
|
|
bool success = BufferedFileRead(file, dataPtr, totalReadCount);
|
|
std::fclose(file);
|
|
|
|
if (!success)
|
|
{
|
|
LOG_ERROR("Failed to read file %s", path);
|
|
ResizeLastScratchAlloc(0);
|
|
return nullptr;
|
|
}
|
|
|
|
if (!ResizeLastScratchAlloc(totalReadCount))
|
|
{
|
|
LOG_ERROR("This should never happen!");
|
|
return nullptr;
|
|
}
|
|
|
|
return bgfx::makeRef(dataPtr, totalReadCount);
|
|
}
|
|
|
|
const bgfx::Memory* loadFile(const char* path, bool appendZero = false, int32_t retryCount = 1)
|
|
{
|
|
FILE* file;
|
|
for (int32_t i = 0; i < retryCount; ++i)
|
|
{
|
|
file = std::fopen(path, "rb");
|
|
if (file == nullptr && i < retryCount - 1)
|
|
{
|
|
std::this_thread::sleep_for(100ms);
|
|
LOG_WARN("Failed to open file, retrying...");
|
|
break;
|
|
}
|
|
|
|
fseek(file, 0, SEEK_END);
|
|
long fileSize = ftell(file);
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
long fileSizeX = appendZero ? (fileSize + 1) : fileSize;
|
|
void* rawMem = AllocateScratch(fileSizeX);
|
|
if (rawMem == nullptr)
|
|
{
|
|
LOG_ERROR("Failed to load file, exceeded scratch memory! %s", path);
|
|
return nullptr;
|
|
}
|
|
const bgfx::Memory* mem = bgfx::makeRef(rawMem, fileSizeX);
|
|
fread(mem->data, 1, fileSize, file);
|
|
if (appendZero)
|
|
{
|
|
mem->data[mem->size - 1] = '\0';
|
|
}
|
|
fclose(file);
|
|
|
|
return mem;
|
|
}
|
|
|
|
LOG_WARN("File inaccessible! %s", path);
|
|
return nullptr;
|
|
}
|
|
|
|
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);
|
|
const bgfx::Memory* mem = loadFile(buffer, true, 3);
|
|
|
|
if (mem == nullptr)
|
|
{
|
|
LOG_WARN("Failed to load shader %s", FILENAME);
|
|
return {};
|
|
}
|
|
return bgfx::createShader(mem);
|
|
}
|
|
|
|
bgfx::TextureHandle loadTexture(const bx::FilePath& _filePath,
|
|
uint64_t _flags,
|
|
uint8_t _skip,
|
|
bgfx::TextureInfo* _info,
|
|
bimg::Orientation::Enum* _orientation)
|
|
{
|
|
BX_UNUSED(_skip);
|
|
bgfx::TextureHandle handle = BGFX_INVALID_HANDLE;
|
|
bx::Error err;
|
|
|
|
const bgfx::Memory* data = LoadBinaryFile(_filePath.getCPtr());
|
|
if (data == nullptr || data->data == nullptr || data->size == 0)
|
|
{
|
|
LOG_WARN("Failed to find image %s", _filePath.getCPtr());
|
|
return handle;
|
|
}
|
|
bimg::ImageContainer imageContainer;
|
|
if (!bimg::imageParse(imageContainer, data->data, data->size, &err))
|
|
{
|
|
LOG_WARN("Failed to load image %s", _filePath.getCPtr());
|
|
return handle;
|
|
}
|
|
if (NULL != _orientation)
|
|
{
|
|
*_orientation = imageContainer.m_orientation;
|
|
}
|
|
|
|
// We're working with ktx textures, so the size should be in front of the actual data
|
|
const uint32_t* texSizePtr = reinterpret_cast<uint32_t*>(data->data + imageContainer.m_offset);
|
|
uint8_t* dataPtr = data->data + imageContainer.m_offset + sizeof(uint32_t);
|
|
uint32_t dataSize = data->size - imageContainer.m_offset - sizeof(uint32_t);
|
|
|
|
// Extra sanity check
|
|
if (*texSizePtr != dataSize)
|
|
{
|
|
LOG_WARN("Texture size sanity check failed! %u != %u", texSizePtr, dataSize);
|
|
return handle;
|
|
}
|
|
const bgfx::Memory* mem = bgfx::makeRef(dataPtr, dataSize);
|
|
|
|
if (NULL != _info)
|
|
{
|
|
bgfx::calcTextureSize(*_info,
|
|
uint16_t(imageContainer.m_width),
|
|
uint16_t(imageContainer.m_height),
|
|
uint16_t(imageContainer.m_depth),
|
|
imageContainer.m_cubeMap,
|
|
1 < imageContainer.m_numMips,
|
|
imageContainer.m_numLayers,
|
|
bgfx::TextureFormat::Enum(imageContainer.m_format));
|
|
}
|
|
|
|
if (imageContainer.m_cubeMap)
|
|
{
|
|
handle = bgfx::createTextureCube(uint16_t(imageContainer.m_width),
|
|
1 < imageContainer.m_numMips,
|
|
imageContainer.m_numLayers,
|
|
bgfx::TextureFormat::Enum(imageContainer.m_format),
|
|
_flags,
|
|
mem);
|
|
}
|
|
else if (1 < imageContainer.m_depth)
|
|
{
|
|
handle = bgfx::createTexture3D(uint16_t(imageContainer.m_width),
|
|
uint16_t(imageContainer.m_height),
|
|
uint16_t(imageContainer.m_depth),
|
|
1 < imageContainer.m_numMips,
|
|
bgfx::TextureFormat::Enum(imageContainer.m_format),
|
|
_flags,
|
|
mem);
|
|
}
|
|
else if (bgfx::isTextureValid(0,
|
|
false,
|
|
imageContainer.m_numLayers,
|
|
bgfx::TextureFormat::Enum(imageContainer.m_format),
|
|
_flags))
|
|
{
|
|
handle = bgfx::createTexture2D(uint16_t(imageContainer.m_width),
|
|
uint16_t(imageContainer.m_height),
|
|
1 < imageContainer.m_numMips,
|
|
imageContainer.m_numLayers,
|
|
bgfx::TextureFormat::Enum(imageContainer.m_format),
|
|
_flags,
|
|
mem);
|
|
}
|
|
|
|
if (bgfx::isValid(handle))
|
|
{
|
|
const bx::StringView name(_filePath);
|
|
bgfx::setName(handle, name.getPtr(), name.getLength());
|
|
}
|
|
|
|
return handle;
|
|
}
|
|
|
|
GameRendering* Instance = nullptr;
|
|
} // namespace
|
|
|
|
GameRendering& GameRendering::Get()
|
|
{
|
|
assert(Instance != nullptr);
|
|
return *Instance;
|
|
}
|
|
|
|
void GameRendering::Setup(const RenderingSetup& setup)
|
|
{
|
|
LOG("--- RENDERING STARTUP ---");
|
|
ZoneScopedN("Setup");
|
|
|
|
SetupData = setup;
|
|
|
|
if (Instance != nullptr) LOG_WARN("old rendering wasn't destroyed!");
|
|
Instance = this;
|
|
SharedData& shared = GetShared();
|
|
|
|
bgfx::Init init;
|
|
init.type = bgfx::RendererType::Direct3D12;
|
|
#ifdef _DEBUG
|
|
init.debug = true;
|
|
// init.debug = false;
|
|
#else
|
|
init.debug = false;
|
|
#endif
|
|
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 = ResetFlags;
|
|
|
|
LastWidth = shared.Window.WindowWidth;
|
|
LastHeight = shared.Window.WindowHeight;
|
|
|
|
LOG("%i by %i", init.resolution.width, init.resolution.height);
|
|
|
|
if (!bgfx::init(init))
|
|
{
|
|
LOG_ERROR("BGFX setup failed!");
|
|
}
|
|
else
|
|
{
|
|
LOG("BGFX setup succeded!");
|
|
}
|
|
bgfx::setViewClear(MainViewID, BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH, 0x3399FFff, 1.0f, 0);
|
|
bgfx::setViewRect(MainViewID, 0, 0, shared.Window.WindowWidth, shared.Window.WindowHeight);
|
|
|
|
DefaultSampler = bgfx::createUniform("s_texColor", bgfx::UniformType::Sampler);
|
|
LoadTextures();
|
|
LoadModels(Models, ModelCount);
|
|
|
|
ReloadShaders();
|
|
|
|
if (SetupData.UseImgui)
|
|
{
|
|
imguiCreate();
|
|
SetImguiStyle();
|
|
if (!ImGui_ImplSDL3_InitForOther(shared.Window.SDLWindow))
|
|
{
|
|
LOG_ERROR("Failed to set up imgui implementation!");
|
|
return;
|
|
}
|
|
|
|
// ImGui::GetIO().BackendFlags |= ImGuiBackendFlags_RendererHasViewports;
|
|
ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_DockingEnable;
|
|
// ImGui::GetIO().ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;
|
|
// auto& platIO = ImGui::GetPlatformIO();
|
|
// platIO.Renderer_CreateWindow = TODO;
|
|
// platIO.Platform_DestroyWindow = TODO;
|
|
// platIO.Platform_SetWindowSize = TODO;
|
|
// platIO.Platform_RenderWindow = TODO;
|
|
}
|
|
|
|
GameInstance& inst = GetInstance();
|
|
if (!inst.IsInitialized)
|
|
{
|
|
inst.Time.StartTime = bx::getHPCounter();
|
|
}
|
|
|
|
if (SetupData.UseImgui)
|
|
{
|
|
if (inst.DebugData.ImguiIniSize > 0)
|
|
{
|
|
ImGui::LoadIniSettingsFromMemory(inst.DebugData.ImguiIni, inst.DebugData.ImguiIniSize);
|
|
}
|
|
else
|
|
{
|
|
ImGui::LoadIniSettingsFromDisk("imgui.ini");
|
|
}
|
|
}
|
|
DitherGen(DitherTextures, DitherRecursion);
|
|
}
|
|
|
|
void GameRendering::HandleEvents()
|
|
{
|
|
ZoneScopedN("Handle Events");
|
|
SharedData& shared = GetShared();
|
|
|
|
for (uint16_t i = 0; i < shared.Window.SDLEventCount; ++i)
|
|
{
|
|
if (SetupData.UseImgui)
|
|
{
|
|
ImGui_ImplSDL3_ProcessEvent(&shared.Window.SDLEvents[i]);
|
|
}
|
|
}
|
|
shared.Window.SDLEventCount = 0;
|
|
|
|
// Resize if necessary
|
|
if (shared.Window.WindowWidth != LastWidth || shared.Window.WindowHeight != LastHeight)
|
|
{
|
|
bgfx::reset(shared.Window.WindowWidth, shared.Window.WindowHeight, ResetFlags);
|
|
bgfx::setViewRect(MainViewID, 0, 0, shared.Window.WindowWidth, shared.Window.WindowHeight);
|
|
|
|
LastWidth = shared.Window.WindowWidth;
|
|
LastHeight = shared.Window.WindowHeight;
|
|
}
|
|
|
|
// Reload shaders if necessary
|
|
FileChangeNotification* shaderChange = nullptr;
|
|
if (shared.Dev.ChangedShaderCount > 0)
|
|
{
|
|
char buf[1024]{0};
|
|
for (int32_t i = 0; i < shared.Dev.ChangedShaderCount; ++i)
|
|
{
|
|
wcstombs(buf, shared.Dev.ChangedShaders[i].FileName, sizeof(buf));
|
|
LOG("Changed: %s", buf);
|
|
}
|
|
shared.Dev.ChangedShaderCount = 0;
|
|
|
|
// TODO: when to destroy shader?
|
|
// TODO: only reload changed shaders
|
|
ReloadShaders();
|
|
}
|
|
}
|
|
|
|
void GameRendering::LoadTextures()
|
|
{
|
|
for (int32_t i = 0; i < MaxTextures; ++i)
|
|
{
|
|
Textures[i] = {};
|
|
}
|
|
|
|
bx::Error err;
|
|
bx::DirectoryReader reader{};
|
|
if (!reader.open("textures", &err) || !err.isOk())
|
|
{
|
|
LOG_ERROR("Failed to read textures dir: %s", err.getMessage());
|
|
}
|
|
bx::FileInfo info;
|
|
int32_t textureFilePathCount = 0;
|
|
bx::FilePath textureFilePaths[GameRendering::MaxTextures];
|
|
while (err.isOk())
|
|
{
|
|
int32_t res = reader.read(&info, sizeof(info), &err);
|
|
if (res == 0) break; // EOF
|
|
if (res != sizeof(info))
|
|
{
|
|
LOG_ERROR("Dir iter error: %s", err.getMessage());
|
|
break;
|
|
}
|
|
const bx::StringView ext = info.filePath.getExt();
|
|
bool isDDS = bx::strCmp(ext, ".dds") == 0;
|
|
bool isKTX = bx::strCmp(ext, ".ktx") == 0;
|
|
if ((isDDS || isKTX) && !ext.isEmpty() && info.type == bx::FileType::File)
|
|
{
|
|
if (textureFilePathCount >= GameRendering::MaxTextures)
|
|
{
|
|
LOG_ERROR("Texture limit reached!");
|
|
break;
|
|
}
|
|
textureFilePaths[textureFilePathCount] = info.filePath;
|
|
textureFilePathCount++;
|
|
}
|
|
}
|
|
LOG("Found %u textures!", textureFilePathCount);
|
|
|
|
for (int32_t i = 0; i < textureFilePathCount; ++i)
|
|
{
|
|
bx::FilePath fullPath{"textures"};
|
|
fullPath.join(textureFilePaths[i]);
|
|
Textures[i].RenderHandle =
|
|
loadTexture(fullPath, BGFX_TEXTURE_NONE | BGFX_SAMPLER_NONE, 0, &Textures[i].Info, nullptr);
|
|
Textures[i].SamplerHandle = DefaultSampler;
|
|
Textures[i].TexHandle.TextureIdx = i;
|
|
Textures[i].TexHandle.Asset.Idx = CrcPath(fullPath.getCPtr());
|
|
auto& debug = GetInstance().DebugData;
|
|
if (debug.AssetCount < debug.MaxAssets)
|
|
{
|
|
debug.AssetHandles[debug.AssetCount] = Textures[i].TexHandle.Asset;
|
|
bx::strCopy(debug.AssetHandlePaths[debug.AssetCount],
|
|
sizeof(debug.AssetHandlePaths[debug.AssetCount]),
|
|
fullPath.getCPtr());
|
|
++debug.AssetCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GameRendering::ReloadShaders()
|
|
{
|
|
Materials[Gen::EMaterial::Default] = Material::LoadFromShader("dither/vert", "dither/frag", MainViewID);
|
|
Materials[Gen::EMaterial::UI] = Material::LoadFromShader("normal/vert", "normal/frag", MainViewID);
|
|
}
|
|
|
|
void GameRendering::Update()
|
|
{
|
|
ZoneScopedN("Rendering");
|
|
SharedData& shared = GetShared();
|
|
auto& time = GetInstance().Time;
|
|
|
|
HandleEvents();
|
|
|
|
// Start Rendering
|
|
if (SetupData.UseImgui)
|
|
{
|
|
ZoneScopedN("Imgui Start Frame");
|
|
imguiBeginFrame(20);
|
|
ImGui_ImplSDL3_NewFrame();
|
|
ImGui::DockSpaceOverViewport(0, 0, ImGuiDockNodeFlags_PassthruCentralNode);
|
|
}
|
|
|
|
Tools::RenderDebugUI(*this);
|
|
|
|
GetInstance().GameLevel.Update();
|
|
GetInstance().GameLevel.Render(MainViewID, Models, Materials, Textures);
|
|
|
|
// Finish Frame
|
|
if (SetupData.UseImgui)
|
|
{
|
|
ZoneScopedN("Imgui End Frame");
|
|
imguiEndFrame();
|
|
}
|
|
|
|
if (SetupData.UseImgui && ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
|
{
|
|
ZoneScopedN("Imgui Platform Update");
|
|
ImGui::UpdatePlatformWindows();
|
|
ImGui::RenderPlatformWindowsDefault();
|
|
}
|
|
|
|
{
|
|
ZoneScopedN("BGFX Frame");
|
|
START_PERF();
|
|
bgfx::frame();
|
|
END_PERF(shared.Window.PerfCounters, PerfCounterType::Submit, shared.Window.FrameCounter);
|
|
}
|
|
}
|
|
|
|
void GameRendering::Shutdown()
|
|
{
|
|
ZoneScopedN("Shutdown");
|
|
LOG("--- RENDERING_SHUTDOWN ---");
|
|
|
|
for (int32_t i = 0; i < BX_COUNTOF(Textures); ++i)
|
|
{
|
|
if (isValid(Textures[i].RenderHandle))
|
|
{
|
|
bgfx::destroy(Textures[i].RenderHandle);
|
|
Textures[i].RenderHandle = {bgfx::kInvalidHandle};
|
|
}
|
|
}
|
|
for (int32_t i = 0; i < ModelCount; ++i)
|
|
{
|
|
bgfx::destroy(Models[i].VertexBuffer);
|
|
bgfx::destroy(Models[i].IndexBuffer);
|
|
}
|
|
ModelCount = 0;
|
|
|
|
CleanupDitherData(DitherTextures);
|
|
if (SetupData.UseImgui)
|
|
{
|
|
ImGui::SaveIniSettingsToDisk("imgui.ini");
|
|
auto& debug = GetInstance().DebugData;
|
|
const char* iniData = ImGui::SaveIniSettingsToMemory(reinterpret_cast<uint64_t*>(&debug.ImguiIniSize));
|
|
assert(debug.ImguiIniSize <= BX_COUNTOF(InstanceDebugData::ImguiIni));
|
|
bx::memCopy(debug.ImguiIni, iniData, bx::min(debug.ImguiIniSize, BX_COUNTOF(InstanceDebugData::ImguiIni)));
|
|
ImGui_ImplSDL3_Shutdown();
|
|
imguiDestroy();
|
|
}
|
|
|
|
bgfx::shutdown();
|
|
Instance = nullptr;
|
|
}
|
|
|
|
Material Material::LoadFromShader(const char* vertPath, const char* fragPath, uint16_t view)
|
|
{
|
|
BX_ASSERT(vertPath != nullptr && fragPath != nullptr, "Invalid shader path!");
|
|
bgfx::ShaderHandle vertexShader = loadShader(vertPath);
|
|
bgfx::ShaderHandle fragmentShader = loadShader(fragPath);
|
|
|
|
Material mat;
|
|
mat.Shader = bgfx::createProgram(vertexShader, fragmentShader, true);
|
|
mat.State = 0 | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_WRITE_Z | BGFX_STATE_DEPTH_TEST_LESS |
|
|
BGFX_STATE_CULL_CCW | BGFX_STATE_MSAA;
|
|
mat.Uniforms[Material::UTime] = bgfx::createUniform("u_time", bgfx::UniformType::Vec4);
|
|
mat.Uniforms[Material::UDotColor] = bgfx::createUniform("u_dotColor", bgfx::UniformType::Vec4);
|
|
mat.Uniforms[Material::UTexInfo] = bgfx::createUniform("u_texInfo", bgfx::UniformType::Vec4);
|
|
mat.Uniforms[Material::UBaseColor] = bgfx::createUniform("u_baseColor", bgfx::UniformType::Vec4);
|
|
mat.ViewID = view;
|
|
return mat;
|
|
}
|
|
|
|
Gen::ModelHandle GameRendering::GetModelHandleFromPath(const char* path)
|
|
{
|
|
uint32_t AssetHandle = CrcPath(path);
|
|
for (int32_t i = 0; i < ModelCount; ++i)
|
|
{
|
|
if (Models[i].Handle.Asset.Idx == AssetHandle)
|
|
{
|
|
return Models[i].Handle;
|
|
}
|
|
}
|
|
LOG_WARN("Failed to find model for path %s", path);
|
|
return {};
|
|
}
|
|
|
|
} // namespace Game
|