diff --git a/Editor/OptionsWindow.cpp b/Editor/OptionsWindow.cpp index b0d5c1225..2476db9e6 100644 --- a/Editor/OptionsWindow.cpp +++ b/Editor/OptionsWindow.cpp @@ -216,6 +216,7 @@ void OptionsWindow::Create(EditorComponent* _editor) scene.names.Create(pick.entity) = "collider"; break; case NEW_TERRAIN: + editor->componentsWnd.terrainWnd.entity = pick.entity; editor->componentsWnd.terrainWnd.SetupAssets(); pick.entity = CreateEntity(); scene.terrains.Create(pick.entity) = editor->componentsWnd.terrainWnd.terrain_preset; diff --git a/Editor/TerrainWindow.cpp b/Editor/TerrainWindow.cpp index 8bb83a9f2..bd6892a8e 100644 --- a/Editor/TerrainWindow.cpp +++ b/Editor/TerrainWindow.cpp @@ -629,7 +629,7 @@ void TerrainWindow::Create(EditorComponent* _editor) ClearTransform(); wi::gui::Window::Create(ICON_TERRAIN " Terrain", wi::gui::Window::WindowControls::COLLAPSE | wi::gui::Window::WindowControls::CLOSE); - SetSize(XMFLOAT2(420, 980)); + SetSize(XMFLOAT2(420, 1000)); closeButton.SetTooltip("Delete Terrain."); OnClose([=](wi::gui::EventArgs args) { @@ -1063,6 +1063,58 @@ void TerrainWindow::Create(EditorComponent* _editor) }); AddWidget(®ion3Slider); + materialCombos[wi::terrain::MATERIAL_BASE].Create("Base material: "); + materialCombos[wi::terrain::MATERIAL_SLOPE].Create("Slope material: "); + materialCombos[wi::terrain::MATERIAL_LOW_ALTITUDE].Create("Low altitude material: "); + materialCombos[wi::terrain::MATERIAL_HIGH_ALTITUDE].Create("High altitude material: "); + + for (size_t i = 0; i < arraysize(materialCombos); ++i) + { + materialCombos[i].SetTooltip("Select material entity"); + materialCombos[i].SetSize(XMFLOAT2(wid, hei)); + materialCombos[i].SetPos(XMFLOAT2(x, y += step)); + materialCombos[i].OnSelect([&, i](wi::gui::EventArgs args) { + const Scene& scene = editor->GetCurrentScene(); + wi::ecs::Entity entity = static_cast(args.userdata); + if (entity != INVALID_ENTITY && scene.materials.Contains(entity)) + { + if (terrain->materialEntities[i] != entity) + { + terrain->materialEntities[i] = entity; + terrain->Generation_Restart(); + } + } + else + { + terrain->materialEntities[i] = INVALID_ENTITY; + } + }); + + AddWidget(&materialCombos[i]); + } + + materialCombo_GrassParticle.Create("Grass material: "); + materialCombo_GrassParticle.SetTooltip("Select material entity"); + materialCombo_GrassParticle.SetSize(XMFLOAT2(wid, hei)); + materialCombo_GrassParticle.SetPos(XMFLOAT2(x, y += step)); + materialCombo_GrassParticle.OnSelect([&](wi::gui::EventArgs args) { + const Scene& scene = editor->GetCurrentScene(); + wi::ecs::Entity entity = static_cast(args.userdata); + if (entity != INVALID_ENTITY && scene.materials.Contains(entity)) + { + if (terrain->grassEntity != entity) + { + terrain->grassEntity = entity; + terrain->Generation_Restart(); + } + } + else + { + terrain->grassEntity = INVALID_ENTITY; + } + }); + AddWidget(&materialCombo_GrassParticle); + propsWindow.reset(new PropsWindow(editor)); AddWidget(propsWindow.get()); @@ -1261,6 +1313,35 @@ void TerrainWindow::SetEntity(Entity entity) region2Slider.SetValue(terrain->region2); region3Slider.SetValue(terrain->region3); + auto fillMaterialCombo = [&](wi::gui::ComboBox& comboBox, Entity selected) { + comboBox.ClearItems(); + comboBox.AddItem("NO MATERIAL", INVALID_ENTITY); + for (size_t i = 0; i < scene.materials.GetCount(); ++i) + { + Entity entity = scene.materials.GetEntity(i); + if (scene.names.Contains(entity)) + { + const NameComponent& name = *scene.names.GetComponent(entity); + comboBox.AddItem(name.name, entity); + } + else + { + comboBox.AddItem(std::to_string(entity), entity); + } + + if (selected == entity) + { + comboBox.SetSelectedWithoutCallback(int(i + 1)); + } + } + }; + + for (size_t i = 0; i < arraysize(materialCombos); ++i) + { + fillMaterialCombo(materialCombos[i], terrain->materialEntities[i]); + } + fillMaterialCombo(materialCombo_GrassParticle, terrain->grassEntity); + for (auto& x : terrain->modifiers) { switch (x->type) @@ -1314,23 +1395,37 @@ void TerrainWindow::SetupAssets() return; // Customize terrain generator before it's initialized: - terrain_preset.material_Base.SetRoughness(1); - terrain_preset.material_Base.SetReflectance(0.005f); - terrain_preset.material_Slope.SetRoughness(0.1f); - terrain_preset.material_LowAltitude.SetRoughness(1); - terrain_preset.material_HighAltitude.SetRoughness(1); - terrain_preset.material_Base.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/base.jpg"; - terrain_preset.material_Base.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/base_nor.jpg"; - terrain_preset.material_Slope.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/slope.jpg"; - terrain_preset.material_Slope.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/slope_nor.jpg"; - terrain_preset.material_LowAltitude.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/low_altitude.jpg"; - terrain_preset.material_LowAltitude.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/low_altitude_nor.jpg"; - terrain_preset.material_HighAltitude.textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/high_altitude.jpg"; - terrain_preset.material_HighAltitude.textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/high_altitude_nor.jpg"; - terrain_preset.material_Base.CreateRenderData(); - terrain_preset.material_Slope.CreateRenderData(); - terrain_preset.material_LowAltitude.CreateRenderData(); - terrain_preset.material_HighAltitude.CreateRenderData(); + Scene& currentScene = editor->GetCurrentScene(); + + for (int i = 0; i < wi::terrain::MATERIAL_COUNT; ++i) + { + terrain_preset.materialEntities[i] = CreateEntity(); + currentScene.materials.Create(terrain_preset.materialEntities[i]); + currentScene.Component_Attach(terrain_preset.materialEntities[i], entity); + } + + MaterialComponent* material_Base = currentScene.materials.GetComponent(terrain_preset.materialEntities[wi::terrain::MATERIAL_BASE]); + MaterialComponent* material_Slope = currentScene.materials.GetComponent(terrain_preset.materialEntities[wi::terrain::MATERIAL_SLOPE]); + MaterialComponent* material_LowAltitude = currentScene.materials.GetComponent(terrain_preset.materialEntities[wi::terrain::MATERIAL_LOW_ALTITUDE]); + MaterialComponent* material_HighAltitude = currentScene.materials.GetComponent(terrain_preset.materialEntities[wi::terrain::MATERIAL_HIGH_ALTITUDE]); + + material_Base->SetRoughness(1); + material_Base->SetReflectance(0.005f); + material_Slope->SetRoughness(0.1f); + material_LowAltitude->SetRoughness(1); + material_HighAltitude->SetRoughness(1); + material_Base->textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/base.jpg"; + material_Base->textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/base_nor.jpg"; + material_Slope->textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/slope.jpg"; + material_Slope->textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/slope_nor.jpg"; + material_LowAltitude->textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/low_altitude.jpg"; + material_LowAltitude->textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/low_altitude_nor.jpg"; + material_HighAltitude->textures[MaterialComponent::BASECOLORMAP].name = wi::helper::GetCurrentPath() + "/terrain/high_altitude.jpg"; + material_HighAltitude->textures[MaterialComponent::NORMALMAP].name = wi::helper::GetCurrentPath() + "/terrain/high_altitude_nor.jpg"; + material_Base->CreateRenderData(); + material_Slope->CreateRenderData(); + material_LowAltitude->CreateRenderData(); + material_HighAltitude->CreateRenderData(); std::string terrain_path = wi::helper::GetCurrentPath() + "/terrain/"; wi::config::File config; @@ -1431,43 +1526,42 @@ void TerrainWindow::SetupAssets() } // Grass config: - terrain_preset.material_GrassParticle.alphaRef = 0.75f; - terrain_preset.grass_properties.length = 2; - terrain_preset.grass_properties.frameCount = 2; - terrain_preset.grass_properties.framesX = 1; - terrain_preset.grass_properties.framesY = 2; - terrain_preset.grass_properties.frameStart = 0; - + terrain_preset.grassEntity = CreateEntity(); + currentScene.Component_Attach(terrain_preset.grassEntity, entity); + currentScene.materials.Create(terrain_preset.grassEntity); + currentScene.hairs.Create(terrain_preset.grassEntity); + MaterialComponent* material_Grass = currentScene.materials.GetComponent(terrain_preset.grassEntity); + wi::HairParticleSystem* grass = currentScene.hairs.GetComponent(terrain_preset.grassEntity); wi::config::File grass_config; grass_config.Open(std::string(terrain_path + "grass.ini").c_str()); if (grass_config.Has("texture")) { - terrain_preset.material_GrassParticle.textures[MaterialComponent::BASECOLORMAP].name = terrain_path + grass_config.GetText("texture"); - terrain_preset.material_GrassParticle.CreateRenderData(); + material_Grass->textures[MaterialComponent::BASECOLORMAP].name = terrain_path + grass_config.GetText("texture"); + material_Grass->CreateRenderData(); } if (grass_config.Has("alphaRef")) { - terrain_preset.material_GrassParticle.alphaRef = grass_config.GetFloat("alphaRef"); + material_Grass->alphaRef = grass_config.GetFloat("alphaRef"); } if (grass_config.Has("length")) { - terrain_preset.grass_properties.length = grass_config.GetFloat("length"); + grass->length = grass_config.GetFloat("length"); } if (grass_config.Has("frameCount")) { - terrain_preset.grass_properties.frameCount = grass_config.GetInt("frameCount"); + grass->frameCount = grass_config.GetInt("frameCount"); } if (grass_config.Has("framesX")) { - terrain_preset.grass_properties.framesX = grass_config.GetInt("framesX"); + grass->framesX = grass_config.GetInt("framesX"); } if (grass_config.Has("framesY")) { - terrain_preset.grass_properties.framesY = grass_config.GetInt("framesY"); + grass->framesY = grass_config.GetInt("framesY"); } if (grass_config.Has("frameCount")) { - terrain_preset.grass_properties.frameStart = grass_config.GetInt("frameStart"); + grass->frameStart = grass_config.GetInt("frameStart"); } terrain = &terrain_preset; @@ -1547,6 +1641,11 @@ void TerrainWindow::ResizeLayout() add(region1Slider); add(region2Slider); add(region3Slider); + for (size_t i = 0; i < arraysize(materialCombos); ++i) + { + add(materialCombos[i]); + } + add(materialCombo_GrassParticle); add(saveHeightmapButton); add(saveRegionButton); add(addModifierCombo); diff --git a/Editor/TerrainWindow.h b/Editor/TerrainWindow.h index 08dbcd17d..c9408f762 100644 --- a/Editor/TerrainWindow.h +++ b/Editor/TerrainWindow.h @@ -122,6 +122,9 @@ public: wi::gui::Slider region2Slider; wi::gui::Slider region3Slider; + wi::gui::ComboBox materialCombos[wi::terrain::MATERIAL_COUNT]; + wi::gui::ComboBox materialCombo_GrassParticle; + std::unique_ptr propsWindow; enum PRESET diff --git a/WickedEngine/wiECS.h b/WickedEngine/wiECS.h index 5cc531748..d9f20d67d 100644 --- a/WickedEngine/wiECS.h +++ b/WickedEngine/wiECS.h @@ -190,24 +190,24 @@ namespace wi::ecs { if (archive.IsReadMode()) { - Clear(); // If we deserialize, we start from empty + const size_t prev_count = components.size(); size_t count; archive >> count; - components.resize(count); + components.resize(prev_count + count); for (size_t i = 0; i < count; ++i) { - components[i].Serialize(archive, seri); + components[prev_count + i].Serialize(archive, seri); } - entities.resize(count); + entities.resize(prev_count + count); for (size_t i = 0; i < count; ++i) { Entity entity; SerializeEntity(archive, entity, seri); - entities[i] = entity; - lookup[entity] = i; + entities[prev_count + i] = entity; + lookup[entity] = prev_count + i; } } else @@ -462,13 +462,39 @@ namespace wi::ecs // The name must be unique, it will be used in serialization // version is optional, it will be propagated to ComponentManager::Serialize() inside the EntitySerializer parameter template - inline ComponentManager& Register(std::string name, uint64_t version = 0) + inline ComponentManager& Register(const std::string& name, uint64_t version = 0) { entries[name].component_manager = std::make_unique>(); entries[name].version = version; return static_cast&>(*entries[name].component_manager); } + template + inline ComponentManager* Get(const std::string& name) + { + auto it = entries.find(name); + if (it == entries.end()) + return nullptr; + return static_cast*>(it->second.component_manager.get()); + } + + template + inline const ComponentManager* Get(const std::string& name) const + { + auto it = entries.find(name); + if (it == entries.end()) + return nullptr; + return static_cast*>(it->second.component_manager.get()); + } + + inline uint64_t GetVersion(std::string name) const + { + auto it = entries.find(name); + if (it == entries.end()) + return 0; + return it->second.version; + } + // Serialize all registered component managers inline void Serialize(wi::Archive& archive, EntitySerializer& seri) { diff --git a/WickedEngine/wiScene.h b/WickedEngine/wiScene.h index 167cb655e..a345f9265 100644 --- a/WickedEngine/wiScene.h +++ b/WickedEngine/wiScene.h @@ -57,7 +57,7 @@ namespace wi::scene wi::ecs::ComponentManager& scripts = componentLibrary.Register("wi::scene::Scene::scripts"); wi::ecs::ComponentManager& expressions = componentLibrary.Register("wi::scene::Scene::expressions"); wi::ecs::ComponentManager& humanoids = componentLibrary.Register("wi::scene::Scene::humanoids", 1); // version = 1 - wi::ecs::ComponentManager& terrains = componentLibrary.Register("wi::scene::Scene::terrains", 3); // version = 3 + wi::ecs::ComponentManager& terrains = componentLibrary.Register("wi::scene::Scene::terrains", 4); // version = 4 wi::ecs::ComponentManager& sprites = componentLibrary.Register("wi::scene::Scene::sprites"); wi::ecs::ComponentManager& fonts = componentLibrary.Register("wi::scene::Scene::fonts"); wi::ecs::ComponentManager& voxel_grids = componentLibrary.Register("wi::scene::Scene::voxel_grids"); diff --git a/WickedEngine/wiTerrain.cpp b/WickedEngine/wiTerrain.cpp index 4adc25128..0c7d6a8e5 100644 --- a/WickedEngine/wiTerrain.cpp +++ b/WickedEngine/wiTerrain.cpp @@ -410,6 +410,27 @@ namespace wi::terrain Generation_Cancel(); generator->scene.Clear(); + // save material parameters: + wi::scene::MaterialComponent materials[MATERIAL_COUNT]; + for (int i = 0; i < MATERIAL_COUNT; ++i) + { + MaterialComponent* material = scene->materials.GetComponent(materialEntities[i]); + if (material == nullptr) + continue; + materials[i] = *material; + materials[i].SetDirty(false); + } + + // save grass parameters: + if (scene->hairs.Contains(grassEntity)) + { + grass_properties = *scene->hairs.GetComponent(grassEntity); + } + if (scene->materials.Contains(grassEntity)) + { + grass_material = *scene->materials.GetComponent(grassEntity); + grass_material.SetDirty(false); + } for (auto it = chunks.begin(); it != chunks.end(); it++) { @@ -463,6 +484,83 @@ namespace wi::terrain transform.RotateRollPitchYaw(XMFLOAT3(XM_PIDIV4, 0, XM_PIDIV4)); transform.Translate(XMFLOAT3(0, 4, 0)); } + + // Restore surface source materials: + { + for (int i = 0; i < MATERIAL_COUNT; ++i) + { + if (materialEntities[i] == INVALID_ENTITY) + { + materialEntities[i] = CreateEntity(); + } + scene->Component_Attach(materialEntities[i], terrainEntity); + if (!scene->materials.Contains(materialEntities[i])) + { + scene->materials.Create(materialEntities[i]); + } + if (!scene->names.Contains(materialEntities[i])) + { + NameComponent& name = scene->names.Create(materialEntities[i]); + switch (i) + { + default: + case MATERIAL_BASE: + name = "MATERIAL_BASE"; + break; + case MATERIAL_SLOPE: + name = "MATERIAL_SLOPE"; + break; + case MATERIAL_LOW_ALTITUDE: + name = "MATERIAL_LOW_ALTITUDE"; + break; + case MATERIAL_HIGH_ALTITUDE: + name = "MATERIAL_HIGH_ALTITUDE"; + break; + } + } + } + + MaterialComponent* material_Base = scene->materials.GetComponent(materialEntities[MATERIAL_BASE]); + MaterialComponent* material_Slope = scene->materials.GetComponent(materialEntities[MATERIAL_SLOPE]); + MaterialComponent* material_LowAltitude = scene->materials.GetComponent(materialEntities[MATERIAL_LOW_ALTITUDE]); + MaterialComponent* material_HighAltitude = scene->materials.GetComponent(materialEntities[MATERIAL_HIGH_ALTITUDE]); + + *material_Base = materials[MATERIAL_BASE]; + *material_Slope = materials[MATERIAL_SLOPE]; + *material_LowAltitude = materials[MATERIAL_LOW_ALTITUDE]; + *material_HighAltitude = materials[MATERIAL_HIGH_ALTITUDE]; + } + + // Restore grass parameters: + { + if (grassEntity == INVALID_ENTITY) + { + grassEntity = CreateEntity(); + } + scene->Component_Attach(grassEntity, terrainEntity); + if (!scene->hairs.Contains(grassEntity)) + { + scene->hairs.Create(grassEntity) = grass_properties; + } + if (!scene->materials.Contains(grassEntity)) + { + scene->materials.Create(grassEntity) = grass_material; + } + if (!scene->names.Contains(grassEntity)) + { + scene->names.Create(grassEntity) = "grass"; + } + } + + if (chunkGroupEntity == INVALID_ENTITY) + { + chunkGroupEntity = CreateEntity(); + } + scene->Component_Attach(chunkGroupEntity, terrainEntity); + if (!scene->names.Contains(chunkGroupEntity)) + { + scene->names.Create(chunkGroupEntity) = "chunks"; + } } void Terrain::Generation_Update(const CameraComponent& camera) @@ -470,9 +568,10 @@ namespace wi::terrain // The generation task is always cancelled every frame so we are sure that generation is not running at this point Generation_Cancel(); + bool restart_generation = false; if (!IsGenerationStarted()) { - Generation_Restart(); + restart_generation = true; } // Check whether any modifiers need to be removed, and we will really remove them here if so: @@ -489,9 +588,36 @@ namespace wi::terrain } } } - Generation_Restart(); + restart_generation = true; modifiers_to_remove.clear(); } + for (wi::ecs::Entity entity : materialEntities) + { + MaterialComponent* material = scene->materials.GetComponent(entity); + if (material == nullptr) + continue; + if (material->IsDirty()) + { + restart_generation = true; + break; + } + } + if (grassEntity != INVALID_ENTITY) + { + MaterialComponent* material_grassparticle_in_scene = scene->materials.GetComponent(grassEntity); + if (material_grassparticle_in_scene != nullptr) + { + if (material_grassparticle_in_scene->IsDirty()) + { + restart_generation = true; + } + } + } + + if (restart_generation) + { + Generation_Restart(); + } if (terrainEntity == INVALID_ENTITY) { @@ -530,34 +656,50 @@ namespace wi::terrain // Check whether there are any materials that would write to virtual textures: bool virtual_texture_any = false; bool virtual_texture_available[TEXTURESLOT_COUNT] = {}; - MaterialComponent* virtual_materials[4] = { - &material_Base, - &material_Slope, - &material_LowAltitude, - &material_HighAltitude, - }; - for (auto& material : virtual_materials) + + if (scene->materials.GetCount() > 0) { - for (int i = 0; i < TEXTURESLOT_COUNT; ++i) + for (wi::ecs::Entity entity : materialEntities) { - virtual_texture_available[i] = false; - switch (i) + MaterialComponent* material = scene->materials.GetComponent(entity); + if (material == nullptr) + continue; + + for (int i = 0; i < TEXTURESLOT_COUNT; ++i) { - case MaterialComponent::BASECOLORMAP: - case MaterialComponent::NORMALMAP: - case MaterialComponent::SURFACEMAP: - if (material->textures[i].resource.IsValid()) + virtual_texture_available[i] = false; + switch (i) { - virtual_texture_available[i] = true; - virtual_texture_any = true; + case MaterialComponent::BASECOLORMAP: + case MaterialComponent::NORMALMAP: + case MaterialComponent::SURFACEMAP: + if (material->textures[i].resource.IsValid()) + { + virtual_texture_available[i] = true; + virtual_texture_any = true; + } + break; + default: + break; } - break; - default: - break; + } + } + virtual_texture_available[MaterialComponent::SURFACEMAP] = true; // this is always needed to bake individual material properties + + if (grassEntity != INVALID_ENTITY) + { + MaterialComponent* material_grassparticle_in_scene = scene->materials.GetComponent(grassEntity); + if (material_grassparticle_in_scene != nullptr) + { + grass_material = *material_grassparticle_in_scene; + } + HairParticleSystem* hair = scene->hairs.GetComponent(grassEntity); + if (hair != nullptr) + { + grass_properties = *hair; } } } - virtual_texture_available[MaterialComponent::SURFACEMAP] = true; // this is always needed to bake individual material properties for (auto it = chunks.begin(); it != chunks.end();) { @@ -734,7 +876,7 @@ namespace wi::terrain ObjectComponent& object = *generator->scene.objects.GetComponent(chunk_data.entity); object.lod_distance_multiplier = lod_multiplier; object.filterMask |= wi::enums::FILTER_NAVIGATION_MESH; - generator->scene.Component_Attach(chunk_data.entity, terrainEntity); + generator->scene.Component_Attach(chunk_data.entity, chunkGroupEntity); TransformComponent& transform = *generator->scene.transforms.GetComponent(chunk_data.entity); transform.ClearTransform(); @@ -899,7 +1041,7 @@ namespace wi::terrain chunk_data.grass_density_current = grass_density; grass.strandCount = uint32_t(grass.strandCount * chunk_data.grass_density_current); grass.CreateRenderData(); - generator->scene.materials.Create(chunk_data.grass_entity) = material_GrassParticle; + generator->scene.materials.Create(chunk_data.grass_entity) = grass_material; generator->scene.transforms.Create(chunk_data.grass_entity); generator->scene.names.Create(chunk_data.grass_entity) = "grass"; generator->scene.Component_Attach(chunk_data.grass_entity, chunk_data.entity, true); @@ -1442,11 +1584,14 @@ namespace wi::terrain device->EventBegin("Render Tile Regions", cmd); - ShaderMaterial materials[4]; - material_Base.WriteShaderMaterial(&materials[0]); - material_Slope.WriteShaderMaterial(&materials[1]); - material_LowAltitude.WriteShaderMaterial(&materials[2]); - material_HighAltitude.WriteShaderMaterial(&materials[3]); + ShaderMaterial materials[MATERIAL_COUNT]; + for (size_t i = 0; i < MATERIAL_COUNT && scene->materials.GetCount() > 0; ++i) + { + const MaterialComponent* material = scene->materials.GetComponent(materialEntities[i]); + if (material == nullptr) + continue; + material->WriteShaderMaterial(&materials[i]); + } device->BindDynamicConstantBuffer(materials, 0, cmd); for (uint32_t map_type = 0; map_type < 3; map_type++) @@ -1642,10 +1787,11 @@ namespace wi::terrain Generation_Cancel(); // Note: separate component types serialized within terrain must NOT use the version of the terrain, but their own! + ComponentLibrary& library = *seri.componentlibrary; const uint64_t terrain_version = seri.GetVersion(); - const uint64_t grass_version = seri.componentlibrary->entries["wi::scene::Scene::hairs"].version; - const uint64_t material_version = seri.componentlibrary->entries["wi::scene::Scene::materials"].version; - const uint64_t weather_version = seri.componentlibrary->entries["wi::scene::Scene::weathers"].version; + const uint64_t grass_version = library.GetVersion("wi::scene::Scene::hairs"); + const uint64_t material_version = library.GetVersion("wi::scene::Scene::materials"); + const uint64_t weather_version = library.GetVersion("wi::scene::Scene::weathers"); if (archive.IsReadMode()) { @@ -1671,11 +1817,11 @@ namespace wi::terrain archive >> center_chunk.x; archive >> center_chunk.z; - if (seri.GetVersion() >= 1) + if (terrain_version >= 1) { archive >> physics_generation; } - if (seri.GetVersion() >= 2 && seri.GetVersion() < 3) + if (terrain_version >= 2 && terrain_version < 3) { uint32_t target_texture_resolution; archive >> target_texture_resolution; @@ -1687,7 +1833,7 @@ namespace wi::terrain for (size_t i = 0; i < props.size(); ++i) { Prop& prop = props[i]; - if (seri.GetVersion() >= 1) + if (terrain_version >= 1) { archive >> prop.data; @@ -1834,7 +1980,7 @@ namespace wi::terrain { archive << _flags; archive << lod_multiplier; - if (seri.GetVersion() < 3) + if (terrain_version < 3) { float texlod = 1; archive << texlod; @@ -1854,11 +2000,11 @@ namespace wi::terrain archive << center_chunk.x; archive << center_chunk.z; - if (seri.GetVersion() >= 1) + if (terrain_version >= 1) { archive << physics_generation; } - if (seri.GetVersion() >= 2 && seri.GetVersion() < 3) + if (terrain_version >= 2 && terrain_version < 3) { uint32_t target_texture_resolution = 1024; archive << target_texture_resolution; @@ -1940,19 +2086,60 @@ namespace wi::terrain } // Caution: seri.version changes must be handled carefully! - seri.version = material_version; - material_Base.Serialize(archive, seri); - material_Slope.Serialize(archive, seri); - material_LowAltitude.Serialize(archive, seri); - material_HighAltitude.Serialize(archive, seri); - material_GrassParticle.Serialize(archive, seri); + + if (terrain_version >= 4) + { + SerializeEntity(archive, chunkGroupEntity, seri); + for (size_t i = 0; i < MATERIAL_COUNT; ++i) + { + SerializeEntity(archive, materialEntities[i], seri); + } + SerializeEntity(archive, grassEntity, seri); + } + else + { + // Convert terrain version below 4 to newer version: + seri.version = material_version; + wi::scene::MaterialComponent materials[MATERIAL_COUNT]; + materials[MATERIAL_BASE].Serialize(archive, seri); + materials[MATERIAL_SLOPE].Serialize(archive, seri); + materials[MATERIAL_LOW_ALTITUDE].Serialize(archive, seri); + materials[MATERIAL_HIGH_ALTITUDE].Serialize(archive, seri); + grass_material.Serialize(archive, seri); + wi::jobsystem::Wait(seri.ctx); // wait for material CreateRenderData() that was asynchronously launched in Serialize()! + + materialEntities[0] = CreateEntity(); + materialEntities[1] = CreateEntity(); + materialEntities[2] = CreateEntity(); + materialEntities[3] = CreateEntity(); + grassEntity = CreateEntity(); + + seri.remap[materialEntities[0]] = materialEntities[0]; + seri.remap[materialEntities[1]] = materialEntities[1]; + seri.remap[materialEntities[2]] = materialEntities[2]; + seri.remap[materialEntities[3]] = materialEntities[3]; + seri.remap[grassEntity] = grassEntity; + + ComponentManager* scene_materials = library.Get("wi::scene::Scene::materials"); + scene_materials->Create(materialEntities[0]) = materials[MATERIAL_BASE]; + scene_materials->Create(materialEntities[1]) = materials[MATERIAL_SLOPE]; + scene_materials->Create(materialEntities[2]) = materials[MATERIAL_LOW_ALTITUDE]; + scene_materials->Create(materialEntities[3]) = materials[MATERIAL_HIGH_ALTITUDE]; + scene_materials->Create(grassEntity) = grass_material; + } seri.version = weather_version; weather.Serialize(archive, seri); - seri.version = grass_version; - grass_properties.Serialize(archive, seri); - seri.version = terrain_version; + if (terrain_version < 4) + { + seri.version = grass_version; + grass_properties.Serialize(archive, seri); + seri.version = terrain_version; + + ComponentManager* scene_hairs = library.Get("wi::scene::Scene::hairs"); + scene_hairs->Create(grassEntity) = grass_properties; + } perlin_noise.Serialize(archive); } diff --git a/WickedEngine/wiTerrain.h b/WickedEngine/wiTerrain.h index de7791437..7a3d9abc4 100644 --- a/WickedEngine/wiTerrain.h +++ b/WickedEngine/wiTerrain.h @@ -43,6 +43,14 @@ namespace wi::terrain static constexpr float chunk_half_width = (chunk_width - 1) * 0.5f; static constexpr float chunk_width_rcp = 1.0f / (chunk_width - 1); static constexpr uint32_t vertexCount = chunk_width * chunk_width; + enum + { + MATERIAL_BASE, + MATERIAL_SLOPE, + MATERIAL_LOW_ALTITUDE, + MATERIAL_HIGH_ALTITUDE, + MATERIAL_COUNT + }; struct VirtualTextureAtlas { @@ -207,14 +215,13 @@ namespace wi::terrain uint32_t _flags = CENTER_TO_CAM | REMOVAL | GRASS; wi::ecs::Entity terrainEntity = wi::ecs::INVALID_ENTITY; + wi::ecs::Entity chunkGroupEntity = wi::ecs::INVALID_ENTITY; wi::scene::Scene* scene = nullptr; - wi::scene::MaterialComponent material_Base; - wi::scene::MaterialComponent material_Slope; - wi::scene::MaterialComponent material_LowAltitude; - wi::scene::MaterialComponent material_HighAltitude; - wi::scene::MaterialComponent material_GrassParticle; + wi::ecs::Entity materialEntities[MATERIAL_COUNT] = {}; + wi::ecs::Entity grassEntity = wi::ecs::INVALID_ENTITY; wi::scene::WeatherComponent weather; wi::HairParticleSystem grass_properties; + wi::scene::MaterialComponent grass_material; wi::unordered_map chunks; Chunk center_chunk = {}; wi::noise::Perlin perlin_noise; diff --git a/WickedEngine/wiVersion.cpp b/WickedEngine/wiVersion.cpp index dc8f80814..4d3a6855f 100644 --- a/WickedEngine/wiVersion.cpp +++ b/WickedEngine/wiVersion.cpp @@ -9,7 +9,7 @@ namespace wi::version // minor features, major updates, breaking compatibility changes const int minor = 71; // minor bug fixes, alterations, refactors, updates - const int revision = 398; + const int revision = 399; const std::string version_string = std::to_string(major) + "." + std::to_string(minor) + "." + std::to_string(revision);