#include "stdafx.h" #include "Editor.h" #include "wiRenderer.h" #include "wiScene_BindLua.h" #include "ModelImporter.h" #include "Translator.h" #include "FontAwesomeV6.h" // font TTF data #include #include #include #include using namespace wi::graphics; using namespace wi::primitive; using namespace wi::scene; using namespace wi::ecs; const float shadow_expand = 1; void Editor::Initialize() { Application::Initialize(); // With this mode, file data for resources will be kept around. This allows serializing embedded resource data inside scenes wi::resourcemanager::SetMode(wi::resourcemanager::Mode::ALLOW_RETAIN_FILEDATA); infoDisplay.active = true; infoDisplay.watermark = true; //infoDisplay.fpsinfo = true; //infoDisplay.resolution = true; //infoDisplay.logical_size = true; //infoDisplay.colorspace = true; //infoDisplay.heap_allocation_counter = true; //infoDisplay.vram_usage = true; wi::renderer::SetOcclusionCullingEnabled(true); loader.Load(); renderComponent.main = this; loader.addLoadingComponent(&renderComponent, this, 0.2f); ActivatePath(&loader, 0.2f); } void EditorLoadingScreen::Load() { font = wi::SpriteFont("Loading...", wi::font::Params(0, 0, 36, wi::font::WIFALIGN_LEFT, wi::font::WIFALIGN_CENTER)); font.anim.typewriter.time = 2; font.anim.typewriter.looped = true; font.anim.typewriter.character_start = 7; AddFont(&font); sprite = wi::Sprite("images/logo_small.png"); sprite.anim.opa = 1; sprite.anim.repeatable = true; sprite.params.siz = XMFLOAT2(128, 128); sprite.params.pivot = XMFLOAT2(0.5f, 1.0f); sprite.params.quality = wi::image::QUALITY_LINEAR; sprite.params.blendFlag = wi::enums::BLENDMODE_ALPHA; AddSprite(&sprite); LoadingScreen::Load(); } void EditorLoadingScreen::Update(float dt) { font.params.posX = GetLogicalWidth()*0.5f - font.TextWidth() * 0.5f; font.params.posY = GetLogicalHeight()*0.5f; sprite.params.pos = XMFLOAT3(GetLogicalWidth()*0.5f, GetLogicalHeight()*0.5f - font.TextHeight(), 0); LoadingScreen::Update(dt); } void EditorComponent::ChangeRenderPath(RENDERPATH path) { switch (path) { case EditorComponent::RENDERPATH_DEFAULT: renderPath = std::make_unique(); pathTraceTargetSlider.SetVisible(false); pathTraceStatisticsLabel.SetVisible(false); break; case EditorComponent::RENDERPATH_PATHTRACING: renderPath = std::make_unique(); pathTraceTargetSlider.SetVisible(true); pathTraceStatisticsLabel.SetVisible(true); break; default: assert(0); break; } if (scenes.empty()) { NewScene(); } else { SetCurrentScene(current_scene); } renderPath->resolutionScale = resolutionScale; renderPath->setBloomThreshold(3.0f); renderPath->Load(); // Destroy and recreate renderer and postprocess windows: optionsWnd.RemoveWidget(&rendererWnd); rendererWnd = {}; rendererWnd.Create(this); rendererWnd.SetShadowRadius(shadow_expand); optionsWnd.AddWidget(&rendererWnd); optionsWnd.RemoveWidget(&postprocessWnd); postprocessWnd = {}; postprocessWnd.Create(this); postprocessWnd.SetShadowRadius(shadow_expand); optionsWnd.AddWidget(&postprocessWnd); themeCombo.SetSelected(themeCombo.GetSelected()); // destroyed windows need theme set again } void EditorComponent::ResizeBuffers() { rendererWnd.UpdateSwapChainFormats(&main->swapChain); init(main->canvas); RenderPath2D::ResizeBuffers(); GraphicsDevice* device = wi::graphics::GetDevice(); renderPath->init(*this); renderPath->ResizeBuffers(); if(renderPath->GetDepthStencil() != nullptr) { bool success = false; XMUINT2 internalResolution = GetInternalResolution(); TextureDesc desc; desc.width = internalResolution.x; desc.height = internalResolution.y; desc.format = Format::R8_UNORM; desc.bind_flags = BindFlag::RENDER_TARGET | BindFlag::SHADER_RESOURCE; if (renderPath->getMSAASampleCount() > 1) { desc.sample_count = renderPath->getMSAASampleCount(); success = device->CreateTexture(&desc, nullptr, &rt_selectionOutline_MSAA); assert(success); desc.sample_count = 1; } success = device->CreateTexture(&desc, nullptr, &rt_selectionOutline[0]); assert(success); success = device->CreateTexture(&desc, nullptr, &rt_selectionOutline[1]); assert(success); { RenderPassDesc desc; desc.attachments.push_back(RenderPassAttachment::RenderTarget(&rt_selectionOutline[0], RenderPassAttachment::LoadOp::CLEAR)); if (renderPath->getMSAASampleCount() > 1) { desc.attachments[0].texture = &rt_selectionOutline_MSAA; desc.attachments.push_back(RenderPassAttachment::Resolve(&rt_selectionOutline[0])); } desc.attachments.push_back( RenderPassAttachment::DepthStencil( renderPath->GetDepthStencil(), RenderPassAttachment::LoadOp::LOAD, RenderPassAttachment::StoreOp::STORE, ResourceState::DEPTHSTENCIL_READONLY, ResourceState::DEPTHSTENCIL_READONLY, ResourceState::DEPTHSTENCIL_READONLY ) ); success = device->CreateRenderPass(&desc, &renderpass_selectionOutline[0]); assert(success); if (renderPath->getMSAASampleCount() == 1) { desc.attachments[0].texture = &rt_selectionOutline[1]; // rendertarget } else { desc.attachments[1].texture = &rt_selectionOutline[1]; // resolve } success = device->CreateRenderPass(&desc, &renderpass_selectionOutline[1]); assert(success); } } { TextureDesc desc; desc.width = renderPath->GetRenderResult().GetDesc().width; desc.height = renderPath->GetRenderResult().GetDesc().height; desc.format = Format::D32_FLOAT; desc.bind_flags = BindFlag::DEPTH_STENCIL; desc.layout = ResourceState::DEPTHSTENCIL; desc.misc_flags = ResourceMiscFlag::TRANSIENT_ATTACHMENT; device->CreateTexture(&desc, nullptr, &editor_depthbuffer); device->SetName(&editor_depthbuffer, "editor_depthbuffer"); { RenderPassDesc desc; desc.attachments.push_back( RenderPassAttachment::DepthStencil( &editor_depthbuffer, RenderPassAttachment::LoadOp::CLEAR, RenderPassAttachment::StoreOp::DONTCARE ) ); desc.attachments.push_back( RenderPassAttachment::RenderTarget( &renderPath->GetRenderResult(), RenderPassAttachment::LoadOp::CLEAR, RenderPassAttachment::StoreOp::STORE ) ); device->CreateRenderPass(&desc, &renderpass_editor); } } } void EditorComponent::ResizeLayout() { RenderPath2D::ResizeLayout(); // GUI elements scaling: float screenW = GetLogicalWidth(); float screenH = GetLogicalHeight(); optionsWnd.SetPos(XMFLOAT2(0, screenH - optionsWnd.GetScale().y)); componentWindow.SetPos(XMFLOAT2(screenW - componentWindow.GetScale().x, screenH - componentWindow.GetScale().y)); //////////////////////////////////////////////////////////////////////////////////// float hei = 25; saveButton.SetPos(XMFLOAT2(screenW - 50 - 55 - 105 * 3, 0)); saveButton.SetSize(XMFLOAT2(100, hei)); openButton.SetPos(XMFLOAT2(screenW - 50 - 55 - 105 * 2, 0)); openButton.SetSize(XMFLOAT2(100, hei)); closeButton.SetPos(XMFLOAT2(screenW - 50 - 55 - 105 * 1, 0)); closeButton.SetSize(XMFLOAT2(100, hei)); aboutButton.SetPos(XMFLOAT2(screenW - 50 - 55, 0)); aboutButton.SetSize(XMFLOAT2(50, hei)); aboutLabel.SetSize(XMFLOAT2(screenW / 2.0f, screenH / 1.5f)); aboutLabel.SetPos(XMFLOAT2(screenW / 2.0f - aboutLabel.scale.x / 2.0f, screenH / 2.0f - aboutLabel.scale.y / 2.0f)); exitButton.SetPos(XMFLOAT2(screenW - 50, 0)); exitButton.SetSize(XMFLOAT2(50, hei)); } void EditorComponent::Load() { // Font icon is from #include "FontAwesomeV6.h" // We will not directly use this font style, but let the font renderer fall back on it // when an icon character is not found in the default font. wi::font::AddFontStyle("FontAwesomeV6", font_awesome_v6, sizeof(font_awesome_v6)); saveButton.Create(ICON_SAVE " Save"); saveButton.font.params.shadowColor = wi::Color::Transparent(); saveButton.SetTooltip("Save the current scene to a new file (Ctrl + Shift + S)"); saveButton.SetColor(wi::Color(50, 180, 100, 180), wi::gui::WIDGETSTATE::IDLE); saveButton.SetColor(wi::Color(50, 220, 140, 255), wi::gui::WIDGETSTATE::FOCUS); saveButton.OnClick([&](wi::gui::EventArgs args) { SaveAs(); }); GetGUI().AddWidget(&saveButton); openButton.Create(ICON_OPEN " Open"); openButton.font.params.shadowColor = wi::Color::Transparent(); openButton.SetTooltip("Open a scene, import a model or execute a Lua script..."); openButton.SetColor(wi::Color(50, 100, 255, 180), wi::gui::WIDGETSTATE::IDLE); openButton.SetColor(wi::Color(120, 160, 255, 255), wi::gui::WIDGETSTATE::FOCUS); openButton.OnClick([&](wi::gui::EventArgs args) { wi::helper::FileDialogParams params; params.type = wi::helper::FileDialogParams::OPEN; params.description = ".wiscene, .obj, .gltf, .glb, .lua"; params.extensions.push_back("wiscene"); params.extensions.push_back("obj"); params.extensions.push_back("gltf"); params.extensions.push_back("glb"); params.extensions.push_back("lua"); wi::helper::FileDialog(params, [&](std::string fileName) { wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { std::string extension = wi::helper::toUpper(wi::helper::GetExtensionFromFileName(fileName)); if (!extension.compare("LUA")) { wi::lua::RunFile(fileName); return; } size_t camera_count_prev = GetCurrentScene().cameras.GetCount(); main->loader.addLoadingFunction([=](wi::jobsystem::JobArgs args) { if (!extension.compare("WISCENE")) // engine-serialized { wi::scene::LoadModel(GetCurrentScene(), fileName); GetCurrentEditorScene().path = fileName; } else if (!extension.compare("OBJ")) // wavefront-obj { Scene scene; ImportModel_OBJ(fileName, scene); GetCurrentScene().Merge(scene); } else if (!extension.compare("GLTF")) // text-based gltf { Scene scene; ImportModel_GLTF(fileName, scene); GetCurrentScene().Merge(scene); } else if (!extension.compare("GLB")) // binary gltf { Scene scene; ImportModel_GLTF(fileName, scene); GetCurrentScene().Merge(scene); } }); main->loader.onFinished([=] { // Detect when the new scene contains a new camera, and snap the camera onto it: size_t camera_count = GetCurrentScene().cameras.GetCount(); if (camera_count > 0 && camera_count > camera_count_prev) { Entity entity = GetCurrentScene().cameras.GetEntity(camera_count_prev); if (entity != INVALID_ENTITY) { TransformComponent* camera_transform = GetCurrentScene().transforms.GetComponent(entity); if (camera_transform != nullptr) { GetCurrentEditorScene().camera_transform = *camera_transform; } CameraComponent* cam = GetCurrentScene().cameras.GetComponent(entity); if (cam != nullptr) { GetCurrentEditorScene().camera = *cam; // camera aspect should be always for the current screen GetCurrentEditorScene().camera.width = (float)renderPath->GetInternalResolution().x; GetCurrentEditorScene().camera.height = (float)renderPath->GetInternalResolution().y; } } } main->ActivatePath(this, 0.2f, wi::Color::Black()); weatherWnd.Update(); RefreshEntityTree(); RefreshSceneList(); }); main->ActivatePath(&main->loader, 0.2f, wi::Color::Black()); ResetHistory(); }); }); }); GetGUI().AddWidget(&openButton); closeButton.Create(ICON_CLOSE " Close"); closeButton.font.params.shadowColor = wi::Color::Transparent(); closeButton.SetTooltip("Close the current scene.\nThis will clear everything from the currently selected scene, and delete the scene.\nThis operation cannot be undone!"); closeButton.SetColor(wi::Color(255, 130, 100, 180), wi::gui::WIDGETSTATE::IDLE); closeButton.SetColor(wi::Color(255, 200, 150, 255), wi::gui::WIDGETSTATE::FOCUS); closeButton.OnClick([&](wi::gui::EventArgs args) { terragen.Generation_Cancel(); terragen.terrainEntity = INVALID_ENTITY; terragen.SetCollapsed(true); translator.selected.clear(); wi::scene::Scene& scene = GetCurrentScene(); wi::renderer::ClearWorld(scene); objectWnd.SetEntity(INVALID_ENTITY); meshWnd.SetEntity(INVALID_ENTITY, -1); lightWnd.SetEntity(INVALID_ENTITY); soundWnd.SetEntity(INVALID_ENTITY); decalWnd.SetEntity(INVALID_ENTITY); envProbeWnd.SetEntity(INVALID_ENTITY); materialWnd.SetEntity(INVALID_ENTITY); emitterWnd.SetEntity(INVALID_ENTITY); hairWnd.SetEntity(INVALID_ENTITY); forceFieldWnd.SetEntity(INVALID_ENTITY); cameraWnd.SetEntity(INVALID_ENTITY); paintToolWnd.SetEntity(INVALID_ENTITY); springWnd.SetEntity(INVALID_ENTITY); ikWnd.SetEntity(INVALID_ENTITY); transformWnd.SetEntity(INVALID_ENTITY); layerWnd.SetEntity(INVALID_ENTITY); nameWnd.SetEntity(INVALID_ENTITY); RefreshEntityTree(); ResetHistory(); GetCurrentEditorScene().path.clear(); wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { if (scenes.size() > 1) { scenes.erase(scenes.begin() + current_scene); } SetCurrentScene(std::max(0, current_scene - 1)); }); }); GetGUI().AddWidget(&closeButton); aboutButton.Create(ICON_HELP); aboutButton.font.params.shadowColor = wi::Color::Transparent(); aboutButton.SetTooltip("About..."); aboutButton.SetColor(wi::Color(50, 160, 200, 180), wi::gui::WIDGETSTATE::IDLE); aboutButton.SetColor(wi::Color(120, 200, 200, 255), wi::gui::WIDGETSTATE::FOCUS); aboutButton.OnClick([&](wi::gui::EventArgs args) { aboutLabel.SetVisible(!aboutLabel.IsVisible()); }); GetGUI().AddWidget(&aboutButton); { std::string ss; ss += "Wicked Engine Editor v"; ss += wi::version::GetVersionString(); ss += "\nCreated by Turánszki János"; ss += "\n\nWebsite: https://wickedengine.net/"; ss += "\nGithub page: https://github.com/turanszkij/WickedEngine"; ss += "\nDiscord chat: https://discord.gg/CFjRYmE"; ss += "\nYou can support the project on Patreon: https://www.patreon.com/wickedengine"; ss += "\n\nControls\n"; ss += "------------\n"; ss += "Move camera: WASD, or Contoller left stick or D-pad\n"; ss += "Look: Middle mouse button / arrow keys / controller right stick\n"; ss += "Select: Right mouse button\n"; ss += "Interact with water: Left mouse button when nothing is selected\n"; ss += "Faster camera: Left Shift button or controller R2/RT\n"; ss += "Snap transform: Left Ctrl (hold while transforming)\n"; ss += "Camera up: E\n"; ss += "Camera down: Q\n"; ss += "Duplicate entity: Ctrl + D\n"; ss += "Select All: Ctrl + A\n"; ss += "Deselect All: Esc\n"; ss += "Undo: Ctrl + Z\n"; ss += "Redo: Ctrl + Y\n"; ss += "Copy: Ctrl + C\n"; ss += "Cut: Ctrl + X\n"; ss += "Paste: Ctrl + V\n"; ss += "Delete: Delete button\n"; ss += "Save As: Ctrl + Shift + S\n"; ss += "Save: Ctrl + S\n"; ss += "Transform: Ctrl + T\n"; ss += "Wireframe mode: Ctrl + W\n"; ss += "Color grading reference: Ctrl + G (color grading palette reference will be displayed in top left corner)\n"; ss += "Inspector mode: I button (hold), hovered entity information will be displayed near mouse position.\n"; ss += "Place Instances: Ctrl + Shift + Left mouse click (place clipboard onto clicked surface)\n"; ss += "Script Console / backlog: HOME button\n"; ss += "\n"; ss += "\nTips\n"; ss += "-------\n"; ss += "You can find sample scenes in the Content/models directory. Try to load one.\n"; ss += "You can also import models from .OBJ, .GLTF, .GLB files.\n"; #ifndef PLATFORM_UWP ss += "You can find a program configuration file at Editor/config.ini\n"; #endif // PLATFORM_UWP ss += "You can find sample LUA scripts in the Content/scripts directory. Try to load one.\n"; ss += "You can find a startup script at Editor/startup.lua (this will be executed on program start, if exists)\n"; ss += "\nFor questions, bug reports, feedback, requests, please open an issue at:\n"; ss += "https://github.com/turanszkij/WickedEngine/issues\n"; aboutLabel.Create("AboutLabel"); aboutLabel.SetText(ss); aboutLabel.SetVisible(false); aboutLabel.SetColor(wi::Color(113, 183, 214, 100)); GetGUI().AddWidget(&aboutLabel); } exitButton.Create(ICON_EXIT); exitButton.font.params.shadowColor = wi::Color::Transparent(); exitButton.SetTooltip("Exit"); exitButton.SetColor(wi::Color(160, 50, 50, 180), wi::gui::WIDGETSTATE::IDLE); exitButton.SetColor(wi::Color(200, 50, 50, 255), wi::gui::WIDGETSTATE::FOCUS); exitButton.OnClick([this](wi::gui::EventArgs args) { terragen.Generation_Cancel(); wi::platform::Exit(); }); GetGUI().AddWidget(&exitButton); //////////////////////////////////////////////////////////////////////////////////// optionsWnd.Create("Options", wi::gui::Window::WindowControls::RESIZE_TOPRIGHT); optionsWnd.SetPos(XMFLOAT2(100, 120)); optionsWnd.SetSize(XMFLOAT2(340, 400)); optionsWnd.SetShadowRadius(2); GetGUI().AddWidget(&optionsWnd); translatorCheckBox.Create("Transform: "); translatorCheckBox.SetTooltip("Enable the transform tool (Ctrl + T).\nTip: hold Left Ctrl to enable snap transform.\nYou can configure snap mode units in the Transform settings."); translatorCheckBox.OnClick([&](wi::gui::EventArgs args) { translator.enabled = args.bValue; }); optionsWnd.AddWidget(&translatorCheckBox); isScalatorCheckBox.Create("S: "); isRotatorCheckBox.Create("R: "); isTranslatorCheckBox.Create("T: "); { isScalatorCheckBox.SetTooltip("Scale"); isScalatorCheckBox.OnClick([&](wi::gui::EventArgs args) { translator.isScalator = args.bValue; translator.isTranslator = false; translator.isRotator = false; isTranslatorCheckBox.SetCheck(false); isRotatorCheckBox.SetCheck(false); }); isScalatorCheckBox.SetCheck(translator.isScalator); optionsWnd.AddWidget(&isScalatorCheckBox); isRotatorCheckBox.SetTooltip("Rotate"); isRotatorCheckBox.OnClick([&](wi::gui::EventArgs args) { translator.isRotator = args.bValue; translator.isScalator = false; translator.isTranslator = false; isScalatorCheckBox.SetCheck(false); isTranslatorCheckBox.SetCheck(false); }); isRotatorCheckBox.SetCheck(translator.isRotator); optionsWnd.AddWidget(&isRotatorCheckBox); isTranslatorCheckBox.SetTooltip("Translate (Move)"); isTranslatorCheckBox.OnClick([&](wi::gui::EventArgs args) { translator.isTranslator = args.bValue; translator.isScalator = false; translator.isRotator = false; isScalatorCheckBox.SetCheck(false); isRotatorCheckBox.SetCheck(false); }); isTranslatorCheckBox.SetCheck(translator.isTranslator); optionsWnd.AddWidget(&isTranslatorCheckBox); } profilerEnabledCheckBox.Create("Profiler: "); profilerEnabledCheckBox.SetTooltip("Toggle Profiler On/Off"); profilerEnabledCheckBox.OnClick([&](wi::gui::EventArgs args) { wi::profiler::SetEnabled(args.bValue); }); profilerEnabledCheckBox.SetCheck(wi::profiler::IsEnabled()); optionsWnd.AddWidget(&profilerEnabledCheckBox); physicsEnabledCheckBox.Create("Physics: "); physicsEnabledCheckBox.SetTooltip("Toggle Physics Simulation On/Off"); physicsEnabledCheckBox.OnClick([&](wi::gui::EventArgs args) { wi::physics::SetSimulationEnabled(args.bValue); }); physicsEnabledCheckBox.SetCheck(wi::physics::IsSimulationEnabled()); optionsWnd.AddWidget(&physicsEnabledCheckBox); cinemaModeCheckBox.Create("Cinema Mode: "); cinemaModeCheckBox.SetTooltip("Toggle Cinema Mode (All HUD disabled). Press ESC to exit."); cinemaModeCheckBox.OnClick([&](wi::gui::EventArgs args) { if (renderPath != nullptr) { renderPath->GetGUI().SetVisible(false); } GetGUI().SetVisible(false); wi::profiler::SetEnabled(false); main->infoDisplay.active = false; }); optionsWnd.AddWidget(&cinemaModeCheckBox); infoDisplayCheckBox.Create("Info Display: "); infoDisplayCheckBox.SetTooltip("Toggle the information display (the text in top left corner)."); infoDisplayCheckBox.OnClick([&](wi::gui::EventArgs args) { main->infoDisplay.active = args.bValue; }); optionsWnd.AddWidget(&infoDisplayCheckBox); infoDisplayCheckBox.SetCheck(main->infoDisplay.active); fpsCheckBox.Create("FPS: "); fpsCheckBox.SetTooltip("Toggle the FPS display."); fpsCheckBox.OnClick([&](wi::gui::EventArgs args) { main->infoDisplay.fpsinfo = args.bValue; }); optionsWnd.AddWidget(&fpsCheckBox); fpsCheckBox.SetCheck(main->infoDisplay.fpsinfo); otherinfoCheckBox.Create("Advanced: "); otherinfoCheckBox.SetTooltip("Toggle advanced data in the info display."); otherinfoCheckBox.OnClick([&](wi::gui::EventArgs args) { main->infoDisplay.heap_allocation_counter = args.bValue; main->infoDisplay.vram_usage = args.bValue; main->infoDisplay.colorspace = args.bValue; main->infoDisplay.resolution = args.bValue; main->infoDisplay.logical_size = args.bValue; main->infoDisplay.pipeline_count = args.bValue; }); optionsWnd.AddWidget(&otherinfoCheckBox); otherinfoCheckBox.SetCheck(main->infoDisplay.heap_allocation_counter); newCombo.Create("New: "); newCombo.AddItem("...", ~0ull); newCombo.AddItem("Transform", 0); newCombo.AddItem("Material", 1); newCombo.AddItem("Point Light", 2); newCombo.AddItem("Spot Light", 3); newCombo.AddItem("Directional Light", 4); newCombo.AddItem("Environment Probe", 5); newCombo.AddItem("Force", 6); newCombo.AddItem("Decal", 7); newCombo.AddItem("Sound", 8); newCombo.AddItem("Weather", 9); newCombo.AddItem("Emitter", 10); newCombo.AddItem("HairParticle", 11); newCombo.AddItem("Camera", 12); newCombo.AddItem("Cube Object", 13); newCombo.AddItem("Plane Object", 14); newCombo.OnSelect([&](wi::gui::EventArgs args) { newCombo.SetSelectedWithoutCallback(0); const EditorScene& editorscene = GetCurrentEditorScene(); const CameraComponent& camera = editorscene.camera; Scene& scene = GetCurrentScene(); PickResult pick; XMFLOAT3 in_front_of_camera; XMStoreFloat3(&in_front_of_camera, XMVectorAdd(camera.GetEye(), camera.GetAt() * 4)); switch (args.userdata) { case 0: pick.entity = scene.Entity_CreateTransform("transform"); break; case 1: pick.entity = scene.Entity_CreateMaterial("material"); break; case 2: pick.entity = scene.Entity_CreateLight("pointlight", in_front_of_camera, XMFLOAT3(1, 1, 1), 2, 60); scene.lights.GetComponent(pick.entity)->type = LightComponent::POINT; scene.lights.GetComponent(pick.entity)->intensity = 20; break; case 3: pick.entity = scene.Entity_CreateLight("spotlight", in_front_of_camera, XMFLOAT3(1, 1, 1), 2, 60); scene.lights.GetComponent(pick.entity)->type = LightComponent::SPOT; scene.lights.GetComponent(pick.entity)->intensity = 100; break; case 4: pick.entity = scene.Entity_CreateLight("dirlight", XMFLOAT3(0, 3, 0), XMFLOAT3(1, 1, 1), 2, 60); scene.lights.GetComponent(pick.entity)->type = LightComponent::DIRECTIONAL; scene.lights.GetComponent(pick.entity)->intensity = 10; break; case 5: pick.entity = scene.Entity_CreateEnvironmentProbe("envprobe", in_front_of_camera); break; case 6: pick.entity = scene.Entity_CreateForce("force"); break; case 7: pick.entity = scene.Entity_CreateDecal("decal", "images/logo_small.png"); scene.transforms.GetComponent(pick.entity)->RotateRollPitchYaw(XMFLOAT3(XM_PIDIV2, 0, 0)); break; case 8: { wi::helper::FileDialogParams params; params.type = wi::helper::FileDialogParams::OPEN; params.description = "Sound"; params.extensions = wi::resourcemanager::GetSupportedSoundExtensions(); wi::helper::FileDialog(params, [=](std::string fileName) { wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { Entity entity = GetCurrentScene().Entity_CreateSound(wi::helper::GetFileNameFromPath(fileName), fileName); wi::Archive& archive = AdvanceHistory(); archive << EditorComponent::HISTORYOP_ADD; RecordSelection(archive); ClearSelected(); AddSelected(entity); RecordSelection(archive); RecordEntity(archive, entity); RefreshEntityTree(); soundWnd.SetEntity(entity); }); }); return; } break; case 9: pick.entity = CreateEntity(); scene.weathers.Create(pick.entity); scene.names.Create(pick.entity) = "weather"; break; case 10: pick.entity = scene.Entity_CreateEmitter("emitter"); break; case 11: pick.entity = scene.Entity_CreateHair("hair"); break; case 12: pick.entity = scene.Entity_CreateCamera("camera", camera.width, camera.height); *scene.cameras.GetComponent(pick.entity) = camera; *scene.transforms.GetComponent(pick.entity) = editorscene.camera_transform; break; case 13: pick.entity = scene.Entity_CreateCube("cube"); pick.subsetIndex = 0; break; case 14: pick.entity = scene.Entity_CreatePlane("plane"); pick.subsetIndex = 0; break; default: break; } if (pick.entity != INVALID_ENTITY) { wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_ADD; RecordSelection(archive); ClearSelected(); AddSelected(pick); RecordSelection(archive); RecordEntity(archive, pick.entity); } RefreshEntityTree(); }); newCombo.SetEnabled(true); newCombo.SetTooltip("Create new entity"); optionsWnd.AddWidget(&newCombo); filterCombo.Create("Filter: "); filterCombo.AddItem("All ", (uint64_t)Filter::All); filterCombo.AddItem("Transform " ICON_TRANSFORM, (uint64_t)Filter::Transform); filterCombo.AddItem("Material " ICON_MATERIAL, (uint64_t)Filter::Material); filterCombo.AddItem("Mesh " ICON_MESH, (uint64_t)Filter::Mesh); filterCombo.AddItem("Object " ICON_OBJECT, (uint64_t)Filter::Object); filterCombo.AddItem("Environment Probe " ICON_ENVIRONMENTPROBE, (uint64_t)Filter::EnvironmentProbe); filterCombo.AddItem("Decal " ICON_DECAL, (uint64_t)Filter::Decal); filterCombo.AddItem("Sound " ICON_SOUND, (uint64_t)Filter::Sound); filterCombo.AddItem("Weather " ICON_WEATHER, (uint64_t)Filter::Weather); filterCombo.AddItem("Light " ICON_POINTLIGHT, (uint64_t)Filter::Light); filterCombo.SetTooltip("Apply filtering to the Entities"); filterCombo.OnSelect([&](wi::gui::EventArgs args) { filter = (Filter)args.userdata; RefreshEntityTree(); }); optionsWnd.AddWidget(&filterCombo); entityTree.Create("Entities"); entityTree.SetSize(XMFLOAT2(300, 300)); entityTree.OnSelect([this](wi::gui::EventArgs args) { if (args.iValue < 0) return; wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_SELECTION; // record PREVIOUS selection state... RecordSelection(archive); translator.selected.clear(); for (int i = 0; i < entityTree.GetItemCount(); ++i) { const wi::gui::TreeList::Item& item = entityTree.GetItem(i); if (item.selected) { wi::scene::PickResult pick; pick.entity = (Entity)item.userdata; AddSelected(pick); } } // record NEW selection state... RecordSelection(archive); }); optionsWnd.AddWidget(&entityTree); renderPathComboBox.Create("Render Path: "); renderPathComboBox.AddItem("Default"); renderPathComboBox.AddItem("Path Tracing"); renderPathComboBox.OnSelect([&](wi::gui::EventArgs args) { ChangeRenderPath((RENDERPATH)args.iValue); }); renderPathComboBox.SetSelected(RENDERPATH_DEFAULT); renderPathComboBox.SetEnabled(true); renderPathComboBox.SetTooltip("Choose a render path..."); optionsWnd.AddWidget(&renderPathComboBox); pathTraceTargetSlider.Create(1, 2048, 1024, 2047, "Sample count: "); pathTraceTargetSlider.SetSize(XMFLOAT2(200, 18)); pathTraceTargetSlider.SetTooltip("The path tracing will perform this many samples per pixel."); optionsWnd.AddWidget(&pathTraceTargetSlider); pathTraceTargetSlider.SetVisible(false); pathTraceStatisticsLabel.Create("Path tracing statistics"); pathTraceStatisticsLabel.SetSize(XMFLOAT2(240, 60)); optionsWnd.AddWidget(&pathTraceStatisticsLabel); pathTraceStatisticsLabel.SetVisible(false); // Renderer and Postprocess windows are created in ChangeRenderPath(), because they deal with // RenderPath related information as well, so it's easier to reset them when changing materialWnd.Create(this); weatherWnd.Create(this); objectWnd.Create(this); meshWnd.Create(this); envProbeWnd.Create(this); soundWnd.Create(this); decalWnd.Create(this); lightWnd.Create(this); animWnd.Create(this); emitterWnd.Create(this); hairWnd.Create(this); forceFieldWnd.Create(this); springWnd.Create(this); ikWnd.Create(this); transformWnd.Create(this); layerWnd.Create(this); nameWnd.Create(this); componentWindow.Create("Components ", wi::gui::Window::WindowControls::RESIZE_TOPLEFT); componentWindow.SetSize(optionsWnd.GetSize()); componentWindow.font.params.h_align = wi::font::WIFALIGN_RIGHT; componentWindow.SetShadowRadius(2); GetGUI().AddWidget(&componentWindow); newComponentCombo.Create("Add: "); newComponentCombo.SetTooltip("Add a component to the first selected entity."); newComponentCombo.AddItem("...", ~0ull); newComponentCombo.AddItem("Name", 0); newComponentCombo.AddItem("Layer " ICON_LAYER, 1); newComponentCombo.AddItem("Transform " ICON_TRANSFORM, 2); newComponentCombo.AddItem("Light " ICON_POINTLIGHT, 3); newComponentCombo.AddItem("Matetial " ICON_MATERIAL, 4); newComponentCombo.AddItem("Spring", 5); newComponentCombo.AddItem("Inverse Kinematics", 6); newComponentCombo.AddItem("Sound " ICON_SOUND, 7); newComponentCombo.AddItem("Environment Probe " ICON_ENVIRONMENTPROBE, 8); newComponentCombo.AddItem("Emitted Particle System " ICON_EMITTER, 9); newComponentCombo.AddItem("Hair Particle System " ICON_HAIR, 10); newComponentCombo.AddItem("Decal " ICON_DECAL, 11); newComponentCombo.AddItem("Weather " ICON_WEATHER, 12); newComponentCombo.AddItem("Force Field " ICON_FORCE, 13); newComponentCombo.OnSelect([=](wi::gui::EventArgs args) { newComponentCombo.SetSelectedWithoutCallback(0); if (translator.selected.empty()) return; Scene& scene = GetCurrentScene(); Entity entity = translator.selected.front().entity; if (entity == INVALID_ENTITY) { assert(0); return; } // Can early exit before creating history entry! switch (args.userdata) { case 0: if(scene.names.Contains(entity)) return; break; case 1: if (scene.layers.Contains(entity)) return; break; case 2: if (scene.transforms.Contains(entity)) return; break; case 3: if (scene.lights.Contains(entity)) return; break; case 4: if (scene.materials.Contains(entity)) return; break; case 5: if (scene.springs.Contains(entity)) return; break; case 6: if (scene.inverse_kinematics.Contains(entity)) return; break; case 7: if (scene.inverse_kinematics.Contains(entity)) return; break; case 8: if (scene.probes.Contains(entity)) return; break; case 9: if (scene.emitters.Contains(entity)) return; break; case 10: if (scene.hairs.Contains(entity)) return; break; case 11: if (scene.decals.Contains(entity)) return; break; case 12: if (scene.weathers.Contains(entity)) return; break; case 13: if (scene.forces.Contains(entity)) return; break; default: return; } wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_COMPONENT_DATA; RecordEntity(archive, entity); switch (args.userdata) { case 0: scene.names.Create(entity); break; case 1: scene.layers.Create(entity); break; case 2: scene.transforms.Create(entity); break; case 3: scene.lights.Create(entity); scene.aabb_lights.Create(entity); break; case 4: scene.materials.Create(entity); break; case 5: scene.springs.Create(entity); break; case 6: scene.inverse_kinematics.Create(entity); break; case 7: scene.sounds.Create(entity); break; case 8: scene.probes.Create(entity); scene.aabb_probes.Create(entity); break; case 9: if (!scene.materials.Contains(entity)) scene.materials.Create(entity); scene.emitters.Create(entity); break; case 10: if (!scene.materials.Contains(entity)) scene.materials.Create(entity); scene.hairs.Create(entity); break; case 11: if (!scene.materials.Contains(entity)) scene.materials.Create(entity); scene.decals.Create(entity); scene.aabb_decals.Create(entity); break; case 12: scene.weathers.Create(entity); break; case 13: scene.forces.Create(entity); break; default: break; } RecordEntity(archive, entity); RefreshEntityTree(); }); componentWindow.AddWidget(&newComponentCombo); componentWindow.AddWidget(&materialWnd); componentWindow.AddWidget(&weatherWnd); componentWindow.AddWidget(&objectWnd); componentWindow.AddWidget(&meshWnd); componentWindow.AddWidget(&envProbeWnd); componentWindow.AddWidget(&soundWnd); componentWindow.AddWidget(&decalWnd); componentWindow.AddWidget(&lightWnd); componentWindow.AddWidget(&animWnd); componentWindow.AddWidget(&emitterWnd); componentWindow.AddWidget(&hairWnd); componentWindow.AddWidget(&forceFieldWnd); componentWindow.AddWidget(&springWnd); componentWindow.AddWidget(&ikWnd); componentWindow.AddWidget(&transformWnd); componentWindow.AddWidget(&layerWnd); componentWindow.AddWidget(&nameWnd); materialWnd.SetVisible(false); weatherWnd.SetVisible(false); objectWnd.SetVisible(false); meshWnd.SetVisible(false); envProbeWnd.SetVisible(false); soundWnd.SetVisible(false); decalWnd.SetVisible(false); lightWnd.SetVisible(false); animWnd.SetVisible(false); emitterWnd.SetVisible(false); hairWnd.SetVisible(false); forceFieldWnd.SetVisible(false); springWnd.SetVisible(false); ikWnd.SetVisible(false); transformWnd.SetVisible(false); layerWnd.SetVisible(false); nameWnd.SetVisible(false); materialWnd.SetShadowRadius(shadow_expand); weatherWnd.SetShadowRadius(shadow_expand); objectWnd.SetShadowRadius(shadow_expand); meshWnd.SetShadowRadius(shadow_expand); envProbeWnd.SetShadowRadius(shadow_expand); soundWnd.SetShadowRadius(shadow_expand); decalWnd.SetShadowRadius(shadow_expand); lightWnd.SetShadowRadius(shadow_expand); animWnd.SetShadowRadius(shadow_expand); emitterWnd.SetShadowRadius(shadow_expand); hairWnd.SetShadowRadius(shadow_expand); forceFieldWnd.SetShadowRadius(shadow_expand); springWnd.SetShadowRadius(shadow_expand); ikWnd.SetShadowRadius(shadow_expand); transformWnd.SetShadowRadius(shadow_expand); layerWnd.SetShadowRadius(shadow_expand); nameWnd.SetShadowRadius(shadow_expand); cameraWnd.Create(this); cameraWnd.ResetCam(); cameraWnd.SetShadowRadius(shadow_expand); cameraWnd.SetCollapsed(true); optionsWnd.AddWidget(&cameraWnd); paintToolWnd.Create(this); paintToolWnd.SetShadowRadius(shadow_expand); paintToolWnd.SetCollapsed(true); optionsWnd.AddWidget(&paintToolWnd); sceneComboBox.Create("Scene: "); sceneComboBox.OnSelect([&](wi::gui::EventArgs args) { if (args.iValue >= int(scenes.size())) { NewScene(); } SetCurrentScene(args.iValue); }); sceneComboBox.SetEnabled(true); sceneComboBox.SetColor(wi::Color(50, 100, 255, 180), wi::gui::IDLE); sceneComboBox.SetColor(wi::Color(120, 160, 255, 255), wi::gui::FOCUS); optionsWnd.AddWidget(&sceneComboBox); saveModeComboBox.Create("Save Mode: "); saveModeComboBox.AddItem("Embed resources", (uint64_t)wi::resourcemanager::Mode::ALLOW_RETAIN_FILEDATA); saveModeComboBox.AddItem("No embedding", (uint64_t)wi::resourcemanager::Mode::ALLOW_RETAIN_FILEDATA_BUT_DISABLE_EMBEDDING); saveModeComboBox.AddItem("Dump to header", (uint64_t)wi::resourcemanager::Mode::ALLOW_RETAIN_FILEDATA); saveModeComboBox.SetTooltip("Choose whether to embed resources (textures, sounds...) in the scene file when saving, or keep them as separate files.\nThe Dump to header option will use embedding and create a C++ header file with byte data of the scene to be used with wi::Archive serialization."); saveModeComboBox.SetColor(wi::Color(50, 180, 100, 180), wi::gui::IDLE); saveModeComboBox.SetColor(wi::Color(50, 220, 140, 255), wi::gui::FOCUS); optionsWnd.AddWidget(&saveModeComboBox); terragen.Create(); terragen.SetShadowRadius(shadow_expand); terragen.OnCollapse([&](wi::gui::EventArgs args) { if (terragen.terrainEntity == INVALID_ENTITY) { // Customize terrain generator before it's initialized: terragen.material_Base.SetRoughness(1); terragen.material_Base.SetReflectance(0.005f); terragen.material_Slope.SetRoughness(0.1f); terragen.material_LowAltitude.SetRoughness(1); terragen.material_HighAltitude.SetRoughness(1); terragen.material_Base.textures[MaterialComponent::BASECOLORMAP].name = "terrain/base.jpg"; terragen.material_Base.textures[MaterialComponent::NORMALMAP].name = "terrain/base_nor.jpg"; terragen.material_Slope.textures[MaterialComponent::BASECOLORMAP].name = "terrain/slope.jpg"; terragen.material_Slope.textures[MaterialComponent::NORMALMAP].name = "terrain/slope_nor.jpg"; terragen.material_LowAltitude.textures[MaterialComponent::BASECOLORMAP].name = "terrain/low_altitude.jpg"; terragen.material_LowAltitude.textures[MaterialComponent::NORMALMAP].name = "terrain/low_altitude_nor.jpg"; terragen.material_HighAltitude.textures[MaterialComponent::BASECOLORMAP].name = "terrain/high_altitude.jpg"; terragen.material_HighAltitude.textures[MaterialComponent::NORMALMAP].name = "terrain/high_altitude_nor.jpg"; terragen.material_GrassParticle.textures[MaterialComponent::BASECOLORMAP].name = "terrain/grassparticle.png"; terragen.material_GrassParticle.alphaRef = 0.75f; terragen.grass_properties.length = 5; terragen.grass_properties.frameCount = 2; terragen.grass_properties.framesX = 1; terragen.grass_properties.framesY = 2; terragen.grass_properties.frameStart = 0; terragen.material_Base.CreateRenderData(); terragen.material_Slope.CreateRenderData(); terragen.material_LowAltitude.CreateRenderData(); terragen.material_HighAltitude.CreateRenderData(); terragen.material_GrassParticle.CreateRenderData(); // Tree prop: { Scene props_scene; wi::scene::LoadModel(props_scene, "terrain/tree.wiscene"); TerrainGenerator::Prop& prop = terragen.props.emplace_back(); prop.name = "tree"; prop.min_count_per_chunk = 0; prop.max_count_per_chunk = 10; prop.region = 0; prop.region_power = 2; prop.noise_frequency = 0.1f; prop.noise_power = 1; prop.threshold = 0.4f; prop.min_size = 2.0f; prop.max_size = 8.0f; prop.min_y_offset = -0.5f; prop.max_y_offset = -0.5f; prop.mesh_entity = props_scene.Entity_FindByName("tree_mesh"); props_scene.impostors.Create(prop.mesh_entity).swapInDistance = 200; Entity object_entity = props_scene.Entity_FindByName("tree_object"); ObjectComponent* object = props_scene.objects.GetComponent(object_entity); if (object != nullptr) { prop.object = *object; prop.object.lod_distance_multiplier = 0.05f; //prop.object.cascadeMask = 1; // they won't be rendered into the largest shadow cascade } props_scene.Entity_Remove(object_entity); // The objects will be placed by terrain generator, we don't need the default object that the scene has anymore GetCurrentScene().Merge(props_scene); } // Rock prop: { Scene props_scene; wi::scene::LoadModel(props_scene, "terrain/rock.wiscene"); TerrainGenerator::Prop& prop = terragen.props.emplace_back(); prop.name = "rock"; prop.min_count_per_chunk = 0; prop.max_count_per_chunk = 8; prop.region = 0; prop.region_power = 1; prop.noise_frequency = 0.005f; prop.noise_power = 2; prop.threshold = 0.5f; prop.min_size = 0.02f; prop.max_size = 4.0f; prop.min_y_offset = -2; prop.max_y_offset = 0.5f; prop.mesh_entity = props_scene.Entity_FindByName("rock_mesh"); Entity object_entity = props_scene.Entity_FindByName("rock_object"); ObjectComponent* object = props_scene.objects.GetComponent(object_entity); if (object != nullptr) { prop.object = *object; prop.object.lod_distance_multiplier = 0.02f; prop.object.cascadeMask = 1; // they won't be rendered into the largest shadow cascade prop.object.draw_distance = 400; } props_scene.Entity_Remove(object_entity); // The objects will be placed by terrain generator, we don't need the default object that the scene has anymore GetCurrentScene().Merge(props_scene); } // Bush prop: { Scene props_scene; wi::scene::LoadModel(props_scene, "terrain/bush.wiscene"); TerrainGenerator::Prop& prop = terragen.props.emplace_back(); prop.name = "bush"; prop.min_count_per_chunk = 0; prop.max_count_per_chunk = 10; prop.region = 0; prop.region_power = 4; prop.noise_frequency = 0.01f; prop.noise_power = 4; prop.threshold = 0.1f; prop.min_size = 0.1f; prop.max_size = 1.5f; prop.min_y_offset = -1; prop.max_y_offset = 0; prop.mesh_entity = props_scene.Entity_FindByName("bush_mesh"); Entity object_entity = props_scene.Entity_FindByName("bush_object"); ObjectComponent* object = props_scene.objects.GetComponent(object_entity); if (object != nullptr) { prop.object = *object; prop.object.lod_distance_multiplier = 0.05f; prop.object.cascadeMask = 1; // they won't be rendered into the largest shadow cascade prop.object.draw_distance = 200; } props_scene.Entity_Remove(object_entity); // The objects will be placed by terrain generator, we don't need the default object that the scene has anymore GetCurrentScene().Merge(props_scene); } terragen.init(); RefreshEntityTree(); } if (!terragen.IsCollapsed() && !GetCurrentScene().transforms.Contains(terragen.terrainEntity)) { terragen.Generation_Restart(); RefreshEntityTree(); } }); optionsWnd.AddWidget(&terragen); enum class Theme { Dark, Bright, Soft, Hacking, }; themeCombo.Create("Theme: "); themeCombo.SetTooltip("Choose a color theme..."); themeCombo.AddItem("Dark", (uint64_t)Theme::Dark); themeCombo.AddItem("Bright", (uint64_t)Theme::Bright); themeCombo.AddItem("Soft", (uint64_t)Theme::Soft); themeCombo.AddItem("Hacking", (uint64_t)Theme::Hacking); themeCombo.OnSelect([=](wi::gui::EventArgs args) { // Dark theme defaults: wi::Color theme_color_idle = wi::Color(50, 80, 100, 200); wi::Color theme_color_focus = wi::Color(90, 170, 190, 220); wi::Color dark_point = wi::Color(10, 10, 30, 220); // darker elements will lerp towards this wi::gui::Theme theme; theme.image.background = true; theme.image.blendFlag = wi::enums::BLENDMODE_OPAQUE; theme.font.color = wi::Color(160, 240, 250, 255); theme.shadow_color = wi::Color(100, 180, 200, 100); switch ((Theme)args.userdata) { default: break; case Theme::Bright: theme_color_idle = wi::Color(200, 210, 220, 230); theme_color_focus = wi::Color(210, 230, 255, 250); dark_point = wi::Color(180, 180, 190, 230); theme.shadow_color = wi::Color::Shadow(); theme.font.color = wi::Color(50, 50, 80, 255); break; case Theme::Soft: theme_color_idle = wi::Color(200, 180, 190, 190); theme_color_focus = wi::Color(240, 190, 200, 230); dark_point = wi::Color(100, 80, 90, 220); theme.shadow_color = wi::Color(240, 190, 200, 100); theme.font.color = wi::Color(255, 230, 240, 255); break; case Theme::Hacking: theme_color_idle = wi::Color(0, 0, 0, 255); theme_color_focus = wi::Color(10, 230, 30, 255); dark_point = wi::Color(0, 0, 0, 255); theme.shadow_color = wi::Color(0, 250, 0, 200); theme.font.color = wi::Color(100, 250, 100, 255); theme.font.shadow_color = wi::Color::Shadow(); break; } theme.tooltipImage = theme.image; theme.tooltipImage.color = theme_color_idle; theme.tooltipFont = theme.font; theme.tooltip_shadow_color = theme.shadow_color; wi::Color theme_color_active = wi::Color::White(); wi::Color theme_color_deactivating = wi::Color::lerp(theme_color_focus, wi::Color::White(), 0.5f); // Customize whole gui: wi::gui::GUI& gui = GetGUI(); gui.SetTheme(theme); // set basic params to all states // customize colors for specific states: gui.SetColor(theme_color_idle, wi::gui::IDLE); gui.SetColor(theme_color_focus, wi::gui::FOCUS); gui.SetColor(theme_color_active, wi::gui::ACTIVE); gui.SetColor(theme_color_deactivating, wi::gui::DEACTIVATING); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.7f), wi::gui::WIDGET_ID_WINDOW_BASE); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.75f), wi::gui::WIDGET_ID_SLIDER_BASE_IDLE); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.8f), wi::gui::WIDGET_ID_SLIDER_BASE_FOCUS); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.85f), wi::gui::WIDGET_ID_SLIDER_BASE_ACTIVE); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.8f), wi::gui::WIDGET_ID_SLIDER_BASE_DEACTIVATING); gui.SetColor(theme_color_idle, wi::gui::WIDGET_ID_SLIDER_KNOB_IDLE); gui.SetColor(theme_color_focus, wi::gui::WIDGET_ID_SLIDER_KNOB_FOCUS); gui.SetColor(theme_color_active, wi::gui::WIDGET_ID_SLIDER_KNOB_ACTIVE); gui.SetColor(theme_color_deactivating, wi::gui::WIDGET_ID_SLIDER_KNOB_DEACTIVATING); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.75f), wi::gui::WIDGET_ID_SCROLLBAR_BASE_IDLE); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.8f), wi::gui::WIDGET_ID_SCROLLBAR_BASE_FOCUS); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.85f), wi::gui::WIDGET_ID_SCROLLBAR_BASE_ACTIVE); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.8f), wi::gui::WIDGET_ID_SCROLLBAR_BASE_DEACTIVATING); gui.SetColor(theme_color_idle, wi::gui::WIDGET_ID_SCROLLBAR_KNOB_INACTIVE); gui.SetColor(theme_color_focus, wi::gui::WIDGET_ID_SCROLLBAR_KNOB_HOVER); gui.SetColor(theme_color_active, wi::gui::WIDGET_ID_SCROLLBAR_KNOB_GRABBED); gui.SetColor(wi::Color::lerp(theme_color_idle, dark_point, 0.8f), wi::gui::WIDGET_ID_COMBO_DROPDOWN); if ((Theme)args.userdata == Theme::Hacking) { gui.SetColor(wi::Color(0, 200, 0, 255), wi::gui::WIDGET_ID_SLIDER_KNOB_IDLE); gui.SetColor(wi::Color(0, 200, 0, 255), wi::gui::WIDGET_ID_SCROLLBAR_KNOB_INACTIVE); } // customize individual elements: materialWnd.textureSlotButton.SetColor(wi::Color::White(), wi::gui::IDLE); paintToolWnd.brushTextureButton.SetColor(wi::Color::White(), wi::gui::IDLE); paintToolWnd.revealTextureButton.SetColor(wi::Color::White(), wi::gui::IDLE); aboutLabel.sprites[wi::gui::FOCUS] = aboutLabel.sprites[wi::gui::IDLE]; }); optionsWnd.AddWidget(&themeCombo); themeCombo.SetSelected(0); RenderPath2D::Load(); } void EditorComponent::Start() { RenderPath2D::Start(); } void EditorComponent::PreUpdate() { RenderPath2D::PreUpdate(); renderPath->PreUpdate(); } void EditorComponent::FixedUpdate() { RenderPath2D::FixedUpdate(); renderPath->FixedUpdate(); } void EditorComponent::Update(float dt) { wi::profiler::range_id profrange = wi::profiler::BeginRangeCPU("Editor Update"); Scene& scene = GetCurrentScene(); EditorScene& editorscene = GetCurrentEditorScene(); CameraComponent& camera = editorscene.camera; terragen.scene = &scene; translator.scene = &scene; if (scene.forces.Contains(grass_interaction_entity)) { scene.Entity_Remove(grass_interaction_entity); } cameraWnd.Update(); animWnd.Update(); weatherWnd.Update(); paintToolWnd.Update(dt); selectionOutlineTimer += dt; bool clear_selected = false; if (wi::input::Press(wi::input::KEYBOARD_BUTTON_ESCAPE)) { if (cinemaModeCheckBox.GetCheck()) { // Exit cinema mode: if (renderPath != nullptr) { renderPath->GetGUI().SetVisible(true); } GetGUI().SetVisible(true); main->infoDisplay.active = infoDisplayCheckBox.GetCheck(); wi::profiler::SetEnabled(profilerEnabledCheckBox.GetCheck()); cinemaModeCheckBox.SetCheck(false); } else { clear_selected = true; } } bool deleting = wi::input::Press(wi::input::KEYBOARD_BUTTON_DELETE); // Camera control: if (!wi::backlog::isActive() && !GetGUI().HasFocus()) { XMFLOAT4 currentMouse = wi::input::GetPointer(); static XMFLOAT4 originalMouse = XMFLOAT4(0, 0, 0, 0); static bool camControlStart = true; if (camControlStart) { originalMouse = wi::input::GetPointer(); } float xDif = 0, yDif = 0; if (wi::input::Down(wi::input::MOUSE_BUTTON_MIDDLE)) { camControlStart = false; #if 0 // Mouse delta from previous frame: xDif = currentMouse.x - originalMouse.x; yDif = currentMouse.y - originalMouse.y; #else // Mouse delta from hardware read: xDif = wi::input::GetMouseState().delta_position.x; yDif = wi::input::GetMouseState().delta_position.y; #endif xDif = 0.1f * xDif * (1.0f / 60.0f); yDif = 0.1f * yDif * (1.0f / 60.0f); wi::input::SetPointer(originalMouse); wi::input::HidePointer(true); } else { camControlStart = true; wi::input::HidePointer(false); } const float buttonrotSpeed = 2.0f * dt; if (wi::input::Down(wi::input::KEYBOARD_BUTTON_LEFT)) { xDif -= buttonrotSpeed; } if (wi::input::Down(wi::input::KEYBOARD_BUTTON_RIGHT)) { xDif += buttonrotSpeed; } if (wi::input::Down(wi::input::KEYBOARD_BUTTON_UP)) { yDif -= buttonrotSpeed; } if (wi::input::Down(wi::input::KEYBOARD_BUTTON_DOWN)) { yDif += buttonrotSpeed; } const XMFLOAT4 leftStick = wi::input::GetAnalog(wi::input::GAMEPAD_ANALOG_THUMBSTICK_L, 0); const XMFLOAT4 rightStick = wi::input::GetAnalog(wi::input::GAMEPAD_ANALOG_THUMBSTICK_R, 0); const XMFLOAT4 rightTrigger = wi::input::GetAnalog(wi::input::GAMEPAD_ANALOG_TRIGGER_R, 0); const float jostickrotspeed = 0.05f; xDif += rightStick.x * jostickrotspeed; yDif += rightStick.y * jostickrotspeed; xDif *= cameraWnd.rotationspeedSlider.GetValue(); yDif *= cameraWnd.rotationspeedSlider.GetValue(); if (cameraWnd.fpsCheckBox.GetCheck()) { // FPS Camera const float clampedDT = std::min(dt, 0.1f); // if dt > 100 millisec, don't allow the camera to jump too far... const float speed = ((wi::input::Down(wi::input::KEYBOARD_BUTTON_LSHIFT) ? 10.0f : 1.0f) + rightTrigger.x * 10.0f) * cameraWnd.movespeedSlider.GetValue() * clampedDT; XMVECTOR move = XMLoadFloat3(&editorscene.cam_move); XMVECTOR moveNew = XMVectorSet(leftStick.x, 0, leftStick.y, 0); if (!wi::input::Down(wi::input::KEYBOARD_BUTTON_LCONTROL)) { // Only move camera if control not pressed if (wi::input::Down((wi::input::BUTTON)'A') || wi::input::Down(wi::input::GAMEPAD_BUTTON_LEFT)) { moveNew += XMVectorSet(-1, 0, 0, 0); } if (wi::input::Down((wi::input::BUTTON)'D') || wi::input::Down(wi::input::GAMEPAD_BUTTON_RIGHT)) { moveNew += XMVectorSet(1, 0, 0, 0); } if (wi::input::Down((wi::input::BUTTON)'W') || wi::input::Down(wi::input::GAMEPAD_BUTTON_UP)) { moveNew += XMVectorSet(0, 0, 1, 0); } if (wi::input::Down((wi::input::BUTTON)'S') || wi::input::Down(wi::input::GAMEPAD_BUTTON_DOWN)) { moveNew += XMVectorSet(0, 0, -1, 0); } if (wi::input::Down((wi::input::BUTTON)'E') || wi::input::Down(wi::input::GAMEPAD_BUTTON_2)) { moveNew += XMVectorSet(0, 1, 0, 0); } if (wi::input::Down((wi::input::BUTTON)'Q') || wi::input::Down(wi::input::GAMEPAD_BUTTON_1)) { moveNew += XMVectorSet(0, -1, 0, 0); } moveNew += XMVector3Normalize(moveNew); } moveNew *= speed; move = XMVectorLerp(move, moveNew, cameraWnd.accelerationSlider.GetValue() * clampedDT / 0.0166f); // smooth the movement a bit float moveLength = XMVectorGetX(XMVector3Length(move)); if (moveLength < 0.0001f) { move = XMVectorSet(0, 0, 0, 0); } if (abs(xDif) + abs(yDif) > 0 || moveLength > 0.0001f) { XMMATRIX camRot = XMMatrixRotationQuaternion(XMLoadFloat4(&editorscene.camera_transform.rotation_local)); XMVECTOR move_rot = XMVector3TransformNormal(move, camRot); XMFLOAT3 _move; XMStoreFloat3(&_move, move_rot); editorscene.camera_transform.Translate(_move); editorscene.camera_transform.RotateRollPitchYaw(XMFLOAT3(yDif, xDif, 0)); camera.SetDirty(); } editorscene.camera_transform.UpdateTransform(); XMStoreFloat3(&editorscene.cam_move, move); } else { // Orbital Camera if (wi::input::Down(wi::input::KEYBOARD_BUTTON_LSHIFT)) { XMVECTOR V = XMVectorAdd(camera.GetRight() * xDif, camera.GetUp() * yDif) * 10; XMFLOAT3 vec; XMStoreFloat3(&vec, V); editorscene.camera_target.Translate(vec); } else if (wi::input::Down(wi::input::KEYBOARD_BUTTON_LCONTROL) || currentMouse.z != 0.0f) { editorscene.camera_transform.Translate(XMFLOAT3(0, 0, yDif * 4 + currentMouse.z)); editorscene.camera_transform.translation_local.z = std::min(0.0f, editorscene.camera_transform.translation_local.z); camera.SetDirty(); } else if (abs(xDif) + abs(yDif) > 0) { editorscene.camera_target.RotateRollPitchYaw(XMFLOAT3(yDif * 2, xDif * 2, 0)); camera.SetDirty(); } editorscene.camera_target.UpdateTransform(); editorscene.camera_transform.UpdateTransform_Parented(editorscene.camera_target); } inspector_mode = wi::input::Down((wi::input::BUTTON)'I'); // Begin picking: unsigned int pickMask = rendererWnd.GetPickType(); Ray pickRay = wi::renderer::GetPickRay((long)currentMouse.x, (long)currentMouse.y, *this, camera); { hovered = wi::scene::PickResult(); if (pickMask & PICK_LIGHT) { for (size_t i = 0; i < scene.lights.GetCount(); ++i) { Entity entity = scene.lights.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if (pickMask & PICK_DECAL) { for (size_t i = 0; i < scene.decals.GetCount(); ++i) { Entity entity = scene.decals.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if (pickMask & PICK_FORCEFIELD) { for (size_t i = 0; i < scene.forces.GetCount(); ++i) { Entity entity = scene.forces.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if (pickMask & PICK_EMITTER) { for (size_t i = 0; i < scene.emitters.GetCount(); ++i) { Entity entity = scene.emitters.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if (pickMask & PICK_HAIR) { for (size_t i = 0; i < scene.hairs.GetCount(); ++i) { Entity entity = scene.hairs.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if (pickMask & PICK_ENVPROBE) { for (size_t i = 0; i < scene.probes.GetCount(); ++i) { Entity entity = scene.probes.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); if (Sphere(transform.GetPosition(), 1).intersects(pickRay)) { float dis = wi::math::Distance(transform.GetPosition(), pickRay.origin); if (dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } } if (pickMask & PICK_CAMERA) { for (size_t i = 0; i < scene.cameras.GetCount(); ++i) { Entity entity = scene.cameras.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if (pickMask & PICK_ARMATURE) { for (size_t i = 0; i < scene.armatures.GetCount(); ++i) { Entity entity = scene.armatures.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if (pickMask & PICK_SOUND) { for (size_t i = 0; i < scene.sounds.GetCount(); ++i) { Entity entity = scene.sounds.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); XMVECTOR disV = XMVector3LinePointDistance(XMLoadFloat3(&pickRay.origin), XMLoadFloat3(&pickRay.origin) + XMLoadFloat3(&pickRay.direction), transform.GetPositionV()); float dis = XMVectorGetX(disV); if (dis > 0.01f && dis < wi::math::Distance(transform.GetPosition(), pickRay.origin) * 0.05f && dis < hovered.distance) { hovered = wi::scene::PickResult(); hovered.entity = entity; hovered.distance = dis; } } } if ((pickMask & PICK_OBJECT) && hovered.entity == INVALID_ENTITY) { // Object picking only when mouse button down, because it can be slow with high polycount if ( wi::input::Down(wi::input::MOUSE_BUTTON_LEFT) || wi::input::Down(wi::input::MOUSE_BUTTON_RIGHT) || paintToolWnd.GetMode() != PaintToolWindow::MODE_DISABLED || inspector_mode ) { hovered = wi::scene::Pick(pickRay, pickMask, ~0u, scene); } } } // Interactions only when paint tool is disabled: if (paintToolWnd.GetMode() == PaintToolWindow::MODE_DISABLED) { // Interact: if (hovered.entity != INVALID_ENTITY && wi::input::Down(wi::input::MOUSE_BUTTON_LEFT)) { const ObjectComponent* object = scene.objects.GetComponent(hovered.entity); if (object != nullptr) { if (translator.selected.empty() && object->GetRenderTypes() & wi::enums::RENDERTYPE_WATER) { // if water, then put a water ripple onto it: scene.PutWaterRipple("images/ripple.png", hovered.position); } else if (decalWnd.IsEnabled() && decalWnd.placementCheckBox.GetCheck() && wi::input::Press(wi::input::MOUSE_BUTTON_LEFT)) { // if not water, put a decal on it: Entity entity = scene.Entity_CreateDecal("editorDecal", ""); // material and decal parameters will be copied from selected: if (scene.decals.Contains(decalWnd.entity)) { *scene.decals.GetComponent(entity) = *scene.decals.GetComponent(decalWnd.entity); } if (scene.materials.Contains(decalWnd.entity)) { *scene.materials.GetComponent(entity) = *scene.materials.GetComponent(decalWnd.entity); } // place it on picked surface: TransformComponent& transform = *scene.transforms.GetComponent(entity); transform.MatrixTransform(hovered.orientation); transform.RotateRollPitchYaw(XMFLOAT3(XM_PIDIV2, 0, 0)); scene.Component_Attach(entity, hovered.entity); wi::Archive& archive = AdvanceHistory(); archive << EditorComponent::HISTORYOP_ADD; RecordSelection(archive); RecordSelection(archive); RecordEntity(archive, entity); RefreshEntityTree(); } else { // Check for interactive grass (hair particle that is child of hovered object: for (size_t i = 0; i < scene.hairs.GetCount(); ++i) { Entity entity = scene.hairs.GetEntity(i); HierarchyComponent* hier = scene.hierarchy.GetComponent(entity); if (hier != nullptr && hier->parentID == hovered.entity) { XMVECTOR P = XMLoadFloat3(&hovered.position); P += XMLoadFloat3(&hovered.normal) * 2; if (grass_interaction_entity == INVALID_ENTITY) { grass_interaction_entity = CreateEntity(); } ForceFieldComponent& force = scene.forces.Create(grass_interaction_entity); TransformComponent& transform = scene.transforms.Create(grass_interaction_entity); force.type = ENTITY_TYPE_FORCEFIELD_POINT; force.gravity = -80; force.range = 3; transform.Translate(P); break; } } } } } } // Select... if (wi::input::Press(wi::input::MOUSE_BUTTON_RIGHT) || selectAll || clear_selected) { wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_SELECTION; // record PREVIOUS selection state... RecordSelection(archive); if (selectAll) { // Add everything to selection: selectAll = false; ClearSelected(); selectAllStorage.clear(); scene.FindAllEntities(selectAllStorage); for (auto& entity : selectAllStorage) { AddSelected(entity); } } else if (hovered.entity != INVALID_ENTITY) { // Add the hovered item to the selection: if (!translator.selected.empty() && wi::input::Down(wi::input::KEYBOARD_BUTTON_LSHIFT)) { // Union selection: wi::vector saved = translator.selected; translator.selected.clear(); for (const wi::scene::PickResult& picked : saved) { AddSelected(picked); } AddSelected(hovered); } else { // Replace selection: translator.selected.clear(); AddSelected(hovered); } } else { clear_selected = true; } if (clear_selected) { ClearSelected(); } // record NEW selection state... RecordSelection(archive); RefreshEntityTree(); } } main->infoDisplay.colorgrading_helper = false; // Control operations... if (wi::input::Down(wi::input::KEYBOARD_BUTTON_LCONTROL)) { // Color Grading helper if (wi::input::Down((wi::input::BUTTON)'G')) { main->infoDisplay.colorgrading_helper = true; } // Toggle wireframe mode if (wi::input::Press((wi::input::BUTTON)'W')) { wi::renderer::SetWireRender(!wi::renderer::IsWireRender()); rendererWnd.wireFrameCheckBox.SetCheck(wi::renderer::IsWireRender()); } // Enable transform tool if (wi::input::Press((wi::input::BUTTON)'T')) { translator.enabled = !translator.enabled; translatorCheckBox.SetCheck(translator.enabled); } // Save if (wi::input::Press((wi::input::BUTTON)'S')) { if (wi::input::Down(wi::input::KEYBOARD_BUTTON_LSHIFT) || GetCurrentEditorScene().path.empty()) { SaveAs(); } else { Save(GetCurrentEditorScene().path); } } // Select All if (wi::input::Press((wi::input::BUTTON)'A')) { selectAll = true; } // Copy/Cut if (wi::input::Press((wi::input::BUTTON)'C') || wi::input::Press((wi::input::BUTTON)'X')) { auto& prevSel = translator.selectedEntitiesNonRecursive; EntitySerializer seri; clipboard.SetReadModeAndResetPos(false); clipboard << prevSel.size(); for (auto& x : prevSel) { scene.Entity_Serialize(clipboard, seri, x); } if (wi::input::Press((wi::input::BUTTON)'X')) { deleting = true; } } // Paste if (wi::input::Press((wi::input::BUTTON)'V')) { wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_ADD; RecordSelection(archive); ClearSelected(); EntitySerializer seri; clipboard.SetReadModeAndResetPos(true); size_t count; clipboard >> count; wi::vector addedEntities; for (size_t i = 0; i < count; ++i) { wi::scene::PickResult picked; picked.entity = scene.Entity_Serialize(clipboard, seri, INVALID_ENTITY, Scene::EntitySerializeFlags::RECURSIVE); AddSelected(picked); addedEntities.push_back(picked.entity); } RecordSelection(archive); RecordEntity(archive, addedEntities); RefreshEntityTree(); } // Duplicate Instances if (wi::input::Press((wi::input::BUTTON)'D')) { wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_ADD; RecordSelection(archive); auto& prevSel = translator.selectedEntitiesNonRecursive; wi::vector addedEntities; for (auto& x : prevSel) { wi::scene::PickResult picked; picked.entity = scene.Entity_Duplicate(x); addedEntities.push_back(picked.entity); } ClearSelected(); for (auto& x : addedEntities) { AddSelected(x); } RecordSelection(archive); RecordEntity(archive, addedEntities); RefreshEntityTree(); } // Put Instances if (clipboard.IsOpen() && hovered.subsetIndex >= 0 && wi::input::Down(wi::input::KEYBOARD_BUTTON_LSHIFT) && wi::input::Press(wi::input::MOUSE_BUTTON_LEFT)) { wi::vector addedEntities; EntitySerializer seri; clipboard.SetReadModeAndResetPos(true); size_t count; clipboard >> count; for (size_t i = 0; i < count; ++i) { Entity entity = scene.Entity_Serialize(clipboard, seri, INVALID_ENTITY, Scene::EntitySerializeFlags::RECURSIVE | Scene::EntitySerializeFlags::KEEP_INTERNAL_ENTITY_REFERENCES); const HierarchyComponent* hier = scene.hierarchy.GetComponent(entity); if (hier != nullptr) { scene.Component_Detach(entity); } TransformComponent* transform = scene.transforms.GetComponent(entity); if (transform != nullptr) { transform->translation_local = {}; #if 0 // orient around surface normal: transform->MatrixTransform(hovered.orientation); #else // orient in random vertical rotation only: transform->RotateRollPitchYaw(XMFLOAT3(0, wi::random::GetRandom(XM_PI), 0)); transform->Translate(hovered.position); #endif transform->UpdateTransform(); } if (hier != nullptr) { scene.Component_Attach(entity, hier->parentID); } addedEntities.push_back(entity); } wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_ADD; // because selection didn't change here, we record same selection state twice, it's not a bug: RecordSelection(archive); RecordSelection(archive); RecordEntity(archive, addedEntities); RefreshEntityTree(); } // Undo if (wi::input::Press((wi::input::BUTTON)'Z')) { ConsumeHistoryOperation(true); RefreshEntityTree(); } // Redo if (wi::input::Press((wi::input::BUTTON)'Y')) { ConsumeHistoryOperation(false); RefreshEntityTree(); } } // Delete if (deleting) { wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_DELETE; RecordSelection(archive); archive << translator.selectedEntitiesNonRecursive; EntitySerializer seri; for (auto& x : translator.selectedEntitiesNonRecursive) { scene.Entity_Serialize(archive, seri, x); } for (auto& x : translator.selectedEntitiesNonRecursive) { scene.Entity_Remove(x); } ClearSelected(); RefreshEntityTree(); } // Update window data bindings... if (translator.selected.empty()) { objectWnd.SetEntity(INVALID_ENTITY); emitterWnd.SetEntity(INVALID_ENTITY); hairWnd.SetEntity(INVALID_ENTITY); meshWnd.SetEntity(INVALID_ENTITY, -1); materialWnd.SetEntity(INVALID_ENTITY); lightWnd.SetEntity(INVALID_ENTITY); soundWnd.SetEntity(INVALID_ENTITY); decalWnd.SetEntity(INVALID_ENTITY); envProbeWnd.SetEntity(INVALID_ENTITY); forceFieldWnd.SetEntity(INVALID_ENTITY); cameraWnd.SetEntity(INVALID_ENTITY); paintToolWnd.SetEntity(INVALID_ENTITY); springWnd.SetEntity(INVALID_ENTITY); ikWnd.SetEntity(INVALID_ENTITY); transformWnd.SetEntity(INVALID_ENTITY); layerWnd.SetEntity(INVALID_ENTITY); nameWnd.SetEntity(INVALID_ENTITY); weatherWnd.SetEntity(INVALID_ENTITY); } else { const wi::scene::PickResult& picked = translator.selected.back(); assert(picked.entity != INVALID_ENTITY); objectWnd.SetEntity(picked.entity); for (auto& x : translator.selected) { if (scene.objects.GetComponent(x.entity) != nullptr) { objectWnd.SetEntity(x.entity); break; } } emitterWnd.SetEntity(picked.entity); hairWnd.SetEntity(picked.entity); lightWnd.SetEntity(picked.entity); soundWnd.SetEntity(picked.entity); decalWnd.SetEntity(picked.entity); envProbeWnd.SetEntity(picked.entity); forceFieldWnd.SetEntity(picked.entity); cameraWnd.SetEntity(picked.entity); paintToolWnd.SetEntity(picked.entity, picked.subsetIndex); springWnd.SetEntity(picked.entity); ikWnd.SetEntity(picked.entity); transformWnd.SetEntity(picked.entity); layerWnd.SetEntity(picked.entity); nameWnd.SetEntity(picked.entity); weatherWnd.SetEntity(picked.entity); if (picked.subsetIndex >= 0) { const ObjectComponent* object = scene.objects.GetComponent(picked.entity); if (object != nullptr) // maybe it was deleted... { meshWnd.SetEntity(object->meshID, picked.subsetIndex); const MeshComponent* mesh = scene.meshes.GetComponent(object->meshID); if (mesh != nullptr && (int)mesh->subsets.size() > picked.subsetIndex) { materialWnd.SetEntity(mesh->subsets[picked.subsetIndex].materialID); } } } else { meshWnd.SetEntity(picked.entity, picked.subsetIndex); materialWnd.SetEntity(picked.entity); } } // Clear highlite state: for (size_t i = 0; i < scene.materials.GetCount(); ++i) { scene.materials[i].SetUserStencilRef(EDITORSTENCILREF_CLEAR); } for (size_t i = 0; i < scene.objects.GetCount(); ++i) { scene.objects[i].SetUserStencilRef(EDITORSTENCILREF_CLEAR); } for (auto& x : translator.selected) { ObjectComponent* object = scene.objects.GetComponent(x.entity); if (object != nullptr) // maybe it was deleted... { object->SetUserStencilRef(EDITORSTENCILREF_HIGHLIGHT_OBJECT); if (x.subsetIndex >= 0) { const MeshComponent* mesh = scene.meshes.GetComponent(object->meshID); if (mesh != nullptr && (int)mesh->subsets.size() > x.subsetIndex) { MaterialComponent* material = scene.materials.GetComponent(mesh->subsets[x.subsetIndex].materialID); if (material != nullptr) { material->SetUserStencilRef(EDITORSTENCILREF_HIGHLIGHT_MATERIAL); } } } } } if (translator.IsDragEnded()) { EntitySerializer seri; wi::Archive& archive = AdvanceHistory(); archive << HISTORYOP_TRANSLATOR; translator.transform_start.Serialize(archive, seri); translator.transform.Serialize(archive, seri); archive << translator.matrices_start; archive << translator.matrices_current; } emitterWnd.UpdateData(); hairWnd.UpdateData(); // Follow camera proxy: if (cameraWnd.followCheckBox.IsEnabled() && cameraWnd.followCheckBox.GetCheck()) { TransformComponent* proxy = scene.transforms.GetComponent(cameraWnd.entity); if (proxy != nullptr) { editorscene.camera_transform.Lerp(editorscene.camera_transform, *proxy, 1.0f - cameraWnd.followSlider.GetValue()); editorscene.camera_transform.UpdateTransform(); } } camera.TransformCamera(editorscene.camera_transform); camera.UpdateCamera(); wi::RenderPath3D_PathTracing* pathtracer = dynamic_cast(renderPath.get()); if (pathtracer != nullptr) { pathtracer->setTargetSampleCount((int)pathTraceTargetSlider.GetValue()); std::string ss; ss += "Sample count: " + std::to_string(pathtracer->getCurrentSampleCount()) + "\n"; ss += "Trace progress: " + std::to_string(int(pathtracer->getProgress() * 100)) + "%\n"; if (pathtracer->isDenoiserAvailable()) { if (pathtracer->getDenoiserProgress() > 0) { ss += "Denoiser progress: " + std::to_string(int(pathtracer->getDenoiserProgress() * 100)) + "%\n"; } } else { ss += "Denoiser not available!\nTo find out how to enable the denoiser, visit the documentation."; } pathTraceStatisticsLabel.SetText(ss); } terragen.Generation_Update(camera); wi::profiler::EndRange(profrange); RenderPath2D::Update(dt); RefreshComponentWindow(); RefreshOptionsWindow(); translator.Update(camera, *this); renderPath->colorspace = colorspace; renderPath->Update(dt); } void EditorComponent::PostUpdate() { RenderPath2D::PostUpdate(); renderPath->PostUpdate(); } void EditorComponent::Render() const { const Scene& scene = GetCurrentScene(); // Hovered item boxes: if (!cinemaModeCheckBox.GetCheck()) { if (hovered.entity != INVALID_ENTITY) { const ObjectComponent* object = scene.objects.GetComponent(hovered.entity); if (object != nullptr) { const AABB& aabb = *scene.aabb_objects.GetComponent(hovered.entity); XMFLOAT4X4 hoverBox; XMStoreFloat4x4(&hoverBox, aabb.getAsBoxMatrix()); wi::renderer::DrawBox(hoverBox, XMFLOAT4(0.5f, 0.5f, 0.5f, 0.5f)); } const LightComponent* light = scene.lights.GetComponent(hovered.entity); if (light != nullptr) { const AABB& aabb = *scene.aabb_lights.GetComponent(hovered.entity); XMFLOAT4X4 hoverBox; XMStoreFloat4x4(&hoverBox, aabb.getAsBoxMatrix()); wi::renderer::DrawBox(hoverBox, XMFLOAT4(0.5f, 0.5f, 0, 0.5f)); } const DecalComponent* decal = scene.decals.GetComponent(hovered.entity); if (decal != nullptr) { wi::renderer::DrawBox(decal->world, XMFLOAT4(0.5f, 0, 0.5f, 0.5f)); } const EnvironmentProbeComponent* probe = scene.probes.GetComponent(hovered.entity); if (probe != nullptr) { const AABB& aabb = *scene.aabb_probes.GetComponent(hovered.entity); XMFLOAT4X4 hoverBox; XMStoreFloat4x4(&hoverBox, aabb.getAsBoxMatrix()); wi::renderer::DrawBox(hoverBox, XMFLOAT4(0.5f, 0.5f, 0.5f, 0.5f)); } const wi::HairParticleSystem* hair = scene.hairs.GetComponent(hovered.entity); if (hair != nullptr) { XMFLOAT4X4 hoverBox; XMStoreFloat4x4(&hoverBox, hair->aabb.getAsBoxMatrix()); wi::renderer::DrawBox(hoverBox, XMFLOAT4(0, 0.5f, 0, 0.5f)); } } // Spring visualizer: if (springWnd.debugCheckBox.GetCheck()) { for (size_t i = 0; i < scene.springs.GetCount(); ++i) { const SpringComponent& spring = scene.springs[i]; wi::renderer::RenderablePoint point; point.position = spring.center_of_mass; point.size = 0.05f; point.color = XMFLOAT4(1, 1, 0, 1); wi::renderer::DrawPoint(point); } } } // Selected items box: if (!cinemaModeCheckBox.GetCheck() && !translator.selected.empty()) { AABB selectedAABB = AABB( XMFLOAT3(std::numeric_limits::max(), std::numeric_limits::max(), std::numeric_limits::max()), XMFLOAT3(std::numeric_limits::lowest(), std::numeric_limits::lowest(), std::numeric_limits::lowest())); for (auto& picked : translator.selected) { if (picked.entity != INVALID_ENTITY) { const ObjectComponent* object = scene.objects.GetComponent(picked.entity); if (object != nullptr) { const AABB& aabb = *scene.aabb_objects.GetComponent(picked.entity); selectedAABB = AABB::Merge(selectedAABB, aabb); } const LightComponent* light = scene.lights.GetComponent(picked.entity); if (light != nullptr) { const AABB& aabb = *scene.aabb_lights.GetComponent(picked.entity); selectedAABB = AABB::Merge(selectedAABB, aabb); } const DecalComponent* decal = scene.decals.GetComponent(picked.entity); if (decal != nullptr) { const AABB& aabb = *scene.aabb_decals.GetComponent(picked.entity); selectedAABB = AABB::Merge(selectedAABB, aabb); // also display decal OBB: XMFLOAT4X4 selectionBox; selectionBox = decal->world; wi::renderer::DrawBox(selectionBox, XMFLOAT4(1, 0, 1, 1)); } const EnvironmentProbeComponent* probe = scene.probes.GetComponent(picked.entity); if (probe != nullptr) { const AABB& aabb = *scene.aabb_probes.GetComponent(picked.entity); selectedAABB = AABB::Merge(selectedAABB, aabb); } const wi::HairParticleSystem* hair = scene.hairs.GetComponent(picked.entity); if (hair != nullptr) { selectedAABB = AABB::Merge(selectedAABB, hair->aabb); } } } XMFLOAT4X4 selectionBox; XMStoreFloat4x4(&selectionBox, selectedAABB.getAsBoxMatrix()); wi::renderer::DrawBox(selectionBox, XMFLOAT4(1, 1, 1, 1)); } paintToolWnd.DrawBrush(); renderPath->Render(); // Editor custom render: if (!cinemaModeCheckBox.GetCheck()) { GraphicsDevice* device = wi::graphics::GetDevice(); CommandList cmd = device->BeginCommandList(); device->EventBegin("Editor", cmd); // Selection outline: if (renderPath->GetDepthStencil() != nullptr && !translator.selected.empty()) { device->EventBegin("Selection Outline Mask", cmd); Viewport vp; vp.width = (float)rt_selectionOutline[0].GetDesc().width; vp.height = (float)rt_selectionOutline[0].GetDesc().height; device->BindViewports(1, &vp, cmd); wi::image::Params fx; fx.enableFullScreen(); fx.stencilComp = wi::image::STENCILMODE::STENCILMODE_EQUAL; // We will specify the stencil ref in user-space, don't care about engine stencil refs here: // Otherwise would need to take into account engine ref and draw multiple permutations of stencil refs. fx.stencilRefMode = wi::image::STENCILREFMODE_USER; // Materials outline: { device->RenderPassBegin(&renderpass_selectionOutline[0], cmd); // Draw solid blocks of selected materials fx.stencilRef = EDITORSTENCILREF_HIGHLIGHT_MATERIAL; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); device->RenderPassEnd(cmd); } // Objects outline: { device->RenderPassBegin(&renderpass_selectionOutline[1], cmd); // Draw solid blocks of selected objects fx.stencilRef = EDITORSTENCILREF_HIGHLIGHT_OBJECT; wi::image::Draw(wi::texturehelper::getWhite(), fx, cmd); device->RenderPassEnd(cmd); } device->EventEnd(cmd); } // Full resolution: { device->RenderPassBegin(&renderpass_editor, cmd); Viewport vp; vp.width = (float)editor_depthbuffer.GetDesc().width; vp.height = (float)editor_depthbuffer.GetDesc().height; device->BindViewports(1, &vp, cmd); // Draw selection outline to the screen: const float selectionColorIntensity = std::sin(selectionOutlineTimer * XM_2PI * 0.8f) * 0.5f + 0.5f; if (renderPath->GetDepthStencil() != nullptr && !translator.selected.empty()) { device->EventBegin("Selection Outline Edge", cmd); wi::renderer::BindCommonResources(cmd); float opacity = wi::math::Lerp(0.4f, 1.0f, selectionColorIntensity); XMFLOAT4 col = selectionColor2; col.w *= opacity; wi::renderer::Postprocess_Outline(rt_selectionOutline[0], cmd, 0.1f, 1, col); col = selectionColor; col.w *= opacity; wi::renderer::Postprocess_Outline(rt_selectionOutline[1], cmd, 0.1f, 1, col); device->EventEnd(cmd); } const wi::Color inactiveEntityColor = wi::Color::fromFloat4(XMFLOAT4(1, 1, 1, 0.5f)); const wi::Color hoveredEntityColor = wi::Color::fromFloat4(XMFLOAT4(1, 1, 1, 1)); const XMFLOAT4 glow = wi::math::Lerp(wi::math::Lerp(XMFLOAT4(1, 1, 1, 1), selectionColor, 0.4f), selectionColor, selectionColorIntensity); const wi::Color selectedEntityColor = wi::Color::fromFloat4(glow); const CameraComponent& camera = GetCurrentEditorScene().camera; const Scene& scene = GetCurrentScene(); // remove camera jittering CameraComponent cam = *renderPath->camera; cam.jitter = XMFLOAT2(0, 0); cam.UpdateCamera(); const XMMATRIX VP = cam.GetViewProjection(); const XMMATRIX R = XMLoadFloat3x3(&cam.rotationMatrix); wi::font::Params fp; fp.customRotation = &R; fp.customProjection = &VP; fp.size = 32; // icon font render quality const float scaling = 0.0025f; fp.h_align = wi::font::WIFALIGN_CENTER; fp.v_align = wi::font::WIFALIGN_CENTER; fp.shadowColor = wi::Color::Shadow(); fp.shadow_softness = 1; if (rendererWnd.GetPickType() & PICK_LIGHT) { for (size_t i = 0; i < scene.lights.GetCount(); ++i) { const LightComponent& light = scene.lights[i]; Entity entity = scene.lights.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } switch (light.GetType()) { case LightComponent::POINT: wi::font::Draw(ICON_POINTLIGHT, fp, cmd); break; case LightComponent::SPOT: wi::font::Draw(ICON_SPOTLIGHT, fp, cmd); break; case LightComponent::DIRECTIONAL: wi::font::Draw(ICON_DIRECTIONALLIGHT, fp, cmd); break; default: break; } } } if (rendererWnd.GetPickType() & PICK_DECAL) { for (size_t i = 0; i < scene.decals.GetCount(); ++i) { Entity entity = scene.decals.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } wi::font::Draw(ICON_DECAL, fp, cmd); } } if (rendererWnd.GetPickType() & PICK_FORCEFIELD) { for (size_t i = 0; i < scene.forces.GetCount(); ++i) { Entity entity = scene.forces.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } wi::font::Draw(ICON_FORCE, fp, cmd); } } if (rendererWnd.GetPickType() & PICK_CAMERA) { for (size_t i = 0; i < scene.cameras.GetCount(); ++i) { Entity entity = scene.cameras.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } wi::font::Draw(ICON_CAMERA, fp, cmd); } } if (rendererWnd.GetPickType() & PICK_ARMATURE) { for (size_t i = 0; i < scene.armatures.GetCount(); ++i) { Entity entity = scene.armatures.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } wi::font::Draw(ICON_ARMATURE, fp, cmd); } } if (rendererWnd.GetPickType() & PICK_EMITTER) { for (size_t i = 0; i < scene.emitters.GetCount(); ++i) { Entity entity = scene.emitters.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } wi::font::Draw(ICON_EMITTER, fp, cmd); } } if (rendererWnd.GetPickType() & PICK_HAIR) { for (size_t i = 0; i < scene.hairs.GetCount(); ++i) { Entity entity = scene.hairs.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } wi::font::Draw(ICON_HAIR, fp, cmd); } } if (rendererWnd.GetPickType() & PICK_SOUND) { for (size_t i = 0; i < scene.sounds.GetCount(); ++i) { Entity entity = scene.sounds.GetEntity(i); if (!scene.transforms.Contains(entity)) continue; const TransformComponent& transform = *scene.transforms.GetComponent(entity); fp.position = transform.GetPosition(); fp.scaling = scaling * wi::math::Distance(transform.GetPosition(), camera.Eye); fp.color = inactiveEntityColor; if (hovered.entity == entity) { fp.color = hoveredEntityColor; } for (auto& picked : translator.selected) { if (picked.entity == entity) { fp.color = selectedEntityColor; break; } } wi::font::Draw(ICON_SOUND, fp, cmd); } } if (rendererWnd.nameDebugCheckBox.GetCheck()) { device->EventBegin("Debug Names", cmd); struct DebugNameEntitySorter { size_t name_index; float distance; XMFLOAT3 position; }; static wi::vector debugNameEntitiesSorted; debugNameEntitiesSorted.clear(); for (size_t i = 0; i < scene.names.GetCount(); ++i) { Entity entity = scene.names.GetEntity(i); const TransformComponent* transform = scene.transforms.GetComponent(entity); if (transform != nullptr) { auto& x = debugNameEntitiesSorted.emplace_back(); x.name_index = i; x.position = transform->GetPosition(); const ObjectComponent* object = scene.objects.GetComponent(entity); if (object != nullptr) { x.position = object->center; } x.distance = wi::math::Distance(x.position, camera.Eye); } } std::sort(debugNameEntitiesSorted.begin(), debugNameEntitiesSorted.end(), [](const DebugNameEntitySorter& a, const DebugNameEntitySorter& b) { return a.distance > b.distance; }); for (auto& x : debugNameEntitiesSorted) { Entity entity = scene.names.GetEntity(x.name_index); wi::font::Params params; params.position = x.position; params.size = wi::font::WIFONTSIZE_DEFAULT; params.scaling = 1.0f / params.size * x.distance * 0.03f; params.color = wi::Color::White(); for (auto& picked : translator.selected) { if (picked.entity == entity) { params.color = selectedEntityColor; break; } } params.h_align = wi::font::WIFALIGN_CENTER; params.v_align = wi::font::WIFALIGN_CENTER; params.softness = 0.1f; params.shadowColor = wi::Color::Black(); params.shadow_softness = 0.5f; params.customProjection = &VP; params.customRotation = &R; wi::font::Draw(scene.names[x.name_index].name, params, cmd); } device->EventEnd(cmd); } if (inspector_mode) { std::string str; str += "Entity: " + std::to_string(hovered.entity); const NameComponent* name = scene.names.GetComponent(hovered.entity); if (name != nullptr) { str += "\nName: " + name->name; } XMFLOAT4 pointer = wi::input::GetPointer(); wi::font::Params params; params.position = XMFLOAT3(pointer.x - 10, pointer.y, 0); params.shadowColor = wi::Color::Black(); params.h_align = wi::font::WIFALIGN_RIGHT; params.v_align = wi::font::WIFALIGN_CENTER; wi::font::Draw(str, params, cmd); } translator.Draw(GetCurrentEditorScene().camera, cmd); device->RenderPassEnd(cmd); } device->EventEnd(cmd); } RenderPath2D::Render(); } void EditorComponent::Compose(CommandList cmd) const { renderPath->Compose(cmd); RenderPath2D::Compose(cmd); } void EditorComponent::RefreshOptionsWindow() { const float padding = 4; XMFLOAT2 pos = XMFLOAT2(padding, padding); const float width = optionsWnd.GetWidgetAreaSize().x - padding * 2; float x_off = 100; translatorCheckBox.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); isScalatorCheckBox.SetPos(XMFLOAT2(pos.x + x_off + 80, pos.y)); isRotatorCheckBox.SetPos(XMFLOAT2(pos.x + x_off + 80 + 50, pos.y)); isTranslatorCheckBox.SetPos(XMFLOAT2(pos.x + x_off + 60 * 3, pos.y)); pos.y += translatorCheckBox.GetSize().y; pos.y += padding; infoDisplayCheckBox.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); fpsCheckBox.SetPos(XMFLOAT2(pos.x + x_off + 80, pos.y)); otherinfoCheckBox.SetPos(XMFLOAT2(pos.x + x_off + 60 * 3, pos.y)); pos.y += infoDisplayCheckBox.GetSize().y; pos.y += padding; cinemaModeCheckBox.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); profilerEnabledCheckBox.SetPos(XMFLOAT2(pos.x + x_off + 80, pos.y)); physicsEnabledCheckBox.SetPos(XMFLOAT2(pos.x + x_off + 60 * 3, pos.y)); pos.y += cinemaModeCheckBox.GetSize().y; pos.y += padding; sceneComboBox.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); sceneComboBox.SetSize(XMFLOAT2(width - x_off - sceneComboBox.GetScale().y - 1, sceneComboBox.GetScale().y)); pos.y += sceneComboBox.GetSize().y; pos.y += padding; saveModeComboBox.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); saveModeComboBox.SetSize(XMFLOAT2(width - x_off - saveModeComboBox.GetScale().y - 1, saveModeComboBox.GetScale().y)); pos.y += saveModeComboBox.GetSize().y; pos.y += padding; themeCombo.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); themeCombo.SetSize(XMFLOAT2(width - x_off - themeCombo.GetScale().y - 1, themeCombo.GetScale().y)); pos.y += themeCombo.GetSize().y; pos.y += padding; renderPathComboBox.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); renderPathComboBox.SetSize(XMFLOAT2(width - x_off - renderPathComboBox.GetScale().y - 1, renderPathComboBox.GetScale().y)); pos.y += renderPathComboBox.GetSize().y; pos.y += padding; if (pathTraceTargetSlider.IsVisible()) { pathTraceTargetSlider.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); pathTraceTargetSlider.SetSize(XMFLOAT2(width - x_off - pathTraceTargetSlider.GetScale().y * 2 - 1, pathTraceTargetSlider.GetScale().y)); pos.y += pathTraceTargetSlider.GetSize().y; pos.y += padding; } if (pathTraceStatisticsLabel.IsVisible()) { pathTraceStatisticsLabel.SetPos(pos); pathTraceStatisticsLabel.SetSize(XMFLOAT2(width, pathTraceStatisticsLabel.GetScale().y)); pos.y += pathTraceStatisticsLabel.GetSize().y; pos.y += padding; } rendererWnd.SetPos(pos); rendererWnd.SetSize(XMFLOAT2(width, rendererWnd.GetScale().y)); pos.y += rendererWnd.GetSize().y; pos.y += padding; postprocessWnd.SetPos(pos); postprocessWnd.SetSize(XMFLOAT2(width, postprocessWnd.GetScale().y)); pos.y += postprocessWnd.GetSize().y; pos.y += padding; cameraWnd.SetPos(pos); cameraWnd.SetSize(XMFLOAT2(width, cameraWnd.GetScale().y)); pos.y += cameraWnd.GetSize().y; pos.y += padding; paintToolWnd.SetPos(pos); paintToolWnd.SetSize(XMFLOAT2(width, paintToolWnd.GetScale().y)); pos.y += paintToolWnd.GetSize().y; pos.y += padding; terragen.SetPos(pos); terragen.SetSize(XMFLOAT2(width, terragen.GetScale().y)); pos.y += terragen.GetSize().y; pos.y += padding; x_off = 45; newCombo.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); newCombo.SetSize(XMFLOAT2(width - x_off - newCombo.GetScale().y - 1, newCombo.GetScale().y)); pos.y += newCombo.GetSize().y; pos.y += padding; filterCombo.SetPos(XMFLOAT2(pos.x + x_off, pos.y)); filterCombo.SetSize(XMFLOAT2(width - x_off - filterCombo.GetScale().y - 1, filterCombo.GetScale().y)); pos.y += filterCombo.GetSize().y; pos.y += padding; entityTree.SetPos(pos); entityTree.SetSize(XMFLOAT2(width, std::max(GetLogicalHeight() * 0.75f, GetLogicalHeight() - pos.y))); pos.y += entityTree.GetSize().y; pos.y += padding; optionsWnd.Update(*this, 0); } void EditorComponent::PushToEntityTree(wi::ecs::Entity entity, int level) { if (entitytree_added_items.count(entity) != 0) { return; } const Scene& scene = GetCurrentScene(); wi::gui::TreeList::Item item; item.level = level; item.userdata = entity; item.selected = IsSelected(entity); item.open = entitytree_opened_items.count(entity) != 0; // Icons: if (scene.layers.Contains(entity)) { item.name += ICON_LAYER " "; } if (scene.transforms.Contains(entity)) { item.name += ICON_TRANSFORM " "; } if (scene.meshes.Contains(entity)) { item.name += ICON_MESH " "; } if (scene.objects.Contains(entity)) { item.name += ICON_OBJECT " "; } if (scene.rigidbodies.Contains(entity)) { item.name += ICON_RIGIDBODY " "; } if (scene.softbodies.Contains(entity)) { item.name += ICON_SOFTBODY " "; } if (scene.emitters.Contains(entity)) { item.name += ICON_EMITTER " "; } if (scene.hairs.Contains(entity)) { item.name += ICON_HAIR " "; } if (scene.forces.Contains(entity)) { item.name += ICON_FORCE " "; } if (scene.sounds.Contains(entity)) { item.name += ICON_SOUND " "; } if (scene.decals.Contains(entity)) { item.name += ICON_DECAL " "; } if (scene.cameras.Contains(entity)) { item.name += ICON_CAMERA " "; } if (scene.probes.Contains(entity)) { item.name += ICON_ENVIRONMENTPROBE " "; } if (scene.animations.Contains(entity)) { item.name += ICON_ANIMATION " "; } if (scene.armatures.Contains(entity)) { item.name += ICON_ARMATURE " "; } if (scene.lights.Contains(entity)) { const LightComponent* light = scene.lights.GetComponent(entity); switch (light->type) { default: case LightComponent::POINT: item.name += ICON_POINTLIGHT " "; break; case LightComponent::SPOT: item.name += ICON_SPOTLIGHT " "; break; case LightComponent::DIRECTIONAL: item.name += ICON_DIRECTIONALLIGHT " "; break; } } if (scene.materials.Contains(entity)) { item.name += ICON_MATERIAL " "; } if (scene.weathers.Contains(entity)) { item.name += ICON_WEATHER " "; } if (entity == terragen.terrainEntity) { item.name += ICON_TERRAIN " "; } const NameComponent* name = scene.names.GetComponent(entity); if (name == nullptr) { item.name += "[no_name] " + std::to_string(entity); } else if(name->name.empty()) { item.name += "[name_empty] " + std::to_string(entity); } else { item.name += name->name; } entityTree.AddItem(item); entitytree_added_items.insert(entity); for (size_t i = 0; i < scene.hierarchy.GetCount(); ++i) { if (scene.hierarchy[i].parentID == entity) { PushToEntityTree(scene.hierarchy.GetEntity(i), level + 1); } } } void EditorComponent::RefreshEntityTree() { const Scene& scene = GetCurrentScene(); for (int i = 0; i < entityTree.GetItemCount(); ++i) { const wi::gui::TreeList::Item& item = entityTree.GetItem(i); if (item.open) { entitytree_opened_items.insert((Entity)item.userdata); } } entityTree.ClearItems(); if (has_flag(filter, Filter::All)) { // Add hierarchy: for (size_t i = 0; i < scene.hierarchy.GetCount(); ++i) { PushToEntityTree(scene.hierarchy[i].parentID, 0); } } if (has_flag(filter, Filter::Transform)) { // Any transform left that is not part of a hierarchy: for (size_t i = 0; i < scene.transforms.GetCount(); ++i) { PushToEntityTree(scene.transforms.GetEntity(i), 0); } } // Add any left over entities that might not have had a hierarchy or transform: if (has_flag(filter, Filter::Light)) { for (size_t i = 0; i < scene.lights.GetCount(); ++i) { PushToEntityTree(scene.lights.GetEntity(i), 0); } } if (has_flag(filter, Filter::Decal)) { for (size_t i = 0; i < scene.decals.GetCount(); ++i) { PushToEntityTree(scene.decals.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.cameras.GetCount(); ++i) { PushToEntityTree(scene.cameras.GetEntity(i), 0); } } if (has_flag(filter, Filter::Material)) { for (size_t i = 0; i < scene.materials.GetCount(); ++i) { PushToEntityTree(scene.materials.GetEntity(i), 0); } } if (has_flag(filter, Filter::Mesh)) { for (size_t i = 0; i < scene.meshes.GetCount(); ++i) { PushToEntityTree(scene.meshes.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.armatures.GetCount(); ++i) { PushToEntityTree(scene.armatures.GetEntity(i), 0); } } if (has_flag(filter, Filter::Object)) { for (size_t i = 0; i < scene.objects.GetCount(); ++i) { PushToEntityTree(scene.objects.GetEntity(i), 0); } } if (has_flag(filter, Filter::Weather)) { for (size_t i = 0; i < scene.weathers.GetCount(); ++i) { PushToEntityTree(scene.weathers.GetEntity(i), 0); } } if (has_flag(filter, Filter::Sound)) { for (size_t i = 0; i < scene.sounds.GetCount(); ++i) { PushToEntityTree(scene.sounds.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.hairs.GetCount(); ++i) { PushToEntityTree(scene.hairs.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.emitters.GetCount(); ++i) { PushToEntityTree(scene.emitters.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.animations.GetCount(); ++i) { PushToEntityTree(scene.animations.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.probes.GetCount(); ++i) { PushToEntityTree(scene.probes.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.forces.GetCount(); ++i) { PushToEntityTree(scene.forces.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.rigidbodies.GetCount(); ++i) { PushToEntityTree(scene.rigidbodies.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.softbodies.GetCount(); ++i) { PushToEntityTree(scene.softbodies.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.springs.GetCount(); ++i) { PushToEntityTree(scene.springs.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.inverse_kinematics.GetCount(); ++i) { PushToEntityTree(scene.inverse_kinematics.GetEntity(i), 0); } } if (has_flag(filter, Filter::All)) { for (size_t i = 0; i < scene.names.GetCount(); ++i) { PushToEntityTree(scene.names.GetEntity(i), 0); } } entitytree_added_items.clear(); entitytree_opened_items.clear(); } void EditorComponent::RefreshComponentWindow() { const wi::scene::Scene& scene = GetCurrentScene(); const float padding = 4; XMFLOAT2 pos = XMFLOAT2(padding, padding); const float width = componentWindow.GetWidgetAreaSize().x - padding * 2; if (!translator.selected.empty()) { newComponentCombo.SetVisible(true); newComponentCombo.SetPos(XMFLOAT2(pos.x + 35, pos.y)); newComponentCombo.SetSize(XMFLOAT2(width - 35 - 21, 20)); pos.y += newComponentCombo.GetSize().y; pos.y += padding; } else { newComponentCombo.SetVisible(false); } if (scene.names.Contains(nameWnd.entity)) { nameWnd.SetVisible(true); nameWnd.SetPos(pos); nameWnd.SetSize(XMFLOAT2(width, nameWnd.GetScale().y)); nameWnd.Update(); pos.y += nameWnd.GetSize().y; pos.y += padding; } else { nameWnd.SetVisible(false); } if (scene.layers.Contains(layerWnd.entity)) { layerWnd.SetVisible(true); layerWnd.SetPos(pos); layerWnd.SetSize(XMFLOAT2(width, layerWnd.GetScale().y)); pos.y += layerWnd.GetSize().y; pos.y += padding; } else { layerWnd.SetVisible(false); } if (scene.transforms.Contains(transformWnd.entity)) { transformWnd.SetVisible(true); transformWnd.SetPos(pos); transformWnd.SetSize(XMFLOAT2(width, transformWnd.GetScale().y)); pos.y += transformWnd.GetSize().y; pos.y += padding; } else { transformWnd.SetVisible(false); } if (scene.inverse_kinematics.Contains(ikWnd.entity)) { ikWnd.SetVisible(true); ikWnd.SetPos(pos); ikWnd.SetSize(XMFLOAT2(width, ikWnd.GetScale().y)); pos.y += ikWnd.GetSize().y; pos.y += padding; } else { ikWnd.SetVisible(false); } if (scene.springs.Contains(springWnd.entity)) { springWnd.SetVisible(true); springWnd.SetPos(pos); springWnd.SetSize(XMFLOAT2(width, springWnd.GetScale().y)); pos.y += springWnd.GetSize().y; pos.y += padding; } else { springWnd.SetVisible(false); } if (scene.forces.Contains(forceFieldWnd.entity)) { forceFieldWnd.SetVisible(true); forceFieldWnd.SetPos(pos); forceFieldWnd.SetSize(XMFLOAT2(width, forceFieldWnd.GetScale().y)); pos.y += forceFieldWnd.GetSize().y; pos.y += padding; } else { forceFieldWnd.SetVisible(false); } if (scene.hairs.Contains(hairWnd.entity)) { hairWnd.SetVisible(true); hairWnd.SetPos(pos); hairWnd.SetSize(XMFLOAT2(width, hairWnd.GetScale().y)); pos.y += hairWnd.GetSize().y; pos.y += padding; } else { hairWnd.SetVisible(false); } if (scene.emitters.Contains(emitterWnd.entity)) { emitterWnd.SetVisible(true); emitterWnd.SetPos(pos); emitterWnd.SetSize(XMFLOAT2(width, emitterWnd.GetScale().y)); pos.y += emitterWnd.GetSize().y; pos.y += padding; } else { emitterWnd.SetVisible(false); } if (scene.animations.Contains(animWnd.entity)) { animWnd.SetVisible(true); animWnd.SetPos(pos); animWnd.SetSize(XMFLOAT2(width, animWnd.GetScale().y)); pos.y += animWnd.GetSize().y; pos.y += padding; } else { animWnd.SetVisible(false); } if (scene.lights.Contains(lightWnd.entity)) { lightWnd.SetVisible(true); lightWnd.SetPos(pos); lightWnd.SetSize(XMFLOAT2(width, lightWnd.GetScale().y)); pos.y += lightWnd.GetSize().y; pos.y += padding; } else { lightWnd.SetVisible(false); } if (scene.sounds.Contains(soundWnd.entity)) { soundWnd.SetVisible(true); soundWnd.SetPos(pos); soundWnd.SetSize(XMFLOAT2(width, soundWnd.GetScale().y)); pos.y += soundWnd.GetSize().y; pos.y += padding; } else { soundWnd.SetVisible(false); } if (scene.decals.Contains(decalWnd.entity)) { decalWnd.SetVisible(true); decalWnd.SetPos(pos); decalWnd.SetSize(XMFLOAT2(width, decalWnd.GetScale().y)); pos.y += decalWnd.GetSize().y; pos.y += padding; } else { decalWnd.SetVisible(false); } if (scene.probes.Contains(envProbeWnd.entity)) { envProbeWnd.SetVisible(true); envProbeWnd.SetPos(pos); envProbeWnd.SetSize(XMFLOAT2(width, envProbeWnd.GetScale().y)); pos.y += envProbeWnd.GetSize().y; pos.y += padding; } else { envProbeWnd.SetVisible(false); } //if (scene.cameras.Contains(cameraWnd.entity)) //{ // cameraWnd.SetVisible(true); // cameraWnd.SetPos(pos); // cameraWnd.SetSize(XMFLOAT2(width, cameraWnd.GetScale().y)); // pos.y += cameraWnd.GetSize().y; // pos.y += padding; //} //else //{ // cameraWnd.SetVisible(false); //} if (scene.materials.Contains(materialWnd.entity)) { materialWnd.SetVisible(true); materialWnd.SetPos(pos); materialWnd.SetSize(XMFLOAT2(width, materialWnd.GetScale().y)); pos.y += materialWnd.GetSize().y; pos.y += padding; } else { materialWnd.SetVisible(false); } if (scene.meshes.Contains(meshWnd.entity)) { meshWnd.SetVisible(true); meshWnd.SetPos(pos); meshWnd.SetSize(XMFLOAT2(width, meshWnd.GetScale().y)); pos.y += meshWnd.GetSize().y; pos.y += padding; } else { meshWnd.SetVisible(false); } if (scene.objects.Contains(objectWnd.entity)) { objectWnd.SetVisible(true); objectWnd.SetPos(pos); objectWnd.SetSize(XMFLOAT2(width, objectWnd.GetScale().y)); pos.y += objectWnd.GetSize().y; pos.y += padding; } else { objectWnd.SetVisible(false); } if (scene.weathers.Contains(weatherWnd.entity)) { weatherWnd.SetVisible(true); weatherWnd.SetPos(pos); weatherWnd.SetSize(XMFLOAT2(width, weatherWnd.GetScale().y)); pos.y += weatherWnd.GetSize().y; pos.y += padding; } else { weatherWnd.SetVisible(false); } componentWindow.Update(*this, 0); } void EditorComponent::ClearSelected() { translator.selected.clear(); } void EditorComponent::AddSelected(Entity entity) { wi::scene::PickResult res; res.entity = entity; AddSelected(res); } void EditorComponent::AddSelected(const PickResult& picked) { bool removal = false; for (size_t i = 0; i < translator.selected.size(); ++i) { if(translator.selected[i] == picked) { // If already selected, it will be deselected now: translator.selected[i] = translator.selected.back(); translator.selected.pop_back(); removal = true; break; } } if (!removal) { translator.selected.push_back(picked); } } bool EditorComponent::IsSelected(Entity entity) const { for (auto& x : translator.selected) { if (x.entity == entity) { return true; } } return false; } void EditorComponent::RecordSelection(wi::Archive& archive) const { archive << translator.selected.size(); for (auto& x : translator.selected) { archive << x.entity; archive << x.position; archive << x.normal; archive << x.subsetIndex; archive << x.distance; } } void EditorComponent::RecordEntity(wi::Archive& archive, wi::ecs::Entity entity) { const wi::vector entities = { entity }; RecordEntity(archive, entities); } void EditorComponent::RecordEntity(wi::Archive& archive, const wi::vector& entities) { Scene& scene = GetCurrentScene(); EntitySerializer seri; archive << entities; for (auto& x : entities) { scene.Entity_Serialize(archive, seri, x); } } void EditorComponent::ResetHistory() { EditorScene& editorscene = GetCurrentEditorScene(); editorscene.historyPos = -1; editorscene.history.clear(); } wi::Archive& EditorComponent::AdvanceHistory() { EditorScene& editorscene = GetCurrentEditorScene(); editorscene.historyPos++; while (static_cast(editorscene.history.size()) > editorscene.historyPos) { editorscene.history.pop_back(); } editorscene.history.emplace_back(); editorscene.history.back().SetReadModeAndResetPos(false); return editorscene.history.back(); } void EditorComponent::ConsumeHistoryOperation(bool undo) { EditorScene& editorscene = GetCurrentEditorScene(); if ((undo && editorscene.historyPos >= 0) || (!undo && editorscene.historyPos < (int)editorscene.history.size() - 1)) { if (!undo) { editorscene.historyPos++; } Scene& scene = GetCurrentScene(); wi::Archive& archive = editorscene.history[editorscene.historyPos]; archive.SetReadModeAndResetPos(true); int temp; archive >> temp; HistoryOperationType type = (HistoryOperationType)temp; switch (type) { case HISTORYOP_TRANSLATOR: { EntitySerializer seri; wi::scene::TransformComponent start; wi::scene::TransformComponent end; start.Serialize(archive, seri); end.Serialize(archive, seri); wi::vector matrices_start; wi::vector matrices_end; archive >> matrices_start; archive >> matrices_end; translator.enabled = true; translator.PreTranslate(); if (undo) { translator.transform = start; translator.matrices_current = matrices_start; } else { translator.transform = end; translator.matrices_current = matrices_end; } translator.transform.UpdateTransform(); translator.PostTranslate(); } break; case HISTORYOP_SELECTION: { // Read selections states from archive: wi::vector selectedBEFORE; size_t selectionCountBEFORE; archive >> selectionCountBEFORE; for (size_t i = 0; i < selectionCountBEFORE; ++i) { wi::scene::PickResult sel; archive >> sel.entity; archive >> sel.position; archive >> sel.normal; archive >> sel.subsetIndex; archive >> sel.distance; selectedBEFORE.push_back(sel); } wi::vector selectedAFTER; size_t selectionCountAFTER; archive >> selectionCountAFTER; for (size_t i = 0; i < selectionCountAFTER; ++i) { wi::scene::PickResult sel; archive >> sel.entity; archive >> sel.position; archive >> sel.normal; archive >> sel.subsetIndex; archive >> sel.distance; selectedAFTER.push_back(sel); } // Restore proper selection state: if (undo) { translator.selected = selectedBEFORE; } else { translator.selected = selectedAFTER; } } break; case HISTORYOP_ADD: { // Read selections states from archive: wi::vector selectedBEFORE; size_t selectionCountBEFORE; archive >> selectionCountBEFORE; for (size_t i = 0; i < selectionCountBEFORE; ++i) { wi::scene::PickResult sel; archive >> sel.entity; archive >> sel.position; archive >> sel.normal; archive >> sel.subsetIndex; archive >> sel.distance; selectedBEFORE.push_back(sel); } wi::vector selectedAFTER; size_t selectionCountAFTER; archive >> selectionCountAFTER; for (size_t i = 0; i < selectionCountAFTER; ++i) { wi::scene::PickResult sel; archive >> sel.entity; archive >> sel.position; archive >> sel.normal; archive >> sel.subsetIndex; archive >> sel.distance; selectedAFTER.push_back(sel); } wi::vector addedEntities; archive >> addedEntities; ClearSelected(); if (undo) { translator.selected = selectedBEFORE; for (size_t i = 0; i < addedEntities.size(); ++i) { scene.Entity_Remove(addedEntities[i]); } } else { translator.selected = selectedAFTER; EntitySerializer seri; seri.allow_remap = false; for (size_t i = 0; i < addedEntities.size(); ++i) { scene.Entity_Serialize(archive, seri); } } } break; case HISTORYOP_DELETE: { // Read selections states from archive: wi::vector selectedBEFORE; size_t selectionCountBEFORE; archive >> selectionCountBEFORE; for (size_t i = 0; i < selectionCountBEFORE; ++i) { wi::scene::PickResult sel; archive >> sel.entity; archive >> sel.position; archive >> sel.normal; archive >> sel.subsetIndex; archive >> sel.distance; selectedBEFORE.push_back(sel); } wi::vector deletedEntities; archive >> deletedEntities; ClearSelected(); if (undo) { translator.selected = selectedBEFORE; EntitySerializer seri; seri.allow_remap = false; for (size_t i = 0; i < deletedEntities.size(); ++i) { scene.Entity_Serialize(archive, seri); } } else { for (size_t i = 0; i < deletedEntities.size(); ++i) { scene.Entity_Remove(deletedEntities[i]); } } } break; case HISTORYOP_COMPONENT_DATA: { Scene before, after; wi::vector entities_before, entities_after; archive >> entities_before; for (auto& x : entities_before) { EntitySerializer seri; seri.allow_remap = false; before.Entity_Serialize(archive, seri); } archive >> entities_after; for (auto& x : entities_after) { EntitySerializer seri; seri.allow_remap = false; after.Entity_Serialize(archive, seri); } if (undo) { for (auto& x : entities_before) { scene.Entity_Remove(x); } scene.Merge(before); } else { for (auto& x : entities_after) { scene.Entity_Remove(x); } scene.Merge(after); } } break; case HISTORYOP_PAINTTOOL: paintToolWnd.ConsumeHistoryOperation(archive, undo); break; case HISTORYOP_NONE: default: assert(0); break; } if (undo) { editorscene.historyPos--; } scene.Update(0); } RefreshEntityTree(); } void EditorComponent::Save(const std::string& filename) { const bool dump_to_header = saveModeComboBox.GetSelected() == 2; wi::Archive archive = dump_to_header ? wi::Archive() : wi::Archive(filename, false); if (archive.IsOpen()) { Scene& scene = GetCurrentScene(); wi::resourcemanager::Mode embed_mode = (wi::resourcemanager::Mode)saveModeComboBox.GetItemUserData(saveModeComboBox.GetSelected()); wi::resourcemanager::SetMode(embed_mode); terragen.BakeVirtualTexturesToFiles(); scene.Serialize(archive); if (dump_to_header) { archive.SaveHeaderFile(filename, wi::helper::RemoveExtension(wi::helper::GetFileNameFromPath(filename))); } GetCurrentEditorScene().path = filename; } else { wi::helper::messageBox("Could not create " + filename + "!"); return; } RefreshSceneList(); wi::backlog::post("Scene " + std::to_string(current_scene) + " saved: " + GetCurrentEditorScene().path); } void EditorComponent::SaveAs() { const bool dump_to_header = saveModeComboBox.GetSelected() == 2; wi::helper::FileDialogParams params; params.type = wi::helper::FileDialogParams::SAVE; if (dump_to_header) { params.description = "C++ header (.h)"; params.extensions.push_back("h"); } else { params.description = "Wicked Scene (.wiscene)"; params.extensions.push_back("wiscene"); } wi::helper::FileDialog(params, [=](std::string fileName) { wi::eventhandler::Subscribe_Once(wi::eventhandler::EVENT_THREAD_SAFE_POINT, [=](uint64_t userdata) { std::string filename = wi::helper::ReplaceExtension(fileName, params.extensions.front()); Save(filename); }); }); }