251 lines
8.8 KiB
C++
251 lines
8.8 KiB
C++
#include <imgui/imgui.h>
|
|
|
|
#include "animation/animation_module.h"
|
|
#include "engine/plugin.h"
|
|
#include "engine/resource_manager.h"
|
|
#include "editor/studio_app.h"
|
|
#include "editor/settings.h"
|
|
#include "editor/utils.h"
|
|
#include "lua_script/lua_script.h"
|
|
#include "renderer/model.h"
|
|
#include "renderer/pose.h"
|
|
#include "renderer/render_module.h"
|
|
#include "renderer/renderer.h"
|
|
#include "world_viewer.h"
|
|
|
|
namespace Lumix {
|
|
|
|
static const ComponentType MODEL_INSTANCE_TYPE = reflection::getComponentType("model_instance");
|
|
static const ComponentType ENVIRONMENT_PROBE_TYPE = reflection::getComponentType("environment_probe");
|
|
static const ComponentType ENVIRONMENT_TYPE = reflection::getComponentType("environment");
|
|
|
|
WorldViewer::WorldViewer(StudioApp& app)
|
|
: m_app(app)
|
|
{
|
|
Engine& engine = m_app.getEngine();
|
|
auto* renderer = static_cast<Renderer*>(engine.getSystemManager().getSystem("renderer"));
|
|
m_viewport.is_ortho = false;
|
|
m_viewport.fov = m_app.getFOV();
|
|
m_viewport.near = 0.1f;
|
|
m_viewport.far = 1000.f;
|
|
m_viewport.pos = DVec3(0, 0, 0);
|
|
m_viewport.rot = Quat::IDENTITY;
|
|
|
|
m_world = &engine.createWorld(false);
|
|
LuaScript* pres = engine.getResourceManager().load<LuaScript>(Path("pipelines/main.lua"));
|
|
m_pipeline = Pipeline::create(*renderer, pres, "PREVIEW");
|
|
|
|
const EntityRef mesh_entity = m_world->createEntity({0, 0, 0}, {0, 0, 0, 1});
|
|
auto* render_module = static_cast<RenderModule*>(m_world->getModule(MODEL_INSTANCE_TYPE));
|
|
m_mesh = mesh_entity;
|
|
m_world->createComponent(MODEL_INSTANCE_TYPE, mesh_entity);
|
|
|
|
const EntityRef env_probe = m_world->createEntity({0, 0, 0}, Quat::IDENTITY);
|
|
m_world->createComponent(ENVIRONMENT_PROBE_TYPE, env_probe);
|
|
render_module->getEnvironmentProbe(env_probe).inner_range = Vec3(1e3);
|
|
render_module->getEnvironmentProbe(env_probe).outer_range = Vec3(1e3);
|
|
|
|
Matrix light_mtx;
|
|
light_mtx.lookAt({10, 10, 10}, Vec3::ZERO, {0, 1, 0});
|
|
const EntityRef light_entity = m_world->createEntity({0, 0, 0}, light_mtx.getRotation());
|
|
m_world->createComponent(ENVIRONMENT_TYPE, light_entity);
|
|
render_module->getEnvironment(light_entity).direct_intensity = 3;
|
|
render_module->getEnvironment(light_entity).indirect_intensity = 1;
|
|
|
|
const EntityRef e = m_world->createEntity(DVec3(0, 0, 0), Quat::IDENTITY);
|
|
m_world->createComponent(MODEL_INSTANCE_TYPE, e);
|
|
m_world->setScale(e, Vec3(100));
|
|
render_module->setModelInstancePath(e, Path("models/shapes/plane.fbx"));
|
|
|
|
m_pipeline->setWorld(m_world);
|
|
}
|
|
|
|
void WorldViewer::setModelPath(const Path& path) {
|
|
RenderModule* module = (RenderModule*)m_world->getModule("renderer");
|
|
module->setModelInstancePath(*m_mesh, path);
|
|
}
|
|
|
|
void WorldViewer::setAnimatorPath(const Path& path) {
|
|
AnimationModule* module = (AnimationModule*)m_world->getModule("animation");
|
|
ComponentType ANIMATOR_TYPE = reflection::getComponentType("animator");
|
|
if (!m_world->hasComponent(*m_mesh, ANIMATOR_TYPE)) {
|
|
m_world->createComponent(ANIMATOR_TYPE, *m_mesh);
|
|
}
|
|
module->setAnimatorSource(*m_mesh, path);
|
|
}
|
|
|
|
void WorldViewer::drawMeshTransform() {
|
|
auto* render_module = (RenderModule*)m_world->getModule("renderer");
|
|
Transform tr = m_world->getTransform(*m_mesh);
|
|
render_module->addDebugLine(tr.pos, tr.pos + tr.rot.rotate(Vec3(1, 0, 0)), Color::RED);
|
|
render_module->addDebugLine(tr.pos, tr.pos + tr.rot.rotate(Vec3(0, 1, 0)), Color::GREEN);
|
|
render_module->addDebugLine(tr.pos, tr.pos + tr.rot.rotate(Vec3(0, 0, 1)), Color::BLUE);
|
|
}
|
|
|
|
void WorldViewer::drawSkeleton(i32 selected_bone) {
|
|
auto* render_module = (RenderModule*)m_world->getModule("renderer");
|
|
Pose* pose = render_module->lockPose(*m_mesh);
|
|
Model* model = render_module->getModelInstanceModel(*m_mesh);
|
|
if (pose && model->isReady()) {
|
|
Transform tr = m_world->getTransform(*m_mesh);
|
|
ASSERT(pose->is_absolute);
|
|
for (u32 i = 0, c = model->getBoneCount(); i < c; ++i) {
|
|
const Model::Bone& bone = model->getBone(i);
|
|
const i32 parent_idx = bone.parent_idx;
|
|
if (parent_idx < 0) continue;
|
|
|
|
Color color = Color::BLUE;
|
|
if (selected_bone == i) {
|
|
color = Color::RED;
|
|
}
|
|
|
|
Vec3 bone_dir = pose->positions[i] - pose->positions[parent_idx];
|
|
const float bone_len = length(bone_dir);
|
|
const Quat r = pose->rotations[parent_idx];
|
|
|
|
Vec3 up = r.rotate(Vec3(0, 0, 0.06f * bone_len));
|
|
Vec3 right = r.rotate(Vec3(0.12f * bone_len, 0, 0));
|
|
|
|
DVec3 a = tr.transform(pose->positions[parent_idx]);
|
|
render_module->addDebugBone(a, tr.rot.rotate(bone_dir), tr.rot.rotate(up), tr.rot.rotate(right), color);
|
|
}
|
|
render_module->unlockPose(*m_mesh, false);
|
|
}
|
|
}
|
|
|
|
|
|
WorldViewer::~WorldViewer() {
|
|
Engine& engine = m_app.getEngine();
|
|
engine.destroyWorld(*m_world);
|
|
m_pipeline.reset();
|
|
}
|
|
|
|
void WorldViewer::resetCamera() {
|
|
RenderModule* module = (RenderModule*)m_world->getModule("renderer");
|
|
Model* model = module->getModelInstanceModel(*m_mesh);
|
|
if (model && model->isReady()) resetCamera(*model);
|
|
}
|
|
|
|
void WorldViewer::resetCamera(const Model& model) {
|
|
if (model.getMeshCount() == 0) return;
|
|
const AABB aabb = model.getAABB();
|
|
const Vec3 center = (aabb.max + aabb.min) * 0.5f;
|
|
m_viewport.pos = DVec3(0) + center + Vec3(1, 1, 1) * length(aabb.max - aabb.min);
|
|
Matrix mtx;
|
|
ASSERT(model.getCenterBoundingRadius() > 0);
|
|
Vec3 eye = center + Vec3(model.getCenterBoundingRadius() * 2);
|
|
mtx.lookAt(eye, center, normalize(Vec3(1, -1, 1)));
|
|
mtx = mtx.inverted();
|
|
m_viewport.rot = mtx.getRotation();
|
|
m_camera_speed = 1;
|
|
}
|
|
|
|
void WorldViewer::gui() {
|
|
auto* render_module = static_cast<RenderModule*>(m_world->getModule(MODEL_INSTANCE_TYPE));
|
|
ASSERT(render_module);
|
|
|
|
ImVec2 image_size = ImGui::GetContentRegionAvail();
|
|
image_size.y = maximum(200.f, image_size.y);
|
|
|
|
m_viewport.fov = m_app.getFOV();
|
|
m_viewport.w = (int)image_size.x;
|
|
m_viewport.h = (int)image_size.y;
|
|
Viewport vp = m_viewport;
|
|
if (m_follow_mesh) {
|
|
vp.pos += m_world->getPosition(*m_mesh);
|
|
}
|
|
m_pipeline->setViewport(vp);
|
|
m_pipeline->render(false);
|
|
gpu::TextureHandle preview = m_pipeline->getOutput();
|
|
const ImVec2 view_pos = ImGui::GetCursorScreenPos();
|
|
if (gpu::isOriginBottomLeft()) {
|
|
ImGui::Image(preview, image_size);
|
|
}
|
|
else {
|
|
ImGui::Image(preview, image_size, ImVec2(0, 1), ImVec2(1, 0));
|
|
}
|
|
|
|
if (m_is_mouse_captured) {
|
|
os::Rect rect;
|
|
rect.left = (i32)view_pos.x;
|
|
rect.top = (i32)view_pos.y;
|
|
rect.width = (i32)image_size.x;
|
|
rect.height = (i32)image_size.y;
|
|
m_app.setMouseClipRect(ImGui::GetWindowViewport()->PlatformHandle, rect);
|
|
}
|
|
|
|
const bool mouse_down = ImGui::IsMouseDown(ImGuiMouseButton_Right);
|
|
if (m_is_mouse_captured && (!mouse_down || !m_app.isMouseCursorClipped())) {
|
|
m_is_mouse_captured = false;
|
|
m_app.unclipMouseCursor();
|
|
os::showCursor(true);
|
|
os::setMouseScreenPos(m_captured_mouse_pos.x, m_captured_mouse_pos.y);
|
|
}
|
|
|
|
if ((m_is_mouse_captured || ImGui::IsItemHovered()) && mouse_down) {
|
|
Vec2 delta(0, 0);
|
|
for (const os::Event e : m_app.getEvents()) {
|
|
if (e.type == os::Event::Type::MOUSE_MOVE) {
|
|
delta += Vec2((float)e.mouse_move.xrel, (float)e.mouse_move.yrel);
|
|
}
|
|
}
|
|
|
|
if (!m_is_mouse_captured) {
|
|
m_is_mouse_captured = true;
|
|
m_app.clipMouseCursor();
|
|
os::showCursor(false);
|
|
m_captured_mouse_pos = os::getMouseScreenPos();
|
|
}
|
|
|
|
m_camera_speed = maximum(0.01f, m_camera_speed + ImGui::GetIO().MouseWheel / 20.0f);
|
|
|
|
auto moveCamera = [&](const Vec3& dir){
|
|
float speed = m_camera_speed;
|
|
if (os::isKeyDown(os::Keycode::SHIFT)) speed *= 10;
|
|
const Vec3 d = m_viewport.rot.rotate(dir);
|
|
m_viewport.pos -= d * m_app.getEngine().getLastTimeDelta() * speed;
|
|
};
|
|
|
|
const CommonActions& actions = m_app.getCommonActions();
|
|
const bool is_orbit = actions.cam_orbit.isActive();
|
|
if (actions.cam_forward.isActive()) moveCamera(Vec3(0, 0, 1));
|
|
else if (actions.cam_backward.isActive()) moveCamera(Vec3(0, 0, -1));
|
|
|
|
if (!is_orbit) {
|
|
if (actions.cam_left.isActive()) moveCamera(Vec3(1, 0, 0));
|
|
else if (actions.cam_right.isActive()) moveCamera(Vec3(-1, 0, 0));
|
|
if (actions.cam_up.isActive()) moveCamera(Vec3(0, 1, 0));
|
|
else if (actions.cam_down.isActive()) moveCamera(Vec3(0, -1, 0));
|
|
}
|
|
|
|
if (delta.x != 0 || delta.y != 0) {
|
|
const Vec2 mouse_sensitivity = m_app.getSettings().m_mouse_sensitivity;
|
|
Quat rot = m_viewport.rot;
|
|
|
|
float yaw = signum(delta.x) * (powf(fabsf(delta.x / 10000 * mouse_sensitivity.x), 1.2f));
|
|
Quat yaw_rot(Vec3(0, 1, 0), yaw);
|
|
rot = normalize(yaw_rot * rot);
|
|
|
|
Vec3 pitch_axis = rot.rotate(Vec3(1, 0, 0));
|
|
float pitch = signum(delta.y) * (powf(fabsf(delta.y / 10000 * mouse_sensitivity.y), 1.2f));
|
|
Quat pitch_rot(pitch_axis, pitch);
|
|
rot = normalize(pitch_rot * rot);
|
|
|
|
if (is_orbit) {
|
|
Vec3 dir = rot.rotate(Vec3(0, 0, 1));
|
|
Vec3 origin = Vec3::ZERO;
|
|
Model* model = render_module->getModelInstanceModel(*m_mesh);
|
|
if (model && model->isReady()) {
|
|
const AABB& aabb = model->getAABB();
|
|
origin = (aabb.min + aabb.max) * 0.5f;
|
|
}
|
|
const float dist = float(length(origin - Vec3(m_viewport.pos)));
|
|
m_viewport.pos = DVec3(origin + dir * dist);
|
|
}
|
|
|
|
m_viewport.rot = rot;
|
|
}
|
|
}
|
|
}
|
|
|
|
} |