Initial claude dump
This commit is contained in:
+58
-3
@@ -1,5 +1,7 @@
|
||||
#include <iostream>
|
||||
#include <sophia/application.hpp>
|
||||
#include <sophia/components.hpp>
|
||||
#include <sophia/entity.hpp>
|
||||
|
||||
#include "panels/asset_browser_panel.hpp"
|
||||
#include "panels/inspector_panel.hpp"
|
||||
@@ -15,12 +17,19 @@ class Editor : public sophia::Application {
|
||||
|
||||
debugUI().show();
|
||||
|
||||
panels().emplace<SceneHierarchyPanel>();
|
||||
panels().emplace<InspectorPanel>();
|
||||
textures.emplace_back("Checker", createCheckerTexture());
|
||||
|
||||
sophia::SceneHandle sceneHandle = scenes().create();
|
||||
sophia::Scene& scene = scenes().get(sceneHandle);
|
||||
sophia::Entity camera = createDemoScene(scene);
|
||||
|
||||
panels().emplace<SceneHierarchyPanel>(scene, selected);
|
||||
panels().emplace<InspectorPanel>(selected, textures);
|
||||
panels().emplace<AssetBrowserPanel>();
|
||||
sophia::RenderTargetHandle sceneTarget =
|
||||
resources().create<sophia::RenderTarget>();
|
||||
views().create({.scene = scenes().create(), .target = sceneTarget});
|
||||
views().create(
|
||||
{.scene = sceneHandle, .target = sceneTarget, .camera = camera});
|
||||
panels().emplace<ViewportPanel>(ui(), sceneTarget);
|
||||
|
||||
panels().setDefaultLayout(
|
||||
@@ -31,6 +40,52 @@ class Editor : public sophia::Application {
|
||||
}
|
||||
|
||||
void onUpdate(const sophia::FrameInfo& frameInfo) override {}
|
||||
|
||||
private:
|
||||
sophia::Entity createDemoScene(sophia::Scene& scene) {
|
||||
sophia::Entity camera = scene.createEntity("Main Camera");
|
||||
camera.addComponent<sophia::CameraComponent>();
|
||||
scene.setActiveCamera(camera);
|
||||
|
||||
sophia::Entity checker = scene.createEntity("Checker");
|
||||
checker.getComponent<sophia::TransformComponent>().position = {-1.5f, 0.f,
|
||||
0.f};
|
||||
checker.addComponent<sophia::SpriteComponent>(sophia::SpriteComponent{
|
||||
.texture = textures[0].second, .size = {2.f, 2.f}});
|
||||
|
||||
sophia::Entity red = scene.createEntity("Red");
|
||||
red.getComponent<sophia::TransformComponent>().position = {1.f, -0.5f, 0.f};
|
||||
red.addComponent<sophia::SpriteComponent>(sophia::SpriteComponent{
|
||||
.color = {0.9f, 0.25f, 0.25f, 1.f}, .size = {2.f, 2.f}});
|
||||
|
||||
// Drawn over "Red" because of its higher sort order.
|
||||
sophia::Entity blue = scene.createEntity("Blue (translucent)");
|
||||
blue.getComponent<sophia::TransformComponent>().position = {1.75f, 0.25f,
|
||||
0.f};
|
||||
blue.addComponent<sophia::SpriteComponent>(
|
||||
sophia::SpriteComponent{.color = {0.25f, 0.45f, 0.95f, 0.6f},
|
||||
.size = {2.f, 2.f},
|
||||
.sortOrder = 1});
|
||||
|
||||
return camera;
|
||||
}
|
||||
|
||||
sophia::TextureHandle createCheckerTexture() {
|
||||
constexpr sophia::u32 SIZE = 8;
|
||||
std::vector<sophia::u8> pixels(SIZE * SIZE * 4);
|
||||
for (sophia::u32 y = 0; y < SIZE; y++) {
|
||||
for (sophia::u32 x = 0; x < SIZE; x++) {
|
||||
sophia::u8 value = ((x + y) % 2 == 0) ? 230 : 60;
|
||||
sophia::u8* pixel = &pixels[(y * SIZE + x) * 4];
|
||||
pixel[0] = pixel[1] = pixel[2] = value;
|
||||
pixel[3] = 255;
|
||||
}
|
||||
}
|
||||
return resources().create<sophia::Texture>(SIZE, SIZE, std::move(pixels));
|
||||
}
|
||||
|
||||
sophia::Entity selected;
|
||||
InspectorPanel::TextureList textures;
|
||||
};
|
||||
|
||||
SOPHIA_MAIN(Editor)
|
||||
|
||||
@@ -3,30 +3,135 @@
|
||||
#include <imgui.h>
|
||||
|
||||
#include <cstdio>
|
||||
#include <sophia/components.hpp>
|
||||
|
||||
void InspectorPanel::onUpdate(const sophia::FrameInfo&) {
|
||||
char nameBuf[128];
|
||||
std::snprintf(nameBuf, sizeof(nameBuf), "%s", name.c_str());
|
||||
if (ImGui::InputText("Name", nameBuf, sizeof(nameBuf))) {
|
||||
name = nameBuf;
|
||||
if (!selected) {
|
||||
ImGui::TextDisabled("No entity selected");
|
||||
return;
|
||||
}
|
||||
ImGui::Text("Tag: %s", tag.c_str());
|
||||
|
||||
drawTag();
|
||||
ImGui::Separator();
|
||||
drawTransform();
|
||||
drawCamera();
|
||||
drawSprite();
|
||||
|
||||
ImGui::Separator();
|
||||
drawAddComponent();
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::DragFloat3("Position", &position.x, 0.1f);
|
||||
ImGui::DragFloat3("Rotation", &rotation.x, 0.1f);
|
||||
ImGui::DragFloat3("Scale", &scale.x, 0.1f);
|
||||
}
|
||||
void InspectorPanel::drawTag() {
|
||||
auto& tag = selected.getComponent<sophia::TagComponent>();
|
||||
|
||||
if (ImGui::CollapsingHeader("Mesh Renderer",
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Text("Mesh: %s", meshName.c_str());
|
||||
ImGui::Text("Material: %s", materialName.c_str());
|
||||
}
|
||||
|
||||
if (ImGui::CollapsingHeader("Script", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
ImGui::Text("%s", scriptName.c_str());
|
||||
char nameBuf[128];
|
||||
std::snprintf(nameBuf, sizeof(nameBuf), "%s", tag.tag.c_str());
|
||||
if (ImGui::InputText("Name", nameBuf, sizeof(nameBuf))) {
|
||||
tag.tag = nameBuf;
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorPanel::drawTransform() {
|
||||
if (!selected.hasComponent<sophia::TransformComponent>() ||
|
||||
!ImGui::CollapsingHeader("Transform", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& transform = selected.getComponent<sophia::TransformComponent>();
|
||||
ImGui::DragFloat3("Position", &transform.position.x, 0.1f);
|
||||
|
||||
// 2D only: edits rotation around Z and drops any X/Y rotation.
|
||||
float degrees = glm::degrees(glm::eulerAngles(transform.rotation).z);
|
||||
if (ImGui::DragFloat("Rotation", °rees, 1.0f)) {
|
||||
transform.rotation =
|
||||
glm::angleAxis(glm::radians(degrees), glm::vec3{0.f, 0.f, 1.f});
|
||||
}
|
||||
|
||||
ImGui::DragFloat3("Scale", &transform.scale.x, 0.1f);
|
||||
}
|
||||
|
||||
void InspectorPanel::drawCamera() {
|
||||
if (!selected.hasComponent<sophia::CameraComponent>() ||
|
||||
!ImGui::CollapsingHeader("Camera", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& camera = selected.getComponent<sophia::CameraComponent>();
|
||||
int projection = static_cast<int>(camera.projection);
|
||||
if (ImGui::Combo("Projection", &projection, "Orthographic\0Perspective\0")) {
|
||||
camera.projection =
|
||||
static_cast<sophia::CameraComponent::Projection>(projection);
|
||||
}
|
||||
|
||||
if (camera.projection == sophia::CameraComponent::Projection::Orthographic) {
|
||||
ImGui::DragFloat("Height", &camera.ortho.height, 0.1f, 0.01f, 10000.f);
|
||||
} else {
|
||||
float fov = glm::degrees(camera.perspective.fov);
|
||||
if (ImGui::DragFloat("FOV", &fov, 0.5f, 1.f, 179.f)) {
|
||||
camera.perspective.fov = glm::radians(fov);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorPanel::drawSprite() {
|
||||
if (!selected.hasComponent<sophia::SpriteComponent>()) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool open = true;
|
||||
if (!ImGui::CollapsingHeader("Sprite", &open,
|
||||
ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||
if (!open) {
|
||||
selected.removeComponent<sophia::SpriteComponent>();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
auto& sprite = selected.getComponent<sophia::SpriteComponent>();
|
||||
|
||||
const char* current = "None";
|
||||
for (const auto& [name, handle] : textures) {
|
||||
if (handle == sprite.texture) {
|
||||
current = name.c_str();
|
||||
}
|
||||
}
|
||||
if (ImGui::BeginCombo("Texture", current)) {
|
||||
if (ImGui::Selectable("None", !sprite.texture.isValid())) {
|
||||
sprite.texture = sophia::INVALID_HANDLE;
|
||||
}
|
||||
for (const auto& [name, handle] : textures) {
|
||||
if (ImGui::Selectable(name.c_str(), handle == sprite.texture)) {
|
||||
sprite.texture = handle;
|
||||
}
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
|
||||
ImGui::ColorEdit4("Color", &sprite.color.x);
|
||||
ImGui::DragFloat2("Size", &sprite.size.x, 0.05f);
|
||||
ImGui::SliderFloat2("Pivot", &sprite.pivot.x, 0.f, 1.f);
|
||||
ImGui::DragFloat4("UV Rect", &sprite.uvRect.x, 0.01f);
|
||||
ImGui::DragInt("Sort Order", &sprite.sortOrder);
|
||||
|
||||
if (!open) {
|
||||
selected.removeComponent<sophia::SpriteComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
void InspectorPanel::drawAddComponent() {
|
||||
if (ImGui::Button("Add Component")) {
|
||||
ImGui::OpenPopup("AddComponent");
|
||||
}
|
||||
|
||||
if (ImGui::BeginPopup("AddComponent")) {
|
||||
if (!selected.hasComponent<sophia::SpriteComponent>() &&
|
||||
ImGui::MenuItem("Sprite")) {
|
||||
selected.addComponent<sophia::SpriteComponent>();
|
||||
}
|
||||
if (!selected.hasComponent<sophia::CameraComponent>() &&
|
||||
ImGui::MenuItem("Camera")) {
|
||||
selected.addComponent<sophia::CameraComponent>();
|
||||
}
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <sophia/entity.hpp>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/handles.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class InspectorPanel : public sophia::Panel {
|
||||
public:
|
||||
InspectorPanel() : Panel{{.name = "Inspector"}} {}
|
||||
// Stand-in for an asset browser: the textures a sprite can pick from.
|
||||
using TextureList =
|
||||
std::vector<std::pair<std::string, sophia::TextureHandle>>;
|
||||
|
||||
InspectorPanel(sophia::Entity& selected, const TextureList& textures)
|
||||
: Panel{{.name = "Inspector"}}, selected{selected}, textures{textures} {}
|
||||
|
||||
void onUpdate(const sophia::FrameInfo& frameInfo) override;
|
||||
|
||||
private:
|
||||
std::string name = "Player";
|
||||
std::string tag = "Player";
|
||||
void drawTag();
|
||||
void drawTransform();
|
||||
void drawCamera();
|
||||
void drawSprite();
|
||||
void drawAddComponent();
|
||||
|
||||
glm::vec3 position{0.0f, 1.0f, 0.0f};
|
||||
glm::vec3 rotation{0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 scale{1.0f, 1.0f, 1.0f};
|
||||
|
||||
std::string meshName = "player_capsule.fbx";
|
||||
std::string materialName = "player_mat";
|
||||
std::string scriptName = "PlayerController.lua";
|
||||
sophia::Entity& selected;
|
||||
const TextureList& textures;
|
||||
};
|
||||
|
||||
@@ -2,31 +2,31 @@
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
void SceneHierarchyPanel::drawNode(const Node& node) {
|
||||
if (node.children.empty()) {
|
||||
#include <sophia/components.hpp>
|
||||
|
||||
void SceneHierarchyPanel::onUpdate(const sophia::FrameInfo&) {
|
||||
if (ImGui::Button("+ Sprite")) {
|
||||
selected = scene.createEntity("Sprite");
|
||||
selected.addComponent<sophia::SpriteComponent>();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
|
||||
// Flat for now: there is no parent/child component yet.
|
||||
scene.forEachEntity([this](sophia::Entity entity) {
|
||||
ImGuiTreeNodeFlags flags = ImGuiTreeNodeFlags_Leaf |
|
||||
ImGuiTreeNodeFlags_NoTreePushOnOpen |
|
||||
ImGuiTreeNodeFlags_SpanAvailWidth;
|
||||
if (node.name == selected) {
|
||||
if (entity == selected) {
|
||||
flags |= ImGuiTreeNodeFlags_Selected;
|
||||
}
|
||||
ImGui::TreeNodeEx(node.name.c_str(), flags);
|
||||
|
||||
const std::string& name = entity.getComponent<sophia::TagComponent>().tag;
|
||||
ImGui::PushID(static_cast<int>(static_cast<sophia::u32>(entity)));
|
||||
ImGui::TreeNodeEx(name.c_str(), flags);
|
||||
if (ImGui::IsItemClicked()) {
|
||||
selected = node.name;
|
||||
selected = entity;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ImGui::TreeNodeEx(node.name.c_str(), ImGuiTreeNodeFlags_SpanAvailWidth)) {
|
||||
for (const Node& child : node.children) {
|
||||
drawNode(child);
|
||||
}
|
||||
ImGui::TreePop();
|
||||
}
|
||||
}
|
||||
|
||||
void SceneHierarchyPanel::onUpdate(const sophia::FrameInfo&) {
|
||||
for (const Node& node : nodes) {
|
||||
drawNode(node);
|
||||
}
|
||||
ImGui::PopID();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,32 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <sophia/entity.hpp>
|
||||
#include <sophia/frame_info.hpp>
|
||||
#include <sophia/scene.hpp>
|
||||
#include <sophia/ui/panel.hpp>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class SceneHierarchyPanel : public sophia::Panel {
|
||||
public:
|
||||
SceneHierarchyPanel() : Panel{{.name = "Scene Hierarchy", .locked = true}} {}
|
||||
SceneHierarchyPanel(sophia::Scene& scene, sophia::Entity& selected)
|
||||
: Panel{{.name = "Scene Hierarchy", .locked = true}},
|
||||
scene{scene},
|
||||
selected{selected} {}
|
||||
|
||||
void onUpdate(const sophia::FrameInfo& frameInfo) override;
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
std::string name;
|
||||
std::vector<Node> children;
|
||||
};
|
||||
|
||||
void drawNode(const Node& node);
|
||||
|
||||
std::vector<Node> nodes = {
|
||||
{"Main Camera", {}},
|
||||
{"Directional Light", {}},
|
||||
{"Ground", {}},
|
||||
{"Player", {}},
|
||||
{"Enemies", {{"Enemy_01", {}}, {"Enemy_02", {}}, {"Enemy_03", {}}}},
|
||||
{"Props", {{"Crate_01", {}}, {"Crate_02", {}}}},
|
||||
};
|
||||
|
||||
std::string selected;
|
||||
sophia::Scene& scene;
|
||||
sophia::Entity& selected;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user