Added korean location

This commit is contained in:
Vladislav Khorev committed 2026-10-25 01:29:38 +06:00
1 parent d5ef126264
commit 96e748715b
80 files changed
+18949 -80

No files matched your search

+102 -31
View File
@@ -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(
+11 -1
View File
@@ -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 = ""