#include "stdafx.h" #include "CameraWindow.h" using namespace wi::ecs; using namespace wi::scene; void CameraWindow::ResetCam() { if (editor == nullptr) return; auto& editorscene = editor->GetCurrentEditorScene(); editorscene.cam_move = {}; CameraComponent& camera = editorscene.camera; float width = camera.width; float height = camera.height; camera = CameraComponent(); camera.width = width; camera.height = height; editorscene.camera_transform.ClearTransform(); editorscene.camera_transform.Translate(XMFLOAT3(0, 2, -10)); editorscene.camera_transform.UpdateTransform(); camera.TransformCamera(editorscene.camera_transform); camera.UpdateCamera(); editorscene.camera_target.ClearTransform(); editorscene.camera_target.UpdateTransform(); orthoCheckBox.SetCheck(camera.IsOrtho()); } void CameraWindow::Create(EditorComponent* _editor) { editor = _editor; wi::gui::Window::Create("Camera", wi::gui::Window::WindowControls::CLOSE | wi::gui::Window::WindowControls::RESIZE_RIGHT); SetText("Camera " ICON_CAMERAOPTIONS); editor->GetCurrentEditorScene().camera_transform.MatrixTransform(editor->GetCurrentEditorScene().camera.GetInvView()); editor->GetCurrentEditorScene().camera_transform.UpdateTransform(); SetSize(XMFLOAT2(300, 390)); float x = 140; float y = 0; float hei = 18; float step = hei + 2; float wid = 120; ResetCam(); auto updateCamera = [this](auto func) { return [this, func](wi::gui::EventArgs args) { CameraComponent& camera = editor->GetCurrentEditorScene().camera; func(camera, args); camera.UpdateCamera(); camera.SetDirty(); }; }; farPlaneSlider.Create(100, 10000, 5000, 100000, "Far Plane: "); farPlaneSlider.SetTooltip("Controls the camera's far clip plane, geometry farther than this will be clipped."); farPlaneSlider.SetSize(XMFLOAT2(wid, hei)); farPlaneSlider.SetPos(XMFLOAT2(x, y)); if (editor->main->config.GetSection("camera").Has("far")) { editor->GetCurrentEditorScene().camera.zFarP = editor->main->config.GetSection("camera").GetFloat("far"); } farPlaneSlider.SetValue(editor->GetCurrentEditorScene().camera.zFarP); farPlaneSlider.OnSlide(updateCamera([this](CameraComponent& camera, auto args) { camera.zFarP = args.fValue; editor->main->config.GetSection("camera").Set("far", args.fValue); editor->main->config.Commit(); })); AddWidget(&farPlaneSlider); nearPlaneSlider.Create(0.01f, 10, 0.1f, 10000, "Near Plane: "); nearPlaneSlider.SetTooltip("Controls the camera's near clip plane, geometry closer than this will be clipped."); nearPlaneSlider.SetSize(XMFLOAT2(wid, hei)); nearPlaneSlider.SetPos(XMFLOAT2(x, y += step)); if (editor->main->config.GetSection("camera").Has("near")) { editor->GetCurrentEditorScene().camera.zNearP = editor->main->config.GetSection("camera").GetFloat("near"); } nearPlaneSlider.SetValue(editor->GetCurrentEditorScene().camera.zNearP); nearPlaneSlider.OnSlide(updateCamera([this](CameraComponent& camera, auto args) { camera.zNearP = args.fValue; editor->main->config.GetSection("camera").Set("near", args.fValue); editor->main->config.Commit(); })); AddWidget(&nearPlaneSlider); fovSlider.Create(1, 179, 60, 10000, "FOV: "); fovSlider.SetTooltip("Controls the camera's top-down field of view (in degrees)"); fovSlider.SetSize(XMFLOAT2(wid, hei)); fovSlider.SetPos(XMFLOAT2(x, y += step)); if (editor->main->config.GetSection("camera").Has("fov")) { editor->GetCurrentEditorScene().camera.fov = editor->main->config.GetSection("camera").GetFloat("fov") / 180.f * XM_PI; } fovSlider.SetValue(editor->GetCurrentEditorScene().camera.fov / XM_PI * 180.f); fovSlider.OnSlide(updateCamera([this](CameraComponent& camera, auto args) { camera.fov = args.fValue / 180.f * XM_PI; editor->main->config.GetSection("camera").Set("fov", args.fValue); editor->main->config.Commit(); })); AddWidget(&fovSlider); focalLengthSlider.Create(0.001f, 100, 1, 10000, "Focal Length: "); focalLengthSlider.SetTooltip("Controls the depth of field effect's focus distance.\nYou can also refocus by holding the C key and picking in the scene with the left mouse button."); focalLengthSlider.SetSize(XMFLOAT2(wid, hei)); focalLengthSlider.SetPos(XMFLOAT2(x, y += step)); focalLengthSlider.OnSlide(updateCamera([](CameraComponent& camera, auto args) { camera.focal_length = args.fValue; })); AddWidget(&focalLengthSlider); apertureSizeSlider.Create(0, 1, 0, 10000, "Aperture Size: "); apertureSizeSlider.SetTooltip("Controls the depth of field effect's strength"); apertureSizeSlider.SetSize(XMFLOAT2(wid, hei)); apertureSizeSlider.SetPos(XMFLOAT2(x, y += step)); apertureSizeSlider.OnSlide(updateCamera([](CameraComponent& camera, auto args) { camera.aperture_size = args.fValue; })); AddWidget(&apertureSizeSlider); apertureShapeXSlider.Create(0, 2, 1, 10000, "Aperture Shape X: "); apertureShapeXSlider.SetTooltip("Controls the depth of field effect's bokeh shape"); apertureShapeXSlider.SetSize(XMFLOAT2(wid, hei)); apertureShapeXSlider.SetPos(XMFLOAT2(x, y += step)); apertureShapeXSlider.OnSlide(updateCamera([](CameraComponent& camera, auto args) { camera.aperture_shape.x = args.fValue; })); AddWidget(&apertureShapeXSlider); apertureShapeYSlider.Create(0, 2, 1, 10000, "Aperture Shape Y: "); apertureShapeYSlider.SetTooltip("Controls the depth of field effect's bokeh shape"); apertureShapeYSlider.SetSize(XMFLOAT2(wid, hei)); apertureShapeYSlider.SetPos(XMFLOAT2(x, y += step)); apertureShapeYSlider.OnSlide(updateCamera([](CameraComponent& camera, auto args) { camera.aperture_shape.y = args.fValue; })); AddWidget(&apertureShapeYSlider); movespeedSlider.Create(1, 100, 10, 10000, "Movement Speed: "); movespeedSlider.SetSize(XMFLOAT2(wid, hei)); movespeedSlider.SetPos(XMFLOAT2(x, y += step)); if (editor->main->config.GetSection("camera").Has("move_speed")) { movespeedSlider.SetValue(editor->main->config.GetSection("camera").GetFloat("move_speed")); } movespeedSlider.OnSlide([=](wi::gui::EventArgs args) { editor->main->config.GetSection("camera").Set("move_speed", args.fValue); editor->main->config.Commit(); }); AddWidget(&movespeedSlider); accelerationSlider.Create(0.01f, 1, 0.18f, 10000, "Acceleration: "); accelerationSlider.SetSize(XMFLOAT2(wid, hei)); accelerationSlider.SetPos(XMFLOAT2(x, y += step)); if (editor->main->config.GetSection("camera").Has("acceleration")) { accelerationSlider.SetValue(editor->main->config.GetSection("camera").GetFloat("acceleration")); } accelerationSlider.OnSlide([=](wi::gui::EventArgs args) { editor->main->config.GetSection("camera").Set("acceleration", args.fValue); editor->main->config.Commit(); }); AddWidget(&accelerationSlider); rotationspeedSlider.Create(0.1f, 2, 1, 10000, "Rotation Speed: "); rotationspeedSlider.SetSize(XMFLOAT2(wid, hei)); rotationspeedSlider.SetPos(XMFLOAT2(x, y += step)); if (editor->main->config.GetSection("camera").Has("rotation_speed")) { rotationspeedSlider.SetValue(editor->main->config.GetSection("camera").GetFloat("rotation_speed")); } rotationspeedSlider.OnSlide([=](wi::gui::EventArgs args) { editor->main->config.GetSection("camera").Set("rotation_speed", args.fValue); editor->main->config.Commit(); }); AddWidget(&rotationspeedSlider); resetButton.Create("Reset Camera"); resetButton.SetSize(XMFLOAT2(wid, hei)); resetButton.SetPos(XMFLOAT2(x, y += step)); resetButton.OnClick([this](wi::gui::EventArgs args) { ResetCam(); CameraComponent& camera = editor->GetCurrentEditorScene().camera; editor->main->config.GetSection("camera").Set("near", camera.zNearP); editor->main->config.GetSection("camera").Set("far", camera.zFarP); editor->main->config.GetSection("camera").Set("fov", camera.fov / XM_PI * 180.f); editor->main->config.Commit(); }); AddWidget(&resetButton); fpsCheckBox.Create("FPS Camera: "); fpsCheckBox.SetSize(XMFLOAT2(hei, hei)); fpsCheckBox.SetPos(XMFLOAT2(x, y += step)); fpsCheckBox.SetCheck(true); if (editor->main->config.GetSection("camera").Has("fps")) { fpsCheckBox.SetCheck(editor->main->config.GetSection("camera").GetBool("fps")); } fpsCheckBox.OnClick([this](wi::gui::EventArgs args) { editor->main->config.GetSection("camera").Set("fps", args.bValue); editor->main->config.Commit(); if (!args.bValue) { // Transitioning from FPS to Orbital: // camera_transform holds full world transform from FPS mode, // decompose it into camera_target (pivot point with rotation) // and camera_transform (local Z offset as the orbit arm) auto& editorscene = editor->GetCurrentEditorScene(); const CameraComponent& camera = editorscene.camera; float arm_length = 10.0f; XMVECTOR eye = camera.GetEye(); XMVECTOR at = camera.GetAt(); XMVECTOR target_pos = XMVectorAdd(eye, at * arm_length); // Build a look-at rotation for the target (facing from target back to eye) XMVECTOR target_forward = XMVector3Normalize(XMVectorSubtract(eye, target_pos)); XMVECTOR up = XMVectorSet(0, 1, 0, 0); XMVECTOR right = XMVector3Normalize(XMVector3Cross(up, target_forward)); up = XMVector3Cross(target_forward, right); XMMATRIX rot_matrix = XMMATRIX(right, up, target_forward, XMVectorSet(0, 0, 0, 1)); XMVECTOR target_quat = XMQuaternionRotationMatrix(rot_matrix); editorscene.camera_target = {}; XMStoreFloat3(&editorscene.camera_target.translation_local, target_pos); XMStoreFloat4(&editorscene.camera_target.rotation_local, target_quat); editorscene.camera_target.UpdateTransform(); editorscene.camera_transform = {}; editorscene.camera_transform.translation_local = XMFLOAT3(0, 0, -arm_length); editorscene.camera_transform.UpdateTransform_Parented(editorscene.camera_target); editorscene.cam_move = {}; } }); AddWidget(&fpsCheckBox); orthoCheckBox.Create("Orthographic: "); orthoCheckBox.SetSize(XMFLOAT2(hei, hei)); orthoCheckBox.SetPos(XMFLOAT2(x, y += step)); orthoCheckBox.SetCheck(editor->GetCurrentEditorScene().camera.IsOrtho()); orthoCheckBox.OnClick([this](wi::gui::EventArgs args) { Scene& scene = editor->GetCurrentScene(); CameraComponent& camera = editor->GetCurrentEditorScene().camera; camera.SetOrtho(args.bValue); camera.ortho_vertical_size = camera.ComputeOrthoVerticalSizeFromPerspective(wi::math::Length(camera.Eye)); // camera distance from origin }); AddWidget(&orthoCheckBox); proxyButton.Create("Place Proxy"); proxyButton.SetTooltip("Copy the current camera and place a proxy of it in the world."); proxyButton.SetSize(XMFLOAT2(wid, hei)); proxyButton.SetPos(XMFLOAT2(x, y += step * 2)); proxyButton.OnClick([=](wi::gui::EventArgs args) { const CameraComponent& camera = editor->GetCurrentEditorScene().camera; Scene& scene = editor->GetCurrentScene(); static int camcounter = 0; Entity entity = scene.Entity_CreateCamera("cam" + std::to_string(camcounter), camera.width, camera.height, camera.zNearP, camera.zFarP, camera.fov); camcounter++; CameraComponent& cam = *scene.cameras.GetComponent(entity); cam = camera; scene.transforms.GetComponent(entity)->MatrixTransform(camera.InvView); wi::Archive& archive = editor->AdvanceHistory(); archive << EditorComponent::HISTORYOP_ADD; editor->RecordSelection(archive); editor->ClearSelected(); editor->AddSelected(entity); editor->RecordSelection(archive); editor->RecordEntity(archive, entity); editor->componentsWnd.RefreshEntityTree(); SetEntity(entity); }); AddWidget(&proxyButton); followCheckBox.Create("Follow Proxy: "); followCheckBox.SetSize(XMFLOAT2(hei, hei)); followCheckBox.SetPos(XMFLOAT2(x, y += step)); followCheckBox.SetCheck(false); AddWidget(&followCheckBox); followSlider.Create(0.0f, 0.999f, 0.0f, 1000.0f, "Follow Proxy Delay: "); followSlider.SetSize(XMFLOAT2(wid, hei)); followSlider.SetPos(XMFLOAT2(x, y += step)); AddWidget(&followSlider); SetEntity(INVALID_ENTITY); SetVisible(false); } void CameraWindow::SetEntity(Entity entity) { this->entity = entity; Scene& scene = editor->GetCurrentScene(); if (scene.cameras.GetComponent(entity) != nullptr) { followCheckBox.SetEnabled(true); followSlider.SetEnabled(true); } else { followCheckBox.SetCheck(false); followCheckBox.SetEnabled(false); followSlider.SetEnabled(false); } } void CameraWindow::UpdateData() { CameraComponent& camera = editor->GetCurrentEditorScene().camera; farPlaneSlider.SetValue(camera.zFarP); nearPlaneSlider.SetValue(camera.zNearP); fovSlider.SetValue(camera.fov * 180.0f / XM_PI); focalLengthSlider.SetValue(camera.focal_length); apertureSizeSlider.SetValue(camera.aperture_size); apertureShapeXSlider.SetValue(camera.aperture_shape.x); apertureShapeYSlider.SetValue(camera.aperture_shape.y); // It's important to feedback new transform from camera as scripts can modify camera too // independently from the editor // however this only works well for fps camera if (fpsCheckBox.GetCheck()) { editor->GetCurrentEditorScene().camera_transform.ClearTransform(); editor->GetCurrentEditorScene().camera_transform.MatrixTransform(camera.InvView); } } void CameraWindow::ResizeLayout() { wi::gui::Window::ResizeLayout(); layout.add_fullwidth(resetButton); layout.add(farPlaneSlider); layout.add(nearPlaneSlider); layout.add(fovSlider); layout.add(focalLengthSlider); layout.add(apertureSizeSlider); layout.add(apertureShapeXSlider); layout.add(apertureShapeYSlider); layout.add(movespeedSlider); layout.add(rotationspeedSlider); layout.add(accelerationSlider); layout.add_right(fpsCheckBox); layout.add_right(orthoCheckBox); layout.jump(); layout.add_fullwidth(proxyButton); layout.add_right(followCheckBox); layout.add(followSlider); }