Add more scripts, fix let* binding, add undo/redo

This commit is contained in:
KP 2024-08-03 23:46:31 -05:00
parent 37977981ad
commit 0579c39da6
8 changed files with 253 additions and 19 deletions

View 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) ))))

View file

@ -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

View file

@ -3005,7 +3005,9 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lisp()
langDef.mKeywords.insert(k); langDef.mKeywords.insert(k);
static const char* const identifiers[] = { 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 // Map editor specific
"make-sector-from-points", "make-sector-from-points",
"fill-selection", "fill-selection",
@ -3014,7 +3016,7 @@ const TextEditor::LanguageDefinition& TextEditor::LanguageDefinition::Lisp()
for (auto& k : identifiers) for (auto& k : identifiers)
{ {
Identifier id; Identifier id;
id.mDeclaration = "Built-in function"; id.mDeclaration = "Built-in function/symbol";
langDef.mIdentifiers.insert(std::make_pair(std::string(k), id)); langDef.mIdentifiers.insert(std::make_pair(std::string(k), id));
} }

View file

@ -82,6 +82,11 @@ std::string GetAudioDir()
return GetResourceDir() + "audio/"; return GetResourceDir() + "audio/";
} }
std::string GetScriptsDir()
{
return GetResourceDir() + "scripts/";
}
ErrCode CreateDir(const std::string& path) ErrCode CreateDir(const std::string& path)
{ {
return static_cast<ErrCode>(!std::filesystem::create_directory(path)); return static_cast<ErrCode>(!std::filesystem::create_directory(path));

View file

@ -18,6 +18,7 @@ std::string GetModelDir();
std::string GetBitmapDir(); std::string GetBitmapDir();
std::string GetFontDir(); std::string GetFontDir();
std::string GetAudioDir(); std::string GetAudioDir();
std::string GetScriptsDir();
ErrCode CreateDir(const std::string& path); ErrCode CreateDir(const std::string& path);
ErrCode DeleteDir(const std::string& path); ErrCode DeleteDir(const std::string& path);

View file

@ -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); 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) if (m_sym_table.count(sym_key) && !force)
return; 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; 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) if (m_sym_table.count(sym_key) != 0)
return this; return this;
else if (m_outer_env != nullptr)
if (m_outer_env != nullptr)
return m_outer_env->Find(sym_key); return m_outer_env->Find(sym_key);
return nullptr; return nullptr;

View file

@ -1,5 +1,7 @@
#include "Editor.h" #include "Editor.h"
#include <filesystem>
#include <imgui.h> #include <imgui.h>
#include <ImGuizmo.h> #include <ImGuizmo.h>
#include <TextEditor.h> #include <TextEditor.h>
@ -14,6 +16,7 @@
#include "KP3D_StringUtils.h" #include "KP3D_StringUtils.h"
#include "KP3D_SystemUtils.h" #include "KP3D_SystemUtils.h"
#include "KP3D_Resources.h" #include "KP3D_Resources.h"
#include "KP3D_Time.h"
#include "Sandbox.h" #include "Sandbox.h"
@ -84,6 +87,7 @@ ksi::Cell FMakeSectorFromPoints()
s->walls.push_back(wall); s->walls.push_back(wall);
} }
sandbox->editor.PushAction();
sandbox->map.sectors.push_back(s); sandbox->map.sectors.push_back(s);
sandbox->editor.RebuildMap(); sandbox->editor.RebuildMap();
@ -184,7 +188,6 @@ Editor::Editor()
auto lang = TextEditor::LanguageDefinition::Lisp(); auto lang = TextEditor::LanguageDefinition::Lisp();
build_text_editor.SetLanguageDefinition(lang); build_text_editor.SetLanguageDefinition(lang);
build_text_editor.SetText("()");
kp3d::EventBus::Subscribe(this, &Editor::OnScrollWheel); kp3d::EventBus::Subscribe(this, &Editor::OnScrollWheel);
kp3d::EventBus::Subscribe(this, &Editor::OnKeyPress); kp3d::EventBus::Subscribe(this, &Editor::OnKeyPress);
@ -192,6 +195,29 @@ Editor::Editor()
// Put in our fancy Lisp integration stuff // Put in our fancy Lisp integration stuff
kp3d::console::environment.Set("make-sector-from-points", FMakeSectorFromPoints()); kp3d::console::environment.Set("make-sector-from-points", FMakeSectorFromPoints());
kp3d::console::environment.Set("fill-selection", FFillSelection()); 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() Editor::~Editor()
@ -212,6 +238,16 @@ void Editor::Update()
KEY_SHORTCUT(SPACE, m_mode = MODE_NORMAL); KEY_SHORTCUT(SPACE, m_mode = MODE_NORMAL);
KEY_SHORTCUT(V, m_mode = MODE_BUILD); 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) switch (m_mode)
{ {
case MODE_BUILD: UpdateModeBuild(); break; case MODE_BUILD: UpdateModeBuild(); break;
@ -258,7 +294,10 @@ void Editor::RenderStem(kp3d::Vec3 position)
void Editor::RebuildMap() void Editor::RebuildMap()
{ {
double start = kp3d::CurrentTimeInMilliseconds().count();
sandbox->map.Rebuild(kp3d::GEN_NORMALS); sandbox->map.Rebuild(kp3d::GEN_NORMALS);
m_build_time = kp3d::CurrentTimeInMilliseconds().count() - start;
} }
void Editor::UpdateModeBuild() void Editor::UpdateModeBuild()
@ -290,6 +329,7 @@ void Editor::UpdateModeBuild()
s->walls.push_back(wall); s->walls.push_back(wall);
} }
sandbox->editor.PushAction();
sandbox->map.sectors.push_back(s); sandbox->map.sectors.push_back(s);
RebuildMap(); RebuildMap();
@ -317,7 +357,9 @@ void Editor::UpdateModeBuild()
{ {
if (sandbox->IsKeyDown(kp3d::KEY_LCTRL)) 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 else
{ {
@ -346,6 +388,7 @@ void Editor::UpdateModeBuild()
s->walls.push_back(wall); s->walls.push_back(wall);
} }
sandbox->editor.PushAction();
sandbox->map.sectors.push_back(s); sandbox->map.sectors.push_back(s);
RebuildMap(); RebuildMap();
points.clear(); points.clear();
@ -380,27 +423,50 @@ void Editor::RenderModeBuild()
if (build_show_options) if (build_show_options)
{ {
ImGui::SetNextWindowSize({320, 240}, ImGuiCond_FirstUseEver); ImGui::SetNextWindowSize({500, 400}, ImGuiCond_FirstUseEver);
if (ImGui::Begin("Build Options", &build_show_options)) if (ImGui::Begin("Build Options", &build_show_options))
{ {
ImGui::SeparatorText("Shortcuts"); // ImGui::SeparatorText("Shortcuts");
if (ImGui::Button("build-box")) {} ImGui::SameLine(); // if (ImGui::Button("build-box")) {} ImGui::SameLine();
if (ImGui::Button("build-stairs")) {} ImGui::SameLine(); // if (ImGui::Button("build-stairs")) {} ImGui::SameLine();
if (ImGui::Button("build-slope")) {} // if (ImGui::Button("build-slope")) {}
ImGui::SeparatorText("Custom Routine"); // Left
if (ImGui::Button("Evaluate")) 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(); std::string txt = build_text_editor.GetText();
kp3d::console::SendKSI(txt); kp3d::console::SendKSI(txt);
points.clear(); points.clear();
build_has_start_pos = false; build_has_start_pos = false;
build_show_options = false; build_show_options = false;
// KP3D_LOG_INFO("EVAL: {}", txt);
} }
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[1]); ImGui::EndGroup();
build_text_editor.Render("Lisp:");
ImGui::PopFont();
ImGui::End(); ImGui::End();
} }
@ -542,6 +608,7 @@ void Editor::UpdateModeNormal()
const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata); const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata);
if (info.wall) if (info.wall)
{ {
sandbox->editor.PushAction();
// Remove a wall // Remove a wall
// This is kinda risky, might wanna pop open a warning or make a backup or something LOL // This is kinda risky, might wanna pop open a warning or make a backup or something LOL
for (const auto& sp : sandbox->map.sectors) for (const auto& sp : sandbox->map.sectors)
@ -567,6 +634,7 @@ void Editor::UpdateModeNormal()
else if (info.sector) else if (info.sector)
{ {
// Remove a sector. We'll want to remove any portals first // Remove a sector. We'll want to remove any portals first
sandbox->editor.PushAction();
for (const auto& sp: sandbox->map.sectors) for (const auto& sp: sandbox->map.sectors)
{ {
if (sp.get() == info.sector) if (sp.get() == info.sector)
@ -612,6 +680,7 @@ void Editor::UpdateModeNormal()
try try
{ {
sandbox->editor.PushAction();
const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata); const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata);
if (info.wall) if (info.wall)
{ {
@ -650,6 +719,7 @@ void Editor::UpdateModeNormal()
{ {
try try
{ {
sandbox->editor.PushAction();
const auto& info = std::any_cast<kp3d::BatchSectorInfo>(kp3d::editor_hovered_batch[0]->userdata); 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))) 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::BeginMenu("Edit"))
{ {
if (ImGui::MenuItem("Undo")) {} if (ImGui::MenuItem("Undo")) { Undo(); }
if (ImGui::MenuItem("Redo")) {} if (ImGui::MenuItem("Redo")) { Redo(); }
ImGui::EndMenu(); ImGui::EndMenu();
} }
if (ImGui::BeginMenu("View")) if (ImGui::BeginMenu("View"))
@ -926,6 +996,8 @@ void Editor::RenderUIInfo()
ImGui::EndPopup(); ImGui::EndPopup();
} }
} }
ImGui::Separator();
ImGui::Text("Map built in %fms\n", m_build_time);
ImGui::End(); 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) void Editor::RenderLine(kp3d::Vec3 p0, kp3d::Vec3 p1, kp3d::uint color)

View file

@ -55,6 +55,21 @@ public:
void OnScrollWheel(const kp3d::ScrollWheelEvent* e); void OnScrollWheel(const kp3d::ScrollWheelEvent* e);
void OnKeyPress(const kp3d::KeyPressEvent* 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: private:
void RenderLine(kp3d::Vec3 start, kp3d::Vec3 end, kp3d::uint color); void RenderLine(kp3d::Vec3 start, kp3d::Vec3 end, kp3d::uint color);
@ -89,4 +104,9 @@ private:
bool build_show_options = false; bool build_show_options = false;
TextEditor build_text_editor; TextEditor build_text_editor;
std::map<std::string, std::string> m_scripts;
std::string m_selected_script;
double m_build_time = 0.0;
}; };