diff --git a/src/editor/world_editor.cpp b/src/editor/world_editor.cpp index 1324c6172..f1f6a9a79 100644 --- a/src/editor/world_editor.cpp +++ b/src/editor/world_editor.cpp @@ -1745,7 +1745,12 @@ public: save(*file); m_is_universe_changed = false; fs.close(*file); - if (save_path) m_universe_path = path; + + if (save_path) + { + m_universe_path = path; + m_universe->setPath(path); + } } @@ -2407,6 +2412,7 @@ public: { destroyUniverse(); createUniverse(false); + m_universe->setPath(m_universe_path); load(file); } diff --git a/src/engine/associative_array.h b/src/engine/associative_array.h index 335d6d971..6f47147d5 100644 --- a/src/engine/associative_array.h +++ b/src/engine/associative_array.h @@ -164,7 +164,8 @@ namespace Lumix } else { - return m_data[m_data.insert(Pair(key, Value()))].m_value; + ASSERT(false); + return m_data[0].m_value; } } diff --git a/src/engine/universe/universe.h b/src/engine/universe/universe.h index 0a410fa4e..923997b52 100644 --- a/src/engine/universe/universe.h +++ b/src/engine/universe/universe.h @@ -5,6 +5,7 @@ #include "engine/array.h" #include "engine/associative_array.h" #include "engine/delegate_list.h" +#include "engine/path.h" #include "engine/quat.h" #include "engine/string.h" #include "engine/vec.h" @@ -55,6 +56,8 @@ public: float getScale(Entity entity); const Vec3& getPosition(Entity entity) const; const Quat& getRotation(Entity entity) const; + Lumix::Path getPath() const { return m_path; } + void setPath(const Lumix::Path& path) { m_path = path; } DelegateList& entityTransformed() { return m_entity_moved; } DelegateList& entityCreated() { return m_entity_created; } @@ -91,6 +94,7 @@ private: DelegateList m_component_destroyed; DelegateList m_component_added; int m_first_free_slot; + Lumix::Path m_path; }; diff --git a/src/renderer/editor/plugins.cpp b/src/renderer/editor/plugins.cpp index 761aeaf7d..5ba30cb37 100644 --- a/src/renderer/editor/plugins.cpp +++ b/src/renderer/editor/plugins.cpp @@ -1,4 +1,12 @@ #include "engine/lumix.h" +#include "editor/asset_browser.h" +#include "editor/ieditor_command.h" +#include "editor/platform_interface.h" +#include "editor/property_grid.h" +#include "editor/render_interface.h" +#include "editor/studio_app.h" +#include "editor/utils.h" +#include "editor/world_editor.h" #include "engine/crc32.h" #include "engine/fs/disk_file_device.h" #include "engine/fs/file_system.h" @@ -10,19 +18,11 @@ #include "engine/path_utils.h" #include "engine/resource_manager.h" #include "engine/resource_manager_base.h" -#include "editor/asset_browser.h" -#include "editor/ieditor_command.h" -#include "editor/platform_interface.h" -#include "editor/property_grid.h" -#include "editor/studio_app.h" -#include "editor/utils.h" -#include "editor/world_editor.h" #include "engine/engine.h" #include "engine/plugin_manager.h" #include "engine/property_register.h" #include "engine/property_descriptor.h" #include "game_view.h" -#include "editor/render_interface.h" #include "import_asset_dialog.h" #include "renderer/frame_buffer.h" #include "renderer/material.h" @@ -39,6 +39,7 @@ #include "shader_compiler.h" #include "terrain_editor.h" #include +#include #include @@ -51,6 +52,8 @@ static const uint32 CAMERA_HASH = crc32("camera"); static const uint32 POINT_LIGHT_HASH = crc32("point_light"); static const uint32 GLOBAL_LIGHT_HASH = crc32("global_light"); static const uint32 RENDERABLE_HASH = crc32("renderable"); +static const uint32 RENDERER_HASH = crc32("renderer"); +static const uint32 ENVIRONMENT_PROBE_HASH = crc32("environment_probe"); static const uint32 MATERIAL_HASH = crc32("MATERIAL"); static const uint32 SHADER_HASH = crc32("SHADER"); static const uint32 TEXTURE_HASH = crc32("TEXTURE"); @@ -707,6 +710,218 @@ struct ShaderPlugin : public AssetBrowser::IPlugin }; +struct EnvironmentProbePlugin : public PropertyGrid::IPlugin +{ + explicit EnvironmentProbePlugin(StudioApp& app) + : m_app(app) + { + auto* world_editor = app.getWorldEditor(); + auto& plugin_manager = world_editor->getEngine().getPluginManager(); + Renderer* renderer = static_cast(plugin_manager.getPlugin("renderer")); + auto& allocator = world_editor->getAllocator(); + Lumix::Path pipeline_path("pipelines/game_view.lua"); + m_pipeline = Pipeline::create(*renderer, pipeline_path, allocator); + m_pipeline->load(); + } + + + ~EnvironmentProbePlugin() + { + Pipeline::destroy(m_pipeline); + } + + + bool saveCubemap(ComponentUID cmp, const Array& data, int texture_size) + { + crn_uint32 size; + crn_comp_params comp_params; + comp_params.m_width = texture_size; + comp_params.m_height = texture_size; + comp_params.m_file_type = cCRNFileTypeDDS; + comp_params.m_format = cCRNFmtDXT1; + comp_params.m_quality_level = cCRNMinQualityLevel; + comp_params.m_dxt_quality = cCRNDXTQualitySuperFast; + comp_params.m_dxt_compressor_type = cCRNDXTCompressorRYG; + comp_params.m_pProgress_func = nullptr; + comp_params.m_pProgress_func_data = nullptr; + comp_params.m_num_helper_threads = 3; + comp_params.m_faces = 6; + for (int i = 0; i < 6; ++i) + { + comp_params.m_pImages[i][0] = (Lumix::uint32*)&data[i * texture_size * texture_size * 4]; + } + crn_mipmap_params mipmap_params; + mipmap_params.m_mode = cCRNMipModeGenerateMips; + + void* compressed_data = crn_compress(comp_params, mipmap_params, size); + if (!compressed_data) + { + g_log_error.log("Editor") << "Failed to compress the probe."; + return false; + } + + Lumix::FS::OsFile file; + const char* base_path = m_app.getWorldEditor()->getEngine().getDiskFileDevice()->getBasePath(); + uint64 universe_guid = m_app.getWorldEditor()->getUniverse()->getPath().getHash(); + Lumix::StaticString path(base_path, "universes/", universe_guid); + if (!PlatformInterface::makePath(path)) g_log_error.log("Editor") << "Failed to create " << path; + path << "/probes/"; + if (!PlatformInterface::makePath(path)) g_log_error.log("Editor") << "Failed to create " << path; + path << cmp.index << ".dds"; + auto& allocator = m_app.getWorldEditor()->getAllocator(); + if (!file.open(path, Lumix::FS::Mode::CREATE_AND_WRITE, allocator)) + { + g_log_error.log("Editor") << "Failed to create " << path; + crn_free_block(compressed_data); + return false; + } + + file.write((const char*)compressed_data, size); + file.close(); + crn_free_block(compressed_data); + return true; + } + + + void flipY(uint32* data, int texture_size) + { + for (int y = 0; y < texture_size / 2; ++y) + { + for (int x = 0; x < texture_size; ++x) + { + uint32 t = data[x + y * texture_size]; + data[x + y * texture_size] = data[x + (texture_size - y - 1) * texture_size]; + data[x + (texture_size - y - 1) * texture_size] = t; + } + } + } + + + void flipX(uint32* data, int texture_size) + { + for (int y = 0; y < texture_size; ++y) + { + uint32* tmp = (uint32*)&data[y * texture_size]; + for (int x = 0; x < texture_size / 2; ++x) + { + uint32 t = tmp[x]; + tmp[x] = tmp[texture_size - x - 1]; + tmp[texture_size - x - 1] = t; + } + } + } + + + void generateCubemap(ComponentUID cmp) + { + static const int TEXTURE_SIZE = 1024; + + Universe* universe = m_app.getWorldEditor()->getUniverse(); + if (!universe->getPath().isValid()) + { + g_log_error.log("Editor") << "Universe must be saved before environment probe can be generated."; + return; + } + + WorldEditor* world_editor = m_app.getWorldEditor(); + Engine& engine = world_editor->getEngine(); + auto& plugin_manager = engine.getPluginManager(); + IAllocator& allocator = engine.getAllocator(); + Lumix::Array data(allocator); + data.resize(6 * TEXTURE_SIZE * TEXTURE_SIZE * 4); + + bgfx::TextureHandle texture = + bgfx::createTexture2D(TEXTURE_SIZE, TEXTURE_SIZE, 1, bgfx::TextureFormat::RGBA8, BGFX_TEXTURE_READ_BACK); + + Vec3 probe_position = universe->getPosition(cmp.entity); + auto* scene = static_cast(universe->getScene(crc32("renderer"))); + ComponentIndex original_camera = scene->getCameraInSlot("main"); + + if(original_camera != INVALID_COMPONENT) scene->setCameraSlot(original_camera, ""); + Entity camera_entity = universe->createEntity({ 0, 0, 0 }, { 0, 0, 0, 1 }); + ComponentIndex camera_cmp = scene->createComponent(CAMERA_HASH, camera_entity); + scene->setCameraSlot(camera_cmp, "main"); + scene->setCameraFOV(camera_cmp, 90); + + m_pipeline->setScene(scene); + m_pipeline->setViewport(0, 0, TEXTURE_SIZE, TEXTURE_SIZE); + + Renderer* renderer = static_cast(plugin_manager.getPlugin("renderer")); + + Vec3 dirs[] = {{-1, 0, 0}, {1, 0, 0}, {0, -1, 0}, {0, 1, 0}, {0, 0, -1}, {0, 0, 1}}; + Vec3 ups[] = {{0, 1, 0}, {0, 1, 0}, {0, 0, 1}, {0, 0, -1}, {0, 1, 0}, {0, 1, 0}}; + Vec3 ups_opengl[] = { { 0, -1, 0 },{ 0, -1, 0 },{ 0, 0, 1 },{ 0, 0, -1 },{ 0, -1, 0 },{ 0, -1, 0 } }; + + renderer->frame(); // submit + renderer->frame(); // wait for gpu + + bool is_opengl = renderer->isOpenGL(); + for (int i = 0; i < 6; ++i) + { + Matrix mtx = Matrix::IDENTITY; + mtx.setTranslation(probe_position); + Vec3 side = crossProduct(is_opengl ? ups_opengl[i] : ups[i], dirs[i]); + mtx.setZVector(dirs[i]); + mtx.setYVector(is_opengl ? ups_opengl[i] : ups[i]); + mtx.setXVector(side); + universe->setMatrix(camera_entity, mtx); + m_pipeline->render(); + + renderer->viewCounterAdd(); + bgfx::touch(renderer->getViewCounter()); + bgfx::setViewName(renderer->getViewCounter(), "probe_blit"); + auto* default_framebuffer = m_pipeline->getFramebuffer("default"); + bgfx::TextureHandle color_renderbuffer = default_framebuffer->getRenderbufferHandle(0); + bgfx::blit(renderer->getViewCounter(), texture, 0, 0, color_renderbuffer); + + renderer->viewCounterAdd(); + bgfx::setViewName(renderer->getViewCounter(), "probe_read"); + bgfx::readTexture(texture, &data[i * TEXTURE_SIZE * TEXTURE_SIZE * 4]); + bgfx::touch(renderer->getViewCounter()); + renderer->frame(); // submit + renderer->frame(); // wait for gpu + + if (is_opengl) continue; + + uint32* tmp = (uint32*)&data[i * TEXTURE_SIZE * TEXTURE_SIZE * 4]; + if (i == 2 || i == 3) + { + flipY(tmp, TEXTURE_SIZE); + } + else + { + flipX(tmp, TEXTURE_SIZE); + } + } + saveCubemap(cmp, data, TEXTURE_SIZE); + bgfx::destroyTexture(texture); + + scene->destroyComponent(camera_cmp, CAMERA_HASH); + universe->destroyEntity(camera_entity); + if (original_camera != INVALID_COMPONENT) scene->setCameraSlot(original_camera, "main"); + + scene->reloadEnvironmentProbe(cmp.index); + } + + + void onGUI(PropertyGrid& grid, ComponentUID cmp) override + { + if (cmp.type != ENVIRONMENT_PROBE_HASH) return; + + auto* scene = static_cast(cmp.scene); + auto* texture = scene->getEnvironmentProbeTexture(cmp.index); + ImGui::LabelText("Path", "%s", texture->getPath().c_str()); + if (ImGui::Button("View")) m_app.getAssetBrowser()->selectResource(texture->getPath()); + ImGui::SameLine(); + if (ImGui::Button("Generate")) generateCubemap(cmp); + } + + + StudioApp& m_app; + Pipeline* m_pipeline; +}; + + struct EmitterPlugin : public PropertyGrid::IPlugin { explicit EmitterPlugin(StudioApp& app) @@ -1503,6 +1718,9 @@ LUMIX_STUDIO_ENTRY(renderer) auto* emitter_plugin = LUMIX_NEW(allocator, EmitterPlugin)(app); app.getPropertyGrid()->addPlugin(*emitter_plugin); + auto* env_probe_plugin = LUMIX_NEW(allocator, EnvironmentProbePlugin)(app); + app.getPropertyGrid()->addPlugin(*env_probe_plugin); + auto* terrain_plugin = LUMIX_NEW(allocator, TerrainPlugin)(app); app.getPropertyGrid()->addPlugin(*terrain_plugin); diff --git a/src/renderer/render_scene.cpp b/src/renderer/render_scene.cpp index 826788bbe..7f3ea958e 100644 --- a/src/renderer/render_scene.cpp +++ b/src/renderer/render_scene.cpp @@ -13,6 +13,7 @@ #include "engine/mtjd/generic_job.h" #include "engine/mtjd/job.h" #include "engine/mtjd/manager.h" +#include "engine/path_utils.h" #include "engine/profiler.h" #include "engine/resource_manager.h" #include "engine/resource_manager_base.h" @@ -33,6 +34,7 @@ #include "renderer/shader.h" #include "renderer/terrain.h" #include "renderer/texture.h" +#include "renderer/texture_manager.h" #include "engine/universe/universe.h" #include @@ -59,7 +61,9 @@ static const uint32 GLOBAL_LIGHT_HASH = crc32("global_light"); static const uint32 CAMERA_HASH = crc32("camera"); static const uint32 TERRAIN_HASH = crc32("terrain"); static const uint32 BONE_ATTACHMENT_HASH = crc32("bone_attachment"); +static const uint32 ENVIRONMENT_PROBE_HASH = crc32("environment_probe"); static const uint32 MATERIAL_HASH = crc32("MATERIAL"); +static const uint32 TEXTURE_HASH = crc32("TEXTURE"); static const uint32 MODEL_HASH = crc32("MODEL"); static bool is_opengl = false; @@ -115,6 +119,12 @@ struct Camera }; +struct EnvironmentProbe +{ + Texture* texture; +}; + + struct BoneAttachment { Entity entity; @@ -196,6 +206,7 @@ public: , m_particle_emitters(m_allocator) , m_point_lights_map(m_allocator) , m_bone_attachments(m_allocator) + , m_environment_probes(m_allocator) { is_opengl = renderer.isOpenGL(); m_is_updating_attachments = false; @@ -241,6 +252,12 @@ public: } } + for (int i = 0, c = m_environment_probes.size(); i < c; ++i) + { + auto& probe = m_environment_probes.at(i); + if (probe.texture) probe.texture->getResourceManager().get(TEXTURE_HASH)->unload(*probe.texture); + } + CullingSystem::destroy(*m_culling_system); } @@ -674,6 +691,51 @@ public: } + void serializeEnvironmentProbes(OutputBlob& serializer) + { + int32 count = m_environment_probes.size(); + serializer.write(count); + for (int i = 0; i < count; ++i) + { + auto& probe = m_environment_probes.at(i); + Entity entity = m_environment_probes.getKey(i); + serializer.write(entity); + } + } + + + void deserializeEnvironmentProbes(InputBlob& serializer) + { + int32 count; + serializer.read(count); + for (int i = 0, c = m_environment_probes.size(); i < c; ++i) + { + auto& probe = m_environment_probes.at(i); + if (probe.texture) + { + probe.texture->getResourceManager().get(TEXTURE_HASH)->unload(*probe.texture); + } + } + + m_environment_probes.clear(); + m_environment_probes.reserve(count); + auto* texture_manager = m_engine.getResourceManager().get(TEXTURE_HASH); + uint64 universe_guid = m_universe.getPath().getHash(); + StaticString probe_dir("universes/", universe_guid, "/probes/"); + for (int i = 0; i < count; ++i) + { + Entity entity; + serializer.read(entity); + EnvironmentProbe probe; + StaticString path_str(probe_dir, entity, ".dds"); + Path path(path_str); + probe.texture = static_cast(texture_manager->load(path)); + m_environment_probes.insert(entity, probe); + m_universe.addComponent(entity, ENVIRONMENT_PROBE_HASH, this, entity); + } + } + + void deserializeBoneAttachments(InputBlob& serializer, int version) { if (version <= (int)RenderSceneVersion::BONE_ATTACHMENTS) return; @@ -786,6 +848,7 @@ public: serializeTerrains(serializer); serializeParticleEmitters(serializer); serializeBoneAttachments(serializer); + serializeEnvironmentProbes(serializer); } void deserializeRenderParams(InputBlob& serializer) @@ -1036,6 +1099,7 @@ public: deserializeRenderParams(serializer); } deserializeBoneAttachments(serializer, version); + if (version > (int)RenderSceneVersion::ENVIRONMENT_PROBES) deserializeEnvironmentProbes(serializer); } @@ -1048,6 +1112,16 @@ public: } + void destroyEnvironmentProbe(ComponentIndex component) + { + Entity entity = (Entity)component; + auto& probe = m_environment_probes[entity]; + if (probe.texture) probe.texture->getResourceManager().get(TEXTURE_HASH)->unload(*probe.texture); + m_environment_probes.erase(entity); + m_universe.destroyComponent(entity, ENVIRONMENT_PROBE_HASH, this, component); + } + + void destroyRenderable(ComponentIndex component) { m_renderable_destroyed.invoke(component); @@ -3275,6 +3349,20 @@ public: return m_global_lights[getGlobalLightIndex(cmp)].m_entity; } + void reloadEnvironmentProbe(ComponentIndex cmp) override + { + auto& probe = m_environment_probes[cmp]; + auto* texture_manager = m_engine.getResourceManager().get(TEXTURE_HASH); + if (probe.texture) texture_manager->unload(*probe.texture); + uint64 universe_guid = m_universe.getPath().getHash(); + StaticString path("universes/", universe_guid, "/probes/", cmp, ".dds"); + probe.texture = static_cast(texture_manager->load(Path(path))); + } + + Texture* getEnvironmentProbeTexture(ComponentIndex cmp) const + { + return m_environment_probes[cmp].texture; + } ComponentIndex getCameraInSlot(const char* slot) override { @@ -3782,6 +3870,18 @@ public: } + ComponentIndex createEnvironmentProbe(Entity entity) + { + EnvironmentProbe probe; + auto* texture_manager = m_engine.getResourceManager().get(TEXTURE_HASH); + probe.texture = static_cast(texture_manager->load(Path("models/editor/default_probe.dds"))); + m_environment_probes.insert(entity, probe); + + m_universe.addComponent(entity, ENVIRONMENT_PROBE_HASH, this, entity); + return entity; + } + + ComponentIndex createBoneAttachment(Entity entity) { BoneAttachment& attachment = m_bone_attachments.emplace(); @@ -3859,6 +3959,7 @@ private: Array m_global_lights; Array m_cameras; Array m_bone_attachments; + AssociativeArray m_environment_probes; Array m_terrains; Universe& m_universe; Renderer& m_renderer; @@ -3917,13 +4018,11 @@ static struct {PARTICLE_EMITTER_PLANE_HASH, &RenderSceneImpl::createParticleEmitterPlane, &RenderSceneImpl::destroyParticleEmitterPlane}, - {BONE_ATTACHMENT_HASH, &RenderSceneImpl::createBoneAttachment, &RenderSceneImpl::destroyBoneAttachment} + {BONE_ATTACHMENT_HASH, &RenderSceneImpl::createBoneAttachment, &RenderSceneImpl::destroyBoneAttachment}, + {ENVIRONMENT_PROBE_HASH, &RenderSceneImpl::createEnvironmentProbe, &RenderSceneImpl::destroyEnvironmentProbe} }; - - - ComponentIndex RenderSceneImpl::createComponent(uint32 type, Entity entity) { for (auto& i : COMPONENT_INFOS) diff --git a/src/renderer/render_scene.h b/src/renderer/render_scene.h index 1583601e6..860732068 100644 --- a/src/renderer/render_scene.h +++ b/src/renderer/render_scene.h @@ -26,6 +26,7 @@ struct RayCastModelHit; class Renderer; class Shader; class Terrain; +class Texture; class Universe; template class Array; template class DelegateList; @@ -46,6 +47,7 @@ enum class RenderSceneVersion : int32 GRASS_TYPE_DISTANCE, ORTHO_CAMERA, BONE_ATTACHMENTS, + ENVIRONMENT_PROBES, LATEST, INVALID = -1, @@ -390,6 +392,9 @@ public: virtual float getPointLightSpecularIntensity(ComponentIndex cmp) = 0; virtual void setPointLightSpecularIntensity(ComponentIndex cmp, float color) = 0; + virtual Texture* getEnvironmentProbeTexture(ComponentIndex cmp) const = 0; + virtual void reloadEnvironmentProbe(ComponentIndex cmp) = 0; + protected: virtual ~RenderScene() {} }; diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp index e73317197..414c81d83 100644 --- a/src/renderer/renderer.cpp +++ b/src/renderer/renderer.cpp @@ -164,6 +164,7 @@ static void registerProperties(IAllocator& allocator) PropertyRegister::registerComponentType("point_light", "Point light"); PropertyRegister::registerComponentType("terrain", "Terrain"); PropertyRegister::registerComponentType("bone_attachment", "Bone attachment"); + PropertyRegister::registerComponentType("environment_probe", "Environment probe"); PropertyRegister::registerComponentDependency("particle_emitter_fade", "particle_emitter"); PropertyRegister::registerComponentDependency("particle_emitter_force", "particle_emitter");