Add more scripts, fix let* binding, add undo/redo
This commit is contained in:
parent
37977981ad
commit
0579c39da6
8 changed files with 253 additions and 19 deletions
17
Data/resources/scripts/build-cylinder.scm
Normal file
17
Data/resources/scripts/build-cylinder.scm
Normal file
|
@ -0,0 +1,17 @@
|
|||
; Routine to build a circle/cylinder shape :3
|
||||
(progn
|
||||
; CONFIG
|
||||
(define RESOLUTION 20.0)
|
||||
(define (build-cylinder sx sy res radius)
|
||||
(let* ((angle-increment (/ (* 2 PI) res))
|
||||
(points ()))
|
||||
(do ((i 0 (+1 i)))
|
||||
((>= i res) (progn (print points)
|
||||
(make-sector-from-points 0.0 4.0 points)))
|
||||
(let* ((angle (* i angle-increment))
|
||||
(x (+ sx (* radius (cos angle))))
|
||||
(y (+ sy (* radius (sin angle)))))
|
||||
(set! points (cons (x y) points))))))
|
||||
(fill-selection
|
||||
(lambda (x1 y1 x2 y2 nh1 nh2 dir)
|
||||
(build-cylinder (/ (+ x1 x2) 2) (/ (+ y1 y2) 2) RESOLUTION (* (abs (- x2 x1)) 0.5) ))))
|
|
@ -0,0 +1,70 @@
|
|||
[11:46:03 PM] Info: Starting...
|
||||
|
||||
KP3D version 2
|
||||
===============================
|
||||
Copyright (C) kpworld.xyz 2018-2024
|
||||
Contact me! @kp_cftsz
|
||||
|
||||
[11:46:03 PM] Info: Initializing SDL
|
||||
[11:46:03 PM] Info: Initializing OpenGL
|
||||
[11:46:03 PM] Info: OpenGL version: 4.6.0 NVIDIA 536.23
|
||||
[11:46:03 PM] Info: Initializing GLEW
|
||||
[11:46:03 PM] Info: Initializing SDL_mixer
|
||||
[11:46:03 PM] Info: Reticulating splines...
|
||||
[11:46:03 PM] Info: Ready!
|
||||
[11:46:03 PM] Info: Loading script: build-cylinder.scm
|
||||
[11:46:03 PM] Info: Loading script: build-stairs.scm
|
||||
[11:46:03 PM] Info: Loading material resource: block.png
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/block_n.png
|
||||
[11:46:03 PM] Info: Loading material resource: brick2.jpg
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/brick2_n.jpg
|
||||
[11:46:03 PM] Info: Loading material resource: bricks.jpg
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/bricks_n.jpg
|
||||
[11:46:03 PM] Info: Loading material resource: FLAT5_7.png
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/FLAT5_7_n.png
|
||||
[11:46:03 PM] Info: Loading material resource: floor0.png
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/floor0_n.png
|
||||
[11:46:03 PM] Info: Loading material resource: floor1.png
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/floor1_n.png
|
||||
[11:46:03 PM] Info: Loading material resource: GRASS2.png
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/GRASS2_n.png
|
||||
[11:46:03 PM] Info: Loading material resource: hardwood.jpg
|
||||
[11:46:03 PM] Info: Found normal map texture: materials/hardwood_n.jpg
|
||||
[11:46:03 PM] Info: Map init
|
||||
[11:46:03 PM] Info: Finalized mesh with 49 batches
|
||||
[11:46:06 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:07 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:08 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:09 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:10 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:11 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:12 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:12 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:14 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:14 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:15 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:15 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:15 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:15 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:16 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:16 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:17 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:17 PM] Info: Finalized mesh with 48 batches
|
||||
[11:46:17 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:18 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:19 PM] Info: Finalized mesh with 54 batches
|
||||
[11:46:19 PM] Info: Finalized mesh with 54 batches
|
|
@ -3005,7 +3005,9 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lisp()
|
|||
langDef.mKeywords.insert(k);
|
||||
|
||||
static const char* const identifiers[] = {
|
||||
"sin", "cos", "sqrt", "abs", "log", "log10", "floor", "ceil", "atan2", "PI", "E", "car", "cdr", "cons", "list", "list-ref", "print", "exit", "+1", "-1", "null?",
|
||||
// KSI
|
||||
"sin", "cos", "sqrt", "abs", "log", "log10", "floor", "ceil", "atan2", "PI", "E", "car", "cdr", "cons", "list",
|
||||
"list-ref", "print", "exit", "+1", "-1", "null?",
|
||||
// Map editor specific
|
||||
"make-sector-from-points",
|
||||
"fill-selection",
|
||||
|
@ -3014,7 +3016,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lisp()
|
|||
for (auto& k : identifiers)
|
||||
{
|
||||
Identifier id;
|
||||
id.mDeclaration = "Built-in function";
|
||||
id.mDeclaration = "Built-in function/symbol";
|
||||
langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,11 @@ std::string GetAudioDir()
|
|||
return GetResourceDir() + "audio/";
|
||||
}
|
||||
|
||||
std::string GetScriptsDir()
|
||||
{
|
||||
return GetResourceDir() + "scripts/";
|
||||
}
|
||||
|
||||
ErrCode CreateDir(const std::string& path)
|
||||
{
|
||||
return static_cast<ErrCode>(!std::filesystem::create_directory(path));
|
||||
|
|
|
@ -18,6 +18,7 @@ std::string GetModelDir();
|
|||
std::string GetBitmapDir();
|
||||
std::string GetFontDir();
|
||||
std::string GetAudioDir();
|
||||
std::string GetScriptsDir();
|
||||
|
||||
ErrCode CreateDir(const std::string& path);
|
||||
ErrCode DeleteDir(const std::string& path);
|
||||
|
|
|
@ -533,7 +533,8 @@ Environment::Environment(Environment* outer_env):
|
|||
{
|
||||
}
|
||||
|
||||
Environment::Environment(const Cell& source)
|
||||
Environment::Environment(const Cell& source):
|
||||
m_outer_env(nullptr)
|
||||
{
|
||||
std::list<Cell> list = std::get<std::list<Cell>>(source.data);
|
||||
|
||||
|
@ -557,6 +558,11 @@ void Environment::Set(const std::string& sym_key, Cell cell_value, bool force)
|
|||
if (m_sym_table.count(sym_key) && !force)
|
||||
return;
|
||||
|
||||
// I realllllly don't think this is the behavior we want.
|
||||
// It's just a hack to work around let* not handling nested environments correctly
|
||||
if (m_outer_env)
|
||||
m_outer_env->Set(sym_key, cell_value, force);
|
||||
|
||||
m_sym_table[sym_key] = cell_value;
|
||||
}
|
||||
|
||||
|
@ -564,7 +570,8 @@ Environment* Environment::Find(const std::string& sym_key)
|
|||
{
|
||||
if (m_sym_table.count(sym_key) != 0)
|
||||
return this;
|
||||
else if (m_outer_env != nullptr)
|
||||
|
||||
if (m_outer_env != nullptr)
|
||||
return m_outer_env->Find(sym_key);
|
||||
|
||||
return nullptr;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "Editor.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include <imgui.h>
|
||||
#include <ImGuizmo.h>
|
||||
#include <TextEditor.h>
|
||||
|
@ -14,6 +16,7 @@
|
|||
#include "KP3D_StringUtils.h"
|
||||
#include "KP3D_SystemUtils.h"
|
||||
#include "KP3D_Resources.h"
|
||||
#include "KP3D_Time.h"
|
||||
|
||||
#include "Sandbox.h"
|
||||
|
||||
|
@ -84,6 +87,7 @@ ksi::Cell FMakeSectorFromPoints()
|
|||
s->walls.push_back(wall);
|
||||
}
|
||||
|
||||
sandbox->editor.PushAction();
|
||||
sandbox->map.sectors.push_back(s);
|
||||
sandbox->editor.RebuildMap();
|
||||
|
||||
|
@ -184,7 +188,6 @@ Editor::Editor()
|
|||
|
||||
auto lang = TextEditor::LanguageDefinition::Lisp();
|
||||
build_text_editor.SetLanguageDefinition(lang);
|
||||
build_text_editor.SetText("()");
|
||||
|
||||
kp3d::EventBus::Subscribe(this, &Editor::OnScrollWheel);
|
||||
kp3d::EventBus::Subscribe(this, &Editor::OnKeyPress);
|
||||
|
@ -192,6 +195,29 @@ Editor::Editor()
|
|||
// Put in our fancy Lisp integration stuff
|
||||
kp3d::console::environment.Set("make-sector-from-points", FMakeSectorFromPoints());
|
||||
kp3d::console::environment.Set("fill-selection", FFillSelection());
|
||||
|
||||
// Load user scripts
|
||||
m_scripts.emplace("New Script", "()");
|
||||
m_selected_script = "New Script";
|
||||
build_text_editor.SetText(m_scripts[m_selected_script]);
|
||||
for (const auto& entry: std::filesystem::recursive_directory_iterator(kp3d::sys::GetScriptsDir()))
|
||||
{
|
||||
if (entry.is_directory())
|
||||
continue;
|
||||
std::string path_str = entry.path().string();
|
||||
if (!kp3d::str::EndsWith(path_str, ".scm"))
|
||||
continue;
|
||||
std::string filename = std::filesystem::proximate(path_str, kp3d::sys::GetScriptsDir()).string();
|
||||
KP3D_LOG_INFO("Loading script: {}", filename);
|
||||
std::string script;
|
||||
kp3d::ErrCode status = kp3d::sys::LoadTextFile(path_str, script);
|
||||
if (status == kp3d::FAILURE)
|
||||
{
|
||||
KP3D_LOG_ERROR("Failed to load script: {}", path_str);
|
||||
continue;
|
||||
}
|
||||
m_scripts.emplace(filename, script);
|
||||
}
|
||||
}
|
||||
|
||||
Editor::~Editor()
|
||||
|
@ -212,6 +238,16 @@ void Editor::Update()
|
|||
KEY_SHORTCUT(SPACE, m_mode = MODE_NORMAL);
|
||||
KEY_SHORTCUT(V, m_mode = MODE_BUILD);
|
||||
|
||||
KEY_SHORTCUT(Z, {
|
||||
if (sandbox->IsKeyDown(kp3d::KEY_LCTRL))
|
||||
Undo();
|
||||
});
|
||||
|
||||
KEY_SHORTCUT(Y, {
|
||||
if (sandbox->IsKeyDown(kp3d::KEY_LCTRL))
|
||||
Redo();
|
||||
});
|
||||
|
||||
switch (m_mode)
|
||||
{
|
||||
case MODE_BUILD: UpdateModeBuild(); break;
|
||||
|
@ -258,7 +294,10 @@ void Editor::RenderStem(kp3d::Vec3 position)
|
|||
|
||||
void Editor::RebuildMap()
|
||||
{
|
||||
double start = kp3d::CurrentTimeInMilliseconds().count();
|
||||
sandbox->map.Rebuild(kp3d::GEN_NORMALS);
|
||||
|
||||
m_build_time = kp3d::CurrentTimeInMilliseconds().count() - start;
|
||||
}
|
||||
|
||||
void Editor::UpdateModeBuild()
|
||||
|
@ -290,6 +329,7 @@ void Editor::UpdateModeBuild()
|
|||
s->walls.push_back(wall);
|
||||
}
|
||||
|
||||
sandbox->editor.PushAction();
|
||||
sandbox->map.sectors.push_back(s);
|
||||
RebuildMap();
|
||||
|
||||
|
@ -317,7 +357,9 @@ void Editor::UpdateModeBuild()
|
|||
{
|
||||
if (sandbox->IsKeyDown(kp3d::KEY_LCTRL))
|
||||
{
|
||||
build_show_options = true;
|
||||
if (!points.empty())
|
||||
build_show_options = true;
|
||||
// build_text_editor.SetText(m_scripts[m_selected_script]);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -346,6 +388,7 @@ void Editor::UpdateModeBuild()
|
|||
s->walls.push_back(wall);
|
||||
}
|
||||
|
||||
sandbox->editor.PushAction();
|
||||
sandbox->map.sectors.push_back(s);
|
||||
RebuildMap();
|
||||
points.clear();
|
||||
|
@ -380,27 +423,50 @@ void Editor::RenderModeBuild()
|
|||
|
||||
if (build_show_options)
|
||||
{
|
||||
ImGui::SetNextWindowSize({320, 240}, ImGuiCond_FirstUseEver);
|
||||
ImGui::SetNextWindowSize({500, 400}, ImGuiCond_FirstUseEver);
|
||||
if (ImGui::Begin("Build Options", &build_show_options))
|
||||
{
|
||||
ImGui::SeparatorText("Shortcuts");
|
||||
if (ImGui::Button("build-box")) {} ImGui::SameLine();
|
||||
if (ImGui::Button("build-stairs")) {} ImGui::SameLine();
|
||||
if (ImGui::Button("build-slope")) {}
|
||||
// ImGui::SeparatorText("Shortcuts");
|
||||
// if (ImGui::Button("build-box")) {} ImGui::SameLine();
|
||||
// if (ImGui::Button("build-stairs")) {} ImGui::SameLine();
|
||||
// if (ImGui::Button("build-slope")) {}
|
||||
|
||||
ImGui::SeparatorText("Custom Routine");
|
||||
if (ImGui::Button("Evaluate"))
|
||||
// Left
|
||||
ImGui::BeginChild("left pane", ImVec2(150, 0), ImGuiChildFlags_Border | ImGuiChildFlags_ResizeX);
|
||||
for (const auto& [k, v]: m_scripts)
|
||||
{
|
||||
if (ImGui::Selectable(k.c_str(), m_selected_script == k))
|
||||
{
|
||||
m_selected_script = k;
|
||||
build_text_editor.SetText(m_scripts[m_selected_script]);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
ImGui::SameLine();
|
||||
|
||||
// Right
|
||||
ImGui::BeginGroup();
|
||||
ImGui::BeginChild("item view", ImVec2(0, -ImGui::GetFrameHeightWithSpacing())); // Leave room for 1 line below us
|
||||
ImGui::Text("Script: %s", m_selected_script.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
build_text_editor.Render("Lisp:");
|
||||
ImGui::PopFont();
|
||||
|
||||
ImGui::EndChild();
|
||||
if (ImGui::Button("Revert"))
|
||||
build_text_editor.SetText(m_scripts[m_selected_script]);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Run Script"))
|
||||
{
|
||||
std::string txt = build_text_editor.GetText();
|
||||
kp3d::console::SendKSI(txt);
|
||||
points.clear();
|
||||
build_has_start_pos = false;
|
||||
build_show_options = false;
|
||||
// KP3D_LOG_INFO("EVAL: {}", txt);
|
||||
}
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]);
|
||||
build_text_editor.Render("Lisp:");
|
||||
ImGui::PopFont();
|
||||
ImGui::EndGroup();
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
@ -542,6 +608,7 @@ void Editor::UpdateModeNormal()
|
|||
const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata);
|
||||
if (info.wall)
|
||||
{
|
||||
sandbox->editor.PushAction();
|
||||
// Remove a wall
|
||||
// This is kinda risky, might wanna pop open a warning or make a backup or something LOL
|
||||
for (const auto& sp : sandbox->map.sectors)
|
||||
|
@ -567,6 +634,7 @@ void Editor::UpdateModeNormal()
|
|||
else if (info.sector)
|
||||
{
|
||||
// Remove a sector. We'll want to remove any portals first
|
||||
sandbox->editor.PushAction();
|
||||
for (const auto& sp: sandbox->map.sectors)
|
||||
{
|
||||
if (sp.get() == info.sector)
|
||||
|
@ -612,6 +680,7 @@ void Editor::UpdateModeNormal()
|
|||
|
||||
try
|
||||
{
|
||||
sandbox->editor.PushAction();
|
||||
const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata);
|
||||
if (info.wall)
|
||||
{
|
||||
|
@ -650,6 +719,7 @@ void Editor::UpdateModeNormal()
|
|||
{
|
||||
try
|
||||
{
|
||||
sandbox->editor.PushAction();
|
||||
const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata);
|
||||
if (info.wall)// && !(info.wall->portal && (info.wall->flags & Wall::FLAG_OPENING) && !(info.wall->flags & Wall::FLAG_SUBSECTOR_OPENING)))
|
||||
{
|
||||
|
@ -794,8 +864,8 @@ void Editor::RenderUI()
|
|||
}
|
||||
if (ImGui::BeginMenu("Edit"))
|
||||
{
|
||||
if (ImGui::MenuItem("Undo")) {}
|
||||
if (ImGui::MenuItem("Redo")) {}
|
||||
if (ImGui::MenuItem("Undo")) { Undo(); }
|
||||
if (ImGui::MenuItem("Redo")) { Redo(); }
|
||||
ImGui::EndMenu();
|
||||
}
|
||||
if (ImGui::BeginMenu("View"))
|
||||
|
@ -926,6 +996,8 @@ void Editor::RenderUIInfo()
|
|||
ImGui::EndPopup();
|
||||
}
|
||||
}
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Map built in %fms\n", m_build_time);
|
||||
ImGui::End();
|
||||
}
|
||||
}
|
||||
|
@ -1121,6 +1193,46 @@ void Editor::OnKeyPress(const kp3d::KeyPressEvent* e)
|
|||
{
|
||||
}
|
||||
|
||||
void Editor::PushAction()
|
||||
{
|
||||
const size_t MAX_UNDOS = 50;
|
||||
previous_maps.push_back({sandbox->map.sectors});
|
||||
if (previous_maps.size() > MAX_UNDOS)
|
||||
previous_maps.erase(previous_maps.begin());
|
||||
changes++;
|
||||
}
|
||||
|
||||
void Editor::Undo()
|
||||
{
|
||||
if (previous_maps.empty())
|
||||
return;
|
||||
|
||||
next_maps.push_back({sandbox->map.sectors});
|
||||
sandbox->map.sectors = previous_maps.back().sectors;
|
||||
previous_maps.pop_back();
|
||||
changes--;
|
||||
RebuildMap();
|
||||
}
|
||||
|
||||
void Editor::Redo()
|
||||
{
|
||||
if (next_maps.empty())
|
||||
return;
|
||||
|
||||
PushAction();
|
||||
sandbox->map.sectors = next_maps.back().sectors;
|
||||
next_maps.pop_back();
|
||||
changes++;
|
||||
RebuildMap();
|
||||
}
|
||||
|
||||
void Editor::ResetActionStack()
|
||||
{
|
||||
changes = 0;
|
||||
previous_maps.clear();
|
||||
next_maps.clear();
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
void Editor::RenderLine(kp3d::Vec3 p0, kp3d::Vec3 p1, kp3d::uint color)
|
||||
|
|
|
@ -55,6 +55,21 @@ public:
|
|||
void OnScrollWheel(const kp3d::ScrollWheelEvent* e);
|
||||
void OnKeyPress(const kp3d::KeyPressEvent* e);
|
||||
|
||||
|
||||
// Undo/redo stack
|
||||
struct MapData
|
||||
{
|
||||
std::vector<std::shared_ptr<kp3d::Sector>> sectors;
|
||||
// things...
|
||||
};
|
||||
std::vector<MapData> previous_maps;
|
||||
std::vector<MapData> next_maps;
|
||||
int changes = 0;
|
||||
void PushAction();
|
||||
void Undo();
|
||||
void Redo();
|
||||
void ResetActionStack();
|
||||
|
||||
private:
|
||||
void RenderLine(kp3d::Vec3 start, kp3d::Vec3 end, kp3d::uint color);
|
||||
|
||||
|
@ -89,4 +104,9 @@ private:
|
|||
bool build_show_options = false;
|
||||
TextEditor build_text_editor;
|
||||
|
||||
std::map<std::string, std::string> m_scripts;
|
||||
std::string m_selected_script;
|
||||
|
||||
double m_build_time = 0.0;
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in a new issue