Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
101 commits
Select commit Hold shift + click to select a range
153af07
Setup initial editor scene
psyGamer Mar 10, 2024
2d1214b
Use Renogare font
psyGamer Mar 10, 2024
27abb15
Only show debug menu when inside a World
psyGamer Mar 10, 2024
af38d56
Add test window
psyGamer Mar 10, 2024
bc4be55
Render basic cube
psyGamer Mar 10, 2024
08db58c
Add camera movement
psyGamer Mar 10, 2024
174e6e9
Use mouse for rotation
psyGamer Mar 10, 2024
c8f5117
Add selection support
psyGamer Mar 10, 2024
6b29d78
Add position, rotation and scale definition properties
psyGamer Mar 10, 2024
7e5dee3
Add example color property
psyGamer Mar 10, 2024
cc132d3
Add reload support for the editor
psyGamer Mar 11, 2024
cbc6265
Rename Map to SledgeMap
psyGamer Mar 11, 2024
04d83b9
Abstract away sledge map loading to common Map type
psyGamer Mar 11, 2024
cd8865d
Setup initial Fuji map format loading
psyGamer Mar 11, 2024
cb7bd9c
Load basic metadata in Fuji format
psyGamer Mar 11, 2024
c1afe06
Add editor definition (de)serialization
psyGamer Mar 11, 2024
8666632
Separate the data and editor definition
psyGamer Mar 11, 2024
5220192
Load solid quad for test definition
psyGamer Mar 11, 2024
8f37772
Load the map in the editor
psyGamer Mar 11, 2024
a2a830f
Make editor camera "smaller"
psyGamer Mar 11, 2024
2911562
Use view-normal based shading, like Blender
psyGamer Mar 11, 2024
b0246b9
Highlight selected object with an outline
psyGamer Mar 11, 2024
64ce2a8
Fix highlight line wrapping around the screen edges
psyGamer Mar 11, 2024
e8e5998
Clean-up some accidental changes
psyGamer Mar 11, 2024
f93b484
Revert more accidental changes
psyGamer Mar 11, 2024
409eb26
Inherit World for editor
psyGamer Mar 12, 2024
31617ab
Re-add camera controls to the editor
psyGamer Mar 12, 2024
1d84b96
Tweak world rendering for editor
psyGamer Mar 12, 2024
557948f
Properly call Entered/Exited
psyGamer Mar 12, 2024
8a4a668
Setup ray casting for selecting actors
psyGamer Mar 12, 2024
b4feb3e
Determine ray cast direction based on mouse position
psyGamer Mar 12, 2024
adcef60
Port over Batcher3D from Celeste64-TAS
psyGamer Mar 12, 2024
73026bd
Fix object selection sometimes not working
psyGamer Mar 12, 2024
614da0a
Add OBB intersection method for the future
psyGamer Mar 12, 2024
067fa49
Highlight currently selected actor
psyGamer Mar 12, 2024
b48c889
Render selected outline in front of everything else
psyGamer Mar 12, 2024
5180474
Scale line thickness based on distance
psyGamer Mar 12, 2024
daedf3a
Attach ActorDefinition to Actor
psyGamer Mar 12, 2024
31c0d63
Mark ActorDefinition as dirty when value is changed
psyGamer Mar 12, 2024
a400b8e
Use oriented bounding boxes
psyGamer Mar 12, 2024
23e54cb
Merge remote-tracking branch 'upstream/dev' into editor
psyGamer Mar 12, 2024
57fc8fb
Merge remote-tracking branch 'upstream/dev' into editor
psyGamer Mar 13, 2024
5725fe3
Remove unused code from editor
psyGamer Mar 13, 2024
1a110fb
Recreate actors of definition every time its changed
psyGamer Mar 13, 2024
01fac4e
Re-enable map serialization
psyGamer Mar 13, 2024
13ae81c
Merge FujiMapWriter with FujiMap
psyGamer Mar 13, 2024
4e9c974
Draw world bounds of selected actors
psyGamer Mar 13, 2024
a8d7ffa
Merge remote-tracking branch 'upstream/dev' into editor
psyGamer Mar 14, 2024
a2d8454
Run format pass over editor
psyGamer Mar 14, 2024
8b21388
Code cleanup
psyGamer Mar 14, 2024
521eb7a
Fix ImGui not receiving keyboard inputs
psyGamer Mar 14, 2024
b0ecb9d
Add settings window for environment
psyGamer Mar 14, 2024
49d1289
Add main menu bar for changing view settings
psyGamer Mar 14, 2024
f3d874f
Properly handle toggling music/ambience
psyGamer Mar 14, 2024
30dcb8b
Add render distance and resolution options
psyGamer Mar 14, 2024
b9f2ed7
Fix "Audio Event doesn't exist" warnings
psyGamer Mar 14, 2024
27e1e69
Improve actor editor window
psyGamer Mar 14, 2024
29dbb61
Properly reload editor scene
psyGamer Mar 14, 2024
fc5e2f5
Add support for removing actors
psyGamer Mar 14, 2024
d17f925
Prevent snow from being selected
psyGamer Mar 14, 2024
e718b95
Add 720p resolution option
psyGamer Mar 14, 2024
2633876
Clean-up Batcher3D a bit
psyGamer Mar 15, 2024
c2842f6
Add cone shape to Batcher3D
psyGamer Mar 15, 2024
b87e1ba
Draw movement gizmos
psyGamer Mar 15, 2024
6b25fcc
Draw XYZ move cube
psyGamer Mar 15, 2024
bab4c11
Draw XZ/YZ/XY move planes
psyGamer Mar 15, 2024
f0f279e
Use better ray/OBB intersection method
psyGamer Mar 15, 2024
bd492fa
Add selection support for the position gizmo
psyGamer Mar 15, 2024
1fc6d68
Add dragging support to position gizmo
psyGamer Mar 15, 2024
16071bf
Refactor out gizmo logic into the gizmo itself
psyGamer Mar 16, 2024
3f9c305
Improve gizmo dragging experience
psyGamer Mar 16, 2024
9a3672d
Improve position gizmo colors
psyGamer Mar 16, 2024
65c0794
Choose much better colors for the axis
psyGamer Mar 16, 2024
7a33aa2
Code formatting
psyGamer Mar 16, 2024
8885c57
Apply outline to gizmos
psyGamer Mar 16, 2024
c105b76
Fix definitions not being added sometimes
psyGamer Mar 16, 2024
32d158a
Generalize PositionGizmo to any property
psyGamer Mar 16, 2024
502835c
Tweak names of attributes for definition properties
psyGamer Mar 16, 2024
0519ffb
Remove requirement of SpikeBlock.Definition for dragging
psyGamer Mar 16, 2024
8a89d5d
Fix RenderGui of custom properties
psyGamer Mar 16, 2024
f7449e6
Add basic ActorDefinition for solids
psyGamer Mar 16, 2024
5cf5292
Fix CVE Severity 9.8 critical in map parser
psyGamer Mar 16, 2024
0e746f7
Add custom SelectionTypes for definitions
psyGamer Mar 17, 2024
a8277fb
Blacklist SelectionTypes in (de)serialization
psyGamer Mar 17, 2024
ecfe0bf
Add hover and drag support to SelectionTargets
psyGamer Mar 17, 2024
1fae743
Refactor gizmos to use SelectionTargets
psyGamer Mar 17, 2024
936119b
Fully refactor gizmos for SelectionTargets
psyGamer Mar 17, 2024
33dc92d
Add vertex position editing
psyGamer Mar 17, 2024
0dd00a1
Allow editing vertices of adjacent faces
psyGamer Mar 17, 2024
357b6c7
Improve selection logic
psyGamer Mar 17, 2024
37c5b2a
Use smaller gizmos for vertex editing
psyGamer Mar 17, 2024
a4a9cdf
Merge remote-tracking branch 'upstream/dev' into editor
psyGamer Apr 4, 2024
6098895
Generalize GeometryDefinition
psyGamer Apr 4, 2024
faeba70
Setup window for actor selection
psyGamer Apr 4, 2024
105624d
Add debug key to enable ImGui demo window
psyGamer Apr 4, 2024
baaac0d
Setup an initial tool system
psyGamer Apr 4, 2024
f7b68aa
Add a tool selection window
psyGamer Apr 4, 2024
adbcd65
Only enable selection for certain tools
psyGamer Apr 4, 2024
4aebe50
Implement basic actor placement
psyGamer Apr 4, 2024
4bf43a9
Add toggle for playing animations
psyGamer Apr 4, 2024
df4043c
Access top-scene in crash handler more safely
psyGamer Apr 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions Content/Shaders/EditorEdge.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
VERTEX:
#version 330

layout(location=0) in vec2 a_pos;
layout(location=1) in vec2 a_tex;


out vec2 v_tex;

void main(void)
{
gl_Position = vec4(a_pos, 0, 1);
v_tex = a_tex;

}

FRAGMENT:
#version 330
#include Partials/Methods.gl

uniform sampler2D u_objectID;
uniform float u_selectedID;

uniform vec2 u_pixel;
uniform vec4 u_edge;

in vec2 v_tex;
out vec4 o_color;

float objectID(vec2 uv)
{
const float Eps = 0.0001;
return texture(u_objectID, clamp(uv, vec2(Eps), vec2(1.0 - Eps))).r;
}

void main(void)
{
float a = objectID(v_tex + vec2(u_pixel.x, 0));
float b = objectID(v_tex + vec2(-u_pixel.x, 0));
float c = objectID(v_tex + vec2(0, u_pixel.y));
float d = objectID(v_tex + vec2(0, -u_pixel.y));

float it = objectID(v_tex);
float other =
a * 0.25 +
b * 0.25 +
c * 0.25 +
d * 0.25;

float edge = step(0.0001, other - it);

if (u_selectedID == 0 || edge == 0 || !(a == u_selectedID || b == u_selectedID || c == u_selectedID || d == u_selectedID || it == u_selectedID))
discard;

o_color = vec4(vec3(u_edge), 1);
}

70 changes: 70 additions & 0 deletions Content/Shaders/EditorWorld.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
VERTEX:
#version 330
#include Partials/Methods.gl

uniform mat4 u_mvp;
uniform mat4 u_model;
uniform mat4 u_view;

layout(location=0) in vec3 a_position;
layout(location=1) in vec2 a_tex;
layout(location=2) in vec3 a_color;
layout(location=3) in vec3 a_normal;

out vec2 v_tex;
out vec3 v_color;
out vec3 v_normal;
out vec3 v_world;

void main(void)
{
gl_Position = u_mvp * vec4(a_position, 1.0);

v_tex = a_tex;
v_color = a_color;
v_normal = TransformNormal(a_normal, u_view * u_model);
v_world = vec3(u_model * vec4(a_position, 1.0));
}

FRAGMENT:
#version 330
#include Partials/Methods.gl

uniform sampler2D u_texture;
uniform vec4 u_color;
uniform float u_near;
uniform float u_far;
uniform vec3 u_sun;
//uniform float u_cutout;
uniform float u_objectID;

in vec2 v_tex;
in vec3 v_normal;
in vec3 v_color;
in vec3 v_world;

layout(location = 0) out vec4 o_color;
layout(location = 1) out vec4 o_objectID;

void main(void)
{
// Get texture color
vec4 src = texture(u_texture, v_tex) * u_color * vec4(v_color, 1);

// TODO: only enable if you want ModelFlags.Cutout types to work, didn't end up using
// if (src.a < u_cutout)
// discard;

// Apply depth values
float depth = LinearizeDepth(gl_FragCoord.z, u_near, u_far);
gl_FragDepth = depth;

// Apply shading based on normal relative to the camera
float shade = clamp(dot(v_normal, vec3(0, 0, 1)), 0.2, 1.0);
src.rgb *= vec3(shade);

o_color = vec4(src.rgb, 1);
// TODO: Support object IDs above 255, since its just 8bits
// NOTE: This is still only a float output, however we need to set alpha to avoid weird blending.
o_objectID = vec4(u_objectID / 255.0, 0, 0, 1);
}
14 changes: 14 additions & 0 deletions Source/Actors/Actor.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using Celeste64.Mod.Editor;

namespace Celeste64;

public class Actor
Expand All @@ -12,6 +14,18 @@ public class Actor
protected BoundingBox worldBounds;
protected bool dirty = true;

public readonly Type? DefinitionType;
public readonly ActorDefinition? _Data;

protected Actor(Type? definitionType = null)
{
DefinitionType = definitionType;
if (DefinitionType != null)
{
_Data = Activator.CreateInstance(DefinitionType) as ActorDefinition;
}
}

/// <summary>
/// Optional GroupName, used by Strawberries to check what unlocks them. Can
/// be used by other stuff for whatever.
Expand Down
2 changes: 1 addition & 1 deletion Source/Actors/Attacher.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace Celeste64;

public abstract class Attacher : Actor, IRidePlatforms
public abstract class Attacher(Type? definitionType = null) : Actor(definitionType), IRidePlatforms
{
public virtual Vec3 AttachNormal => -Vec3.UnitZ;
public virtual Vec3 AttachOrigin => Position;
Expand Down
77 changes: 77 additions & 0 deletions Source/Actors/Solid.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,84 @@
using Celeste64.Mod;
using Celeste64.Mod.Editor;
using ImGuiNET;
using System.Runtime.InteropServices;

namespace Celeste64;

public class Solid : Actor, IHaveModels
{
public class Definition : GeometryDefinition
{
protected override Matrix Transform => Matrix.CreateTranslation(Position);

[SpecialProperty(SpecialPropertyType.PositionXYZ)]
public Vec3 Position { get; set; }

public override Actor[] Load(World.WorldType type)
{
// Calculate bounds
var bounds = new BoundingBox();
foreach (var face in Faces)
{
var faceMin = face.Select(idx => Vertices[idx]).Aggregate(Vec3.Min);
var faceMax = face.Select(idx => Vertices[idx]).Aggregate(Vec3.Max);
bounds = new BoundingBox(Vec3.Min(bounds.Min, faceMin), Vec3.Max(bounds.Max, faceMax));
}

// Generate visual / collision mesh
var colliderVertices = new List<Vec3>();
var colliderFaces = new List<Face>();

var meshVertices = new List<Vertex>();
var meshIndices = new List<int>();

foreach (var face in Faces)
{
int vertexIndex = colliderVertices.Count;
var plane = Plane.CreateFromVertices(Vertices[face[0]], Vertices[face[1]], Vertices[face[2]]);

colliderFaces.Add(new Face
{
Plane = plane,
VertexStart = vertexIndex,
VertexCount = face.Count
});

// Triangulate the mesh
for (int i = 0; i < face.Count - 2; i++)
{
meshIndices.Add(vertexIndex + 0);
meshIndices.Add(vertexIndex + i + 1);
meshIndices.Add(vertexIndex + i + 2);
}

// The center of the bounding box should always be <0, 0, 0>
colliderVertices.AddRange(face.Select(idx => Vertices[idx] - bounds.Center));
meshVertices.AddRange(face.Select(idx => new Vertex(
position: Vertices[idx] - bounds.Center,
texcoord: Vec2.Zero,
color: Vec3.One,
normal: plane.Normal)));
}

var solid = new Solid();
solid.Model.Mesh.SetVertices<Vertex>(CollectionsMarshal.AsSpan(meshVertices));
solid.Model.Mesh.SetIndices<int>(CollectionsMarshal.AsSpan(meshIndices));
solid.Model.Materials.Add(new DefaultMaterial(Assets.Textures["wall"]));
solid.Model.Parts.Add(new SimpleModel.Part(0, 0, meshIndices.Count));

solid.LocalBounds = new BoundingBox(
colliderVertices.Aggregate(Vec3.Min),
colliderVertices.Aggregate(Vec3.Max)
);
solid.LocalVertices = colliderVertices.ToArray();
solid.LocalFaces = colliderFaces.ToArray();
solid.Position = Position + bounds.Center;

return [solid];
}
}

/// <summary>
/// If we're currently solid
/// </summary>
Expand Down
26 changes: 24 additions & 2 deletions Source/Actors/SpikeBlock.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,29 @@
namespace Celeste64;
using Celeste64.Mod.Editor;

public class SpikeBlock : Attacher, IHaveModels
namespace Celeste64;

public class SpikeBlock() : Attacher(typeof(Definition)), IHaveModels
{
public class Definition : ActorDefinition
{
[SpecialProperty(SpecialPropertyType.PositionXYZ)]
public Vec3 Position { get; set; } = Vec3.Zero;
public Vec3 Rotation { get; set; } = Vec3.Zero;
public Vec3 Size { get; set; } = new Vec3(50.0f, 10.0f, 100.0f);

public override Actor[] Load(World.WorldType type)
{
return [new SpikeBlock
{
Position = Position,
RotationXYZ = Rotation * Calc.DegToRad,
LocalBounds = new BoundingBox(-Size / 2.0f, Size / 2.0f)
}];
}
}

private Definition Data => (Definition)_Data!;

public SimpleModel? Model;
public Vec3 Direction;

Expand Down
30 changes: 24 additions & 6 deletions Source/Data/Assets.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Celeste64.Mod;
using Celeste64.Mod.Editor;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Text;
Expand All @@ -12,7 +13,8 @@ public static class Assets
public const string AssetFolder = "Content";

public const string MapsFolder = "Maps";
public const string MapsExtension = "map";
public const string MapsExtensionSledge = "map";
public const string MapsExtensionFuji = "bin";
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like "bin" might be a pretty common file extension in general. If this is a custom format, we may want to come up with a custom extension, just to avoid confusion.


public const string TexturesFolder = "Textures";
public const string TexturesExtension = "png";
Expand Down Expand Up @@ -112,7 +114,7 @@ public static void Load()
Music.Clear();
Audio.Unload();

Map.ModActorFactories.Clear();
SledgeMap.ModActorFactories.Clear();
ModLoader.RegisterAllMods();

var maps = new ConcurrentBag<(Map, GameMod)>();
Expand All @@ -126,7 +128,7 @@ public static void Load()
// NOTE: Make sure to update ModManager.OnModFileChanged() as well, for hot-reloading to work!

var globalFs = ModManager.Instance.GlobalFilesystem;
foreach (var (file, mod) in globalFs.FindFilesInDirectoryRecursiveWithMod(MapsFolder, MapsExtension))
foreach (var (file, mod) in globalFs.FindFilesInDirectoryRecursiveWithMod(MapsFolder, MapsExtensionSledge))
{
// Skip the "autosave" folder
if (file.StartsWith($"{MapsFolder}/autosave", StringComparison.OrdinalIgnoreCase))
Expand All @@ -135,7 +137,23 @@ public static void Load()
tasks.Add(Task.Run(() =>
{
if (mod.Filesystem != null && mod.Filesystem.TryOpenFile(file,
stream => new Map(GetResourceNameFromVirt(file, MapsFolder), file, stream), out var map))
stream => new SledgeMap(GetResourceNameFromVirt(file, MapsFolder), file, stream), out var map))
{
maps.Add((map, mod));
}
}));
}
foreach (var (file, mod) in globalFs.FindFilesInDirectoryRecursiveWithMod(MapsFolder, MapsExtensionFuji))
{
// Skip the "autosave" folder
if (file.StartsWith($"{MapsFolder}/autosave", StringComparison.OrdinalIgnoreCase))
continue;

tasks.Add(Task.Run(() =>
{
var fullPath = mod.Filesystem is FolderModFilesystem fs ? fs.VirtToRealPath(file) : null;
if (mod.Filesystem != null && mod.Filesystem.TryOpenFile(file,
stream => new FujiMap(GetResourceNameFromVirt(file, MapsFolder), file, stream, fullPath), out var map))
{
maps.Add((map, mod));
}
Expand Down Expand Up @@ -251,8 +269,8 @@ public static void Load()
Levels.AddRange(levels);
}

// if (mod.Filesystem != null && mod.Filesystem.TryOpenFile("Dialog.json",
// stream => JsonSerializer.Deserialize(stream, DialogLineDictContext.Default.DictionaryStringListDialogLine) ?? [],
// if (mod.Filesystem != null && mod.Filesystem.TryOpenFile("Dialog.json",
// stream => JsonSerializer.Deserialize(stream, DialogLineDictContext.Default.DictionaryStringListDialogLine) ?? [],
// out var dialog))
// {
// foreach (var (key, value) in dialog)
Expand Down
Loading