Compare commits
No commits in common. "master" and "feature/serialization" have entirely different histories.
master
...
feature/se
11
.clangd
Normal file
11
.clangd
Normal file
|
@ -0,0 +1,11 @@
|
|||
CompileFlags: # Tweak the parse settings, example directory given to show format
|
||||
Add:
|
||||
- "--include-directory=../../src"
|
||||
- "--include-directory=../src"
|
||||
- "--include-directory=./editor_autogen/include"
|
||||
- "--include-directory=../.."
|
||||
- "--include-directory=/usr/include/qt6/QtWidgets"
|
||||
- "--include-directory=/usr/include/qt6/QtOpenGLWidgets"
|
||||
- "--include-directory=/usr/include/qt6"
|
||||
- "--include-directory=/usr/include/qt6/QtGui"
|
||||
- "--include-directory=/usr/include/qt6/QtCore"
|
22
.gitignore
vendored
22
.gitignore
vendored
|
@ -1,14 +1,12 @@
|
|||
/bin/
|
||||
/lib/
|
||||
/build/
|
||||
bin/
|
||||
lib/
|
||||
CMakeFiles/
|
||||
cmake_install.cmake
|
||||
CMakeCache.txt
|
||||
Makefile
|
||||
|
||||
# Qt
|
||||
/*.pro.user*
|
||||
/CMakeLists.txt.user*
|
||||
|
||||
# Clangd
|
||||
/compile_commands.json
|
||||
/.cache
|
||||
|
||||
# Gdb
|
||||
/.gdb_history
|
||||
.lupdate/
|
||||
*.pro.user*
|
||||
CMakeLists.txt.user*
|
||||
*_autogen/
|
|
@ -1,14 +1,50 @@
|
|||
cmake_minimum_required(VERSION 3.31..)
|
||||
cmake_minimum_required(VERSION 3.5.0)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
project(openblocks VERSION 0.1.0)
|
||||
set(OpenGL_GL_PREFERENCE "GLVND")
|
||||
|
||||
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin )
|
||||
set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
|
||||
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib )
|
||||
set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/lib )
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
SET(BASEPATH "${CMAKE_SOURCE_DIR}")
|
||||
include_directories("${BASEPATH}")
|
||||
include_directories("src")
|
||||
|
||||
add_subdirectory(core)
|
||||
add_subdirectory(client)
|
||||
add_subdirectory(editor)
|
||||
find_package(OpenGL REQUIRED COMPONENTS OpenGL)
|
||||
|
||||
find_package(SDL2 REQUIRED)
|
||||
include_directories(${SDL2_INCLUDE_DIRS})
|
||||
|
||||
find_package(GLEW REQUIRED)
|
||||
include_directories(${GLEW_INCLUDE_DIRS})
|
||||
|
||||
find_package(GLUT REQUIRED)
|
||||
include_directories(${GLUT_INCLUDE_DIRS})
|
||||
|
||||
find_package(glfw3 REQUIRED)
|
||||
find_package(OpenGL)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
find_package(assimp REQUIRED)
|
||||
find_package(ReactPhysics3D REQUIRED)
|
||||
find_package(pugixml REQUIRED)
|
||||
|
||||
# PkgConfig packages
|
||||
# find_package(PkgConfig REQUIRED)
|
||||
# pkg_check_modules(PUGIXML REQUIRED pugixml)
|
||||
|
||||
file(MAKE_DIRECTORY bin)
|
||||
|
||||
include_directories("include")
|
||||
|
||||
file(GLOB_RECURSE SOURCES "src/*.cpp" "src/*.h")
|
||||
add_library(openblocks ${SOURCES})
|
||||
set_target_properties(openblocks PROPERTIES OUTPUT_NAME "openblocks")
|
||||
target_link_libraries(openblocks ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} ${PUGIXML_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
|
||||
|
||||
# add_executable(client "client/src/main.cpp" $<TARGET_OBJECTS:openblocks>)
|
||||
# include_directories("src")
|
||||
# target_link_libraries(client ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D)
|
||||
|
||||
add_subdirectory("client")
|
||||
add_subdirectory("editor")
|
Binary file not shown.
Before Width: | Height: | Size: 868 B |
Binary file not shown.
Before Width: | Height: | Size: 836 B |
Binary file not shown.
Before Width: | Height: | Size: 865 B |
|
@ -1,109 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
// Implements the Phong lighting model with respect to materials and lighting materials
|
||||
|
||||
// Structs
|
||||
|
||||
struct Material {
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
float shininess;
|
||||
};
|
||||
|
||||
struct DirLight {
|
||||
vec3 direction;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
};
|
||||
|
||||
struct PointLight {
|
||||
vec3 position;
|
||||
// vec3 direction;
|
||||
vec3 ambient;
|
||||
vec3 diffuse;
|
||||
vec3 specular;
|
||||
|
||||
float constant;
|
||||
float linear;
|
||||
float quadratic;
|
||||
};
|
||||
|
||||
|
||||
// I/O
|
||||
|
||||
in vec3 vPos;
|
||||
in vec3 vNormal;
|
||||
in vec2 vTexCoords;
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
#define NR_POINT_LIGHTS 4
|
||||
|
||||
uniform vec3 viewPos;
|
||||
uniform PointLight pointLights[NR_POINT_LIGHTS];
|
||||
uniform int numPointLights;
|
||||
uniform DirLight sunLight;
|
||||
uniform Material material;
|
||||
|
||||
// Functions
|
||||
|
||||
vec3 calculateDirectionalLight(DirLight light);
|
||||
vec3 calculatePointLight(PointLight light);
|
||||
|
||||
|
||||
// Main
|
||||
|
||||
void main() {
|
||||
vec3 result = vec3(0.0);
|
||||
|
||||
result += calculateDirectionalLight(sunLight);
|
||||
|
||||
for (int i = 0; i < numPointLights; i++) {
|
||||
result += calculatePointLight(pointLights[i]);
|
||||
}
|
||||
|
||||
FragColor = vec4(result, 1);
|
||||
}
|
||||
|
||||
vec3 calculateDirectionalLight(DirLight light) {
|
||||
// Calculate diffuse
|
||||
vec3 norm = normalize(vNormal);
|
||||
vec3 lightDir = normalize(-light.direction);
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
|
||||
// Calculate specular
|
||||
vec3 viewDir = normalize(viewPos - vPos);
|
||||
vec3 reflectDir = reflect(-lightDir, norm);
|
||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
|
||||
|
||||
vec3 ambient = light.ambient * material.diffuse;
|
||||
vec3 diffuse = light.diffuse * diff * material.diffuse;
|
||||
vec3 specular = light.specular * spec * material.specular;
|
||||
|
||||
return (ambient + diffuse + specular);
|
||||
}
|
||||
|
||||
vec3 calculatePointLight(PointLight light) {
|
||||
// Calculate ambient light
|
||||
|
||||
// Calculate diffuse light
|
||||
vec3 norm = normalize(vNormal);
|
||||
vec3 lightDir = normalize(light.position - vPos);
|
||||
float diff = max(dot(norm, lightDir), 0.0);
|
||||
|
||||
// Calculate specular
|
||||
vec3 viewDir = normalize(viewPos - vPos);
|
||||
vec3 reflectDir = reflect(-lightDir, norm);
|
||||
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
|
||||
|
||||
// Calculate attenuation
|
||||
float distance = length(light.position - vPos);
|
||||
float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance));
|
||||
|
||||
vec3 ambient = light.ambient * material.diffuse;
|
||||
vec3 diffuse = light.diffuse * diff * material.diffuse;
|
||||
vec3 specular = light.specular * spec * material.specular;
|
||||
|
||||
return (ambient + diffuse + specular) * attenuation;
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
layout (location = 1) in vec3 aNormal;
|
||||
layout (location = 2) in vec2 aTexCoords;
|
||||
|
||||
out vec3 vPos;
|
||||
out vec3 vNormal;
|
||||
out vec2 vTexCoords;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat3 normalMatrix;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
|
||||
void main()
|
||||
{
|
||||
gl_Position = projection * view * model * vec4(aPos, 1.0);
|
||||
vPos = vec3(model * vec4(aPos, 1.0));
|
||||
vNormal = normalMatrix * aNormal;
|
||||
}
|
|
@ -1,13 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
// I/O
|
||||
|
||||
out vec4 FragColor;
|
||||
|
||||
uniform vec3 aColor;
|
||||
|
||||
// Main
|
||||
|
||||
void main() {
|
||||
FragColor = vec4(aColor, 1);
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#version 330 core
|
||||
layout (location = 0) in vec3 aPos;
|
||||
|
||||
out vec3 vPos;
|
||||
void main()
|
||||
{
|
||||
gl_Position = vec4(aPos, 1.0);
|
||||
}
|
|
@ -53,7 +53,6 @@ uniform int numPointLights;
|
|||
uniform DirLight sunLight;
|
||||
uniform Material material;
|
||||
uniform sampler2DArray studs;
|
||||
uniform float transparency;
|
||||
|
||||
// Functions
|
||||
|
||||
|
@ -73,7 +72,7 @@ void main() {
|
|||
}
|
||||
|
||||
vec4 studPx = texture(studs, vec3(vTexCoords, vSurfaceZ));
|
||||
FragColor = vec4(mix(result, vec3(studPx), studPx.w), 1) * (1-transparency);
|
||||
FragColor = vec4(mix(result, vec3(studPx), studPx.w), 1);
|
||||
}
|
||||
|
||||
vec3 calculateDirectionalLight(DirLight light) {
|
||||
|
|
|
@ -1,7 +1,4 @@
|
|||
find_package(SDL2 REQUIRED)
|
||||
include_directories(${SDL2_INCLUDE_DIRS})
|
||||
|
||||
find_package(glfw3 REQUIRED)
|
||||
|
||||
add_executable(client "src/main.cpp")
|
||||
target_link_libraries(client PRIVATE ${SDL2_LIBRARIES} openblocks glfw)
|
||||
add_executable(client "src/main.cpp" $<TARGET_OBJECTS:openblocks>)
|
||||
include_directories("../src")
|
||||
target_link_libraries(client ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
|
|
@ -1,15 +0,0 @@
|
|||
find_package(OpenGL REQUIRED COMPONENTS OpenGL)
|
||||
|
||||
find_package(GLEW REQUIRED)
|
||||
include_directories(${GLEW_INCLUDE_DIRS})
|
||||
|
||||
find_package(OpenGL)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
find_package(ReactPhysics3D REQUIRED)
|
||||
find_package(pugixml REQUIRED)
|
||||
|
||||
file(GLOB_RECURSE SOURCES "src/*.cpp" "src/*.h")
|
||||
add_library(openblocks ${SOURCES})
|
||||
set_target_properties(openblocks PROPERTIES OUTPUT_NAME "openblocks")
|
||||
target_link_libraries(openblocks ${GLEW_LIBRARIES} OpenGL::GL ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
|
||||
target_include_directories(openblocks PUBLIC "src" "../include")
|
|
@ -1,103 +0,0 @@
|
|||
#include "math_helper.h"
|
||||
|
||||
#define CMP_EPSILON 0.00001
|
||||
|
||||
// After a long time researching, I was able to use and adapt Godot's implementation of movable handles (godot/editor/plugins/gizmos/gizmo_3d_helper.cpp)
|
||||
// All thanks goes to them and David Eberly for his algorithm.
|
||||
|
||||
void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt) {
|
||||
// Based on David Eberly's Computation of Distance Between Line Segments algorithm.
|
||||
|
||||
glm::vec3 p = p_p1 - p_p0;
|
||||
glm::vec3 q = p_q1 - p_q0;
|
||||
glm::vec3 r = p_p0 - p_q0;
|
||||
|
||||
float a = glm::dot(p, p);
|
||||
float b = glm::dot(p, q);
|
||||
float c = glm::dot(q, q);
|
||||
float d = glm::dot(p, r);
|
||||
float e = glm::dot(q, r);
|
||||
|
||||
float s = 0.0f;
|
||||
float t = 0.0f;
|
||||
|
||||
float det = a * c - b * b;
|
||||
if (det > CMP_EPSILON) {
|
||||
// Non-parallel segments
|
||||
float bte = b * e;
|
||||
float ctd = c * d;
|
||||
|
||||
if (bte <= ctd) {
|
||||
// s <= 0.0f
|
||||
if (e <= 0.0f) {
|
||||
// t <= 0.0f
|
||||
s = (-d >= a ? 1 : (-d > 0.0f ? -d / a : 0.0f));
|
||||
t = 0.0f;
|
||||
} else if (e < c) {
|
||||
// 0.0f < t < 1
|
||||
s = 0.0f;
|
||||
t = e / c;
|
||||
} else {
|
||||
// t >= 1
|
||||
s = (b - d >= a ? 1 : (b - d > 0.0f ? (b - d) / a : 0.0f));
|
||||
t = 1;
|
||||
}
|
||||
} else {
|
||||
// s > 0.0f
|
||||
s = bte - ctd;
|
||||
if (s >= det) {
|
||||
// s >= 1
|
||||
if (b + e <= 0.0f) {
|
||||
// t <= 0.0f
|
||||
s = (-d <= 0.0f ? 0.0f : (-d < a ? -d / a : 1));
|
||||
t = 0.0f;
|
||||
} else if (b + e < c) {
|
||||
// 0.0f < t < 1
|
||||
s = 1;
|
||||
t = (b + e) / c;
|
||||
} else {
|
||||
// t >= 1
|
||||
s = (b - d <= 0.0f ? 0.0f : (b - d < a ? (b - d) / a : 1));
|
||||
t = 1;
|
||||
}
|
||||
} else {
|
||||
// 0.0f < s < 1
|
||||
float ate = a * e;
|
||||
float btd = b * d;
|
||||
|
||||
if (ate <= btd) {
|
||||
// t <= 0.0f
|
||||
s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a));
|
||||
t = 0.0f;
|
||||
} else {
|
||||
// t > 0.0f
|
||||
t = ate - btd;
|
||||
if (t >= det) {
|
||||
// t >= 1
|
||||
s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a));
|
||||
t = 1;
|
||||
} else {
|
||||
// 0.0f < t < 1
|
||||
s /= det;
|
||||
t /= det;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Parallel segments
|
||||
if (e <= 0.0f) {
|
||||
s = (-d <= 0.0f ? 0.0f : (-d >= a ? 1 : -d / a));
|
||||
t = 0.0f;
|
||||
} else if (e >= c) {
|
||||
s = (b - d <= 0.0f ? 0.0f : (b - d >= a ? 1 : (b - d) / a));
|
||||
t = 1;
|
||||
} else {
|
||||
s = 0.0f;
|
||||
t = e / c;
|
||||
}
|
||||
}
|
||||
|
||||
r_ps = (1 - s) * p_p0 + s * p_p1;
|
||||
r_qt = (1 - t) * p_q0 + t * p_q1;
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#pragma once
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
// From godot/editor/plugins/gizmos/gizmo_3d_helper.h
|
||||
void get_closest_points_between_segments(const glm::vec3 &p_p0, const glm::vec3 &p_p1, const glm::vec3 &p_q0, const glm::vec3 &p_q1, glm::vec3 &r_ps, glm::vec3 &r_qt);
|
|
@ -1,90 +0,0 @@
|
|||
#include "handles.h"
|
||||
#include "common.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "datatypes/vector.h"
|
||||
#include <optional>
|
||||
#include <reactphysics3d/collision/RaycastInfo.h>
|
||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||
#include <reactphysics3d/engine/PhysicsWorld.h>
|
||||
#include <reactphysics3d/mathematics/Transform.h>
|
||||
|
||||
HandleFace HandleFace::XPos(0, glm::vec3(1,0,0));
|
||||
HandleFace HandleFace::XNeg(1, glm::vec3(-1,0,0));
|
||||
HandleFace HandleFace::YPos(2, glm::vec3(0,1,0));
|
||||
HandleFace HandleFace::YNeg(3, glm::vec3(0,-1,0));
|
||||
HandleFace HandleFace::ZPos(4, glm::vec3(0,0,1));
|
||||
HandleFace HandleFace::ZNeg(5, glm::vec3(0,0,-1));
|
||||
std::array<HandleFace, 6> HandleFace::Faces { HandleFace::XPos, HandleFace::XNeg, HandleFace::YPos, HandleFace::YNeg, HandleFace::ZPos, HandleFace::ZNeg };
|
||||
|
||||
// Shitty solution
|
||||
static rp3d::PhysicsCommon common;
|
||||
static rp3d::PhysicsWorld* world = common.createPhysicsWorld();
|
||||
|
||||
const InstanceType Handles::TYPE = {
|
||||
.super = &Instance::TYPE,
|
||||
.className = "Handles",
|
||||
// .constructor = &Workspace::Create,
|
||||
// .explorerIcon = "",
|
||||
};
|
||||
|
||||
const InstanceType* Handles::GetClass() {
|
||||
return &TYPE;
|
||||
}
|
||||
|
||||
Handles::Handles(): Instance(&TYPE) {
|
||||
}
|
||||
|
||||
Data::CFrame Handles::GetCFrameOfHandle(HandleFace face) {
|
||||
if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0));
|
||||
|
||||
Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe;
|
||||
|
||||
// We don't want this to align with local * face.normal, or else we have problems.
|
||||
glm::vec3 upAxis(0, 0, 1);
|
||||
if (glm::abs(glm::dot(glm::vec3(localFrame.Rotation() * face.normal), upAxis)) > 0.9999f)
|
||||
upAxis = glm::vec3(0, 1, 0);
|
||||
|
||||
Data::Vector3 handlePos = localFrame * ((2.f + adornee->lock()->size * 0.5f) * face.normal);
|
||||
Data::CFrame cframe(handlePos, handlePos + localFrame.Rotation() * face.normal, upAxis);
|
||||
|
||||
return cframe;
|
||||
}
|
||||
|
||||
Data::CFrame Handles::PartCFrameFromHandlePos(HandleFace face, Data::Vector3 newPos) {
|
||||
if (!adornee.has_value() || adornee->expired()) return Data::CFrame(glm::vec3(0,0,0), (Data::Vector3)glm::vec3(0,0,0));
|
||||
|
||||
Data::CFrame localFrame = worldMode ? Data::CFrame::IDENTITY + adornee->lock()->position() : adornee->lock()->cframe;
|
||||
Data::CFrame inverseFrame = localFrame.Inverse();
|
||||
|
||||
Data::Vector3 handlePos = localFrame * ((2.f + adornee->lock()->size * 0.5f) * face.normal);
|
||||
|
||||
// glm::vec3 localPos = inverseFrame * newPos;
|
||||
glm::vec3 newPartPos = newPos - localFrame.Rotation() * ((2.f + adornee->lock()->size * 0.5f) * face.normal);
|
||||
return adornee->lock()->cframe.Rotation() + newPartPos;
|
||||
}
|
||||
|
||||
std::optional<HandleFace> Handles::RaycastHandle(rp3d::Ray ray) {
|
||||
for (HandleFace face : HandleFace::Faces) {
|
||||
Data::CFrame cframe = GetCFrameOfHandle(face);
|
||||
// Implement manual detection via boxes instead of... this shit
|
||||
// This code also hardly works, and is not good at all... Hooo nope.
|
||||
rp3d::RigidBody* body = world->createRigidBody(Data::CFrame::IDENTITY + cframe.Position());
|
||||
body->addCollider(common.createBoxShape(cframe.Rotation() * Data::Vector3(HandleSize(face) / 2.f)), rp3d::Transform::identity());
|
||||
|
||||
rp3d::RaycastInfo info;
|
||||
if (body->raycast(ray, info)) {
|
||||
world->destroyRigidBody(body);
|
||||
return face;
|
||||
}
|
||||
|
||||
world->destroyRigidBody(body);
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
Data::Vector3 Handles::HandleSize(HandleFace face) {
|
||||
if (handlesType == HandlesType::MoveHandles)
|
||||
return glm::vec3(0.5f, 0.5f, 2.f);
|
||||
return glm::vec3(1,1,1);
|
||||
}
|
|
@ -1,55 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
#include "datatypes/cframe.h"
|
||||
#include "objects/base/service.h"
|
||||
#include "objects/part.h"
|
||||
#include <array>
|
||||
#include <memory>
|
||||
#include <reactphysics3d/body/RigidBody.h>
|
||||
|
||||
class HandleFace {
|
||||
HandleFace(int index, glm::vec3 normal) : index(index), normal(normal){}
|
||||
|
||||
public:
|
||||
int index;
|
||||
glm::vec3 normal;
|
||||
|
||||
static HandleFace XPos;
|
||||
static HandleFace XNeg;
|
||||
static HandleFace YPos;
|
||||
static HandleFace YNeg;
|
||||
static HandleFace ZPos;
|
||||
static HandleFace ZNeg;
|
||||
static std::array<HandleFace, 6> Faces;
|
||||
};
|
||||
|
||||
enum HandlesType {
|
||||
MoveHandles,
|
||||
ScaleHandles,
|
||||
RotateHandles,
|
||||
};
|
||||
|
||||
class Handles : public Instance {
|
||||
public:
|
||||
const static InstanceType TYPE;
|
||||
|
||||
bool active;
|
||||
std::optional<std::weak_ptr<Part>> adornee;
|
||||
HandlesType handlesType;
|
||||
|
||||
// inline std::optional<std::weak_ptr<Part>> GetAdornee() { return adornee; }
|
||||
// inline void SetAdornee(std::optional<std::weak_ptr<Part>> newAdornee) { this->adornee = newAdornee; updateAdornee(); };
|
||||
|
||||
Handles();
|
||||
|
||||
// World-space handles vs local-space handles
|
||||
bool worldMode = false;
|
||||
Data::CFrame GetCFrameOfHandle(HandleFace face);
|
||||
Data::CFrame PartCFrameFromHandlePos(HandleFace face, Data::Vector3 newPos);
|
||||
Data::Vector3 HandleSize(HandleFace face);
|
||||
std::optional<HandleFace> RaycastHandle(rp3d::Ray ray);
|
||||
|
||||
static inline std::shared_ptr<Handles> New() { return std::make_shared<Handles>(); };
|
||||
virtual const InstanceType* GetClass() override;
|
||||
};
|
File diff suppressed because it is too large
Load diff
9
deps.txt
9
deps.txt
|
@ -1,9 +1,10 @@
|
|||
opengl (Linux: glvnd, Windows: [built-in/none])
|
||||
glfw
|
||||
glew
|
||||
glm
|
||||
opengl
|
||||
assimp
|
||||
sdl2
|
||||
stb
|
||||
glfw
|
||||
glut
|
||||
glew
|
||||
qt6
|
||||
reactphysics3d
|
||||
pugixml
|
|
@ -1,4 +1,4 @@
|
|||
cmake_minimum_required(VERSION 3.31..)
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(editor VERSION 0.1 LANGUAGES CXX)
|
||||
|
||||
|
@ -14,6 +14,8 @@ set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
|||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets LinguistTools)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets LinguistTools)
|
||||
|
||||
include_directories("../src")
|
||||
|
||||
set(TS_FILES editor_en_US.ts)
|
||||
|
||||
set(PROJECT_SOURCES
|
||||
|
@ -38,6 +40,7 @@ if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
|
|||
qt_add_executable(editor
|
||||
MANUAL_FINALIZATION
|
||||
${PROJECT_SOURCES}
|
||||
$<TARGET_OBJECTS:openblocks>
|
||||
)
|
||||
# Define target properties for Android with Qt 6 as:
|
||||
# set_property(TARGET editor APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
|
||||
|
@ -56,24 +59,14 @@ else()
|
|||
add_executable(editor
|
||||
${PROJECT_SOURCES}
|
||||
mainglwidget.h mainglwidget.cpp
|
||||
$<TARGET_OBJECTS:openblocks>
|
||||
)
|
||||
endif()
|
||||
|
||||
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
|
||||
endif()
|
||||
|
||||
target_include_directories(editor PUBLIC "../core/src" "../include")
|
||||
target_link_libraries(editor PRIVATE openblocks Qt${QT_VERSION_MAJOR}::Widgets)
|
||||
|
||||
# Qt6 does not include QOpenGLWidgets as part of Widgets base anymore, so
|
||||
# we have to include it manually
|
||||
if (${QT_VERSION} GREATER_EQUAL 6)
|
||||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS OpenGL OpenGLWidgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS OpenGL OpenGLWidgets)
|
||||
|
||||
target_include_directories(editor PUBLIC Qt6::OpenGL Qt6::OpenGLWidgets)
|
||||
target_link_libraries(editor PRIVATE Qt6::OpenGL Qt6::OpenGLWidgets)
|
||||
endif()
|
||||
target_link_libraries(editor PRIVATE Qt${QT_VERSION_MAJOR}::Widgets ${SDL2_LIBRARIES} ${GLEW_LIBRARIES} ${GLUT_LIBRARIES} OpenGL::GL OpenGL::GLU glfw glm::glm assimp ReactPhysics3D::ReactPhysics3D pugixml::pugixml)
|
||||
|
||||
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||
# If you are developing for iOS or macOS you should consider setting an
|
||||
|
@ -96,8 +89,6 @@ install(TARGETS editor
|
|||
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||
)
|
||||
|
||||
install(FILES $<TARGET_RUNTIME_DLLS:editor> TYPE BIN)
|
||||
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
qt_finalize_executable(editor)
|
||||
endif()
|
|
@ -1 +1,10 @@
|
|||
#pragma once
|
||||
#pragma once
|
||||
|
||||
enum SelectedTool {
|
||||
SELECT,
|
||||
MOVE,
|
||||
SCALE,
|
||||
ROTATE,
|
||||
};
|
||||
|
||||
extern SelectedTool selectedTool;
|
|
@ -2,22 +2,15 @@
|
|||
#include <chrono>
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <glm/common.hpp>
|
||||
#include <glm/ext/matrix_projection.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <glm/geometric.hpp>
|
||||
#include <glm/gtc/round.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <reactphysics3d/collision/RaycastInfo.h>
|
||||
#include <vector>
|
||||
|
||||
#include "datatypes/cframe.h"
|
||||
#include "editorcommon.h"
|
||||
#include "mainwindow.h"
|
||||
#include "objects/handles.h"
|
||||
#include "physics/util.h"
|
||||
#include "qcursor.h"
|
||||
#include "qevent.h"
|
||||
|
@ -28,11 +21,8 @@
|
|||
#include "camera.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "rendering/shader.h"
|
||||
|
||||
#include "mainglwidget.h"
|
||||
#include "../core/src/rendering/defaultmeshes.h"
|
||||
#include "math_helper.h"
|
||||
|
||||
MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent) {
|
||||
setFocusPolicy(Qt::FocusPolicy::ClickFocus);
|
||||
|
@ -55,12 +45,6 @@ void MainGLWidget::resizeGL(int w, int h) {
|
|||
setViewport(w, h);
|
||||
}
|
||||
|
||||
glm::vec2 firstPoint;
|
||||
glm::vec2 secondPoint;
|
||||
|
||||
extern std::optional<std::weak_ptr<Part>> draggingObject;
|
||||
extern std::optional<HandleFace> draggingHandle;
|
||||
extern Shader* shader;
|
||||
void MainGLWidget::paintGL() {
|
||||
::render(NULL);
|
||||
}
|
||||
|
@ -78,9 +62,8 @@ void MainGLWidget::handleCameraRotate(QMouseEvent* evt) {
|
|||
|
||||
bool isMouseDragging = false;
|
||||
std::optional<std::weak_ptr<Part>> draggingObject;
|
||||
std::optional<HandleFace> draggingHandle;
|
||||
void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
||||
if (!isMouseDragging || !draggingObject) return;
|
||||
if (!isMouseDragging) return;
|
||||
|
||||
QPoint position = evt->pos();
|
||||
|
||||
|
@ -96,109 +79,17 @@ void MainGLWidget::handleObjectDrag(QMouseEvent* evt) {
|
|||
syncPartPhysics(draggingObject->lock());
|
||||
}
|
||||
|
||||
inline glm::vec3 vec3fy(glm::vec4 vec) {
|
||||
return vec / vec.w;
|
||||
}
|
||||
|
||||
QPoint lastPoint;
|
||||
void MainGLWidget::handleHandleDrag(QMouseEvent* evt) {
|
||||
QPoint cLastPoint = lastPoint;
|
||||
lastPoint = evt->pos();
|
||||
|
||||
if (!isMouseDragging || !draggingHandle || !editorToolHandles->adornee || !editorToolHandles->active) return;
|
||||
|
||||
QPoint position = evt->pos();
|
||||
|
||||
auto part = editorToolHandles->adornee->lock();
|
||||
|
||||
// This was actually quite a difficult problem to solve, managing to get the handle to go underneath the cursor
|
||||
|
||||
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
|
||||
pointDir = glm::normalize(pointDir);
|
||||
|
||||
Data::CFrame handleCFrame = editorToolHandles->GetCFrameOfHandle(draggingHandle.value());
|
||||
|
||||
// Current frame. Identity frame if worldMode == true, selected object's frame if worldMode == false
|
||||
Data::CFrame frame = editorToolHandles->worldMode ? Data::CFrame::IDENTITY + part->position() : part->cframe.Rotation();
|
||||
|
||||
// Segment from axis stretching -4096 to +4096 rel to handle's position
|
||||
glm::vec3 axisSegment0 = handleCFrame.Position() + (-handleCFrame.LookVector() * 4096.0f);
|
||||
glm::vec3 axisSegment1 = handleCFrame.Position() + (-handleCFrame.LookVector() * -4096.0f);
|
||||
|
||||
// Segment from camera stretching 4096 forward
|
||||
glm::vec3 mouseSegment0 = camera.cameraPos;
|
||||
glm::vec3 mouseSegment1 = camera.cameraPos + pointDir * 4096.0f;
|
||||
|
||||
// Closest point on the axis segment between the two segments
|
||||
glm::vec3 handlePoint, rb;
|
||||
get_closest_points_between_segments(axisSegment0, axisSegment1, mouseSegment0, mouseSegment1, handlePoint, rb);
|
||||
|
||||
// Find new part position
|
||||
glm::vec3 centerPoint = editorToolHandles->PartCFrameFromHandlePos(draggingHandle.value(), handlePoint).Position();
|
||||
|
||||
// Apply snapping in the current frame
|
||||
glm::vec3 diff = centerPoint - (glm::vec3)editorToolHandles->adornee->lock()->position();
|
||||
// printf("\n=======\nPre-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z);
|
||||
if (snappingFactor()) diff = frame.Rotation() * (glm::round(glm::vec3(frame.Inverse().Rotation() * diff) / snappingFactor()) * snappingFactor());
|
||||
// printf("Post-snap: (%f, %f, %f)\n", diff.x, diff.y, diff.z);
|
||||
|
||||
switch (mainWindow()->selectedTool) {
|
||||
case SelectedTool::SELECT: break;
|
||||
case SelectedTool::MOVE: {
|
||||
// Add difference
|
||||
editorToolHandles->adornee->lock()->cframe = editorToolHandles->adornee->lock()->cframe + diff;
|
||||
} break;
|
||||
|
||||
case SelectedTool::SCALE: {
|
||||
// Find local difference
|
||||
glm::vec3 localDiff = frame.Inverse() * diff;
|
||||
// Find outwarwd difference
|
||||
localDiff = localDiff * glm::sign(draggingHandle->normal);
|
||||
|
||||
// Add local difference to size
|
||||
part->size += localDiff;
|
||||
|
||||
// If ctrl is not pressed, offset the part by half the size difference to keep the other bound where it was originally
|
||||
if (!(evt->modifiers() & Qt::ControlModifier))
|
||||
part->cframe = part->cframe + diff * 0.5f;
|
||||
} break;
|
||||
|
||||
case SelectedTool::ROTATE: {
|
||||
// TODO: Implement rotation
|
||||
} break;
|
||||
}
|
||||
|
||||
syncPartPhysics(std::dynamic_pointer_cast<Part>(editorToolHandles->adornee->lock()));
|
||||
}
|
||||
|
||||
std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
|
||||
if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return std::nullopt;
|
||||
return editorToolHandles->RaycastHandle(rp3d::Ray(glmToRp(camera.cameraPos), glmToRp(glm::normalize(pointDir)) * 50000));
|
||||
}
|
||||
|
||||
void MainGLWidget::handleCursorChange(QMouseEvent* evt) {
|
||||
QPoint position = evt->pos();
|
||||
|
||||
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
|
||||
|
||||
if (raycastHandle(pointDir)) {
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
return;
|
||||
};
|
||||
|
||||
std::optional<const RaycastResult> rayHit = castRayNearest(camera.cameraPos, pointDir, 50000);
|
||||
if (rayHit && partFromBody(rayHit->body)->name != "Baseplate") {
|
||||
setCursor(Qt::OpenHandCursor);
|
||||
return;
|
||||
}
|
||||
|
||||
setCursor(Qt::ArrowCursor);
|
||||
setCursor((rayHit && partFromBody(rayHit->body)->name != "Baseplate") ? Qt::OpenHandCursor : Qt::ArrowCursor);
|
||||
}
|
||||
|
||||
void MainGLWidget::mouseMoveEvent(QMouseEvent* evt) {
|
||||
handleCameraRotate(evt);
|
||||
handleObjectDrag(evt);
|
||||
handleHandleDrag(evt);
|
||||
handleCursorChange(evt);
|
||||
}
|
||||
|
||||
|
@ -214,15 +105,6 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
|
|||
QPoint position = evt->pos();
|
||||
|
||||
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
|
||||
// raycast handles
|
||||
auto handle = raycastHandle(pointDir);
|
||||
if (handle.has_value()) {
|
||||
isMouseDragging = true;
|
||||
draggingHandle = handle;
|
||||
return;
|
||||
}
|
||||
|
||||
// raycast part
|
||||
std::optional<const RaycastResult> rayHit = castRayNearest(camera.cameraPos, pointDir, 50000);
|
||||
if (!rayHit || !partFromBody(rayHit->body)) return;
|
||||
std::shared_ptr<Part> part = partFromBody(rayHit->body);
|
||||
|
@ -246,7 +128,6 @@ void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) {
|
|||
isMouseRightDragging = false;
|
||||
isMouseDragging = false;
|
||||
draggingObject = std::nullopt;
|
||||
draggingHandle = std::nullopt;
|
||||
}
|
||||
|
||||
static int moveZ = 0;
|
||||
|
@ -285,17 +166,4 @@ void MainGLWidget::keyPressEvent(QKeyEvent* evt) {
|
|||
void MainGLWidget::keyReleaseEvent(QKeyEvent* evt) {
|
||||
if (evt->key() == Qt::Key_W || evt->key() == Qt::Key_S) moveZ = 0;
|
||||
else if (evt->key() == Qt::Key_A || evt->key() == Qt::Key_D) moveX = 0;
|
||||
}
|
||||
|
||||
MainWindow* MainGLWidget::mainWindow() {
|
||||
return dynamic_cast<MainWindow*>(window());
|
||||
}
|
||||
|
||||
float MainGLWidget::snappingFactor() {
|
||||
switch (mainWindow()->snappingMode) {
|
||||
case GridSnappingMode::SNAP_1_STUD: return 1;
|
||||
case GridSnappingMode::SNAP_05_STUDS: return 0.5;
|
||||
case GridSnappingMode::SNAP_OFF: return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -1,15 +1,12 @@
|
|||
#ifndef MAINGLWIDGET_H
|
||||
#define MAINGLWIDGET_H
|
||||
|
||||
#include "mainwindow.h"
|
||||
#include "objects/part.h"
|
||||
#include "qevent.h"
|
||||
#include <QOpenGLWidget>
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
class HandleFace;
|
||||
|
||||
class MainGLWidget : public QOpenGLWidget {
|
||||
public:
|
||||
MainGLWidget(QWidget *parent = nullptr);
|
||||
|
@ -23,18 +20,13 @@ protected:
|
|||
|
||||
void handleCameraRotate(QMouseEvent* evt);
|
||||
void handleObjectDrag(QMouseEvent* evt);
|
||||
void handleHandleDrag(QMouseEvent* evt);
|
||||
void handleCursorChange(QMouseEvent* evt);
|
||||
std::optional<HandleFace> raycastHandle(glm::vec3 pointDir);
|
||||
|
||||
void mouseMoveEvent(QMouseEvent* evt) override;
|
||||
void mousePressEvent(QMouseEvent* evt) override;
|
||||
void mouseReleaseEvent(QMouseEvent* evt) override;
|
||||
void keyPressEvent(QKeyEvent* evt) override;
|
||||
void keyReleaseEvent(QKeyEvent* evt) override;
|
||||
|
||||
MainWindow* mainWindow();
|
||||
float snappingFactor();
|
||||
};
|
||||
|
||||
#endif // MAINGLWIDGET_H
|
||||
|
|
|
@ -10,25 +10,21 @@
|
|||
#include <QWidget>
|
||||
#include <QTreeView>
|
||||
#include <QAbstractItemView>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <qglobal.h>
|
||||
#include <qwindowdefs.h>
|
||||
#include <sstream>
|
||||
|
||||
#include "common.h"
|
||||
#include "editorcommon.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/datamodel.h"
|
||||
#include "objects/handles.h"
|
||||
#include "physics/simulation.h"
|
||||
#include "objects/part.h"
|
||||
#include "qfiledialog.h"
|
||||
#include "qclipboard.h"
|
||||
#include "qmimedata.h"
|
||||
#include "qitemselectionmodel.h"
|
||||
#include "qobject.h"
|
||||
#include "qsysinfo.h"
|
||||
|
||||
SelectedTool selectedTool;
|
||||
|
||||
bool simulationPlaying = false;
|
||||
|
||||
MainWindow::MainWindow(QWidget *parent)
|
||||
|
@ -41,20 +37,13 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
timer.start(33, this);
|
||||
setMouseTracking(true);
|
||||
|
||||
ui->explorerView->buildContextMenu();
|
||||
ConnectSelectionChangeHandler();
|
||||
|
||||
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = SelectedTool::SELECT; updateToolbars(); });
|
||||
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::MOVE : SelectedTool::SELECT; updateToolbars(); });
|
||||
connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::SCALE : SelectedTool::SELECT; updateToolbars(); });
|
||||
connect(ui->actionToolRotate, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::ROTATE : SelectedTool::SELECT; updateToolbars(); });
|
||||
connect(ui->actionToolSelect, &QAction::triggered, this, [&]() { selectedTool = SelectedTool::SELECT; updateSelectedTool(); });
|
||||
connect(ui->actionToolMove, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::MOVE : SelectedTool::SELECT; updateSelectedTool(); });
|
||||
connect(ui->actionToolScale, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::SCALE : SelectedTool::SELECT; updateSelectedTool(); });
|
||||
connect(ui->actionToolRotate, &QAction::triggered, this, [&](bool state) { selectedTool = state ? SelectedTool::ROTATE : SelectedTool::SELECT; updateSelectedTool(); });
|
||||
ui->actionToolSelect->setChecked(true);
|
||||
selectedTool = SelectedTool::SELECT;
|
||||
|
||||
connect(ui->actionGridSnap1, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_1_STUD; updateToolbars(); });
|
||||
connect(ui->actionGridSnap05, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_05_STUDS; updateToolbars(); });
|
||||
connect(ui->actionGridSnapOff, &QAction::triggered, this, [&]() { snappingMode = GridSnappingMode::SNAP_OFF; updateToolbars(); });
|
||||
ui->actionGridSnap1->setChecked(true);
|
||||
snappingMode = GridSnappingMode::SNAP_1_STUD;
|
||||
|
||||
connect(ui->actionToggleSimulation, &QAction::triggered, this, [&]() {
|
||||
simulationPlaying = !simulationPlaying;
|
||||
|
@ -72,144 +61,23 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
connect(ui->actionSave, &QAction::triggered, this, [&]() {
|
||||
std::optional<std::string> path;
|
||||
if (!dataModel->HasFile())
|
||||
path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptSave, QString::fromStdString("Save " + dataModel->name));
|
||||
path = QFileDialog::getSaveFileName(this, QString::fromStdString("Save " + dataModel->name), "", "*.obl").toStdString();
|
||||
if (path == "") return;
|
||||
|
||||
dataModel->SaveToFile(path);
|
||||
});
|
||||
|
||||
connect(ui->actionOpen, &QAction::triggered, this, [&]() {
|
||||
std::optional<std::string> path = openFileDialog("Openblocks Level (*.obl)", ".obl", QFileDialog::AcceptOpen);
|
||||
if (!path) return;
|
||||
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path.value());
|
||||
std::string path = QFileDialog::getOpenFileName(this, "Load file", "", "*.obl").toStdString();
|
||||
if (path == "") return;
|
||||
std::shared_ptr<DataModel> newModel = DataModel::LoadFromFile(path);
|
||||
dataModel = newModel;
|
||||
ui->explorerView->updateRoot(newModel);
|
||||
});
|
||||
|
||||
connect(ui->actionDelete, &QAction::triggered, this, [&]() {
|
||||
for (InstanceRefWeak inst : getSelection()) {
|
||||
if (inst.expired()) continue;
|
||||
inst.lock()->SetParent(std::nullopt);
|
||||
}
|
||||
setSelection(std::vector<InstanceRefWeak> {});
|
||||
});
|
||||
|
||||
connect(ui->actionCopy, &QAction::triggered, this, [&]() {
|
||||
pugi::xml_document rootDoc;
|
||||
for (InstanceRefWeak inst : getSelection()) {
|
||||
if (inst.expired()) continue;
|
||||
inst.lock()->Serialize(&rootDoc);
|
||||
}
|
||||
|
||||
std::ostringstream encoded;
|
||||
rootDoc.save(encoded);
|
||||
|
||||
QMimeData* mimeData = new QMimeData;
|
||||
mimeData->setData("application/xml", QByteArray::fromStdString(encoded.str()));
|
||||
QApplication::clipboard()->setMimeData(mimeData);
|
||||
});
|
||||
connect(ui->actionCut, &QAction::triggered, this, [&]() {
|
||||
pugi::xml_document rootDoc;
|
||||
for (InstanceRefWeak inst : getSelection()) {
|
||||
if (inst.expired()) continue;
|
||||
inst.lock()->Serialize(&rootDoc);
|
||||
inst.lock()->SetParent(std::nullopt);
|
||||
}
|
||||
|
||||
std::ostringstream encoded;
|
||||
rootDoc.save(encoded);
|
||||
|
||||
QMimeData* mimeData = new QMimeData;
|
||||
mimeData->setData("application/xml", QByteArray::fromStdString(encoded.str()));
|
||||
QApplication::clipboard()->setMimeData(mimeData);
|
||||
});
|
||||
|
||||
connect(ui->actionPaste, &QAction::triggered, this, [&]() {
|
||||
const QMimeData* mimeData = QApplication::clipboard()->mimeData();
|
||||
if (!mimeData || !mimeData->hasFormat("application/xml")) return;
|
||||
QByteArray bytes = mimeData->data("application/xml");
|
||||
std::string encoded = bytes.toStdString();
|
||||
|
||||
pugi::xml_document rootDoc;
|
||||
rootDoc.load_string(encoded.c_str());
|
||||
|
||||
for (pugi::xml_node instNode : rootDoc.children()) {
|
||||
InstanceRef inst = Instance::Deserialize(&instNode);
|
||||
workspace()->AddChild(inst);
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->actionPasteInto, &QAction::triggered, this, [&]() {
|
||||
if (getSelection().size() != 1 || getSelection()[0].expired()) return;
|
||||
|
||||
InstanceRef selectedParent = getSelection()[0].lock();
|
||||
|
||||
const QMimeData* mimeData = QApplication::clipboard()->mimeData();
|
||||
if (!mimeData || !mimeData->hasFormat("application/xml")) return;
|
||||
QByteArray bytes = mimeData->data("application/xml");
|
||||
std::string encoded = bytes.toStdString();
|
||||
|
||||
pugi::xml_document rootDoc;
|
||||
rootDoc.load_string(encoded.c_str());
|
||||
|
||||
for (pugi::xml_node instNode : rootDoc.children()) {
|
||||
InstanceRef inst = Instance::Deserialize(&instNode);
|
||||
selectedParent->AddChild(inst);
|
||||
}
|
||||
});
|
||||
|
||||
connect(ui->actionSaveModel, &QAction::triggered, this, [&]() {
|
||||
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptSave);
|
||||
if (!path) return;
|
||||
std::ofstream outStream(path.value());
|
||||
|
||||
// Serialized XML for exporting
|
||||
pugi::xml_document modelDoc;
|
||||
pugi::xml_node modelRoot = modelDoc.append_child("openblocks");
|
||||
|
||||
for (InstanceRefWeak inst : getSelection()) {
|
||||
if (inst.expired()) continue;
|
||||
inst.lock()->Serialize(&modelRoot);
|
||||
}
|
||||
|
||||
modelDoc.save(outStream);
|
||||
});
|
||||
|
||||
connect(ui->actionInsertModel, &QAction::triggered, this, [&]() {
|
||||
if (getSelection().size() != 1 || getSelection()[0].expired()) return;
|
||||
InstanceRef selectedParent = getSelection()[0].lock();
|
||||
|
||||
std::optional<std::string> path = openFileDialog("Openblocks Model (*.obm)", ".obm", QFileDialog::AcceptOpen);
|
||||
if (!path) return;
|
||||
std::ifstream inStream(path.value());
|
||||
|
||||
pugi::xml_document modelDoc;
|
||||
modelDoc.load(inStream);
|
||||
|
||||
for (pugi::xml_node instNode : modelDoc.child("openblocks").children("Item")) {
|
||||
InstanceRef inst = Instance::Deserialize(&instNode);
|
||||
selectedParent->AddChild(inst);
|
||||
}
|
||||
delete ui->explorerView->selectionModel();
|
||||
ui->explorerView->reset();
|
||||
ui->explorerView->setModel(new ExplorerModel(dataModel));
|
||||
ConnectSelectionChangeHandler();
|
||||
});
|
||||
|
||||
// Update handles
|
||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||
editorToolHandles->adornee = std::nullopt;
|
||||
if (newSelection.size() == 0) return;
|
||||
InstanceRef inst = newSelection[0].lock();
|
||||
if (inst->GetClass() != &Part::TYPE) return;
|
||||
|
||||
editorToolHandles->adornee = std::dynamic_pointer_cast<Part>(inst);
|
||||
});
|
||||
|
||||
// Update properties
|
||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||
if (newSelection.size() == 0) return;
|
||||
if (newSelection.size() > 1)
|
||||
ui->propertiesView->setSelected(std::nullopt);
|
||||
ui->propertiesView->setSelected(newSelection[0].lock());
|
||||
});
|
||||
|
||||
// ui->explorerView->Init(ui);
|
||||
|
||||
simulationInit();
|
||||
|
@ -227,13 +95,24 @@ MainWindow::MainWindow(QWidget *parent)
|
|||
|
||||
workspace()->AddChild(ui->mainWidget->lastPart = Part::New({
|
||||
.position = glm::vec3(0),
|
||||
.rotation = glm::vec3(0.5, 2, 1),
|
||||
.rotation = glm::vec3(0),
|
||||
.size = glm::vec3(4, 1.2, 2),
|
||||
.color = glm::vec3(0.639216f, 0.635294f, 0.647059f),
|
||||
}));
|
||||
syncPartPhysics(ui->mainWidget->lastPart);
|
||||
}
|
||||
|
||||
void MainWindow::ConnectSelectionChangeHandler() {
|
||||
connect(ui->explorerView->selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) {
|
||||
if (selected.count() == 0) return;
|
||||
|
||||
std::optional<InstanceRef> inst = selected.count() == 0 ? std::nullopt
|
||||
: std::make_optional(((Instance*)selected.indexes()[0].internalPointer())->shared_from_this());
|
||||
|
||||
ui->propertiesView->setSelected(inst);
|
||||
});
|
||||
}
|
||||
|
||||
static std::chrono::time_point lastTime = std::chrono::steady_clock::now();
|
||||
void MainWindow::timerEvent(QTimerEvent* evt) {
|
||||
if (evt->timerId() != timer.timerId()) {
|
||||
|
@ -250,37 +129,11 @@ void MainWindow::timerEvent(QTimerEvent* evt) {
|
|||
ui->mainWidget->updateCycle();
|
||||
}
|
||||
|
||||
void MainWindow::updateToolbars() {
|
||||
void MainWindow::updateSelectedTool() {
|
||||
ui->actionToolSelect->setChecked(selectedTool == SelectedTool::SELECT);
|
||||
ui->actionToolMove->setChecked(selectedTool == SelectedTool::MOVE);
|
||||
ui->actionToolScale->setChecked(selectedTool == SelectedTool::SCALE);
|
||||
ui->actionToolRotate->setChecked(selectedTool == SelectedTool::ROTATE);
|
||||
|
||||
ui->actionGridSnap1->setChecked(snappingMode == GridSnappingMode::SNAP_1_STUD);
|
||||
ui->actionGridSnap05->setChecked(snappingMode == GridSnappingMode::SNAP_05_STUDS);
|
||||
ui->actionGridSnapOff->setChecked(snappingMode == GridSnappingMode::SNAP_OFF);
|
||||
|
||||
// editorToolHandles->worldMode = false;
|
||||
if (selectedTool == SelectedTool::MOVE) editorToolHandles->worldMode = true;
|
||||
if (selectedTool == SelectedTool::SCALE) editorToolHandles->worldMode = false;
|
||||
editorToolHandles->active = selectedTool != SelectedTool::SELECT;
|
||||
editorToolHandles->handlesType =
|
||||
selectedTool == SelectedTool::MOVE ? HandlesType::MoveHandles
|
||||
: selectedTool == SelectedTool::SCALE ? HandlesType::ScaleHandles
|
||||
: selectedTool == SelectedTool::ROTATE ? HandlesType::RotateHandles
|
||||
: HandlesType::ScaleHandles;
|
||||
}
|
||||
|
||||
std::optional<std::string> MainWindow::openFileDialog(QString filter, QString defaultExtension, QFileDialog::AcceptMode acceptMode, QString title) {
|
||||
QFileDialog dialog(this);
|
||||
if (title != "") dialog.setWindowTitle(title);
|
||||
dialog.setNameFilters(QStringList { filter, "All Files (*)" });
|
||||
dialog.setDefaultSuffix(defaultExtension);
|
||||
dialog.setAcceptMode(acceptMode);
|
||||
if (!dialog.exec())
|
||||
return std::nullopt;
|
||||
|
||||
return dialog.selectedFiles().front().toStdString();
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
|
|
|
@ -7,20 +7,6 @@
|
|||
#include "qmenu.h"
|
||||
#include <QMainWindow>
|
||||
#include <QLineEdit>
|
||||
#include <qfiledialog.h>
|
||||
|
||||
enum SelectedTool {
|
||||
SELECT,
|
||||
MOVE,
|
||||
SCALE,
|
||||
ROTATE,
|
||||
};
|
||||
|
||||
enum GridSnappingMode {
|
||||
SNAP_1_STUD,
|
||||
SNAP_05_STUDS,
|
||||
SNAP_OFF,
|
||||
};
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
namespace Ui {
|
||||
|
@ -36,16 +22,12 @@ public:
|
|||
MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
SelectedTool selectedTool;
|
||||
GridSnappingMode snappingMode;
|
||||
|
||||
Ui::MainWindow *ui;
|
||||
private:
|
||||
QBasicTimer timer;
|
||||
|
||||
void updateToolbars();
|
||||
void updateSelectedTool();
|
||||
void timerEvent(QTimerEvent*) override;
|
||||
|
||||
std::optional<std::string> openFileDialog(QString filter, QString defaultExtension, QFileDialog::AcceptMode acceptMode, QString title = "");
|
||||
void ConnectSelectionChangeHandler();
|
||||
};
|
||||
#endif // MAINWINDOW_H
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>1027</width>
|
||||
<height>30</height>
|
||||
<height>29</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menuFile">
|
||||
|
@ -115,16 +115,6 @@
|
|||
<addaction name="actionToolScale"/>
|
||||
<addaction name="actionToolRotate"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionDelete"/>
|
||||
<addaction name="actionCopy"/>
|
||||
<addaction name="actionCut"/>
|
||||
<addaction name="actionPaste"/>
|
||||
<addaction name="actionPasteInto"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionGridSnap1"/>
|
||||
<addaction name="actionGridSnap05"/>
|
||||
<addaction name="actionGridSnapOff"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="actionToggleSimulation"/>
|
||||
</widget>
|
||||
<action name="actionAddPart">
|
||||
|
@ -267,167 +257,6 @@
|
|||
<string>F5</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGridSnap1">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>assets/icons/editor/snap1.png</normaloff>assets/icons/editor/snap1.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1-Stud Snapping</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set grid snapping to 1 stud</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGridSnap05">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>assets/icons/editor/snap05.png</normaloff>assets/icons/editor/snap05.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>1/2-Stud Snapping</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Set grid snapping to 1/2 studs</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionGridSnapOff">
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset>
|
||||
<normaloff>assets/icons/editor/snapoff.png</normaloff>assets/icons/editor/snapoff.png</iconset>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>No Grid Snapping</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Turn grid snapping off</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCopy">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-copy"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Copy</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy objects to clipboard</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+C</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionCut">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-cut"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Cut</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Cut objects into clipboard</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+X</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPaste">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-paste"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Paste</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Paste objects from clipboard</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+V</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionPasteInto">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-paste"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Paste Into</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Paste objects from clipboard into selected object</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Ctrl+Shift+V</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionDelete">
|
||||
<property name="icon">
|
||||
<iconset theme="edit-delete"/>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Delete Object</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Delete selected objects</string>
|
||||
</property>
|
||||
<property name="shortcut">
|
||||
<string>Del</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionSaveModel">
|
||||
<property name="text">
|
||||
<string>Save Model to File</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Saves objects to file as XML model</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
<action name="actionInsertModel">
|
||||
<property name="text">
|
||||
<string>Insert Model from File</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Insert model from XML file</string>
|
||||
</property>
|
||||
<property name="menuRole">
|
||||
<enum>QAction::MenuRole::NoRole</enum>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
|
|
@ -234,12 +234,6 @@ bool ExplorerModel::dropMimeData(const QMimeData *data, Qt::DropAction action, i
|
|||
return true;
|
||||
}
|
||||
|
||||
void ExplorerModel::updateRoot(InstanceRef newRoot) {
|
||||
beginResetModel();
|
||||
rootItem = newRoot;
|
||||
endResetModel();
|
||||
}
|
||||
|
||||
QMimeData* ExplorerModel::mimeData(const QModelIndexList& indexes) const {
|
||||
// application/x-openblocks-instance-pointers
|
||||
DragDropSlot* slot = new DragDropSlot();
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
#pragma once
|
||||
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/part.h"
|
||||
#include "qabstractitemmodel.h"
|
||||
#include "qevent.h"
|
||||
#include "qmenu.h"
|
||||
#include "qnamespace.h"
|
||||
#include "qtreeview.h"
|
||||
#include <QOpenGLWidget>
|
||||
#include <QWidget>
|
||||
#include <memory>
|
||||
|
||||
// #ifndef EXPLORERMODEL_H
|
||||
// #define EXPLORERMODEL_H
|
||||
|
||||
class ExplorerModel : public QAbstractItemModel {
|
||||
Q_OBJECT
|
||||
|
@ -36,8 +42,6 @@ public:
|
|||
Qt::DropActions supportedDropActions() const override;
|
||||
InstanceRef fromIndex(const QModelIndex index) const;
|
||||
QModelIndex ObjectToIndex(InstanceRef item);
|
||||
|
||||
void updateRoot(InstanceRef newRoot);
|
||||
private:
|
||||
InstanceRef rootItem;
|
||||
QModelIndex toIndex(InstanceRef item);
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
#include "explorerview.h"
|
||||
#include "explorermodel.h"
|
||||
#include "mainwindow.h"
|
||||
#include "../ui_mainwindow.h"
|
||||
#include "common.h"
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "qabstractitemmodel.h"
|
||||
#include <qaction.h>
|
||||
#include <qnamespace.h>
|
||||
#include <qitemselectionmodel.h>
|
||||
|
||||
#define M_mainWindow dynamic_cast<MainWindow*>(window())
|
||||
#include "qaction.h"
|
||||
#include "qnamespace.h"
|
||||
|
||||
ExplorerView::ExplorerView(QWidget* parent):
|
||||
QTreeView(parent),
|
||||
|
@ -36,23 +32,7 @@ ExplorerView::ExplorerView(QWidget* parent):
|
|||
contextMenu.exec(this->viewport()->mapToGlobal(point));
|
||||
});
|
||||
|
||||
connect(selectionModel(), &QItemSelectionModel::selectionChanged, this, [&](const QItemSelection &selected, const QItemSelection &deselected) {
|
||||
std::vector<InstanceRefWeak> selectedInstances;
|
||||
selectedInstances.reserve(selected.count()); // This doesn't reserve everything, but should enhance things anyway
|
||||
|
||||
for (auto range : selected) {
|
||||
for (auto index : range.indexes()) {
|
||||
selectedInstances.push_back(reinterpret_cast<Instance*>(index.internalPointer())->weak_from_this());
|
||||
}
|
||||
}
|
||||
|
||||
::setSelection(selectedInstances, true);
|
||||
});
|
||||
|
||||
addSelectionListener([&](auto oldSelection, auto newSelection, bool fromExplorer) {
|
||||
// It's from us, ignore it.
|
||||
if (fromExplorer) return;
|
||||
|
||||
this->clearSelection();
|
||||
for (InstanceRefWeak inst : newSelection) {
|
||||
if (inst.expired()) continue;
|
||||
|
@ -60,6 +40,8 @@ ExplorerView::ExplorerView(QWidget* parent):
|
|||
this->selectionModel()->select(index, QItemSelectionModel::SelectionFlag::Select);
|
||||
}
|
||||
});
|
||||
|
||||
buildContextMenu();
|
||||
}
|
||||
|
||||
ExplorerView::~ExplorerView() {
|
||||
|
@ -68,23 +50,19 @@ ExplorerView::~ExplorerView() {
|
|||
void ExplorerView::keyPressEvent(QKeyEvent* event) {
|
||||
switch (event->key()) {
|
||||
case Qt::Key_Delete:
|
||||
M_mainWindow->ui->actionDelete->trigger();
|
||||
actionDelete->trigger();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ExplorerView::buildContextMenu() {
|
||||
contextMenu.addAction(M_mainWindow->ui->actionDelete);
|
||||
contextMenu.addSeparator();
|
||||
contextMenu.addAction(M_mainWindow->ui->actionCopy);
|
||||
contextMenu.addAction(M_mainWindow->ui->actionCut);
|
||||
contextMenu.addAction(M_mainWindow->ui->actionPaste);
|
||||
contextMenu.addAction(M_mainWindow->ui->actionPasteInto);
|
||||
contextMenu.addSeparator();
|
||||
contextMenu.addAction(M_mainWindow->ui->actionSaveModel);
|
||||
contextMenu.addAction(M_mainWindow->ui->actionInsertModel);
|
||||
}
|
||||
// This will leak memory. Anyway...
|
||||
contextMenu.addAction(this->actionDelete = new QAction(QIcon("assets/icons/editor/delete"), "Delete"));
|
||||
|
||||
void ExplorerView::updateRoot(InstanceRef newRoot) {
|
||||
model.updateRoot(newRoot);
|
||||
connect(actionDelete, &QAction::triggered, this, [&]() {
|
||||
QModelIndexList selectedIndexes = this->selectionModel()->selectedIndexes();
|
||||
for (QModelIndex index : selectedIndexes) {
|
||||
model.fromIndex(index)->SetParent(std::nullopt);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -20,10 +20,18 @@ public:
|
|||
|
||||
void keyPressEvent(QKeyEvent*) override;
|
||||
// void dropEvent(QDropEvent*) override;
|
||||
|
||||
void buildContextMenu();
|
||||
void updateRoot(InstanceRef newRoot);
|
||||
private:
|
||||
ExplorerModel model;
|
||||
QMenu contextMenu;
|
||||
|
||||
// TODO: Move these to a separate top-level namespace so these can be
|
||||
// accessed from multiple locations
|
||||
QAction* actionDelete;
|
||||
QAction* actionCopy;
|
||||
QAction* actionCut;
|
||||
QAction* actionPaste;
|
||||
QAction* actionPasteInto;
|
||||
QAction* actionSelectChildren;
|
||||
|
||||
void buildContextMenu();
|
||||
};
|
|
@ -56,10 +56,10 @@ bool PropertiesModel::setData(const QModelIndex &index, const QVariant &value, i
|
|||
|
||||
switch (role) {
|
||||
case Qt::EditRole:
|
||||
if (!meta.type->fromString)
|
||||
if (meta.type != &Data::String::TYPE)
|
||||
return false;
|
||||
|
||||
selectedItem->SetPropertyValue(propertyName, meta.type->fromString(value.toString().toStdString()));
|
||||
selectedItem->SetPropertyValue(propertyName, value.toString().toStdString());
|
||||
return true;
|
||||
case Qt::CheckStateRole:
|
||||
if (meta.type != &Data::Bool::TYPE)
|
||||
|
|
4
run.sh
4
run.sh
|
@ -1,9 +1,7 @@
|
|||
if [ $# -eq 0 ] || ([ "$1" != "editor" ] && [ "$1" != "client" ]); then echo "Argument missing, must be 'client' or 'editor'"; exit; fi
|
||||
|
||||
[ "$2" = "-debug" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=Debug
|
||||
[ "$2" = "-release" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=Release
|
||||
[ "$2" = "-reldbg" ] && CMAKE_OPTS=-DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
|
||||
[ "$3" = "-gdb" ] && PRE_COMMAND="gdb -ex run "
|
||||
|
||||
cmake -Bbuild $CMAKE_OPTS . && (cd build; cmake --build .; cd ..) && $PRE_COMMAND ./build/bin/$1
|
||||
cmake $CMAKE_OPTS . && cmake --build . && $PRE_COMMAND ./bin/$1
|
||||
|
|
|
@ -8,7 +8,6 @@ Camera camera(glm::vec3(0.0, 0.0, 3.0));
|
|||
std::shared_ptr<DataModel> dataModel = DataModel::New();
|
||||
std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
|
||||
std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
||||
std::shared_ptr<Handles> editorToolHandles = Handles::New();
|
||||
|
||||
|
||||
std::vector<InstanceRefWeak> currentSelection;
|
|
@ -1,6 +1,5 @@
|
|||
#pragma once
|
||||
#include "objects/base/instance.h"
|
||||
#include "objects/handles.h"
|
||||
#include "objects/workspace.h"
|
||||
#include "camera.h"
|
||||
#include <functional>
|
||||
|
@ -19,7 +18,6 @@ extern std::shared_ptr<DataModel> dataModel;
|
|||
inline std::shared_ptr<Workspace> workspace() { return std::dynamic_pointer_cast<Workspace>(dataModel->services["Workspace"]); }
|
||||
extern std::optional<HierarchyPreUpdateHandler> hierarchyPreUpdateHandler;
|
||||
extern std::optional<HierarchyPostUpdateHandler> hierarchyPostUpdateHandler;
|
||||
extern std::shared_ptr<Handles> editorToolHandles;
|
||||
|
||||
void setSelection(std::vector<InstanceRefWeak> newSelection, bool fromExplorer = false);
|
||||
const std::vector<InstanceRefWeak> getSelection();
|
|
@ -4,7 +4,7 @@
|
|||
#define IMPL_WRAPPER_CLASS(CLASS_NAME, WRAPPED_TYPE, TYPE_NAME) Data::CLASS_NAME::CLASS_NAME(WRAPPED_TYPE in) : value(in) {} \
|
||||
Data::CLASS_NAME::~CLASS_NAME() = default; \
|
||||
Data::CLASS_NAME::operator const WRAPPED_TYPE() const { return value; } \
|
||||
const Data::TypeInfo Data::CLASS_NAME::TYPE = { .name = TYPE_NAME, .deserializer = &Data::CLASS_NAME::Deserialize, .fromString = &Data::CLASS_NAME::FromString }; \
|
||||
const Data::TypeInfo Data::CLASS_NAME::TYPE = { .name = TYPE_NAME, .deserializer = &Data::CLASS_NAME::Deserialize }; \
|
||||
const Data::TypeInfo& Data::CLASS_NAME::GetType() const { return Data::CLASS_NAME::TYPE; }; \
|
||||
void Data::CLASS_NAME::Serialize(pugi::xml_node* node) const { node->text().set(std::string(this->ToString())); }
|
||||
|
||||
|
@ -45,11 +45,6 @@ Data::Variant Data::Bool::Deserialize(pugi::xml_node* node) {
|
|||
return Data::Bool(node->text().as_bool());
|
||||
}
|
||||
|
||||
Data::Variant Data::Bool::FromString(std::string string) {
|
||||
return Data::Bool(string[0] == 't' || string[0] == 'T' || string[0] == '1' || string[0] == 'y' || string[0] == 'Y');
|
||||
}
|
||||
|
||||
|
||||
const Data::String Data::Int::ToString() const {
|
||||
return Data::String(std::to_string(value));
|
||||
}
|
||||
|
@ -58,11 +53,6 @@ Data::Variant Data::Int::Deserialize(pugi::xml_node* node) {
|
|||
return Data::Int(node->text().as_int());
|
||||
}
|
||||
|
||||
Data::Variant Data::Int::FromString(std::string string) {
|
||||
return Data::Int(std::stoi(string));
|
||||
}
|
||||
|
||||
|
||||
const Data::String Data::Float::ToString() const {
|
||||
return Data::String(std::to_string(value));
|
||||
}
|
||||
|
@ -71,19 +61,10 @@ Data::Variant Data::Float::Deserialize(pugi::xml_node* node) {
|
|||
return Data::Float(node->text().as_float());
|
||||
}
|
||||
|
||||
Data::Variant Data::Float::FromString(std::string string) {
|
||||
return Data::Float(std::stof(string));
|
||||
}
|
||||
|
||||
|
||||
const Data::String Data::String::ToString() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
Data::Variant Data::String::Deserialize(pugi::xml_node* node) {
|
||||
return Data::String(node->text().as_string());
|
||||
}
|
||||
|
||||
Data::Variant Data::String::FromString(std::string string) {
|
||||
return Data::String(string);
|
||||
}
|
|
@ -16,18 +16,15 @@ public: \
|
|||
virtual const Data::String ToString() const override; \
|
||||
virtual void Serialize(pugi::xml_node* node) const override; \
|
||||
static Data::Variant Deserialize(pugi::xml_node* node); \
|
||||
static Data::Variant FromString(std::string); \
|
||||
};
|
||||
|
||||
namespace Data {
|
||||
class Variant;
|
||||
typedef std::function<Data::Variant(pugi::xml_node*)> Deserializer;
|
||||
typedef std::function<Data::Variant(std::string)> FromString;
|
||||
|
||||
struct TypeInfo {
|
||||
std::string name;
|
||||
Deserializer deserializer;
|
||||
FromString fromString;
|
||||
TypeInfo(const TypeInfo&) = delete;
|
||||
};
|
||||
|
|
@ -2,16 +2,11 @@
|
|||
#include "datatypes/vector.h"
|
||||
#include "physics/util.h"
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <reactphysics3d/mathematics/Transform.h>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/euler_angles.hpp>
|
||||
// #include "meta.h" // IWYU pragma: keep
|
||||
|
||||
const Data::CFrame Data::CFrame::IDENTITY(glm::vec3(0, 0, 0), glm::mat3(1.f));
|
||||
const Data::CFrame Data::CFrame::YToZ(glm::vec3(0, 0, 0), glm::mat3(glm::vec3(1, 0, 0), glm::vec3(0, 0, 1), glm::vec3(0, 1, 0)));
|
||||
|
||||
Data::CFrame::CFrame(float x, float y, float z, float R00, float R01, float R02, float R10, float R11, float R12, float R20, float R21, float R22)
|
||||
: translation(x, y, z)
|
||||
, rotation({
|
||||
|
@ -38,13 +33,17 @@ Data::CFrame::CFrame(const rp::Transform& transform) : Data::CFrame::CFrame(rpTo
|
|||
}
|
||||
|
||||
glm::mat3 lookAt(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up) {
|
||||
// https://github.com/sgorsten/linalg/issues/29#issuecomment-743989030
|
||||
// https://github.com/sgorsten/glm/issues/29#issuecomment-743989030
|
||||
Data::Vector3 f = (lookAt - position).Unit(); // Forward/Look
|
||||
Data::Vector3 u = up.Unit(); // Up
|
||||
Data::Vector3 s = f.Cross(u).Unit(); // Right
|
||||
u = s.Cross(f);
|
||||
u = s.Cross(u);
|
||||
|
||||
return { s, u, f };
|
||||
return {
|
||||
{ s.X(), u.X(), -f.X() },
|
||||
{ s.Y(), u.Y(), -f.Y() },
|
||||
{ s.Z(), u.Z(), -f.Z() },
|
||||
};
|
||||
}
|
||||
|
||||
Data::CFrame::CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up)
|
||||
|
@ -86,21 +85,8 @@ Data::CFrame Data::CFrame::FromEulerAnglesXYZ(Data::Vector3 vector) {
|
|||
return Data::CFrame(Data::Vector3::ZERO, glm::column(mat, 2), (Data::Vector3)glm::column(mat, 1)); // Getting LookAt (3rd) and Up (2nd) vectors
|
||||
}
|
||||
|
||||
Data::CFrame Data::CFrame::Inverse() const {
|
||||
return CFrame { -translation * glm::transpose(glm::inverse(rotation)), glm::inverse(rotation) };
|
||||
}
|
||||
|
||||
|
||||
// Operators
|
||||
|
||||
Data::CFrame Data::CFrame::operator *(Data::CFrame otherFrame) const {
|
||||
return CFrame { this->translation + this->rotation * otherFrame.translation, this->rotation * otherFrame.rotation };
|
||||
}
|
||||
|
||||
Data::Vector3 Data::CFrame::operator *(Data::Vector3 vector) const {
|
||||
return this->translation + this->rotation * vector;
|
||||
}
|
||||
|
||||
Data::CFrame Data::CFrame::operator +(Data::Vector3 vector) const {
|
||||
return CFrame { this->translation + glm::vec3(vector), this->rotation };
|
||||
}
|
|
@ -7,8 +7,6 @@
|
|||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <glm/fwd.hpp>
|
||||
#include <glm/gtc/matrix_access.hpp>
|
||||
#include <glm/gtc/matrix_inverse.hpp>
|
||||
#include <glm/matrix.hpp>
|
||||
#include <reactphysics3d/mathematics/Transform.h>
|
||||
#include <reactphysics3d/reactphysics3d.h>
|
||||
|
||||
|
@ -30,9 +28,6 @@ namespace Data {
|
|||
CFrame(Data::Vector3 position, Data::Vector3 lookAt, Data::Vector3 up = Data::Vector3(0, 1, 0));
|
||||
~CFrame();
|
||||
|
||||
static const CFrame IDENTITY;
|
||||
static const CFrame YToZ;
|
||||
|
||||
virtual const TypeInfo& GetType() const override;
|
||||
static const TypeInfo TYPE;
|
||||
|
||||
|
@ -46,21 +41,18 @@ namespace Data {
|
|||
//inline static CFrame identity() { }
|
||||
inline Vector3 Position() const { return translation; }
|
||||
inline CFrame Rotation() const { return CFrame { glm::vec3(0, 0, 0), rotation }; }
|
||||
CFrame Inverse() const;
|
||||
inline float X() const { return translation.x; }
|
||||
inline float Y() const { return translation.y; }
|
||||
inline float Z() const { return translation.z; }
|
||||
|
||||
inline Vector3 RightVector() { return glm::column(rotation, 0); }
|
||||
inline Vector3 UpVector() { return glm::column(rotation, 1); }
|
||||
inline Vector3 LookVector() { return -glm::column(rotation, 2); }
|
||||
inline Vector3 LookVector() { return glm::column(rotation, 2); }
|
||||
|
||||
Vector3 ToEulerAnglesXYZ();
|
||||
static CFrame FromEulerAnglesXYZ(Data::Vector3);
|
||||
|
||||
// Operators
|
||||
Data::CFrame operator *(Data::CFrame) const;
|
||||
Data::Vector3 operator *(Data::Vector3) const;
|
||||
Data::CFrame operator +(Data::Vector3) const;
|
||||
Data::CFrame operator -(Data::Vector3) const;
|
||||
};
|
|
@ -25,14 +25,6 @@ Data::Vector3::operator glm::vec3() const { return vector; };
|
|||
Data::Vector3::operator rp::Vector3() const { return rp::Vector3(X(), Y(), Z()); };
|
||||
|
||||
// Operators
|
||||
Data::Vector3 Data::Vector3::operator *(float scale) const {
|
||||
return Data::Vector3(this->X() * scale, this->Y() * scale, this->Z() * scale);
|
||||
}
|
||||
|
||||
Data::Vector3 Data::Vector3::operator /(float scale) const {
|
||||
return Data::Vector3(this->X() / scale, this->Y() / scale, this->Z() / scale);
|
||||
}
|
||||
|
||||
Data::Vector3 Data::Vector3::operator +(Data::Vector3 other) const {
|
||||
return Data::Vector3(this->X() + other.X(), this->Y() + other.Y(), this->Z() + other.Z());
|
||||
}
|
|
@ -40,8 +40,6 @@ namespace Data {
|
|||
float Dot(Data::Vector3) const;
|
||||
|
||||
// Operators
|
||||
Data::Vector3 operator *(float) const;
|
||||
Data::Vector3 operator /(float) const;
|
||||
Data::Vector3 operator +(Data::Vector3) const;
|
||||
Data::Vector3 operator -(Data::Vector3) const;
|
||||
Data::Vector3 operator -() const;
|
|
@ -10,8 +10,7 @@
|
|||
#include <variant>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
// #include <../../include/expected.hpp>
|
||||
#include <expected.hpp>
|
||||
#include <../include/expected.hpp>
|
||||
#include <pugixml.hpp>
|
||||
|
||||
#include "member.h"
|
|
@ -69,37 +69,33 @@ Part::Part(PartConstructParams params): Instance(&TYPE), cframe(Data::CFrame(par
|
|||
.type = &Data::Bool::TYPE,
|
||||
.codec = fieldCodecOf<Data::Bool, bool>(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this)
|
||||
}}, { "Position", {
|
||||
} }, { "Position", {
|
||||
.backingField = &cframe,
|
||||
.type = &Data::Vector3::TYPE,
|
||||
.codec = cframePositionCodec(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||
.flags = PropertyFlags::PROP_NOSAVE
|
||||
}}, { "Rotation", {
|
||||
} }, { "Rotation", {
|
||||
.backingField = &cframe,
|
||||
.type = &Data::Vector3::TYPE,
|
||||
.codec = cframeRotationCodec(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||
.flags = PropertyFlags::PROP_NOSAVE
|
||||
}}, { "CFrame", {
|
||||
} }, { "CFrame", {
|
||||
.backingField = &cframe,
|
||||
.type = &Data::CFrame::TYPE,
|
||||
.codec = fieldCodecOf<Data::CFrame>(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this),
|
||||
}}, { "Size", {
|
||||
} }, { "Size", {
|
||||
.backingField = &size,
|
||||
.type = &Data::Vector3::TYPE,
|
||||
.codec = fieldCodecOf<Data::Vector3, glm::vec3>(),
|
||||
.updateCallback = memberFunctionOf(&Part::onUpdated, this)
|
||||
}}, { "Color", {
|
||||
} }, { "Color", {
|
||||
.backingField = &color,
|
||||
.type = &Data::Color3::TYPE,
|
||||
.codec = fieldCodecOf<Data::Color3>(),
|
||||
}}, { "Transparency", {
|
||||
.backingField = &transparency,
|
||||
.type = &Data::Float::TYPE,
|
||||
.codec = fieldCodecOf<Data::Float, float>(),
|
||||
}}
|
||||
} }
|
||||
}
|
||||
});
|
||||
}
|
|
@ -32,7 +32,6 @@ public:
|
|||
Data::CFrame cframe;
|
||||
glm::vec3 size;
|
||||
Data::Color3 color;
|
||||
float transparency = 0.f;
|
||||
bool selected = false;
|
||||
|
||||
bool anchored = false;
|
|
@ -6,7 +6,6 @@
|
|||
#include <reactphysics3d/collision/shapes/BoxShape.h>
|
||||
#include <reactphysics3d/collision/shapes/CollisionShape.h>
|
||||
#include <reactphysics3d/components/RigidBodyComponents.h>
|
||||
#include <reactphysics3d/configuration.h>
|
||||
#include <reactphysics3d/engine/EventListener.h>
|
||||
#include <reactphysics3d/engine/PhysicsCommon.h>
|
||||
#include <reactphysics3d/mathematics/Quaternion.h>
|
||||
|
@ -40,9 +39,6 @@ void simulationInit() {
|
|||
world = physicsCommon->createPhysicsWorld();
|
||||
|
||||
world->setGravity(rp::Vector3(0, -196.2, 0));
|
||||
// world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::BAUMGARTE_CONTACTS);
|
||||
world->setNbIterationsPositionSolver(2000);
|
||||
world->setNbIterationsVelocitySolver(2000);
|
||||
|
||||
world->setEventListener(&eventListener);
|
||||
}
|
||||
|
@ -73,7 +69,7 @@ void syncPartPhysics(std::shared_ptr<Part> part) {
|
|||
|
||||
void physicsStep(float deltaTime) {
|
||||
// Step the simulation a few steps
|
||||
world->update(std::min(deltaTime / 2, (1/60.f)));
|
||||
world->update(deltaTime / 2);
|
||||
|
||||
// Naive implementation. Parts are only considered so if they are just under Workspace
|
||||
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
|
47
src/rendering/defaultmeshes.cpp
Normal file
47
src/rendering/defaultmeshes.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#include "defaultmeshes.h"
|
||||
|
||||
Mesh* CUBE_MESH;
|
||||
|
||||
void initMeshes() {
|
||||
static float vertices[] = {
|
||||
// positions // normals // texture coords
|
||||
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
|
||||
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,
|
||||
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
|
||||
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,
|
||||
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,
|
||||
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,
|
||||
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
|
||||
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f,
|
||||
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
|
||||
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f,
|
||||
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f,
|
||||
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,
|
||||
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
-0.5f, 0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
|
||||
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
-0.5f, -0.5f, -0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
-0.5f, -0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
-0.5f, 0.5f, 0.5f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f,
|
||||
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f,
|
||||
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f,
|
||||
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 1.0f,
|
||||
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f,
|
||||
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 0.0f,
|
||||
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f,
|
||||
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
|
||||
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f,
|
||||
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
|
||||
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f,
|
||||
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f,
|
||||
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f,
|
||||
};
|
||||
|
||||
CUBE_MESH = new Mesh(36, vertices);
|
||||
}
|
|
@ -2,7 +2,5 @@
|
|||
#include "mesh.h"
|
||||
|
||||
extern Mesh* CUBE_MESH;
|
||||
extern Mesh* SPHERE_MESH;
|
||||
extern Mesh* ARROW_MESH;
|
||||
|
||||
void initMeshes();
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "mesh.h"
|
||||
|
||||
Mesh::Mesh(int vertexCount, float *vertices) : vertexCount(vertexCount) {
|
||||
Mesh::Mesh(int vertexCount, float *vertices) {
|
||||
// Generate buffers
|
||||
glGenBuffers(1, &VBO);
|
||||
glGenVertexArrays(1, &VAO);
|
|
@ -4,8 +4,6 @@ class Mesh {
|
|||
unsigned int VBO, VAO;
|
||||
|
||||
public:
|
||||
int vertexCount;
|
||||
|
||||
Mesh(int vertexCount, float* vertices);
|
||||
~Mesh();
|
||||
void bind();
|
|
@ -5,13 +5,11 @@
|
|||
#include <glm/ext.hpp>
|
||||
#include <glm/ext/matrix_float4x4.hpp>
|
||||
#include <glm/ext/matrix_transform.hpp>
|
||||
#include <glm/ext/vector_float3.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/trigonometric.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "datatypes/cframe.h"
|
||||
#include "physics/util.h"
|
||||
#include "shader.h"
|
||||
#include "mesh.h"
|
||||
|
@ -25,10 +23,8 @@
|
|||
|
||||
#include "renderer.h"
|
||||
|
||||
Shader* shader = NULL;
|
||||
Shader* skyboxShader = NULL;
|
||||
Shader* handleShader = NULL;
|
||||
Shader* identityShader = NULL;
|
||||
Shader *shader = NULL;
|
||||
Shader *skyboxShader = NULL;
|
||||
extern Camera camera;
|
||||
Skybox* skyboxTexture = NULL;
|
||||
Texture3D* studsTexture = NULL;
|
||||
|
@ -58,20 +54,13 @@ void renderInit(GLFWwindow* window, int width, int height) {
|
|||
|
||||
studsTexture = new Texture3D("assets/textures/studs.png", 128, 128, 6, GL_RGBA);
|
||||
|
||||
// Compile shaders
|
||||
// Compile shader
|
||||
shader = new Shader("assets/shaders/phong.vs", "assets/shaders/phong.fs");
|
||||
skyboxShader = new Shader("assets/shaders/skybox.vs", "assets/shaders/skybox.fs");
|
||||
handleShader = new Shader("assets/shaders/handle.vs", "assets/shaders/handle.fs");
|
||||
identityShader = new Shader("assets/shaders/identity.vs", "assets/shaders/identity.fs");
|
||||
}
|
||||
|
||||
void renderParts() {
|
||||
glDepthMask(GL_TRUE);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glFrontFace(GL_CW);
|
||||
glEnable(GL_BLEND);
|
||||
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
// Use shader
|
||||
shader->use();
|
||||
|
@ -79,7 +68,7 @@ void renderParts() {
|
|||
// shader->set("lightColor", glm::vec3(1.0f, 1.0f, 1.0f));
|
||||
|
||||
// view/projection transformations
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)viewportWidth / (float)viewportHeight, 0.1f, 1000.0f);
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)viewportWidth / (float)viewportHeight, 0.1f, 100.0f);
|
||||
glm::mat4 view = camera.getLookAt();
|
||||
shader->set("projection", projection);
|
||||
shader->set("view", view);
|
||||
|
@ -116,40 +105,27 @@ void renderParts() {
|
|||
// Pass in the camera position
|
||||
shader->set("viewPos", camera.cameraPos);
|
||||
|
||||
// Sort by nearest
|
||||
std::map<float, std::shared_ptr<Part>> sorted;
|
||||
// TODO: Same as todo in src/physics/simulation.cpp
|
||||
for (InstanceRef inst : workspace()->GetChildren()) {
|
||||
if (inst->GetClass()->className != "Part") continue;
|
||||
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
|
||||
if (part->transparency > 0.00001) {
|
||||
float distance = glm::length(glm::vec3(Data::Vector3(camera.cameraPos) - part->position()));
|
||||
sorted[distance] = part;
|
||||
} else {
|
||||
glm::mat4 model = part->cframe;
|
||||
if (part->name == "camera") model = camera.getLookAt();
|
||||
model = glm::scale(model, part->size);
|
||||
shader->set("model", model);
|
||||
shader->set("material", Material {
|
||||
.diffuse = part->color,
|
||||
.specular = glm::vec3(0.5f, 0.5f, 0.5f),
|
||||
.shininess = 16.0f,
|
||||
});
|
||||
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
|
||||
shader->set("normalMatrix", normalMatrix);
|
||||
shader->set("texScale", part->size);
|
||||
shader->set("transparency", part->transparency);
|
||||
|
||||
CUBE_MESH->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount);
|
||||
}
|
||||
}
|
||||
// if (inst->name == "Target") printf("(%f,%f,%f):(%f,%f,%f;%f,%f,%f;%f,%f,%f)\n",
|
||||
// part->cframe.X(),
|
||||
// part->cframe.Y(),
|
||||
// part->cframe.Z(),
|
||||
// part->cframe.RightVector().X(),
|
||||
// part->cframe.UpVector().X(),
|
||||
// part->cframe.LookVector().X(),
|
||||
// part->cframe.RightVector().Y(),
|
||||
// part->cframe.UpVector().Y(),
|
||||
// part->cframe.LookVector().Y(),
|
||||
// part->cframe.RightVector().Z(),
|
||||
// part->cframe.UpVector().Z(),
|
||||
// part->cframe.LookVector().Z()
|
||||
// );
|
||||
|
||||
// TODO: Same as todo in src/physics/simulation.cpp
|
||||
// According to LearnOpenGL, std::map automatically sorts its contents.
|
||||
for (std::map<float, std::shared_ptr<Part>>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); it++) {
|
||||
std::shared_ptr<Part> part = it->second;
|
||||
glm::mat4 model = part->cframe;
|
||||
if (part->name == "camera") model = camera.getLookAt();
|
||||
model = glm::scale(model, part->size);
|
||||
shader->set("model", model);
|
||||
shader->set("material", Material {
|
||||
|
@ -160,20 +136,18 @@ void renderParts() {
|
|||
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
|
||||
shader->set("normalMatrix", normalMatrix);
|
||||
shader->set("texScale", part->size);
|
||||
shader->set("transparency", part->transparency);
|
||||
|
||||
CUBE_MESH->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount);
|
||||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
}
|
||||
}
|
||||
|
||||
void renderSkyBox() {
|
||||
glDepthMask(GL_FALSE);
|
||||
glCullFace(GL_FRONT);
|
||||
|
||||
skyboxShader->use();
|
||||
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)viewportWidth / (float)viewportHeight, 0.1f, 1000.0f);
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)viewportWidth / (float)viewportHeight, 0.1f, 100.0f);
|
||||
// Remove translation component of view, making us always at (0, 0, 0)
|
||||
glm::mat4 view = glm::mat4(glm::mat3(camera.getLookAt()));
|
||||
|
||||
|
@ -186,82 +160,11 @@ void renderSkyBox() {
|
|||
glDrawArrays(GL_TRIANGLES, 0, 36);
|
||||
}
|
||||
|
||||
void renderHandles() {
|
||||
if (!editorToolHandles->adornee.has_value() || !editorToolHandles->active) return;
|
||||
|
||||
glDepthMask(GL_TRUE);
|
||||
glCullFace(GL_BACK);
|
||||
|
||||
// Use shader
|
||||
handleShader->use();
|
||||
|
||||
// view/projection transformations
|
||||
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)viewportWidth / (float)viewportHeight, 0.1f, 1000.0f);
|
||||
glm::mat4 view = camera.getLookAt();
|
||||
handleShader->set("projection", projection);
|
||||
handleShader->set("view", view);
|
||||
handleShader->set("sunLight", DirLight {
|
||||
.direction = glm::vec3(-0.2f, -1.0f, -0.3f),
|
||||
.ambient = glm::vec3(0.2f, 0.2f, 0.2f),
|
||||
.diffuse = glm::vec3(0.5f, 0.5f, 0.5f),
|
||||
.specular = glm::vec3(1.0f, 1.0f, 1.0f),
|
||||
});
|
||||
handleShader->set("numPointLights", 0);
|
||||
|
||||
// Pass in the camera position
|
||||
handleShader->set("viewPos", camera.cameraPos);
|
||||
|
||||
for (auto face : HandleFace::Faces) {
|
||||
glm::mat4 model = editorToolHandles->GetCFrameOfHandle(face);
|
||||
model = glm::scale(model, (glm::vec3)editorToolHandles->HandleSize(face));
|
||||
handleShader->set("model", model);
|
||||
handleShader->set("material", Material {
|
||||
.diffuse = glm::abs(face.normal),
|
||||
.specular = glm::vec3(0.5f, 0.5f, 0.5f),
|
||||
.shininess = 16.0f,
|
||||
});
|
||||
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
|
||||
handleShader->set("normalMatrix", normalMatrix);
|
||||
|
||||
if (editorToolHandles->handlesType == HandlesType::MoveHandles) {
|
||||
ARROW_MESH->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, ARROW_MESH->vertexCount);
|
||||
} else {
|
||||
SPHERE_MESH->bind();
|
||||
glDrawArrays(GL_TRIANGLES, 0, SPHERE_MESH->vertexCount);
|
||||
}
|
||||
}
|
||||
|
||||
// 2d square overlay
|
||||
glDisable(GL_CULL_FACE);
|
||||
|
||||
identityShader->use();
|
||||
identityShader->set("aColor", glm::vec3(0.f, 1.f, 1.f));
|
||||
|
||||
for (auto face : HandleFace::Faces) {
|
||||
Data::CFrame cframe = editorToolHandles->GetCFrameOfHandle(face);
|
||||
glm::vec4 screenPos = projection * view * glm::vec4((glm::vec3)cframe.Position(), 1.0f);
|
||||
glm::vec3 ndcCoords = screenPos / screenPos.w;
|
||||
|
||||
float rad = 5;
|
||||
float xRad = rad * 1/viewportWidth;
|
||||
float yRad = rad * 1/viewportHeight;
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glVertex3f(ndcCoords.x - xRad, ndcCoords.y - yRad, 0);
|
||||
glVertex3f(ndcCoords.x + xRad, ndcCoords.y - yRad, 0);
|
||||
glVertex3f(ndcCoords.x + xRad, ndcCoords.y + yRad, 0);
|
||||
glVertex3f(ndcCoords.x - xRad, ndcCoords.y + yRad, 0);
|
||||
glEnd();
|
||||
}
|
||||
}
|
||||
|
||||
void render(GLFWwindow* window) {
|
||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
renderSkyBox();
|
||||
renderHandles();
|
||||
renderParts();
|
||||
}
|
||||
|
|
@ -1,78 +0,0 @@
|
|||
#!/usr/bin/python3
|
||||
|
||||
# Default mesh generator
|
||||
# Input from wavefront obj file
|
||||
|
||||
# Usage: ./genmesh.py mesh.obj > mesh.cpp
|
||||
|
||||
import sys
|
||||
|
||||
HEADER = """
|
||||
static float CUBE_VERTICES[] = {
|
||||
// positions // normals // texture coords
|
||||
"""
|
||||
|
||||
FOOTER = """
|
||||
};"""
|
||||
|
||||
file = open(sys.argv[1], "r")
|
||||
|
||||
vert_coords = []
|
||||
vert_norms = []
|
||||
vert_uvs = []
|
||||
|
||||
out_vertices = []
|
||||
|
||||
min_coords: tuple[float, float, float] | None = None
|
||||
max_coords: tuple[float, float, float] | None = None
|
||||
|
||||
def normalize(x, y, z):
|
||||
assert min_coords
|
||||
assert max_coords
|
||||
return ((x-max_coords[0])/(max_coords[0]-min_coords[0])+0.5, (y-max_coords[1])/(max_coords[1]-min_coords[1])+0.5, (z-max_coords[2])/(max_coords[2]-min_coords[2])+0.5)
|
||||
|
||||
for line in file:
|
||||
if line.startswith('v '):
|
||||
coords = line.split(' ')[1:]
|
||||
coords = (float(coords[0]), float(coords[1]), float(coords[2]))
|
||||
vert_coords.append(coords)
|
||||
|
||||
if not min_coords: min_coords = coords
|
||||
if not max_coords: max_coords = coords
|
||||
|
||||
if coords[0] > max_coords[0]: max_coords = (coords[0], max_coords[1], max_coords[2])
|
||||
if coords[1] > max_coords[1]: max_coords = (max_coords[0], coords[1], max_coords[2])
|
||||
if coords[2] > max_coords[2]: max_coords = (max_coords[0], max_coords[1], coords[2])
|
||||
|
||||
if coords[0] < min_coords[0]: min_coords = (coords[0], min_coords[1], min_coords[2])
|
||||
if coords[1] < min_coords[1]: min_coords = (min_coords[0], coords[1], min_coords[2])
|
||||
if coords[2] < min_coords[2]: min_coords = (min_coords[0], min_coords[1], coords[2])
|
||||
|
||||
if line.startswith('vn '):
|
||||
coords = line.split(' ')[1:]
|
||||
vert_norms.append((float(coords[0]), float(coords[1]), float(coords[2])))
|
||||
|
||||
if line.startswith('vt '):
|
||||
coords = line.split(' ')[1:]
|
||||
vert_uvs.append((float(coords[0]), float(coords[1])))
|
||||
|
||||
if line.startswith('f '):
|
||||
verts = line.split(' ')[1:]
|
||||
for vert in verts:
|
||||
coords, uv, normal = vert.split('/')
|
||||
coords, uv, normal = int(coords), int(uv), int(normal)
|
||||
coords, uv, normal = vert_coords[coords-1], vert_uvs[uv-1], vert_norms[normal-1]
|
||||
|
||||
coords = normalize(*coords)
|
||||
# for coord in [*normal]:
|
||||
# if coord > greatest_coord: greatest_coord = coord
|
||||
# if coord < least_coord: least_coord = coord
|
||||
|
||||
out_vertices.append((coords, normal, uv))
|
||||
|
||||
print(HEADER)
|
||||
|
||||
for coords, normal, uv in out_vertices:
|
||||
print(f"\t{coords[0]}, {coords[1]}, {coords[2]},\t{normal[0]}, {normal[1]}, {normal[2]},\t{uv[0]}, {uv[1]},")
|
||||
|
||||
print(FOOTER)
|
Loading…
Reference in a new issue