Added korean location
This commit is contained in:
1 parent
d5ef126264
commit
96e748715b
80 files changed
+18949
-80
No files matched your search
+17
-1
@@ -243,7 +243,15 @@ namespace FRG
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning", "resources/shaders/night_fog_skinning_web.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE);
|
||||
|
||||
|
||||
// Instancing variants of the static-object shaders (same sources + INSTANCED define)
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_inst", "resources/shaders/fog.vertex", "resources/shaders/fog_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("darklands_fog_inst", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth_inst", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_shadow_inst", "resources/shaders/fog_shadow_web.vertex", "resources/shaders/fog_shadow_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_inst", "resources/shaders/night_fog_web.vertex", "resources/shaders/night_fog_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow_inst", "resources/shaders/night_fog_shadow_web.vertex", "resources/shaders/night_fog_shadow_web.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
|
||||
renderer.shaderManager.AddShaderFromFiles("text2d",
|
||||
"resources/shaders/text2d.vertex",
|
||||
"resources/shaders/text2d_web.fragment",
|
||||
@@ -303,6 +311,14 @@ namespace FRG
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_skinning_shadow", "resources/shaders/night_fog_skinning_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE);
|
||||
|
||||
// Instancing variants of the static-object shaders (same sources + INSTANCED define)
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_inst", "resources/shaders/fog.vertex", "resources/shaders/fog_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("darklands_fog_inst", "resources/shaders/darklands_fog.vertex", "resources/shaders/darklands_fog_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("shadow_depth_inst", "resources/shaders/shadow_depth.vertex", "resources/shaders/shadow_depth_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("fog_shadow_inst", "resources/shaders/fog_shadow.vertex", "resources/shaders/fog_shadow_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_inst", "resources/shaders/night_fog.vertex", "resources/shaders/night_fog_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
renderer.shaderManager.AddShaderFromFiles("night_fog_shadow_inst", "resources/shaders/night_fog_shadow.vertex", "resources/shaders/night_fog_shadow_desktop.fragment", CONST_ZIP_FILE, "#define INSTANCED\n");
|
||||
|
||||
renderer.shaderManager.AddShaderFromFiles("text2d",
|
||||
"resources/shaders/text2d.vertex",
|
||||
"resources/shaders/text2d_desktop.fragment",
|
||||
|
||||
+68
-1
@@ -106,7 +106,11 @@ namespace FRG
|
||||
{
|
||||
|
||||
// Load static game objects
|
||||
gameObjects = GameObjectLoader::loadAndCreateGameObjects(params.gameObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
||||
{
|
||||
StaticObjectSet staticObjects = GameObjectLoader::loadAndCreateGameObjects(params.gameObjectsJsonPath, renderer, CONST_ZIP_FILE);
|
||||
gameObjects = std::move(staticObjects.single);
|
||||
instancedBatches = std::move(staticObjects.batches);
|
||||
}
|
||||
SDL_PumpEvents();
|
||||
|
||||
// Load interactive objects
|
||||
@@ -683,6 +687,27 @@ namespace FRG
|
||||
}
|
||||
|
||||
|
||||
void Location::drawInstancedStatics(const std::string& instancedShaderName, InstancedTexture textureKind,
|
||||
const std::function<void()>& setUniforms)
|
||||
{
|
||||
if (instancedBatches.empty()) return;
|
||||
|
||||
renderer.shaderManager.PushShader(instancedShaderName);
|
||||
if (setUniforms) setUniforms();
|
||||
renderer.SetMatrix();
|
||||
|
||||
for (auto& batch : instancedBatches) {
|
||||
if (textureKind != InstancedTexture::None) {
|
||||
const auto& texture = (textureKind == InstancedTexture::Darklands) ? batch.textureDarklands : batch.texture;
|
||||
if (!texture) continue;
|
||||
glBindTexture(GL_TEXTURE_2D, texture->getTexID());
|
||||
}
|
||||
renderer.DrawInstanced(batch);
|
||||
}
|
||||
|
||||
renderer.shaderManager.PopShader();
|
||||
}
|
||||
|
||||
void Location::drawGame()
|
||||
{
|
||||
|
||||
@@ -718,6 +743,11 @@ namespace FRG
|
||||
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
drawInstancedStatics("fog_inst", InstancedTexture::Color, [&]() {
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePos);
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
});
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.state.isActive) {
|
||||
@@ -812,6 +842,7 @@ namespace FRG
|
||||
for (auto& [name, gameObj] : gameObjects) {
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
drawInstancedStatics("shadow_depth_inst", InstancedTexture::None);
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.state.castShadow && intObj.loadedObject.texture) {
|
||||
@@ -907,6 +938,16 @@ namespace FRG
|
||||
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
#ifndef DEBUG_LIGHT
|
||||
drawInstancedStatics("fog_shadow_inst", InstancedTexture::Color, [&]() {
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform1i("uShadowMap", 1);
|
||||
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePos);
|
||||
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||
renderer.RenderUniform3fv("uLightDir", lightDirCamera.data());
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
});
|
||||
#endif
|
||||
|
||||
CheckGlError(__FILE__, __LINE__);
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
@@ -1004,6 +1045,11 @@ namespace FRG
|
||||
glBindTexture(GL_TEXTURE_2D, gameObj.textureDarklands->getTexID());
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
drawInstancedStatics("darklands_fog_inst", InstancedTexture::Darklands, [&]() {
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePos);
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
});
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.state.isActive) {
|
||||
@@ -1067,6 +1113,7 @@ namespace FRG
|
||||
for (auto& [name, gameObj] : gameObjects) {
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
drawInstancedStatics("shadow_depth_inst", InstancedTexture::None);
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.state.castShadowNight && intObj.loadedObject.texture) {
|
||||
renderer.PushMatrix();
|
||||
@@ -1279,6 +1326,26 @@ namespace FRG
|
||||
glBindTexture(GL_TEXTURE_2D, gameObj.texture->getTexID());
|
||||
renderer.DrawVertexRenderStruct(gameObj.mesh);
|
||||
}
|
||||
drawInstancedStatics(hasShadows ? "night_fog_shadow_inst" : "night_fog_inst", InstancedTexture::Color, [&]() {
|
||||
renderer.RenderUniform1i(textureUniformName, 0);
|
||||
if (hasShadows) {
|
||||
renderer.RenderUniform1i("uShadowMap", 1);
|
||||
renderer.RenderUniformMatrix4fv("uLightFromCamera", false, lightFromCamera.data());
|
||||
renderer.RenderUniform3fv("uLightDir", plDirEye);
|
||||
}
|
||||
renderer.RenderUniform3fv("uPlayerEyePos", playerEyePos);
|
||||
renderer.RenderUniform3fv("uAmbientColor", ambientColor);
|
||||
renderer.RenderUniform3fv("uFogColor", fogColor);
|
||||
renderer.RenderUniform1i("uPointLightCount", lightCount);
|
||||
if (lightCount > 0) {
|
||||
renderer.RenderUniform3fvArray("uPointLightPos[0]", lightCount, plPosEye);
|
||||
renderer.RenderUniform3fvArray("uPointLightDir[0]", lightCount, plDirEye);
|
||||
renderer.RenderUniform3fvArray("uPointLightColor[0]", lightCount, plColors);
|
||||
renderer.RenderUniform2fvArray("uPointLightWorldXZ[0]", lightCount, plWorldXZ);
|
||||
renderer.RenderUniform2fvArray("uPointLightLimitXZ[0]", lightCount, plLimitXZ);
|
||||
}
|
||||
renderer.RenderUniform1f("uAlpha", 1.0f);
|
||||
});
|
||||
|
||||
for (auto& intObj : interactiveObjects) {
|
||||
if (intObj.state.isActive) {
|
||||
|
||||
@@ -77,6 +77,9 @@ namespace FRG
|
||||
|
||||
std::unordered_map<std::string, LoadedGameObject> gameObjects;
|
||||
|
||||
// Repeated static objects (same mesh + textures) drawn with one instanced call each.
|
||||
std::vector<InstancedBatch> instancedBatches;
|
||||
|
||||
std::vector<InteractiveObject> interactiveObjects;
|
||||
|
||||
// Shared: the same player moves from location to location and outlives them.
|
||||
@@ -207,6 +210,14 @@ namespace FRG
|
||||
int findInteractiveObjectIndex(const InteractiveObject* obj) const;
|
||||
int findTeleportZoneIndex(const TeleportZone* tz) const;
|
||||
|
||||
enum class InstancedTexture { None, Color, Darklands };
|
||||
|
||||
// Draws instancedBatches with the instancing variant of the current pass shader
|
||||
// (e.g. "fog_inst"). The variant is a separate program, so setUniforms must re-send
|
||||
// the pass uniforms; the matrix stack tops are uploaded here.
|
||||
void drawInstancedStatics(const std::string& instancedShaderName, InstancedTexture textureKind,
|
||||
const std::function<void()>& setUniforms = {});
|
||||
|
||||
void resolveCharacterCollisions();
|
||||
void updateDynamicReplans(int64_t deltaMs);
|
||||
void loadTeleportZones(const std::string& jsonPath, const char* zipFile);
|
||||
|
||||
+102
-31
@@ -55,6 +55,7 @@ namespace FRG {
|
||||
data.positionY = item.value("positionY", 0.0f);
|
||||
data.positionZ = item.value("positionZ", 0.0f);
|
||||
data.scale = item.value("scale", 1.0f);
|
||||
data.instanced = item.value("instanced", true);
|
||||
return data;
|
||||
}
|
||||
|
||||
@@ -154,6 +155,36 @@ namespace FRG {
|
||||
return st;
|
||||
}
|
||||
|
||||
static VertexDataStruct loadMeshData(const std::string& meshPath, const std::string& zipPath)
|
||||
{
|
||||
if (meshPath.size() > 4 && meshPath.compare(meshPath.size() - 4, 4, ".bin") == 0)
|
||||
return LoadModelFromBinFile(meshPath, zipPath);
|
||||
return LoadFromTextFile02(meshPath, zipPath);
|
||||
}
|
||||
|
||||
static Eigen::Matrix3f makeRotationMatrix(const GameObjectData& data)
|
||||
{
|
||||
constexpr float kDeg2Rad = static_cast<float>(M_PI) / 180.0f;
|
||||
Eigen::Quaternionf rot = Eigen::Quaternionf::Identity();
|
||||
if (data.rotationX != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationX * kDeg2Rad, Eigen::Vector3f::UnitX())) * rot;
|
||||
if (data.rotationY != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationY * kDeg2Rad, Eigen::Vector3f::UnitY())) * rot;
|
||||
if (data.rotationZ != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationZ * kDeg2Rad, Eigen::Vector3f::UnitZ())) * rot;
|
||||
return rot.toRotationMatrix();
|
||||
}
|
||||
|
||||
// Equivalent of what buildLoadedObject + Move() bake into the vertices:
|
||||
// RotateByMatrix computes v^T * R (= R^T * v), then Scale, then Move.
|
||||
static Eigen::Matrix4f makeInstanceMatrix(const GameObjectData& data)
|
||||
{
|
||||
Eigen::Matrix4f m = Eigen::Matrix4f::Identity();
|
||||
m.block<3, 3>(0, 0) = data.scale * makeRotationMatrix(data).transpose();
|
||||
m.block<3, 1>(0, 3) = Eigen::Vector3f(data.positionX, data.positionY, data.positionZ);
|
||||
return m;
|
||||
}
|
||||
|
||||
LoadedGameObject GameObjectLoader::buildLoadedObject(const GameObjectData& data, Renderer& renderer, const std::string& zipPath)
|
||||
{
|
||||
LoadedGameObject obj;
|
||||
@@ -164,20 +195,9 @@ namespace FRG {
|
||||
if (!data.textureDarkandsPath.empty())
|
||||
obj.textureDarklands = renderer.textureManager.LoadFromPng(data.textureDarkandsPath, zipPath);
|
||||
|
||||
if (data.meshPath.size() > 4 && data.meshPath.compare(data.meshPath.size() - 4, 4, ".bin") == 0)
|
||||
obj.mesh.data = LoadModelFromBinFile(data.meshPath, zipPath);
|
||||
else
|
||||
obj.mesh.data = LoadFromTextFile02(data.meshPath, zipPath);
|
||||
obj.mesh.data = loadMeshData(data.meshPath, zipPath);
|
||||
|
||||
constexpr float kDeg2Rad = static_cast<float>(M_PI) / 180.0f;
|
||||
Eigen::Quaternionf rot = Eigen::Quaternionf::Identity();
|
||||
if (data.rotationX != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationX * kDeg2Rad, Eigen::Vector3f::UnitX())) * rot;
|
||||
if (data.rotationY != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationY * kDeg2Rad, Eigen::Vector3f::UnitY())) * rot;
|
||||
if (data.rotationZ != 0.0f)
|
||||
rot = Eigen::Quaternionf(Eigen::AngleAxisf(data.rotationZ * kDeg2Rad, Eigen::Vector3f::UnitZ())) * rot;
|
||||
obj.mesh.data.RotateByMatrix(rot.toRotationMatrix());
|
||||
obj.mesh.data.RotateByMatrix(makeRotationMatrix(data));
|
||||
|
||||
if (data.scale != 1.0f)
|
||||
obj.mesh.data.Scale(data.scale);
|
||||
@@ -189,34 +209,85 @@ namespace FRG {
|
||||
// Public API
|
||||
// -----------------------------------------------------------------------
|
||||
|
||||
std::unordered_map<std::string, LoadedGameObject> GameObjectLoader::loadAndCreateGameObjects(
|
||||
StaticObjectSet GameObjectLoader::loadAndCreateGameObjects(
|
||||
const std::string& jsonPath,
|
||||
Renderer& renderer,
|
||||
const std::string& zipPath)
|
||||
{
|
||||
std::unordered_map<std::string, LoadedGameObject> gameObjects;
|
||||
StaticObjectSet result;
|
||||
|
||||
for (const auto& data : loadFromJson(jsonPath, zipPath)) {
|
||||
// Service the OS message queue between loads so the window doesn't
|
||||
// get flagged as "not responding" during long location loads.
|
||||
SDL_PumpEvents();
|
||||
logger() << "Loading game object: " << data.name << std::endl;
|
||||
try {
|
||||
LoadedGameObject obj = buildLoadedObject(data, renderer, zipPath);
|
||||
std::vector<GameObjectData> allData = loadFromJson(jsonPath, zipPath);
|
||||
|
||||
if (data.positionX != 0.0f || data.positionY != 0.0f || data.positionZ != 0.0f)
|
||||
obj.mesh.data.Move({ data.positionX, data.positionY, data.positionZ });
|
||||
// Group objects sharing mesh + textures (in file order) so they can be drawn instanced.
|
||||
std::vector<std::vector<const GameObjectData*>> groups;
|
||||
std::unordered_map<std::string, size_t> groupIndexByKey;
|
||||
|
||||
obj.mesh.RefreshVBO();
|
||||
gameObjects[data.name] = std::move(obj);
|
||||
logger() << "Successfully loaded: " << data.name << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "GameObjectLoader: Failed to load '" << data.name << "': " << e.what() << std::endl;
|
||||
for (const auto& data : allData) {
|
||||
if (!data.instanced) {
|
||||
groups.push_back({ &data });
|
||||
continue;
|
||||
}
|
||||
std::string key = data.meshPath + '\n' + data.texturePath + '\n' + data.textureDarkandsPath;
|
||||
auto it = groupIndexByKey.find(key);
|
||||
if (it == groupIndexByKey.end()) {
|
||||
groupIndexByKey[key] = groups.size();
|
||||
groups.push_back({ &data });
|
||||
} else {
|
||||
groups[it->second].push_back(&data);
|
||||
}
|
||||
}
|
||||
|
||||
logger() << "Total game objects loaded: " << gameObjects.size() << std::endl;
|
||||
return gameObjects;
|
||||
for (const auto& group : groups) {
|
||||
// Service the OS message queue between loads so the window doesn't
|
||||
// get flagged as "not responding" during long location loads.
|
||||
SDL_PumpEvents();
|
||||
|
||||
const GameObjectData& first = *group.front();
|
||||
|
||||
if (group.size() == 1) {
|
||||
logger() << "Loading game object: " << first.name << std::endl;
|
||||
try {
|
||||
LoadedGameObject obj = buildLoadedObject(first, renderer, zipPath);
|
||||
|
||||
if (first.positionX != 0.0f || first.positionY != 0.0f || first.positionZ != 0.0f)
|
||||
obj.mesh.data.Move({ first.positionX, first.positionY, first.positionZ });
|
||||
|
||||
obj.mesh.RefreshVBO();
|
||||
result.single[first.name] = std::move(obj);
|
||||
logger() << "Successfully loaded: " << first.name << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "GameObjectLoader: Failed to load '" << first.name << "': " << e.what() << std::endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
logger() << "Loading instanced batch: " << first.meshPath << " x" << group.size() << std::endl;
|
||||
try {
|
||||
InstancedBatch batch;
|
||||
batch.name = first.name;
|
||||
batch.texture = renderer.textureManager.LoadFromPng(first.texturePath, zipPath);
|
||||
if (!first.textureDarkandsPath.empty())
|
||||
batch.textureDarklands = renderer.textureManager.LoadFromPng(first.textureDarkandsPath, zipPath);
|
||||
|
||||
// Mesh stays in local space; rotation, scale and position live in the instance matrices.
|
||||
batch.mesh.data = loadMeshData(first.meshPath, zipPath);
|
||||
batch.mesh.RefreshVBO();
|
||||
|
||||
batch.instances.reserve(group.size());
|
||||
for (const GameObjectData* d : group)
|
||||
batch.instances.push_back(makeInstanceMatrix(*d));
|
||||
batch.RefreshInstanceVBO();
|
||||
|
||||
result.batches.push_back(std::move(batch));
|
||||
logger() << "Successfully loaded instanced batch: " << first.meshPath << std::endl;
|
||||
} catch (const std::exception& e) {
|
||||
std::cerr << "GameObjectLoader: Failed to load instanced batch '" << first.meshPath << "': " << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
logger() << "Total game objects loaded: " << result.single.size() << " single, "
|
||||
<< result.batches.size() << " instanced batches" << std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<InteractiveObject> GameObjectLoader::loadAndCreateInteractiveObjects(
|
||||
|
||||
@@ -26,10 +26,20 @@ namespace FRG {
|
||||
float positionY = 0.0f;
|
||||
float positionZ = 0.0f;
|
||||
float scale = 1.0f;
|
||||
// Objects sharing mesh and textures are drawn with one instanced call.
|
||||
// Set "instanced": false in the JSON to keep an object as a standalone mesh.
|
||||
bool instanced = true;
|
||||
|
||||
LoadedGameObjectState toLoadedGameObjectState() const;
|
||||
};
|
||||
|
||||
// Result of loading a static object list: unique objects keep their own mesh,
|
||||
// repeated ones are merged into instanced batches.
|
||||
struct StaticObjectSet {
|
||||
std::unordered_map<std::string, LoadedGameObject> single;
|
||||
std::vector<InstancedBatch> batches;
|
||||
};
|
||||
|
||||
struct InteractiveObjectData {
|
||||
GameObjectData base;
|
||||
float interactionRadius = 2.0f;
|
||||
@@ -86,7 +96,7 @@ namespace FRG {
|
||||
|
||||
class GameObjectLoader {
|
||||
public:
|
||||
static std::unordered_map<std::string, LoadedGameObject> loadAndCreateGameObjects(
|
||||
static StaticObjectSet loadAndCreateGameObjects(
|
||||
const std::string& jsonPath,
|
||||
Renderer& renderer,
|
||||
const std::string& zipPath = ""
|
||||
|
||||
@@ -113,9 +113,12 @@ PFNGLBINDBUFFERBASEPROC glBindBufferBase = NULL;
|
||||
|
||||
PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = NULL;
|
||||
PFNGLBINDVERTEXARRAYPROC glBindVertexArray = NULL;
|
||||
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray = NULL;
|
||||
PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays = NULL;
|
||||
|
||||
#endif
|
||||
PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced = NULL;
|
||||
PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor = NULL;
|
||||
|
||||
#endif
|
||||
|
||||
namespace FRG {
|
||||
|
||||
@@ -308,11 +311,20 @@ namespace FRG {
|
||||
|
||||
glGenVertexArrays = (PFNGLGENVERTEXARRAYSPROC)wglGetProcAddress("glGenVertexArrays");
|
||||
glBindVertexArray = (PFNGLBINDVERTEXARRAYPROC)wglGetProcAddress("glBindVertexArray");
|
||||
glDeleteVertexArray = (PFNGLDELETEVERTEXARRAYSPROC)wglGetProcAddress("glBindVertexArray");
|
||||
glDeleteVertexArrays = (PFNGLDELETEVERTEXARRAYSPROC)wglGetProcAddress("glDeleteVertexArrays");
|
||||
|
||||
if (glGenVertexArrays == NULL ||
|
||||
glBindVertexArray == NULL ||
|
||||
glDeleteVertexArray == NULL)
|
||||
glDeleteVertexArrays == NULL)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
glDrawArraysInstanced = (PFNGLDRAWARRAYSINSTANCEDPROC)wglGetProcAddress("glDrawArraysInstanced");
|
||||
glVertexAttribDivisor = (PFNGLVERTEXATTRIBDIVISORPROC)wglGetProcAddress("glVertexAttribDivisor");
|
||||
|
||||
if (glDrawArraysInstanced == NULL ||
|
||||
glVertexAttribDivisor == NULL)
|
||||
{
|
||||
ok = false;
|
||||
}
|
||||
|
||||
@@ -150,7 +150,11 @@ extern PFNGLBINDBUFFERBASEPROC glBindBufferBase;
|
||||
|
||||
extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays;
|
||||
extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray;
|
||||
extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArray;
|
||||
extern PFNGLDELETEVERTEXARRAYSPROC glDeleteVertexArrays;
|
||||
|
||||
//Instancing (core since OpenGL 3.3)
|
||||
extern PFNGLDRAWARRAYSINSTANCEDPROC glDrawArraysInstanced;
|
||||
extern PFNGLVERTEXATTRIBDIVISORPROC glVertexAttribDivisor;
|
||||
#else
|
||||
|
||||
#endif
|
||||
|
||||
+67
-2
@@ -131,7 +131,7 @@ namespace FRG {
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
#else
|
||||
//Windows
|
||||
glDeleteVertexArray(1, &vao);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
@@ -920,7 +920,73 @@ namespace FRG {
|
||||
glDisableVertexAttribArray(it->second);
|
||||
}
|
||||
|
||||
void InstancedBatch::RefreshInstanceVBO()
|
||||
{
|
||||
if (instances.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!instanceVBO)
|
||||
{
|
||||
instanceVBO = std::make_shared<VBOHolder>();
|
||||
}
|
||||
|
||||
// Matrix4f is column-major with no padding: 16 floats per instance.
|
||||
glBindBuffer(GL_ARRAY_BUFFER, instanceVBO->getBuffer());
|
||||
glBufferData(GL_ARRAY_BUFFER, instances.size() * sizeof(Matrix4f), instances.data(), GL_STATIC_DRAW);
|
||||
}
|
||||
|
||||
void Renderer::DrawVertexRenderStruct(const VertexRenderStruct& VertexRenderStruct)
|
||||
{
|
||||
BindVertexRenderStructAttribs(VertexRenderStruct);
|
||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(VertexRenderStruct.data.PositionData.size()));
|
||||
}
|
||||
|
||||
void Renderer::DrawInstanced(const InstancedBatch& batch)
|
||||
{
|
||||
if (batch.instances.empty() || !batch.instanceVBO || !batch.mesh.positionVBO)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BindVertexRenderStructAttribs(batch.mesh);
|
||||
|
||||
static const char* const instanceAttribNames[4] = { "iModel0", "iModel1", "iModel2", "iModel3" };
|
||||
|
||||
auto shader = shaderManager.GetCurrentShader();
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, batch.instanceVBO->getBuffer());
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
auto it = shader->attribList.find(instanceAttribNames[i]);
|
||||
if (it == shader->attribList.end())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
glVertexAttribPointer(it->second, 4, GL_FLOAT, GL_FALSE, sizeof(Matrix4f),
|
||||
reinterpret_cast<const void*>(static_cast<size_t>(i) * 4 * sizeof(float)));
|
||||
glVertexAttribDivisor(it->second, 1);
|
||||
}
|
||||
|
||||
glDrawArraysInstanced(GL_TRIANGLES, 0,
|
||||
static_cast<GLsizei>(batch.mesh.data.PositionData.size()),
|
||||
static_cast<GLsizei>(batch.instances.size()));
|
||||
|
||||
// The divisor is vertex array state: reset it so that later non-instanced draws
|
||||
// with these attribute locations are not affected.
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
auto it = shader->attribList.find(instanceAttribNames[i]);
|
||||
if (it != shader->attribList.end())
|
||||
{
|
||||
glVertexAttribDivisor(it->second, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Renderer::BindVertexRenderStructAttribs(const VertexRenderStruct& VertexRenderStruct)
|
||||
{
|
||||
#ifndef EMSCRIPTEN
|
||||
#ifndef __ANDROID__
|
||||
@@ -988,7 +1054,6 @@ namespace FRG {
|
||||
|
||||
glBindBuffer(GL_ARRAY_BUFFER, VertexRenderStruct.positionVBO->getBuffer());
|
||||
VertexAttribPointer3fv(vPosition, 0, NULL);
|
||||
glDrawArrays(GL_TRIANGLES, 0, static_cast<GLsizei>(VertexRenderStruct.data.PositionData.size()));
|
||||
}
|
||||
|
||||
void worldToScreenCoordinates(Vector3f objectPos,
|
||||
|
||||
@@ -87,6 +87,22 @@ namespace FRG {
|
||||
void AssignFrom(const VertexDataStruct& v);
|
||||
};
|
||||
|
||||
// One mesh drawn many times with a per-instance model matrix (glDrawArraysInstanced).
|
||||
// The mesh is stored in local space; each instance matrix is fed to the *_inst shaders
|
||||
// as four vec4 attributes (iModel0..iModel3, divisor 1).
|
||||
struct InstancedBatch
|
||||
{
|
||||
std::string name;
|
||||
VertexRenderStruct mesh;
|
||||
std::shared_ptr<Texture> texture;
|
||||
std::shared_ptr<Texture> textureDarklands;
|
||||
|
||||
std::vector<Matrix4f> instances;
|
||||
std::shared_ptr<VBOHolder> instanceVBO;
|
||||
|
||||
void RefreshInstanceVBO();
|
||||
};
|
||||
|
||||
VertexDataStruct CreateRect2D(Vector2f center, Vector2f halfWidthHeight, float zLevel);
|
||||
VertexDataStruct CreatePolygonFloor(const std::vector<Eigen::Vector2f>& polygon, float yLevel, const Vector3f& color);
|
||||
VertexDataStruct CreateRectHorizontalSections2D(Vector2f center, Vector2f halfWidthHeight, float zLevel, size_t sectionCount);
|
||||
@@ -150,6 +166,12 @@ namespace FRG {
|
||||
void DisableVertexAttribArray(const std::string& attribName);
|
||||
|
||||
void DrawVertexRenderStruct(const VertexRenderStruct& VertexRenderStruct);
|
||||
|
||||
// The current shader must be an instancing variant (compiled with INSTANCED).
|
||||
void DrawInstanced(const InstancedBatch& batch);
|
||||
|
||||
protected:
|
||||
void BindVertexRenderStructAttribs(const VertexRenderStruct& VertexRenderStruct);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -160,10 +160,36 @@ namespace FRG {
|
||||
}
|
||||
|
||||
|
||||
static std::string insertDefinesAfterVersion(const std::string &source, const std::string &defines) {
|
||||
if (defines.empty()) {
|
||||
return source;
|
||||
}
|
||||
|
||||
size_t versionPos = source.find("#version");
|
||||
if (versionPos == std::string::npos) {
|
||||
return defines + source;
|
||||
}
|
||||
|
||||
size_t lineEnd = source.find('\n', versionPos);
|
||||
if (lineEnd == std::string::npos) {
|
||||
return source + "\n" + defines;
|
||||
}
|
||||
|
||||
return source.substr(0, lineEnd + 1) + defines + source.substr(lineEnd + 1);
|
||||
}
|
||||
|
||||
void ShaderManager::AddShaderFromFiles(const std::string &shaderName,
|
||||
const std::string &vertexShaderFileName,
|
||||
const std::string &fragmentShaderFileName,
|
||||
const std::string &ZIPFileName) {
|
||||
AddShaderFromFiles(shaderName, vertexShaderFileName, fragmentShaderFileName, ZIPFileName, "");
|
||||
}
|
||||
|
||||
void ShaderManager::AddShaderFromFiles(const std::string &shaderName,
|
||||
const std::string &vertexShaderFileName,
|
||||
const std::string &fragmentShaderFileName,
|
||||
const std::string &ZIPFileName,
|
||||
const std::string &vertexDefines) {
|
||||
#ifdef __ANDROID__
|
||||
__android_log_print(ANDROID_LOG_INFO, "ShaderManager",
|
||||
"Loading shader: %s", shaderName.c_str());
|
||||
@@ -194,6 +220,8 @@ namespace FRG {
|
||||
fragmentShader = readTextFile(fragmentShaderFileName);
|
||||
}
|
||||
|
||||
vertexShader = insertDefinesAfterVersion(vertexShader, vertexDefines);
|
||||
|
||||
shaderResourceMap[shaderName] = std::make_shared<ShaderResource>(vertexShader,
|
||||
fragmentShader);
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ namespace FRG {
|
||||
public:
|
||||
void AddShaderFromFiles(const std::string& shaderName, const std::string& vertexShaderFileName, const std::string& fragmentShaderFileName, const std::string& ZIPFileName = "");
|
||||
|
||||
// Same as above, but vertexDefines (e.g. "#define INSTANCED\n") is inserted into the
|
||||
// vertex shader source right after its #version line (or at the very top if it has none).
|
||||
void AddShaderFromFiles(const std::string& shaderName, const std::string& vertexShaderFileName, const std::string& fragmentShaderFileName, const std::string& ZIPFileName, const std::string& vertexDefines);
|
||||
|
||||
void PushShader(const std::string& shaderName);
|
||||
void PopShader();
|
||||
void EnableVertexAttribArrays();
|
||||
|
||||
Reference in new issue
Block a user