Files
WickedEngine/Editor/Translator.cpp
T
2026-02-06 07:15:13 +01:00

1394 lines
48 KiB
C++

#include "stdafx.h"
#include "Translator.h"
#include "wiRenderer.h"
#include "wiInput.h"
#include "wiMath.h"
#include "shaders/ShaderInterop_Renderer.h"
#include "wiEventHandler.h"
using namespace wi::ecs;
using namespace wi::scene;
using namespace wi::graphics;
using namespace wi::primitive;
namespace Translator_Internal
{
PipelineState pso_solidpart;
PipelineState pso_wirepart;
const float origin_size = 0.2f;
const float axis_length = 3.5f;
const float plane_min = 0.5f;
const float plane_max = 1.5f;
const float circle_radius = axis_length;
const float circle_width = 1;
const float circle2_radius = circle_radius + 0.7f;
const float circle2_width = 0.3f;
void LoadShaders()
{
GraphicsDevice* device = wi::graphics::GetDevice();
{
PipelineStateDesc desc;
desc.vs = wi::renderer::GetShader(wi::enums::VSTYPE_VERTEXCOLOR);
desc.ps = wi::renderer::GetShader(wi::enums::PSTYPE_VERTEXCOLOR);
desc.il = wi::renderer::GetInputLayout(wi::enums::ILTYPE_VERTEXCOLOR);
desc.dss = wi::renderer::GetDepthStencilState(wi::enums::DSSTYPE_DEFAULT);
desc.rs = wi::renderer::GetRasterizerState(wi::enums::RSTYPE_DOUBLESIDED);
desc.bs = wi::renderer::GetBlendState(wi::enums::BSTYPE_TRANSPARENT);
desc.pt = PrimitiveTopology::TRIANGLELIST;
device->CreatePipelineState(&desc, &pso_solidpart);
}
{
PipelineStateDesc desc;
desc.vs = wi::renderer::GetShader(wi::enums::VSTYPE_VERTEXCOLOR);
desc.ps = wi::renderer::GetShader(wi::enums::PSTYPE_VERTEXCOLOR);
desc.il = wi::renderer::GetInputLayout(wi::enums::ILTYPE_VERTEXCOLOR);
desc.dss = wi::renderer::GetDepthStencilState(wi::enums::DSSTYPE_DEFAULT);
desc.rs = wi::renderer::GetRasterizerState(wi::enums::RSTYPE_WIRE_DOUBLESIDED);
desc.bs = wi::renderer::GetBlendState(wi::enums::BSTYPE_TRANSPARENT);
desc.pt = PrimitiveTopology::LINELIST;
device->CreatePipelineState(&desc, &pso_wirepart);
}
}
struct Vertex
{
XMFLOAT4 position;
XMFLOAT4 color;
};
const Vertex cubeVerts[] = {
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,-1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,-1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,-1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(1,1,1,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(-1,1,-1,1), XMFLOAT4(1,1,1,1)},
};
}
using namespace Translator_Internal;
void Translator::Update(const CameraComponent& camera, const XMFLOAT4& currentMouse, const wi::Canvas& canvas)
{
if (selected.empty())
{
transform.ClearTransform();
transform.UpdateTransform();
state = TRANSLATOR_IDLE;
return;
}
dragStarted = false;
dragEnded = false;
XMVECTOR pos = transform.GetPositionV();
// Non recursive selection will be computed to not apply recursive operations two times
// A recursive operation is for example translating a parent transform
// An other recursive operation is serializing selected parent entities
Scene& scene = *this->scene;
selectedEntitiesLookup.clear();
for (auto& x : selected)
{
selectedEntitiesLookup.insert(x.entity);
}
selectedEntitiesNonRecursive.clear();
for (auto& x : selected)
{
const HierarchyComponent* hier = scene.hierarchy.GetComponent(x.entity);
if (hier == nullptr || selectedEntitiesLookup.count(hier->parentID) == 0)
{
selectedEntitiesNonRecursive.push_back(x.entity);
}
}
if (IsEnabled())
{
PreTranslate();
if (!has_selected_transform)
{
state = TRANSLATOR_IDLE;
return;
}
const Ray ray = wi::renderer::GetPickRay((long)currentMouse.x, (long)currentMouse.y, canvas, camera);
const XMVECTOR rayOrigin = XMLoadFloat3(&ray.origin);
const XMVECTOR rayDir = XMLoadFloat3(&ray.direction);
if (!dragging)
{
state = TRANSLATOR_IDLE;
// Decide which state to enter for dragging:
XMMATRIX P = camera.GetProjection();
XMMATRIX V = camera.GetView();
XMMATRIX W = XMMatrixIdentity();
XMFLOAT3 p = transform.GetPosition();
dist = std::max(wi::math::Distance(p, camera.Eye) * 0.05f, 0.0001f);
if (isRotator)
{
XMMATRIX localRotation = GetLocalRotation();
XMVECTOR localX = XMVector3TransformNormal(XMVectorSet(1, 0, 0, 0), localRotation);
XMVECTOR localY = XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), localRotation);
XMVECTOR localZ = XMVector3TransformNormal(XMVectorSet(0, 0, 1, 0), localRotation);
XMVECTOR plane_zy = XMPlaneFromPointNormal(pos, localX);
XMVECTOR plane_xz = XMPlaneFromPointNormal(pos, localY);
XMVECTOR plane_xy = XMPlaneFromPointNormal(pos, localZ);
XMVECTOR intersection = XMPlaneIntersectLine(plane_zy, rayOrigin, rayOrigin + rayDir * camera.zFarP);
float dist_x = XMVectorGetX(XMVector3LengthSq(intersection - rayOrigin));
float len_x = XMVectorGetX(XMVector3Length(intersection - pos)) / dist;
intersection = XMPlaneIntersectLine(plane_xz, rayOrigin, rayOrigin + rayDir * camera.zFarP);
float dist_y = XMVectorGetX(XMVector3LengthSq(intersection - rayOrigin));
float len_y = XMVectorGetX(XMVector3Length(intersection - pos)) / dist;
intersection = XMPlaneIntersectLine(plane_xy, rayOrigin, rayOrigin + rayDir * camera.zFarP);
float dist_z = XMVectorGetX(XMVector3LengthSq(intersection - rayOrigin));
float len_z = XMVectorGetX(XMVector3Length(intersection - pos)) / dist;
float range = circle_width * 0.5f;
float perimeter = circle_radius - range;
float best_dist = std::numeric_limits<float>::max();
if (std::abs(perimeter - len_x) <= range && dist_x < best_dist)
{
state = TRANSLATOR_X;
XMStoreFloat3(&axis, localX);
best_dist = dist_x;
}
if (std::abs(perimeter - len_y) <= range && dist_y < best_dist)
{
state = TRANSLATOR_Y;
XMStoreFloat3(&axis, localY);
best_dist = dist_y;
}
if (std::abs(perimeter - len_z) <= range && dist_z < best_dist)
{
state = TRANSLATOR_Z;
XMStoreFloat3(&axis, localZ);
best_dist = dist_z;
}
XMVECTOR screen_normal = XMVector3Normalize(camera.GetEye() - pos);
XMVECTOR plane_screen = XMPlaneFromPointNormal(pos, screen_normal);
intersection = XMPlaneIntersectLine(plane_screen, rayOrigin, rayOrigin + rayDir * camera.zFarP);
float len_screen = XMVectorGetX(XMVector3Length(intersection - pos)) / dist;
range = circle2_width * 0.5f;
perimeter = circle2_radius - range;
if (std::abs(perimeter - len_screen) <= range)
{
state = TRANSLATOR_XYZ;
XMStoreFloat3(&axis, screen_normal);
}
}
else
{
XMMATRIX localRotation = GetLocalRotation();
AABB aabb_origin;
aabb_origin.createFromHalfWidth(p, XMFLOAT3(origin_size * dist, origin_size * dist, origin_size * dist));
XMFLOAT3 maxp;
XMStoreFloat3(&maxp, pos + XMVector3Transform(XMVectorSet(axis_length, 0, 0, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_X, camera)));
AABB aabb_x = AABB::Merge(AABB(wi::math::Min(p, maxp), wi::math::Max(p, maxp)), aabb_origin);
XMStoreFloat3(&maxp, pos + XMVector3Transform(XMVectorSet(0, axis_length, 0, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_Y, camera)));
AABB aabb_y = AABB::Merge(AABB(wi::math::Min(p, maxp), wi::math::Max(p, maxp)), aabb_origin);
XMStoreFloat3(&maxp, pos + XMVector3Transform(XMVectorSet(0, 0, axis_length, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_Z, camera)));
AABB aabb_z = AABB::Merge(AABB(wi::math::Min(p, maxp), wi::math::Max(p, maxp)), aabb_origin);
XMFLOAT3 minp;
XMStoreFloat3(&minp, pos + XMVector3Transform(XMVectorSet(plane_min, plane_min, 0, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_XY, camera)));
XMStoreFloat3(&maxp, pos + XMVector3Transform(XMVectorSet(plane_max, plane_max, 0, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_XY, camera)));
AABB aabb_xy = AABB(wi::math::Min(minp, maxp), wi::math::Max(minp, maxp));
XMStoreFloat3(&minp, pos + XMVector3Transform(XMVectorSet(plane_min, 0, plane_min, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_XZ, camera)));
XMStoreFloat3(&maxp, pos + XMVector3Transform(XMVectorSet(plane_max, 0, plane_max, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_XZ, camera)));
AABB aabb_xz = AABB(wi::math::Min(minp, maxp), wi::math::Max(minp, maxp));
XMStoreFloat3(&minp, pos + XMVector3Transform(XMVectorSet(0, plane_min, plane_min, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_YZ, camera)));
XMStoreFloat3(&maxp, pos + XMVector3Transform(XMVectorSet(0, plane_max, plane_max, 0) * dist, localRotation * GetMirrorMatrix(TRANSLATOR_YZ, camera)));
AABB aabb_yz = AABB(wi::math::Min(minp, maxp), wi::math::Max(minp, maxp));
if (aabb_origin.intersects(ray))
{
state = TRANSLATOR_XYZ;
}
else if (aabb_x.intersects(ray))
{
state = TRANSLATOR_X;
}
else if (aabb_y.intersects(ray))
{
state = TRANSLATOR_Y;
}
else if (aabb_z.intersects(ray))
{
state = TRANSLATOR_Z;
}
if (isTranslator && state != TRANSLATOR_XYZ)
{
// these can overlap, so take closest one (by checking plane ray trace distance):
XMVECTOR N = XMVector3TransformNormal(XMVectorSet(0, 0, 1, 0), localRotation);
float prio = FLT_MAX;
if (aabb_xy.intersects(ray))
{
state = TRANSLATOR_XY;
prio = XMVectorGetX(XMVector3Dot(N, (rayOrigin - pos) / XMVectorAbs(XMVector3Dot(N, rayDir))));
}
N = XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), localRotation);
float d = XMVectorGetX(XMVector3Dot(N, (rayOrigin - pos) / XMVectorAbs(XMVector3Dot(N, rayDir))));
if (d < prio && aabb_xz.intersects(ray))
{
state = TRANSLATOR_XZ;
prio = d;
}
N = XMVector3TransformNormal(XMVectorSet(1, 0, 0, 0), localRotation);
d = XMVectorGetX(XMVector3Dot(N, (rayOrigin - pos) / XMVectorAbs(XMVector3Dot(N, rayDir))));
if (d < prio && aabb_yz.intersects(ray))
{
state = TRANSLATOR_YZ;
}
}
}
}
if (dragging || (state != TRANSLATOR_IDLE && wi::input::Press(wi::input::MOUSE_BUTTON_LEFT)))
{
// Dragging operation:
if (isRotator)
{
XMVECTOR intersection = XMPlaneIntersectLine(XMPlaneFromPointNormal(pos, XMLoadFloat3(&axis)), rayOrigin, rayOrigin + rayDir * camera.zFarP);
if (!dragging)
{
dragStarted = true;
transform_start = transform;
XMStoreFloat3(&intersection_start, intersection);
matrices_start = matrices_current;
}
XMVECTOR intersectionPrev = XMLoadFloat3(&intersection_start);
XMVECTOR o = XMVector3Normalize(intersectionPrev - pos);
XMVECTOR c = XMVector3Normalize(intersection - pos);
XMFLOAT3 original, current;
XMStoreFloat3(&original, o);
XMStoreFloat3(&current, c);
angle = wi::math::GetAngle(original, current, axis);
switch (state)
{
case Translator::TRANSLATOR_X:
angle_start = wi::math::GetAngle(XMFLOAT3(0, 1, 0), original, axis);
break;
case Translator::TRANSLATOR_Y:
angle_start = wi::math::GetAngle(XMFLOAT3(0, 0, 1), original, axis);
break;
case Translator::TRANSLATOR_Z:
angle_start = wi::math::GetAngle(XMFLOAT3(1, 0, 0), original, axis);
break;
case Translator::TRANSLATOR_XYZ:
{
XMMATRIX M = XMMatrixInverse(nullptr, XMMatrixLookToLH(XMVectorZero(), XMVector3Normalize(transform.GetPositionV() - camera.GetEye()), camera.GetUp()));
XMFLOAT3 ref;
XMStoreFloat3(&ref, XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), M));
angle_start = wi::math::GetAngle(ref, original, axis);
}
break;
default:
break;
}
if (wi::input::Down(wi::input::BUTTON::KEYBOARD_BUTTON_LCONTROL))
{
// Snap mode:
angle = std::round(angle / rotate_snap) * rotate_snap;
}
angle = std::fmod(angle, XM_2PI);
transform = transform_start;
transform.Rotate(XMQuaternionRotationAxis(XMLoadFloat3(&axis), angle));
}
else
{
XMVECTOR plane, planeNormal;
XMMATRIX localRotation = GetLocalRotation();
if (state == TRANSLATOR_X)
{
XMVECTOR axis = XMVector3TransformNormal(XMVectorSet(1, 0, 0, 0), localRotation);
XMVECTOR wrong = XMVector3Cross(camera.GetAt(), axis);
planeNormal = XMVector3Cross(wrong, axis);
XMStoreFloat3(&this->axis, axis);
}
else if (state == TRANSLATOR_Y)
{
XMVECTOR axis = XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), localRotation);
XMVECTOR wrong = XMVector3Cross(camera.GetAt(), axis);
planeNormal = XMVector3Cross(wrong, axis);
XMStoreFloat3(&this->axis, axis);
}
else if (state == TRANSLATOR_Z)
{
XMVECTOR axis = XMVector3TransformNormal(XMVectorSet(0, 0, 1, 0), localRotation);
XMVECTOR wrong = XMVector3Cross(camera.GetAt(), axis);
planeNormal = XMVector3Cross(wrong, axis);
XMStoreFloat3(&this->axis, axis);
}
else if (state == TRANSLATOR_XY)
{
planeNormal = XMVector3TransformNormal(XMVectorSet(0, 0, 1, 0), localRotation);
}
else if (state == TRANSLATOR_XZ)
{
planeNormal = XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), localRotation);
}
else if (state == TRANSLATOR_YZ)
{
planeNormal = XMVector3TransformNormal(XMVectorSet(1, 0, 0, 0), localRotation);
}
else
{
// xyz
planeNormal = camera.GetAt();
}
// Use stored plane from drag start to avoid drift
if (dragging)
{
plane = XMLoadFloat4(&drag_plane);
}
else
{
plane = XMPlaneFromPointNormal(pos, XMVector3Normalize(planeNormal));
}
// Check if ray is nearly parallel to plane - only abort if not already dragging
if (XMVectorGetX(XMVectorAbs(XMVector3Dot(XMPlaneNormalize(plane), rayDir))) < 0.001f)
{
if (!dragging)
{
state = TRANSLATOR_IDLE;
return;
}
// If already dragging, skip this frame's update but maintain state
return;
}
XMVECTOR intersection = XMPlaneIntersectLine(plane, rayOrigin, rayOrigin + rayDir * camera.zFarP);
if (!dragging)
{
dragStarted = true;
transform_start = transform;
XMStoreFloat3(&intersection_start, intersection);
XMStoreFloat4(&drag_plane, plane);
matrices_start = matrices_current;
}
XMVECTOR intersectionPrev = XMLoadFloat3(&intersection_start);
XMVECTOR startPosition = transform_start.GetPositionV();
XMVECTOR deltaV;
if (state == TRANSLATOR_X)
{
XMVECTOR axisDir = XMVector3TransformNormal(XMVectorSet(1, 0, 0, 0), localRotation);
XMVECTOR A = startPosition, B = startPosition + axisDir;
XMVECTOR P = wi::math::ClosestPointOnLine(A, B, intersection);
XMVECTOR PPrev = wi::math::ClosestPointOnLine(A, B, intersectionPrev);
deltaV = P - PPrev;
}
else if (state == TRANSLATOR_Y)
{
XMVECTOR axisDir = XMVector3TransformNormal(XMVectorSet(0, 1, 0, 0), localRotation);
XMVECTOR A = startPosition, B = startPosition + axisDir;
XMVECTOR P = wi::math::ClosestPointOnLine(A, B, intersection);
XMVECTOR PPrev = wi::math::ClosestPointOnLine(A, B, intersectionPrev);
deltaV = P - PPrev;
}
else if (state == TRANSLATOR_Z)
{
XMVECTOR axisDir = XMVector3TransformNormal(XMVectorSet(0, 0, 1, 0), localRotation);
XMVECTOR A = startPosition, B = startPosition + axisDir;
XMVECTOR P = wi::math::ClosestPointOnLine(A, B, intersection);
XMVECTOR PPrev = wi::math::ClosestPointOnLine(A, B, intersectionPrev);
deltaV = P - PPrev;
}
else
{
deltaV = intersection - intersectionPrev;
if (isScalator)
{
deltaV = XMVectorReplicate(XMVectorGetY(deltaV));
}
}
transform = transform_start;
if (isTranslator)
{
transform.Translate(deltaV);
}
if (isScalator)
{
deltaV = XMVector3TransformNormal(deltaV, GetMirrorMatrix(state, camera));
deltaV = XMVector3Rotate(deltaV, transform.GetRotationV());
XMFLOAT3 delta;
XMStoreFloat3(&delta, deltaV);
XMFLOAT3 scale = transform.GetScale();
scale = wi::math::Max(scale, XMFLOAT3(0.001f, 0.001f, 0.001f)); // no zero division
scale = XMFLOAT3((1.0f / scale.x) * (scale.x + delta.x), (1.0f / scale.y) * (scale.y + delta.y), (1.0f / scale.z) * (scale.z + delta.z));
transform.Scale(scale);
}
if (wi::input::Down(wi::input::BUTTON::KEYBOARD_BUTTON_LCONTROL) || wi::input::Down(wi::input::BUTTON::KEYBOARD_BUTTON_RCONTROL))
{
// Snap to grid mode:
if (isTranslator)
{
transform.translation_local.x = std::round(transform.translation_local.x / translate_snap) * translate_snap;
transform.translation_local.y = std::round(transform.translation_local.y / translate_snap) * translate_snap;
transform.translation_local.z = std::round(transform.translation_local.z / translate_snap) * translate_snap;
}
if (isScalator)
{
transform.scale_local.x = std::max(scale_snap, std::round(transform.scale_local.x / scale_snap) * scale_snap);
transform.scale_local.y = std::max(scale_snap, std::round(transform.scale_local.y / scale_snap) * scale_snap);
transform.scale_local.z = std::max(scale_snap, std::round(transform.scale_local.z / scale_snap) * scale_snap);
}
}
if (wi::input::Down(wi::input::BUTTON::KEYBOARD_BUTTON_LSHIFT) || wi::input::Down(wi::input::BUTTON::KEYBOARD_BUTTON_RSHIFT))
{
// Snap to surface mode:
// 1.) Collect all objects which could be in the hierarchy of any of the selected ones
// This is important because we could select a top parent that doesn't have object
// but I still want to disable children's filters
selectedWithHierarchy.clear();
for (size_t i = 0; i < scene.objects.GetCount(); ++i)
{
Entity entity = scene.objects.GetEntity(i);
for (auto& potential_parent : selectedEntitiesNonRecursive)
{
if (entity == potential_parent || scene.Entity_IsDescendant(entity, potential_parent))
{
selectedWithHierarchy.push_back(entity);
break;
}
}
}
// 2.) Disable all object filters which are part of selection hierarchy for the next picking
// This is to filter them out from picking, we only want to pick what's underneath
temp_filters.reserve(selectedWithHierarchy.size());
for (auto& entity : selectedWithHierarchy)
{
ObjectComponent* object = scene.objects.GetComponent(entity);
if (object == nullptr)
continue;
temp_filters.push_back(uint64_t(object->filterMask) | (uint64_t(object->filterMaskDynamic) << 32ull));
object->filterMask = 0;
object->filterMaskDynamic = 0;
}
// 3.) Pick into scene to determine placement position
Ray ray = wi::renderer::GetPickRay((long)currentMouse.x, (long)currentMouse.y, canvas, camera);
wi::scene::Scene::RayIntersectionResult result = scene.Intersects(ray, wi::enums::FILTER_OBJECT_ALL);
transform.translation_local = result.position;
// 4.) Restore all filters to original values
size_t ind = 0;
for (auto& entity : selectedWithHierarchy)
{
ObjectComponent* object = scene.objects.GetComponent(entity);
if (object == nullptr)
continue;
uint64_t tmp = temp_filters[ind++];
object->filterMask = uint32_t(tmp);
object->filterMaskDynamic = uint32_t(tmp >> 32ull);
}
}
}
transform.UpdateTransform();
dragging = true;
}
if (isScalator || isRotator || isTranslator)
{
if (dragging)
{
PostTranslate();
}
}
if (!wi::input::Down(wi::input::MOUSE_BUTTON_LEFT))
{
if (dragging)
{
dragEnded = true;
}
dragging = false;
}
}
else
{
if (dragging)
{
dragEnded = true;
}
dragging = false;
state = TRANSLATOR_IDLE;
}
}
void Translator::Draw(const CameraComponent& camera, const XMFLOAT4& currentMouse, CommandList cmd) const
{
if (!IsEnabled() || selected.empty() || !has_selected_transform)
{
return;
}
static bool shaders_loaded = false;
if (!shaders_loaded)
{
shaders_loaded = true;
static wi::eventhandler::Handle handle = wi::eventhandler::Subscribe(wi::eventhandler::EVENT_RELOAD_SHADERS, [](uint64_t userdata) { Translator_Internal::LoadShaders(); });
Translator_Internal::LoadShaders();
}
Scene& scene = *this->scene;
GraphicsDevice* device = wi::graphics::GetDevice();
device->EventBegin("Translator", cmd);
CameraComponent cam_tmp = camera;
cam_tmp.jitter = XMFLOAT2(0, 0); // remove temporal jitter
cam_tmp.UpdateCamera();
XMMATRIX VP = cam_tmp.GetViewProjection();
MiscCB sb;
XMMATRIX localRotation = GetLocalRotation();
XMMATRIX mat = XMMatrixScaling(dist, dist, dist) * localRotation * XMMatrixTranslationFromVector(transform.GetPositionV()) * VP;
XMMATRIX matX = XMMatrixIdentity();
XMMATRIX matY = XMMatrixRotationZ(XM_PIDIV2)*XMMatrixRotationY(XM_PIDIV2);
XMMATRIX matZ = XMMatrixRotationY(-XM_PIDIV2)*XMMatrixRotationZ(-XM_PIDIV2);
const float channel_min = 0.25f; // min color channel, to avoid pure red/green/blue
const XMFLOAT4 highlight_color = XMFLOAT4(1, 0.6f, 0, 1);
// Axes:
{
device->BindPipelineState(&pso_solidpart, cmd);
uint32_t vertexCount = 0;
GraphicsDevice::GPUAllocation mem;
if (isRotator)
{
const uint32_t segmentCount = 90;
const uint32_t circle_triangleCount = segmentCount * 2;
vertexCount = circle_triangleCount * 3;
mem = device->AllocateGPU(sizeof(Vertex) * vertexCount, cmd);
uint8_t* dst = (uint8_t*)mem.data;
for (uint32_t i = 0; i < segmentCount; ++i)
{
const float angle0 = (float)i / (float)segmentCount * XM_2PI;
const float angle1 = (float)(i + 1) / (float)segmentCount * XM_2PI;
// circle:
const float circle_radius_inner = circle_radius - circle_width;
const float circle_halfway = circle_radius - circle_width * 0.5f;
const Vertex verts[] = {
{XMFLOAT4(0, std::sin(angle0) * circle_radius_inner, std::cos(angle0) * circle_radius_inner, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle1) * circle_radius_inner, std::cos(angle1) * circle_radius_inner, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle0) * circle_radius, std::cos(angle0) * circle_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle0) * circle_radius, std::cos(angle0) * circle_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle1) * circle_radius, std::cos(angle1) * circle_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle1) * circle_radius_inner, std::cos(angle1) * circle_radius_inner, 1), XMFLOAT4(1,1,1,1)},
};
std::memcpy(dst, verts, sizeof(verts));
dst += sizeof(verts);
}
}
else
{
const uint32_t segmentCount = 18;
const uint32_t cylinder_triangleCount = segmentCount * 2;
const uint32_t cone_triangleCount = cylinder_triangleCount;
if (isTranslator)
{
vertexCount = (cylinder_triangleCount + cone_triangleCount) * 3;
}
else if (isScalator)
{
vertexCount = cylinder_triangleCount * 3 + arraysize(cubeVerts);
}
mem = device->AllocateGPU(sizeof(Vertex) * vertexCount, cmd);
const float cone_length = 0.75f;
float cylinder_length = axis_length;
if (isTranslator)
{
cylinder_length -= cone_length;
}
uint8_t* dst = (uint8_t*)mem.data;
for (uint32_t i = 0; i < segmentCount; ++i)
{
const float angle0 = (float)i / (float)segmentCount * XM_2PI;
const float angle1 = (float)(i + 1) / (float)segmentCount * XM_2PI;
// cylinder base:
{
const float cylinder_radius = 0.075f;
const Vertex verts[] = {
{XMFLOAT4(origin_size, std::sin(angle0) * cylinder_radius, std::cos(angle0) * cylinder_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(origin_size, std::sin(angle1) * cylinder_radius, std::cos(angle1) * cylinder_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(cylinder_length, std::sin(angle0) * cylinder_radius, std::cos(angle0) * cylinder_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(cylinder_length, std::sin(angle0) * cylinder_radius, std::cos(angle0) * cylinder_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(cylinder_length, std::sin(angle1) * cylinder_radius, std::cos(angle1) * cylinder_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(origin_size, std::sin(angle1) * cylinder_radius, std::cos(angle1) * cylinder_radius, 1), XMFLOAT4(1,1,1,1)},
};
std::memcpy(dst, verts, sizeof(verts));
dst += sizeof(verts);
}
if (isTranslator)
{
// cone cap:
const float cone_radius = origin_size;
const Vertex verts[] = {
{XMFLOAT4(cylinder_length, 0, 0, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(cylinder_length, std::sin(angle0) * cone_radius, std::cos(angle0) * cone_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(cylinder_length, std::sin(angle1) * cone_radius, std::cos(angle1) * cone_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(axis_length, 0, 0, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(cylinder_length, std::sin(angle0) * cone_radius, std::cos(angle0) * cone_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(cylinder_length, std::sin(angle1) * cone_radius, std::cos(angle1) * cone_radius, 1), XMFLOAT4(1,1,1,1)},
};
std::memcpy(dst, verts, sizeof(verts));
dst += sizeof(verts);
}
}
if (isScalator)
{
// cube cap:
for (uint32_t i = 0; i < arraysize(cubeVerts); ++i)
{
Vertex vert = cubeVerts[i];
vert.position.x = vert.position.x * origin_size + cylinder_length - origin_size;
vert.position.y = vert.position.y * origin_size;
vert.position.z = vert.position.z * origin_size;
std::memcpy(dst, &vert, sizeof(vert));
dst += sizeof(vert);
}
}
}
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
float darken = 1;
// x
XMStoreFloat4x4(&sb.g_xTransform, matX * GetMirrorMatrix(TRANSLATOR_X, camera) * mat);
darken = isLocalSpace ? 1 : (camera.Eye.x < transform.translation_local.x ? darken_negative_axes : 1);
sb.g_xColor = state == TRANSLATOR_X ? highlight_color : XMFLOAT4(darken, channel_min * darken, channel_min * darken, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(vertexCount, 0, cmd);
// y
XMStoreFloat4x4(&sb.g_xTransform, matY * GetMirrorMatrix(TRANSLATOR_Y, camera)* mat);
darken = isLocalSpace ? 1 : (camera.Eye.y < transform.translation_local.y ? darken_negative_axes : 1);
sb.g_xColor = state == TRANSLATOR_Y ? highlight_color : XMFLOAT4(channel_min * darken, darken, channel_min * darken, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(vertexCount, 0, cmd);
// z
XMStoreFloat4x4(&sb.g_xTransform, matZ * GetMirrorMatrix(TRANSLATOR_Z, camera)* mat);
darken = isLocalSpace ? 1 : (camera.Eye.z < transform.translation_local.z ? darken_negative_axes : 1);
sb.g_xColor = state == TRANSLATOR_Z ? highlight_color : XMFLOAT4(channel_min * darken, channel_min * darken, darken, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(vertexCount, 0, cmd);
}
if (isRotator)
{
// An other circle for rotator, a bit thinner and screen facing, so new geo:
const uint32_t segmentCount = 90;
const uint32_t circle2_triangleCount = segmentCount * 2;
uint32_t vertexCount = circle2_triangleCount * 3;
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(Vertex) * vertexCount, cmd);
uint8_t* dst = (uint8_t*)mem.data;
for (uint32_t i = 0; i < segmentCount; ++i)
{
const float angle0 = (float)i / (float)segmentCount * XM_2PI;
const float angle1 = (float)(i + 1) / (float)segmentCount * XM_2PI;
// circle:
const float circle2_radius_inner = circle2_radius - circle2_width;
const float circle2_halfway = circle2_radius - circle2_width * 0.5f;
const Vertex verts[] = {
{XMFLOAT4(0, std::sin(angle0) * circle2_radius_inner, std::cos(angle0) * circle2_radius_inner, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle1) * circle2_radius_inner, std::cos(angle1) * circle2_radius_inner, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle0) * circle2_radius, std::cos(angle0) * circle2_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle0) * circle2_radius, std::cos(angle0) * circle2_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle1) * circle2_radius, std::cos(angle1) * circle2_radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::sin(angle1) * circle2_radius_inner, std::cos(angle1) * circle2_radius_inner, 1), XMFLOAT4(1,1,1,1)},
};
std::memcpy(dst, verts, sizeof(verts));
dst += sizeof(verts);
}
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
XMMATRIX mat_no_localrot = XMMatrixScaling(dist, dist, dist) * XMMatrixTranslationFromVector(transform.GetPositionV()) * VP;
XMStoreFloat4x4(&sb.g_xTransform,
XMMatrixRotationY(XM_PIDIV2) *
XMMatrixInverse(nullptr, XMMatrixLookToLH(XMVectorZero(), XMVector3Normalize(transform.GetPositionV() - camera.GetEye()), camera.GetUp())) *
mat_no_localrot
);
sb.g_xColor = state == TRANSLATOR_XYZ ? highlight_color : XMFLOAT4(1, 1, 1, 0.5f);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(vertexCount, 0, cmd);
}
// Origin:
if(!isRotator)
{
device->BindPipelineState(&pso_solidpart, cmd);
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(cubeVerts), cmd);
std::memcpy(mem.data, cubeVerts, sizeof(cubeVerts));
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
XMStoreFloat4x4(&sb.g_xTransform, XMMatrixScaling(origin_size, origin_size, origin_size) * mat);
sb.g_xColor = state == TRANSLATOR_XYZ ? highlight_color : XMFLOAT4(0.5f, 0.5f, 0.5f, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(cubeVerts), 0, cmd);
}
// Planes:
if (isTranslator)
{
// Wire part:
{
device->BindPipelineState(&pso_wirepart, cmd);
const Vertex verts[] = {
{XMFLOAT4(plane_min,plane_min,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_min,plane_max,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_min,plane_max,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_max,plane_max,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_max,plane_max,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_max,plane_min,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_max,plane_min,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_min,plane_min,0,1), XMFLOAT4(1,1,1,1)},
};
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(verts), cmd);
std::memcpy(mem.data, verts, sizeof(verts));
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
// xy
XMStoreFloat4x4(&sb.g_xTransform, matX * GetMirrorMatrix(TRANSLATOR_XY, camera) * mat);
sb.g_xColor = state == TRANSLATOR_XY ? highlight_color : XMFLOAT4(channel_min, channel_min, 1, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(verts), 0, cmd);
// xz
XMStoreFloat4x4(&sb.g_xTransform, matZ * GetMirrorMatrix(TRANSLATOR_XZ, camera) * mat);
sb.g_xColor = state == TRANSLATOR_XZ ? highlight_color : XMFLOAT4(channel_min, 1, channel_min, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(verts), 0, cmd);
// yz
XMStoreFloat4x4(&sb.g_xTransform, matY * GetMirrorMatrix(TRANSLATOR_YZ, camera) * mat);
sb.g_xColor = state == TRANSLATOR_YZ ? highlight_color : XMFLOAT4(1, channel_min, channel_min, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(verts), 0, cmd);
}
// Quad part:
{
device->BindPipelineState(&pso_solidpart, cmd);
const Vertex verts[] = {
{XMFLOAT4(plane_min,plane_min,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_max,plane_min,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_max,plane_max,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_min,plane_min,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_max,plane_max,0,1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(plane_min,plane_max,0,1), XMFLOAT4(1,1,1,1)},
};
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(verts), cmd);
std::memcpy(mem.data, verts, sizeof(verts));
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
// xy
XMStoreFloat4x4(&sb.g_xTransform, matX * GetMirrorMatrix(TRANSLATOR_XY, camera) * mat);
sb.g_xColor = state == TRANSLATOR_XY ? highlight_color : XMFLOAT4(channel_min, channel_min, 1, 0.4f);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(verts), 0, cmd);
// xz
XMStoreFloat4x4(&sb.g_xTransform, matZ * GetMirrorMatrix(TRANSLATOR_XZ, camera) * mat);
sb.g_xColor = state == TRANSLATOR_XZ ? highlight_color : XMFLOAT4(channel_min, 1, channel_min, 0.4f);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(verts), 0, cmd);
// yz
XMStoreFloat4x4(&sb.g_xTransform, matY * GetMirrorMatrix(TRANSLATOR_YZ, camera) * mat);
sb.g_xColor = state == TRANSLATOR_YZ ? highlight_color : XMFLOAT4(1, channel_min, channel_min, 0.4f);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(verts), 0, cmd);
}
}
// Axis texts:
if(!isRotator && !isLocalSpace)
{
char TEXT[3];
XMMATRIX R = XMLoadFloat3x3(&camera.rotationMatrix);
wi::font::Params params;
params.v_align = wi::font::WIFALIGN_CENTER;
params.h_align = wi::font::WIFALIGN_CENTER;
params.scaling = 0.04f * dist;
params.customProjection = &VP;
params.customRotation = &R;
params.shadowColor = wi::Color(0, 0, 0, uint8_t(127 * opacity));
params.shadow_softness = 0.8f;
XMVECTOR pos = transform.GetPositionV();
float darken = 1;
darken = camera.Eye.x < transform.translation_local.x ? darken_negative_axes : 1;
params.color = wi::Color::fromFloat4(XMFLOAT4(darken, channel_min * darken, channel_min * darken, opacity));
XMStoreFloat3(&params.position, pos + XMVector3Transform(XMVectorSet(axis_length + 0.5f, 0, 0, 0) * dist, GetMirrorMatrix(TRANSLATOR_X, camera)));
std::memset(TEXT, 0, sizeof(TEXT));
WriteAxisText(TRANSLATOR_X, camera, TEXT);
wi::font::Draw(TEXT, strlen(TEXT), params, cmd);
darken = camera.Eye.y < transform.translation_local.y ? darken_negative_axes : 1;
params.color = wi::Color::fromFloat4(XMFLOAT4(channel_min * darken, darken, channel_min * darken, opacity));
XMStoreFloat3(&params.position, pos + XMVector3Transform(XMVectorSet(0, axis_length + 0.5f, 0, 0) * dist, GetMirrorMatrix(TRANSLATOR_Y, camera)));
std::memset(TEXT, 0, sizeof(TEXT));
WriteAxisText(TRANSLATOR_Y, camera, TEXT);
wi::font::Draw(TEXT, strlen(TEXT), params, cmd);
darken = camera.Eye.z < transform.translation_local.z ? darken_negative_axes : 1;
params.color = wi::Color::fromFloat4(XMFLOAT4(channel_min * darken, channel_min * darken, darken, opacity));
XMStoreFloat3(&params.position, pos + XMVector3Transform(XMVectorSet(0, 0, axis_length + 0.5f, 0) * dist, GetMirrorMatrix(TRANSLATOR_Z, camera)));
std::memset(TEXT, 0, sizeof(TEXT));
WriteAxisText(TRANSLATOR_Z, camera, TEXT);
wi::font::Draw(TEXT, strlen(TEXT), params, cmd);
}
// Dragging visualizer:
if (dragging)
{
if (isTranslator)
{
// Origin circle:
{
device->BindPipelineState(&pso_solidpart, cmd);
const uint32_t segmentCount = 36;
const uint32_t vertexCount = segmentCount * 3;
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(Vertex) * vertexCount, cmd);
uint8_t* dst = (uint8_t*)mem.data;
for (uint32_t i = 0; i < segmentCount; ++i)
{
const float angle0 = (float)i / (float)segmentCount * XM_2PI;
const float angle1 = (float)(i + 1) / (float)segmentCount * XM_2PI;
const float radius = 0.2f * dist;
const Vertex verts[] = {
{XMFLOAT4(0, 0, 0, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::cos(angle0) * radius, std::sin(angle0) * radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::cos(angle1) * radius, std::sin(angle1) * radius, 1), XMFLOAT4(1,1,1,1)},
};
std::memcpy(dst, verts, sizeof(verts));
dst += sizeof(verts);
}
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
XMStoreFloat4x4(&sb.g_xTransform,
XMMatrixRotationY(XM_PIDIV2)*
XMMatrixInverse(nullptr, XMMatrixLookToLH(XMVectorZero(), XMVector3Normalize(transform_start.GetPositionV() - camera.GetEye()), camera.GetUp()))*
XMMatrixTranslationFromVector(transform_start.GetPositionV())*
VP
);
sb.g_xColor = XMFLOAT4(1, 1, 1, 0.5f);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(vertexCount, 0, cmd);
}
// Line:
{
device->BindPipelineState(&pso_wirepart, cmd);
const Vertex verts[] = {
{XMFLOAT4(transform_start.translation_local.x, transform_start.translation_local.y, transform_start.translation_local.z, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(transform.translation_local.x, transform.translation_local.y, transform.translation_local.z, 1), XMFLOAT4(1,1,1,1)},
};
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(verts), cmd);
std::memcpy(mem.data, verts, sizeof(verts));
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
XMStoreFloat4x4(&sb.g_xTransform, VP);
sb.g_xColor = XMFLOAT4(1, 1, 1, 1);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(arraysize(verts), 0, cmd);
}
}
else if (isRotator)
{
device->BindPipelineState(&pso_solidpart, cmd);
const uint32_t segmentCount = 90;
const uint32_t vertexCount = segmentCount * 3;
GraphicsDevice::GPUAllocation mem = device->AllocateGPU(sizeof(Vertex) * vertexCount, cmd);
switch (state)
{
case Translator::TRANSLATOR_X:
XMStoreFloat4x4(&sb.g_xTransform, matX * mat);
break;
case Translator::TRANSLATOR_Y:
XMStoreFloat4x4(&sb.g_xTransform, matY * mat);
break;
case Translator::TRANSLATOR_Z:
XMStoreFloat4x4(&sb.g_xTransform, matZ * mat);
break;
case Translator::TRANSLATOR_XYZ:
XMStoreFloat4x4(&sb.g_xTransform,
XMMatrixRotationY(XM_PIDIV2) *
XMMatrixInverse(nullptr, XMMatrixLookToLH(XMVectorZero(), XMVector3Normalize(transform.GetPositionV() - camera.GetEye()), camera.GetUp())) *
mat
);
break;
default:
break;
}
uint8_t* dst = (uint8_t*)mem.data;
for (uint32_t i = 0; i < segmentCount; ++i)
{
const float angle0 = (float)i / (float)segmentCount * angle + angle_start;
const float angle1 = (float)(i + 1) / (float)segmentCount * angle + angle_start;
const float radius = state == TRANSLATOR_XYZ ? (circle2_radius - circle2_width) : (circle_radius - circle_width);
const Vertex verts[] = {
{XMFLOAT4(0, 0, 0, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::cos(angle0) * radius, std::sin(angle0) * radius, 1), XMFLOAT4(1,1,1,1)},
{XMFLOAT4(0, std::cos(angle1) * radius, std::sin(angle1) * radius, 1), XMFLOAT4(1,1,1,1)},
};
std::memcpy(dst, verts, sizeof(verts));
dst += sizeof(verts);
}
const GPUBuffer* vbs[] = {
&mem.buffer,
};
const uint32_t strides[] = {
sizeof(Vertex),
};
const uint64_t offsets[] = {
mem.offset,
};
device->BindVertexBuffers(vbs, 0, arraysize(vbs), strides, offsets, cmd);
sb.g_xColor = XMFLOAT4(1, 1, 1, 0.5f);
sb.g_xColor.w *= opacity;
device->BindDynamicConstantBuffer(sb, CBSLOT_RENDERER_MISC, cmd);
device->Draw(vertexCount, 0, cmd);
}
wi::font::Params params;
params.posX = currentMouse.x - 20;
params.posY = currentMouse.y + 20;
params.v_align = wi::font::WIFALIGN_TOP;
params.h_align = wi::font::WIFALIGN_RIGHT;
params.scaling = 0.8f;
params.shadowColor = wi::Color::Black();
char text[256] = {};
if (isTranslator)
{
snprintf(text, arraysize(text), "Offset = %.2f, %.2f, %.2f", transform.translation_local.x - transform_start.translation_local.x, transform.translation_local.y - transform_start.translation_local.y, transform.translation_local.z - transform_start.translation_local.z);
}
if (isRotator)
{
switch (state)
{
case Translator::TRANSLATOR_X:
snprintf(text, arraysize(text), "Axis = X\nAngle = %.2f degrees", wi::math::RadiansToDegrees(angle));
break;
case Translator::TRANSLATOR_Y:
snprintf(text, arraysize(text), "Axis = Y\nAngle = %.2f degrees", wi::math::RadiansToDegrees(angle));
break;
case Translator::TRANSLATOR_Z:
snprintf(text, arraysize(text), "Axis = Z\nAngle = %.2f degrees", wi::math::RadiansToDegrees(angle));
break;
case Translator::TRANSLATOR_XYZ:
snprintf(text, arraysize(text), "Axis = Screen\nAngle = %.2f degrees", wi::math::RadiansToDegrees(angle));
break;
default:
break;
}
}
if (isScalator)
{
snprintf(text, arraysize(text), "Scaling = %.2f, %.2f, %.2f", transform.scale_local.x / transform_start.scale_local.x, transform.scale_local.y / transform_start.scale_local.y, transform.scale_local.z / transform_start.scale_local.z);
}
params.shadowColor.setA(uint8_t(opacity * 255.0f));
params.color.setA(uint8_t(opacity * 255.0f));
wi::font::Draw(text, params, cmd);
}
device->EventEnd(cmd);
}
void Translator::PreTranslate()
{
Scene& scene = *this->scene;
if (!dragging)
{
transform.ClearTransform();
}
has_selected_transform = false;
// Find the center of all the entities that are selected:
XMVECTOR centerV = XMVectorSet(0, 0, 0, 0);
float count = 0;
for (auto& x : selected)
{
TransformComponent* transform = scene.transforms.GetComponent(x.entity);
if (transform != nullptr)
{
transform->UpdateTransform();
centerV = XMVectorAdd(centerV, transform->GetPositionV());
count += 1.0f;
has_selected_transform = true;
}
}
if (!has_selected_transform)
return;
// Offset translator to center position and perform attachments:
if (count > 0)
{
centerV /= count;
XMStoreFloat3(&transform.translation_local, centerV);
transform.SetDirty();
transform.UpdateTransform();
}
// translator "bind matrix"
XMMATRIX B = XMMatrixInverse(nullptr, XMLoadFloat4x4(&transform.world));
matrices_current.clear();
for (auto& x : selectedEntitiesNonRecursive)
{
TransformComponent* transform_selected = scene.transforms.GetComponent(x);
if (transform_selected != nullptr)
{
XMFLOAT4X4 m;
XMStoreFloat4x4(&m, XMLoadFloat4x4(&transform_selected->world) * B);
matrices_current.push_back(m);
}
}
}
void Translator::PostTranslate()
{
Scene& scene = *this->scene;
int i = 0;
for (auto& x : selectedEntitiesNonRecursive)
{
TransformComponent* transform_selected = scene.transforms.GetComponent(x);
if (transform_selected != nullptr)
{
const XMFLOAT4X4& m = matrices_current[i++];
XMMATRIX W = XMLoadFloat4x4(&m);
XMMATRIX W_parent = XMLoadFloat4x4(&transform.world);
W = W * W_parent;
XMStoreFloat4x4(&transform_selected->world, W);
// selected to world space:
transform_selected->ApplyTransform();
// selected to parent local space (if has parent):
const HierarchyComponent* hier = scene.hierarchy.GetComponent(x);
if (hier != nullptr)
{
const TransformComponent* transform_parent = scene.transforms.GetComponent(hier->parentID);
if (transform_parent != nullptr)
{
transform_selected->MatrixTransform(XMMatrixInverse(nullptr, XMLoadFloat4x4(&transform_parent->world)));
}
}
}
}
}
XMMATRIX Translator::GetMirrorMatrix(TRANSLATOR_STATE state, const CameraComponent& camera) const
{
XMMATRIX mirror = XMMatrixIdentity();
if (isRotator || isLocalSpace)
return mirror;
switch (state)
{
case Translator::TRANSLATOR_X:
if (camera.Eye.x < transform.translation_local.x)
{
mirror *= XMMatrixScaling(-1, 1, 1);
}
break;
case Translator::TRANSLATOR_Y:
if (camera.Eye.y < transform.translation_local.y)
{
mirror *= XMMatrixScaling(1, -1, 1);
}
break;
case Translator::TRANSLATOR_Z:
if (camera.Eye.z < transform.translation_local.z)
{
mirror *= XMMatrixScaling(1, 1, -1);
}
break;
case Translator::TRANSLATOR_XY:
if (camera.Eye.x < transform.translation_local.x)
{
mirror *= XMMatrixScaling(-1, 1, 1);
}
if (camera.Eye.y < transform.translation_local.y)
{
mirror *= XMMatrixScaling(1, -1, 1);
}
break;
case Translator::TRANSLATOR_XZ:
if (camera.Eye.x < transform.translation_local.x)
{
mirror *= XMMatrixScaling(-1, 1, 1);
}
if (camera.Eye.z < transform.translation_local.z)
{
mirror *= XMMatrixScaling(1, 1, -1);
}
break;
case Translator::TRANSLATOR_YZ:
if (camera.Eye.y < transform.translation_local.y)
{
mirror *= XMMatrixScaling(1, -1, 1);
}
if (camera.Eye.z < transform.translation_local.z)
{
mirror *= XMMatrixScaling(1, 1, -1);
}
break;
default:
break;
}
return mirror;
}
void Translator::WriteAxisText(TRANSLATOR_STATE axis, const wi::scene::CameraComponent& camera, char* text) const
{
switch (axis)
{
case Translator::TRANSLATOR_X:
if (camera.Eye.x < transform.translation_local.x)
{
std::memcpy(text, "-X", 2);
}
else
{
std::memcpy(text, "X", 1);
}
break;
case Translator::TRANSLATOR_Y:
if (camera.Eye.y < transform.translation_local.y)
{
std::memcpy(text, "-Y", 2);
}
else
{
std::memcpy(text, "Y", 1);
}
break;
case Translator::TRANSLATOR_Z:
if (camera.Eye.z < transform.translation_local.z)
{
std::memcpy(text, "-Z", 2);
}
else
{
std::memcpy(text, "Z", 1);
}
break;
default:
break;
}
}
XMMATRIX Translator::GetLocalRotation() const
{
if (!isLocalSpace || selected.empty())
return XMMatrixIdentity();
// Get the rotation from the first selected entity with a transform
for (const auto& x : selected)
{
const TransformComponent* transform_selected = scene->transforms.GetComponent(x.entity);
if (transform_selected != nullptr)
{
XMMATRIX rotation = XMMatrixRotationQuaternion(XMLoadFloat4(&transform_selected->rotation_local));
const HierarchyComponent* hierarchy_component = scene->hierarchy.GetComponent(x.entity);
Entity parent = hierarchy_component != nullptr ? hierarchy_component->parentID : INVALID_ENTITY;
while (parent != INVALID_ENTITY)
{
const TransformComponent* parent_transform = scene->transforms.GetComponent(parent);
if (parent_transform != nullptr)
{
rotation = rotation * XMMatrixRotationQuaternion(XMLoadFloat4(&parent_transform->rotation_local));
}
const HierarchyComponent* parent_hierarchy = scene->hierarchy.GetComponent(parent);
parent = parent_hierarchy != nullptr ? parent_hierarchy->parentID : INVALID_ENTITY;
}
return rotation;
}
}
return XMMatrixIdentity();
}