diff --git a/application/lucre/scenes/terrainScene.cpp b/application/lucre/scenes/terrainScene.cpp index 0e5f1f1d..d1a314bb 100644 --- a/application/lucre/scenes/terrainScene.cpp +++ b/application/lucre/scenes/terrainScene.cpp @@ -318,8 +318,8 @@ namespace LucreApp m_CameraController->SetZoomFactor(1.0f); auto& cameraTransform = m_Registry.get(m_Camera); - cameraTransform.SetTranslation({-0.8f, 3.0f, 23.0f}); - cameraTransform.SetRotation({0.0610371f, 6.2623f, 0.0f}); + cameraTransform.SetTranslation({1.792f, 4.220f, -13.696f}); + cameraTransform.SetRotation({-0.074769905f, 3.01f, 0.0f}); // global camera transform is not yet available // because UpdateTransformCache didn't run yet diff --git a/application/lucre/terrainDescriptions/lucre island.json b/application/lucre/terrainDescriptions/lucre island.json index 005c143e..a4480641 100644 --- a/application/lucre/terrainDescriptions/lucre island.json +++ b/application/lucre/terrainDescriptions/lucre island.json @@ -3,12 +3,30 @@ "description": "height map 2", "author": "Copyright (c) 2024 Engine Development Team", "heightMapPath": "application/lucre/models/assets/terrain/lucreIsland1 heightmap.png", - "grassHeightMapPath": "application/lucre/models/assets/terrain/lucreIsland1 heightmap2.png", "colorMapPath": "application/lucre/models/assets/terrain/lucreIsland1 colormap.png", "material": { "roughness": 0.5, "metallic": 0.1 }, - "grass": "application/lucre/models/assets/grass/grass1.glb" + "grass": + { + "modelPath": "application/lucre/models/assets/grass/grass1.glb", + "heightMapPath": "application/lucre/models/assets/terrain/lucreIsland1 heightmap2.png", + "transform": + { + "scale": + [ + 0.13, 0.0376, 0.13 + ], + "rotation": + [ + 3.14159, 0.767164, 3.14159 + ], + "translation": + [ + 4.37885, -1.14346, 59.3405 + ] + } + } } diff --git a/engine/renderer/builder/terrainBuilder.cpp b/engine/renderer/builder/terrainBuilder.cpp index b02089a8..728f1da4 100644 --- a/engine/renderer/builder/terrainBuilder.cpp +++ b/engine/renderer/builder/terrainBuilder.cpp @@ -284,11 +284,12 @@ namespace GfxRenderEngine { // populate landscape // create a height map on the GPU for grass locations and load a grass model - bool fileExists = EngineCore::FileExists(terrainSpec.m_FilepathGrassModel) && - !EngineCore::IsDirectory(terrainSpec.m_FilepathGrassModel); + Terrain::GrassSpec const& grassSpec = terrainSpec.m_GrassSpec; + bool fileExists = EngineCore::FileExists(grassSpec.m_FilepathGrassModel) && + !EngineCore::IsDirectory(grassSpec.m_FilepathGrassModel); if (fileExists) { - Image heightMap(terrainSpec.m_FilepathGrassHeightMap); + Image heightMap(grassSpec.m_FilepathGrassHeightMap); uint heightMapSize = heightMap.Size(); Resources::ResourceBuffers resourceBuffers; { @@ -306,10 +307,10 @@ namespace GfxRenderEngine ubo->WriteToBuffer(bufferData.data()); ubo->Flush(); - FastgltfBuilder builder(terrainSpec.m_FilepathGrassModel, scene, &resourceBuffers); + FastgltfBuilder builder(grassSpec.m_FilepathGrassModel, scene, &resourceBuffers); builder.Load(1 /*1 instance in scene graph (grass has the instance count in the tag)*/); - entt::entity grassEntityRoot = dictionary.Retrieve(terrainSpec.m_FilepathGrassModel + "::0::root"); + entt::entity grassEntityRoot = dictionary.Retrieve(grassSpec.m_FilepathGrassModel + "::0::root"); if (grassEntityRoot != entt::null) { TreeNode rootNode = sceneGraph.GetNodeByGameObject(grassEntityRoot); @@ -319,9 +320,9 @@ namespace GfxRenderEngine registry.emplace(grassNode.GetGameObject(), grassTag); auto& transform = registry.get(grassEntityRoot); - transform.SetRotation({3.14159f, 0.767164f, 3.14159f}); - transform.SetTranslation({4.37885f, -1.14346f, 59.3405f}); - transform.SetScale({0.1299984f, 0.0376f, 0.1299984f}); + transform.SetRotation(terrainSpec.m_GrassSpec.m_Rotation); + transform.SetTranslation(terrainSpec.m_GrassSpec.m_Translation); + transform.SetScale({terrainSpec.m_GrassSpec.m_Scale}); } } } diff --git a/engine/scene/terrain.h b/engine/scene/terrain.h index 7b8b400f..a8a983aa 100644 --- a/engine/scene/terrain.h +++ b/engine/scene/terrain.h @@ -47,14 +47,22 @@ namespace GfxRenderEngine TerrainDescription(std::string const& filename) : m_Filename{filename} {} }; + struct GrassSpec + { + std::string m_FilepathGrassModel; + std::string m_FilepathGrassHeightMap; + glm::vec3 m_Translation{}; + glm::vec3 m_Rotation{}; + glm::vec3 m_Scale{}; + }; + struct TerrainSpec { - Material::PbrMaterial m_PbrMaterial{}; std::string m_FilepathTerrainDescription; std::string m_FilepathHeightMap; - std::string m_FilepathGrassHeightMap; std::string m_FilepathColorMap; - std::string m_FilepathGrassModel; + Material::PbrMaterial m_PbrMaterial{}; + GrassSpec m_GrassSpec; }; } // namespace Terrain diff --git a/engine/scene/terrainLoaderDeserializeJSON.cpp b/engine/scene/terrainLoaderDeserializeJSON.cpp index a99ed409..5296e03c 100644 --- a/engine/scene/terrainLoaderDeserializeJSON.cpp +++ b/engine/scene/terrainLoaderDeserializeJSON.cpp @@ -45,7 +45,7 @@ namespace GfxRenderEngine ondemand::document terrainDocument = parser.iterate(json); ondemand::object terrainAttributess = terrainDocument.get_object(); - Terrain::TerrainSpec terrainSpec{}; + Terrain::TerrainSpec& terrainSpec = m_TerrainDescriptionFile.m_TerrainSpec; terrainSpec.m_FilepathTerrainDescription = filepath; for (auto terrainAttributes : terrainAttributess) @@ -81,32 +81,21 @@ namespace GfxRenderEngine CORE_ASSERT((terrainAttributes.value().type() == ondemand::json_type::string), "heightmap path must be string"); std::string_view heightMapPath = terrainAttributes.value().get_string(); - m_TerrainDescriptionFile.m_FilepathHeightMap = std::string(heightMapPath); - terrainSpec.m_FilepathHeightMap = m_TerrainDescriptionFile.m_FilepathHeightMap; - LOG_CORE_INFO("Heightmap Path: {0}", m_TerrainDescriptionFile.m_FilepathHeightMap); - } - else if (terrainAttributesKey == "grassHeightMapPath") - { - CORE_ASSERT((terrainAttributes.value().type() == ondemand::json_type::string), - "grass heightmap path must be string"); - std::string_view grassHeightMapPath = terrainAttributes.value().get_string(); - m_TerrainDescriptionFile.m_FilepathGrassHeightMap = std::string(grassHeightMapPath); - terrainSpec.m_FilepathGrassHeightMap = m_TerrainDescriptionFile.m_FilepathGrassHeightMap; - LOG_CORE_INFO("Heightmap Path: {0}", m_TerrainDescriptionFile.m_FilepathGrassHeightMap); + terrainSpec.m_FilepathHeightMap = std::string(heightMapPath); + LOG_CORE_INFO("Heightmap Path: {0}", terrainSpec.m_FilepathHeightMap); } else if (terrainAttributesKey == "colorMapPath") { CORE_ASSERT((terrainAttributes.value().type() == ondemand::json_type::string), "colormap path must be string"); std::string_view colorMapPath = terrainAttributes.value().get_string(); - m_TerrainDescriptionFile.m_FilepathColorMap = std::string(colorMapPath); - terrainSpec.m_FilepathColorMap = m_TerrainDescriptionFile.m_FilepathColorMap; - LOG_CORE_INFO("Colormap Path: {0}", m_TerrainDescriptionFile.m_FilepathColorMap); + terrainSpec.m_FilepathColorMap = std::string(colorMapPath); + LOG_CORE_INFO("Colormap Path: {0}", terrainSpec.m_FilepathColorMap); } else if (terrainAttributesKey == "material") { CORE_ASSERT((terrainAttributes.value().type() == ondemand::json_type::object), "type must be object"); - Material::PbrMaterial& pbrMaterial = m_TerrainDescriptionFile.m_PbrMaterial; + Material::PbrMaterial& pbrMaterial = terrainSpec.m_PbrMaterial; ondemand::object materialJSON = terrainAttributes.value(); for (auto materialComponent : materialJSON) { @@ -129,12 +118,10 @@ namespace GfxRenderEngine } else if (terrainAttributesKey == "grass") { - CORE_ASSERT((terrainAttributes.value().type() == ondemand::json_type::string), - "grass 3D model path must be string"); - std::string_view grassModelPath = terrainAttributes.value().get_string(); - m_TerrainDescriptionFile.m_FilepathGrassModel = std::string(grassModelPath); - terrainSpec.m_FilepathGrassModel = m_TerrainDescriptionFile.m_FilepathGrassModel; - LOG_CORE_INFO("Grass Model Path: {0}", m_TerrainDescriptionFile.m_FilepathGrassModel); + CORE_ASSERT((terrainAttributes.value().type() == ondemand::json_type::object), + "grass 3D model path must be object"); + ondemand::object grassSpec = terrainAttributes.value().get_object(); + ParseGrassSpecification(grassSpec); } else { @@ -146,4 +133,132 @@ namespace GfxRenderEngine return builder.LoadTerrain(m_Scene, instanceCount, terrainSpec); } + void TerrainLoaderJSON::ParseGrassSpecification(ondemand::object grassSpecification) + { + Terrain::TerrainSpec& terrainSpec = m_TerrainDescriptionFile.m_TerrainSpec; + Terrain::GrassSpec& grassSpec = terrainSpec.m_GrassSpec; + + for (auto grassAttribute : grassSpecification) + { + std::string_view grassAttributeKey = grassAttribute.unescaped_key(); + + if (grassAttributeKey == "modelPath") + { + CORE_ASSERT((grassAttribute.value().type() == ondemand::json_type::string), + "grass model filepath must be string"); + std::string_view grassModelFilenameStringView = grassAttribute.value().get_string(); + std::string& filepath = grassSpec.m_FilepathGrassModel; + filepath = std::string(grassModelFilenameStringView); + if (EngineCore::FileExists(filepath)) + { + LOG_CORE_INFO("grass model found {0}", filepath); + } + else + { + LOG_CORE_ERROR("grass model not found: {0}", filepath); + return; + } + } + else if (grassAttributeKey == "heightMapPath") + { + CORE_ASSERT((grassAttribute.value().type() == ondemand::json_type::string), + "grass model filepath must be string"); + std::string_view heightmapFilenameStringView = grassAttribute.value().get_string(); + std::string& filepath = grassSpec.m_FilepathGrassHeightMap; + filepath = std::string(heightmapFilenameStringView); + if (EngineCore::FileExists(filepath)) + { + LOG_CORE_INFO("heightmap for grass found {0}", filepath); + } + else + { + LOG_CORE_ERROR("heightmap for grass not found: {0}", filepath); + return; + } + } + else if (grassAttributeKey == "transform") + { + CORE_ASSERT((grassAttribute.value().type() == ondemand::json_type::object), "transform must be object"); + ondemand::object transformJSON = grassAttribute.value().get_object(); + ParseTransform(transformJSON); + } + else + { + LOG_CORE_CRITICAL("unrecognized grass attribute"); + } + } + } + + void TerrainLoaderJSON::ParseTransform(ondemand::object transformJSON) + { + Terrain::TerrainSpec& terrainSpec = m_TerrainDescriptionFile.m_TerrainSpec; + Terrain::GrassSpec& grassSpec = terrainSpec.m_GrassSpec; + + glm::vec3 scale{1.0f}; + glm::vec3 rotation{0.0f}; + glm::vec3 translation{0.0f}; + + for (auto transformComponent : transformJSON) + { + std::string_view transformComponentKey = transformComponent.unescaped_key(); + if (transformComponentKey == "scale") + { + ondemand::array scaleJSON = transformComponent.value(); + scale = ConvertToVec3(scaleJSON); + } + else if (transformComponentKey == "rotation") + { + ondemand::array rotationJSON = transformComponent.value(); + rotation = ConvertToVec3(rotationJSON); + } + else if (transformComponentKey == "translation") + { + ondemand::array translationJSON = transformComponent.value(); + translation = ConvertToVec3(translationJSON); + } + else + { + LOG_CORE_CRITICAL("unrecognized transform component"); + } + } + + grassSpec.m_Scale = scale; + grassSpec.m_Rotation = rotation; + grassSpec.m_Translation = translation; + } + + glm::vec3 TerrainLoaderJSON::ConvertToVec3(ondemand::array arrayJSON) + { + glm::vec3 returnVec3{0.0f}; + uint componentIndex = 0; + for (auto component : arrayJSON) + { + switch (componentIndex) + { + case 0: + { + returnVec3.x = component.get_double(); + break; + } + case 1: + { + returnVec3.y = component.get_double(); + break; + } + case 2: + { + returnVec3.z = component.get_double(); + break; + } + default: + { + LOG_CORE_ERROR("JSON::CConvertToVec3(...) argument must have 3 components"); + break; + } + } + ++componentIndex; + } + return returnVec3; + } + } // namespace GfxRenderEngine diff --git a/engine/scene/terrainLoaderJSON.h b/engine/scene/terrainLoaderJSON.h index 993ddf4a..06b4885d 100644 --- a/engine/scene/terrainLoaderJSON.h +++ b/engine/scene/terrainLoaderJSON.h @@ -29,10 +29,8 @@ using namespace simdjson; #include "engine.h" -#include "scene/fbx.h" -#include "scene/gltf.h" -#include "scene/obj.h" #include "scene/scene.h" +#include "scene/terrain.h" namespace GfxRenderEngine { @@ -52,13 +50,13 @@ namespace GfxRenderEngine double m_FileFormatIdentifier; std::string m_Description; std::string m_Author; - std::string m_FilepathHeightMap; - std::string m_FilepathGrassHeightMap; - std::string m_FilepathColorMap; - std::string m_FilepathGrassModel; - Material::PbrMaterial m_PbrMaterial; + Terrain::TerrainSpec m_TerrainSpec{}; }; + void ParseGrassSpecification(ondemand::object grassSpecification); + void ParseTransform(ondemand::object transformJSON); + glm::vec3 ConvertToVec3(ondemand::array arrayJSON); + private: static constexpr double SUPPORTED_FILE_FORMAT_VERSION = 1.2;