mirror of
https://github.com/aharabada/glitchy-engine-beef.git
synced 2026-09-05 13:01:52 +00:00
Basic Entity hierarchy sync
This commit is contained in:
@@ -9,5 +9,5 @@ StartupObject = "EditorUI.Program"
|
|||||||
BuildKind = "StaticLib"
|
BuildKind = "StaticLib"
|
||||||
BuildCommandsOnCompile = "IfFilesChanged"
|
BuildCommandsOnCompile = "IfFilesChanged"
|
||||||
BuildCommandsOnRun = "IfFilesChanged"
|
BuildCommandsOnRun = "IfFilesChanged"
|
||||||
PreBuildCmds = ["npm run build"]
|
PreBuildCmds = ["npm run build:debug"]
|
||||||
PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist\")"]
|
PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist\")"]
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "tsc -b && vite build",
|
"build": "tsc -b && vite build",
|
||||||
|
"build:debug": "tsc -b && vite --config vite.config.debug.ts build",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|
||||||
--menu-item-height: 2em;
|
--menu-item-height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove indentation from lists for menu */
|
/* Remove indentation from lists for menu */
|
||||||
@@ -25,7 +25,7 @@
|
|||||||
.menu-item
|
.menu-item
|
||||||
{
|
{
|
||||||
/* Doesn't change our position, but does something to it, so that
|
/* Doesn't change our position, but does something to it, so that
|
||||||
following absolutes are absolute to us? */
|
following absolutes are relative to us? */
|
||||||
position: relative;
|
position: relative;
|
||||||
height: var(--menu-item-height);
|
height: var(--menu-item-height);
|
||||||
}
|
}
|
||||||
@@ -105,6 +105,7 @@
|
|||||||
background-color: var(--menu-bar-background-color-click);
|
background-color: var(--menu-bar-background-color-click);
|
||||||
list-style: none;
|
list-style: none;
|
||||||
z-index: 10;
|
z-index: 10;
|
||||||
|
height: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Show submenu */
|
/* Show submenu */
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
export interface IEngineGlue {
|
import { Entity } from "./Entity.ts";
|
||||||
|
|
||||||
|
export interface IEngineGlue
|
||||||
|
{
|
||||||
// Titlebar Events
|
// Titlebar Events
|
||||||
handleClickCloseWindow(): void;
|
handleClickCloseWindow(): void;
|
||||||
handleClickMaximizeWindow(): void;
|
handleClickMaximizeWindow(): void;
|
||||||
@@ -9,6 +12,9 @@
|
|||||||
handleHoverNonClientArea(hover: boolean): void;
|
handleHoverNonClientArea(hover: boolean): void;
|
||||||
|
|
||||||
onClickCreateEmptyEntity(): void;
|
onClickCreateEmptyEntity(): void;
|
||||||
|
|
||||||
|
requestEntityHierarchyUpdate(): void;
|
||||||
|
onUpdateEntities?: (entities: Entity[]) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
function logNotImplemented(functionName: string)
|
function logNotImplemented(functionName: string)
|
||||||
@@ -54,19 +60,38 @@ class DevEngineGlue implements IEngineGlue {
|
|||||||
{
|
{
|
||||||
logNotImplemented("onClickCreateEmptyEntity");
|
logNotImplemented("onClickCreateEmptyEntity");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestEntityHierarchyUpdate(): void
|
||||||
|
{
|
||||||
|
logNotImplemented("requestEntityHierarchyUpdate");
|
||||||
|
}
|
||||||
|
|
||||||
|
private callFromEngine_updateEntities(entities: Entity[]): void
|
||||||
|
{
|
||||||
|
console.log("Yeah!")
|
||||||
|
|
||||||
|
if (EngineGlue.onUpdateEntities)
|
||||||
|
{
|
||||||
|
EngineGlue.onUpdateEntities(entities);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logNotImplemented("onUpdateEntities");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Globales Objekt deklarieren
|
declare global
|
||||||
declare global {
|
{
|
||||||
interface Window {
|
interface Window
|
||||||
|
{
|
||||||
EngineGlue: IEngineGlue;
|
EngineGlue: IEngineGlue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialisierung: DevEngineGlue als Fallback setzen wenn noch kein EngineGlue existiert
|
if (typeof window.EngineGlue === 'undefined')
|
||||||
if (typeof window.EngineGlue === 'undefined') {
|
{
|
||||||
window.EngineGlue = new DevEngineGlue();
|
window.EngineGlue = new DevEngineGlue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Export für einfacheren Zugriff in der Anwendung
|
|
||||||
export const EngineGlue: IEngineGlue = window.EngineGlue;
|
export const EngineGlue: IEngineGlue = window.EngineGlue;
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
export type EntityId = number;
|
||||||
|
|
||||||
|
export class Entity
|
||||||
|
{
|
||||||
|
name: string;
|
||||||
|
id: EntityId;
|
||||||
|
visible: boolean = true;
|
||||||
|
children: EntityId[];
|
||||||
|
|
||||||
|
constructor(name: string, key: number, children: EntityId[] = [])
|
||||||
|
{
|
||||||
|
this.name = name;
|
||||||
|
this.id = key;
|
||||||
|
this.children = children;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
import {MenuBar, MenuItem} from "../Components/Menu.tsx";
|
import {MenuBar, MenuItem} from "../Components/Menu.tsx";
|
||||||
|
|
||||||
import "./EntityHierarchyWindow.css";
|
import "./EntityHierarchyWindow.css";
|
||||||
import {MouseEventHandler, ReactElement, MouseEvent} from "react";
|
import {MouseEvent, MouseEventHandler, ReactElement, useEffect} from "react";
|
||||||
|
|
||||||
import IconVisible from "../assets/Icons/AntDesign/eye-visible.svg";
|
import IconVisible from "../assets/Icons/AntDesign/eye-visible.svg";
|
||||||
import IconInvisible from "../assets/Icons/AntDesign/eye-invisible.svg";
|
import IconInvisible from "../assets/Icons/AntDesign/eye-invisible.svg";
|
||||||
@@ -10,47 +10,61 @@ import {Updater, useImmer} from "use-immer";
|
|||||||
|
|
||||||
import {EngineGlue} from "../EngineGlue";
|
import {EngineGlue} from "../EngineGlue";
|
||||||
|
|
||||||
type EntityId = number;
|
import {enableMapSet} from "immer"
|
||||||
|
import {Entity, EntityId} from "../Entity.ts";
|
||||||
|
|
||||||
class Entity
|
enableMapSet()
|
||||||
{
|
|
||||||
name: string;
|
|
||||||
id: EntityId;
|
|
||||||
visible: boolean = true;
|
|
||||||
children: EntityId[];
|
|
||||||
|
|
||||||
constructor(name: string, key: number, children: EntityId[] = [])
|
|
||||||
{
|
|
||||||
this.name = name;
|
|
||||||
this.id = key;
|
|
||||||
this.children = children;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type EntityMap = {
|
type EntityMap = {
|
||||||
selectedIds: EntityId[];
|
selectedIds: Set<EntityId>;
|
||||||
[id: EntityId]: Entity;
|
[id: EntityId]: Entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
const entityHierarchy: EntityMap = {
|
const entityHierarchy: EntityMap = {
|
||||||
selectedIds: [],
|
selectedIds: new Set<EntityId>(),
|
||||||
|
// 0 is hardcoded to be the root. The editor must provide this "pseudo"-entity.
|
||||||
0: new Entity("Entity Root", 0, [1, 4]),
|
0: new Entity("Root", 0, [])
|
||||||
1: new Entity("Entity Entity 1", 1, [2, 3]),
|
|
||||||
2: new Entity("Entity Entity 1.1", 2),
|
|
||||||
3: new Entity("Entity Entity 1.2", 3),
|
|
||||||
4: new Entity("Entity Entity 2", 4, [5, 6]),
|
|
||||||
5: new Entity("Entity Entity 2.1", 5),
|
|
||||||
6: new Entity("Entity Entity 2.2", 6, [7]),
|
|
||||||
7: new Entity("Entity Entity 2.2.1", 7, [8, 9]),
|
|
||||||
8: new Entity("Entity Entity 2.2.1.1", 8),
|
|
||||||
9: new Entity("Entity Entity 2.2.1.2", 9)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function EntityHierarchyWindow(props: IDockviewPanelProps)
|
export default function EntityHierarchyWindow(props: IDockviewPanelProps)
|
||||||
{
|
{
|
||||||
const [entities, updateEntities] = useImmer(entityHierarchy);
|
const [entities, updateEntities] = useImmer(entityHierarchy);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
EngineGlue.onUpdateEntities = (entities: Entity[]) => {
|
||||||
|
updateEntities(draft => {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (const entity of entities)
|
||||||
|
{
|
||||||
|
// console.log(`${entity.id}: "${entity.name ?? "deleted"}",
|
||||||
|
// ${entity.visible ? "Visible" : "Invisible"}, ${entity.children.length} Children: [${entity.children}]`)
|
||||||
|
|
||||||
|
if (entity.name === "undefined")
|
||||||
|
{
|
||||||
|
delete draft[entity.id];
|
||||||
|
draft.selectedIds.delete(entity.id);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
draft[entity.id] = entity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
console.log(`Failed to update entities: ${e}`);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
EngineGlue.requestEntityHierarchyUpdate();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
delete EngineGlue.onUpdateEntities;
|
||||||
|
};
|
||||||
|
}, [updateEntities]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MenuBar>
|
<MenuBar>
|
||||||
@@ -83,7 +97,6 @@ export default function EntityHierarchyWindow(props: IDockviewPanelProps)
|
|||||||
<input className="filterEntities" placeholder="Filter entities..."/>
|
<input className="filterEntities" placeholder="Filter entities..."/>
|
||||||
</MenuBar>
|
</MenuBar>
|
||||||
<EntityTree items={entities} updateItems={updateEntities}/>
|
<EntityTree items={entities} updateItems={updateEntities}/>
|
||||||
<button className="title-bar__button close" onClick={EngineGlue.handleClickCloseWindow}>🗙</button>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -94,7 +107,7 @@ function EntityTreeItem({item, allEntities, updateEntities}: {
|
|||||||
{
|
{
|
||||||
//const [isOpen, setOpen] = useState(false);
|
//const [isOpen, setOpen] = useState(false);
|
||||||
|
|
||||||
const isSelected = allEntities.selectedIds.includes(item.id);
|
const isSelected = allEntities.selectedIds.has(item.id);
|
||||||
//const isVisible = allEntities[item.id].visible;
|
//const isVisible = allEntities[item.id].visible;
|
||||||
|
|
||||||
function handleClick(event: MouseEvent)
|
function handleClick(event: MouseEvent)
|
||||||
@@ -103,11 +116,11 @@ function EntityTreeItem({item, allEntities, updateEntities}: {
|
|||||||
updateEntities(draft => {
|
updateEntities(draft => {
|
||||||
if (isSelected)
|
if (isSelected)
|
||||||
{
|
{
|
||||||
draft.selectedIds = allEntities.selectedIds.filter((id) => {return id != item.id});
|
draft.selectedIds.delete(item.id);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
draft.selectedIds.push(item.id);
|
draft.selectedIds.add(item.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -182,6 +195,9 @@ function EntityTree({items, updateItems}: { items: EntityMap, updateItems: Updat
|
|||||||
items[0].children.map((childId) => {
|
items[0].children.map((childId) => {
|
||||||
const entity: Entity = items[childId];
|
const entity: Entity = items[childId];
|
||||||
|
|
||||||
|
if (entity === undefined)
|
||||||
|
return;
|
||||||
|
|
||||||
return <EntityTreeItem key={entity.id} item={entity} allEntities={items} updateEntities={updateItems} />;
|
return <EntityTreeItem key={entity.id} item={entity} allEntities={items} updateEntities={updateItems} />;
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
{
|
{
|
||||||
display: flex;
|
display: flex;
|
||||||
grid-area: title;
|
grid-area: title;
|
||||||
height: 3em;
|
height: 2em;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import react from '@vitejs/plugin-react'
|
||||||
|
|
||||||
|
export default defineConfig(({ mode }) => {
|
||||||
|
const isDebug = mode === 'debug'
|
||||||
|
|
||||||
|
return {
|
||||||
|
plugins: [react()],
|
||||||
|
base: "./",
|
||||||
|
build: {
|
||||||
|
target: "es2015",
|
||||||
|
minify: false,
|
||||||
|
terserOptions: undefined,
|
||||||
|
rollupOptions: {
|
||||||
|
preserveModules: true,
|
||||||
|
output: {
|
||||||
|
dir: 'dist',
|
||||||
|
entryFileNames: '[name].js',
|
||||||
|
chunkFileNames: '[name].js',
|
||||||
|
assetFileNames: '[name].[ext]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -1,6 +1,10 @@
|
|||||||
using Ultralight.CAPI;
|
using Ultralight.CAPI;
|
||||||
using System;
|
using System;
|
||||||
using GlitchyEngine;
|
using GlitchyEngine;
|
||||||
|
using GlitchyEngine.Collections;
|
||||||
|
using GlitchyEngine.World;
|
||||||
|
using System.Collections;
|
||||||
|
using GlitchyEngine.Core;
|
||||||
using static GlitchyEngine.UI.Window;
|
using static GlitchyEngine.UI.Window;
|
||||||
|
|
||||||
namespace GlitchyEditor.Ultralight;
|
namespace GlitchyEditor.Ultralight;
|
||||||
@@ -12,6 +16,253 @@ class UltralightMainWindow : UltralightWindow
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
UpdateEntityHierarchy();
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EntityEntry : IDisposable
|
||||||
|
{
|
||||||
|
public Entity Entity;
|
||||||
|
public String Name;
|
||||||
|
public UUID ParentId;
|
||||||
|
public List<UUID> Children;
|
||||||
|
public bool Visible;
|
||||||
|
|
||||||
|
public this(Entity entity, StringView name, UUID parentId, bool visible)
|
||||||
|
{
|
||||||
|
Entity = entity;
|
||||||
|
Name = new String(name);
|
||||||
|
ParentId = parentId;
|
||||||
|
Children = new List<UUID>();
|
||||||
|
Visible = visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
delete Name;
|
||||||
|
delete Children;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<UUID, EntityEntry> _entities = new .() ~ DeleteDictionaryAndDisposeValues!(_);
|
||||||
|
|
||||||
|
bool _forceEntityHierarchyRebuild = false;
|
||||||
|
|
||||||
|
enum Operation
|
||||||
|
{
|
||||||
|
None,
|
||||||
|
Add,
|
||||||
|
Delete,
|
||||||
|
Modify
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateEntityHierarchy()
|
||||||
|
{
|
||||||
|
if (DoStuffFunction == null)
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Error($"{nameof(DoStuffFunction)} is null, skipping entity hierarchy update.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dictionary<UUID, Operation> updates = scope .();
|
||||||
|
GetEntityUpdates(updates);
|
||||||
|
SendUpdateToUI(updates);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetEntityUpdates(Dictionary<UUID, Operation> updates)
|
||||||
|
{
|
||||||
|
Scene scene = Editor.Instance.CurrentScene;
|
||||||
|
|
||||||
|
// If we have no root, or the scene changed, rebuild the entire tree
|
||||||
|
if (_forceEntityHierarchyRebuild || !_entities.ContainsKey(.Zero) || scene != _entities[.Zero].Entity.Scene)
|
||||||
|
{
|
||||||
|
_forceEntityHierarchyRebuild = false;
|
||||||
|
|
||||||
|
ClearDictionaryAndDisposeValues!(_entities);
|
||||||
|
|
||||||
|
_entities.Add(.Zero, EntityEntry(Entity(.InvalidEntity, scene), "Root", .Zero, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: entity.EditorFlags.HasFlag(.HideInHierarchy)
|
||||||
|
|
||||||
|
EntityEntry AddEntity(Entity entity)
|
||||||
|
{
|
||||||
|
UUID entityId = entity.UUID;
|
||||||
|
|
||||||
|
if (!_entities.TryGetValue(entityId, var entry))
|
||||||
|
{
|
||||||
|
Entity? parent = entity.Parent;
|
||||||
|
|
||||||
|
entry = EntityEntry(entity, entity.Name, parent?.UUID ?? .Zero, !entity.EditorFlags.HasFlag(.HideInScene));
|
||||||
|
|
||||||
|
_entities.Add(entityId, entry);
|
||||||
|
updates[entityId] = .Add;
|
||||||
|
|
||||||
|
//if (parent != null)
|
||||||
|
//{
|
||||||
|
UUID parentId = parent?.UUID ?? .Zero;
|
||||||
|
|
||||||
|
if (!_entities.TryGetValue(parentId, var parentEntry))
|
||||||
|
{
|
||||||
|
parentEntry = AddEntity(parent.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
updates[parentId] = .Modify;
|
||||||
|
}
|
||||||
|
|
||||||
|
parentEntry.Children.Add(entityId);
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let (entityId, entityEntry) in ref _entities)
|
||||||
|
{
|
||||||
|
// Root node isn't a real entity
|
||||||
|
if (entityId == .Zero)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (scene.GetEntityByID(entityId) case .Ok(let sceneEntity))
|
||||||
|
{
|
||||||
|
bool changed = false;
|
||||||
|
|
||||||
|
if (entityEntry.Name != sceneEntity.Name)
|
||||||
|
{
|
||||||
|
entityEntry.Name.Set(sceneEntity.Name);
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Entity? parentEntity = sceneEntity.Parent;
|
||||||
|
UUID newParentEntityId = parentEntity?.UUID ?? .Zero;
|
||||||
|
UUID oldParentEntityId = entityEntry.ParentId;
|
||||||
|
|
||||||
|
if (oldParentEntityId != newParentEntityId)
|
||||||
|
{
|
||||||
|
if (!_entities.TryGetValue(newParentEntityId, var newParentEntry))
|
||||||
|
{
|
||||||
|
newParentEntry = AddEntity(parentEntity.Value);
|
||||||
|
}
|
||||||
|
newParentEntry.Children.Add(entityId);
|
||||||
|
updates[newParentEntityId] = .Modify;
|
||||||
|
|
||||||
|
if (_entities.TryGetValue(oldParentEntityId, var oldParentEntry))
|
||||||
|
{
|
||||||
|
oldParentEntry.Children.Remove(entityId);
|
||||||
|
updates[oldParentEntityId] = .Modify;
|
||||||
|
}
|
||||||
|
|
||||||
|
entityEntry.ParentId = newParentEntityId;
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (changed)
|
||||||
|
{
|
||||||
|
updates[entityId] = .Modify;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
entityEntry.Dispose();
|
||||||
|
|
||||||
|
_entities.Remove(entityId);
|
||||||
|
updates[entityId] = .Delete;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (EcsEntity entityId in scene.GetEntities())
|
||||||
|
{
|
||||||
|
let entity = Entity(entityId, scene);
|
||||||
|
|
||||||
|
AddEntity(entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SendUpdateToUI(Dictionary<UUID, Operation> updates)
|
||||||
|
{
|
||||||
|
if (updates.Count == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
JSContextRef context = ulViewLockJSContext(_view);
|
||||||
|
defer ulViewUnlockJSContext(_view);
|
||||||
|
|
||||||
|
JSStringRef nameProperty = JSStringCreateWithUTF8CString("name");
|
||||||
|
JSStringRef idProperty = JSStringCreateWithUTF8CString("id");
|
||||||
|
JSStringRef visibleProperty = JSStringCreateWithUTF8CString("visible");
|
||||||
|
JSStringRef childrenProperty = JSStringCreateWithUTF8CString("children");
|
||||||
|
defer JSStringRelease(nameProperty);
|
||||||
|
defer JSStringRelease(idProperty);
|
||||||
|
defer JSStringRelease(visibleProperty);
|
||||||
|
defer JSStringRelease(childrenProperty);
|
||||||
|
|
||||||
|
JSObjectRef jsArray = JSObjectMakeArray(context, 0, null, null);
|
||||||
|
|
||||||
|
uint32 index = 0;
|
||||||
|
for (let (entityId, operation) in updates)
|
||||||
|
{
|
||||||
|
if (operation == .None)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObjectRef jsEntity = JSObjectMake(context, null, null);
|
||||||
|
|
||||||
|
/*
|
||||||
|
name: string;
|
||||||
|
id: EntityId;
|
||||||
|
visible: boolean = true;
|
||||||
|
children: EntityId[];
|
||||||
|
*/
|
||||||
|
|
||||||
|
JSValueRef idObject = JSValueMakeNumber(context, (double)(uint64)entityId);
|
||||||
|
JSObjectSetProperty(context, jsEntity, idProperty, idObject, 0, null);
|
||||||
|
|
||||||
|
if (operation != .Delete)
|
||||||
|
{
|
||||||
|
EntityEntry entry = _entities[entityId];
|
||||||
|
|
||||||
|
JSStringRef entityName = JSStringCreateWithUTF8CString(entry.Name);
|
||||||
|
JSValueRef nameObject = JSValueMakeString(context, entityName);
|
||||||
|
JSStringRelease(entityName);
|
||||||
|
|
||||||
|
JSObjectSetProperty(context, jsEntity, nameProperty, nameObject, 0, null);
|
||||||
|
|
||||||
|
JSValueRef visibleObject = JSValueMakeBoolean(context, true);
|
||||||
|
JSObjectSetProperty(context, jsEntity, visibleProperty, visibleObject, 0, null);
|
||||||
|
|
||||||
|
JSObjectRef childrenArray = JSObjectMakeArray(context, 0, null, null);
|
||||||
|
|
||||||
|
for (let childId in entry.Children)
|
||||||
|
{
|
||||||
|
JSValueRef childIdObject = JSValueMakeNumber(context, (double)(uint64)childId);
|
||||||
|
JSObjectSetPropertyAtIndex(context, childrenArray, (uint32)@childId.Index, childIdObject, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObjectSetProperty(context, jsEntity, childrenProperty, childrenArray, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObjectSetPropertyAtIndex(context, jsArray, index, jsEntity, null);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSValueRef exception = null;
|
||||||
|
DoStuffFunction(context, Span<JSValueRef>(&jsArray, 1), &exception);
|
||||||
|
|
||||||
|
if (exception != null)
|
||||||
|
{
|
||||||
|
/*if (JSValueIsString(context, exception))
|
||||||
|
{
|
||||||
|
Log.EngineLogger.Error($"Failed to update entities: {StringView(JsStringGet)}");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{*/
|
||||||
|
Log.EngineLogger.Error("Failed to update entities.");
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool _hoveringNonClientArea;
|
private bool _hoveringNonClientArea;
|
||||||
|
|
||||||
void HandleHoverNonClientArea(JSContextRef context, JSObjectRef thisObject, Span<JSValueRef> arguments, JSValueRef* exception = null)
|
void HandleHoverNonClientArea(JSContextRef context, JSObjectRef thisObject, Span<JSValueRef> arguments, JSValueRef* exception = null)
|
||||||
@@ -70,6 +321,33 @@ class UltralightMainWindow : UltralightWindow
|
|||||||
JSStringRelease(name);
|
JSStringRelease(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private JsFunctionCall DoStuffFunction;
|
||||||
|
|
||||||
|
delegate JSValueRef JsFunctionCall(JSContextRef context, Span<JSValueRef> arguments, JSValueRef* exception);
|
||||||
|
|
||||||
|
private JsFunctionCall GetJsCallbackDelegate(JSContextRef context, JSObjectRef object, StringView functionName)
|
||||||
|
{
|
||||||
|
JSStringRef functionNameString = JSStringCreateWithUTF8CString(functionName.ToScopeCStr!());
|
||||||
|
defer JSStringRelease(functionNameString);
|
||||||
|
|
||||||
|
JSValueRef func = JSObjectGetProperty(context, object, functionNameString, null);
|
||||||
|
|
||||||
|
if (!JSValueIsObject(context, func))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObjectRef functionObject = JSValueToObject(context, func, null);
|
||||||
|
|
||||||
|
if (functionObject == null || !JSObjectIsFunction(context, functionObject))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new (c, args, ex) => {
|
||||||
|
return JSObjectCallAsFunction(c, functionObject, object, (uint32)args.Length, args.Ptr, ex);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private void RegisterEngineGlueFunctions()
|
private void RegisterEngineGlueFunctions()
|
||||||
{
|
{
|
||||||
@@ -98,6 +376,10 @@ class UltralightMainWindow : UltralightWindow
|
|||||||
RegisterBeefFunction(context, scriptGlue, "handleClickMaximizeWindow", new:stdAlloc (c, t, a, e) => { _window.ToggleMaximize(); });
|
RegisterBeefFunction(context, scriptGlue, "handleClickMaximizeWindow", new:stdAlloc (c, t, a, e) => { _window.ToggleMaximize(); });
|
||||||
RegisterBeefFunction(context, scriptGlue, "handleClickMinimizeWindow", new:stdAlloc (c, t, a, e) => { _window.Minimize(); });
|
RegisterBeefFunction(context, scriptGlue, "handleClickMinimizeWindow", new:stdAlloc (c, t, a, e) => { _window.Minimize(); });
|
||||||
RegisterBeefFunction(context, scriptGlue, "handleClickCloseWindow", new:stdAlloc (c, t, a, e) => { _window.Close(); });
|
RegisterBeefFunction(context, scriptGlue, "handleClickCloseWindow", new:stdAlloc (c, t, a, e) => { _window.Close(); });
|
||||||
|
|
||||||
|
RegisterBeefFunction(context, scriptGlue, "requestEntityHierarchyUpdate", new:stdAlloc (c, t, a, e) => { _forceEntityHierarchyRebuild = true; });
|
||||||
|
|
||||||
|
DoStuffFunction = GetJsCallbackDelegate(context, scriptGlue, "callFromEngine_updateEntities");
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnDOMReady(C_View* caller, uint64 frame_id, bool is_main_frame, C_String* url)
|
protected override void OnDOMReady(C_View* caller, uint64 frame_id, bool is_main_frame, C_String* url)
|
||||||
|
|||||||
@@ -112,10 +112,28 @@ abstract class UltralightWindow
|
|||||||
Blit.Blit(_texture, _window.SwapChain.BackBuffer, viewport: _window.SwapChain.BackbufferViewport);
|
Blit.Blit(_texture, _window.SwapChain.BackBuffer, viewport: _window.SwapChain.BackbufferViewport);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void Update() { }
|
||||||
|
|
||||||
public void Render()
|
public void Render()
|
||||||
{
|
{
|
||||||
CopyToImmediateTexture();
|
CopyToImmediateTexture();
|
||||||
CopyToBackBuffer();
|
CopyToBackBuffer();
|
||||||
|
|
||||||
|
if (Input.IsKeyPressing(Key.A))
|
||||||
|
{
|
||||||
|
ULString str = ulCreateString("window.updateEntities([])");
|
||||||
|
ULString exception = null;
|
||||||
|
ulViewEvaluateScript(_view, str, &exception);
|
||||||
|
|
||||||
|
if (exception != null && ulStringGetLength(exception) != 0)
|
||||||
|
{
|
||||||
|
StringView ex = StringView(ulStringGetData(exception), ulStringGetLength(exception));
|
||||||
|
|
||||||
|
Log.EngineLogger.Error($"Failed to evaluate script: {ex}");
|
||||||
|
}
|
||||||
|
|
||||||
|
ulDestroyString(str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateTexture()
|
private void CreateTexture()
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ class UltralightLayer : Layer
|
|||||||
|
|
||||||
for (var window in _windows)
|
for (var window in _windows)
|
||||||
{
|
{
|
||||||
|
window.Update();
|
||||||
window.Render();
|
window.Render();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -56,5 +56,10 @@ namespace GlitchyEngine.Core
|
|||||||
|
|
||||||
return .Ok;
|
return .Ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static explicit operator uint64(UUID id)
|
||||||
|
{
|
||||||
|
return id._uuid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -49,15 +49,39 @@ namespace GlitchyEngine
|
|||||||
container.Clear();
|
container.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static mixin DeleteDictionaryAndReleaseValues(var container)
|
public static mixin DeleteDictionaryAndReleaseValues(var dictionary)
|
||||||
{
|
{
|
||||||
if (container != null)
|
if (dictionary != null)
|
||||||
{
|
{
|
||||||
for (var value in container)
|
for (var value in dictionary)
|
||||||
{
|
{
|
||||||
value.value?.ReleaseRef();
|
value.value?.ReleaseRef();
|
||||||
}
|
}
|
||||||
delete container;
|
delete dictionary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static mixin ClearDictionaryAndDisposeValues(var dictionary)
|
||||||
|
{
|
||||||
|
if (dictionary != null)
|
||||||
|
{
|
||||||
|
for (var value in dictionary)
|
||||||
|
{
|
||||||
|
value.value?.Dispose();
|
||||||
|
}
|
||||||
|
dictionary.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static mixin DeleteDictionaryAndDisposeValues(var dictionary)
|
||||||
|
{
|
||||||
|
if (dictionary != null)
|
||||||
|
{
|
||||||
|
for (var value in dictionary)
|
||||||
|
{
|
||||||
|
value.value?.Dispose();
|
||||||
|
}
|
||||||
|
delete dictionary;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -137,9 +137,9 @@ namespace GlitchyEngine.Renderer
|
|||||||
if (slices.Length == 0)
|
if (slices.Length == 0)
|
||||||
return .Err;
|
return .Err;
|
||||||
|
|
||||||
if (slices.Length != 1)
|
if (nativeTexture != null && slices.Length != 1)
|
||||||
{
|
{
|
||||||
Runtime.NotImplemented("PlatformSetData(Span<TextureSliceData> slices) with more than 1 slice is not implemented yet.");
|
Runtime.NotImplemented("PlatformSetData(Span<TextureSliceData> slices) with more than 1 slice is not implemented for already created textures.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if(nativeTexture == null)
|
if(nativeTexture == null)
|
||||||
|
|||||||
@@ -1087,6 +1087,11 @@ namespace GlitchyEngine.World
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WorldEnumerator GetEntities()
|
||||||
|
{
|
||||||
|
return _ecsWorld.Enumerate();
|
||||||
|
}
|
||||||
|
|
||||||
public WorldEnumerator<TComponent> GetEntities<TComponent>() where TComponent : struct
|
public WorldEnumerator<TComponent> GetEntities<TComponent>() where TComponent : struct
|
||||||
{
|
{
|
||||||
return _ecsWorld.Enumerate<TComponent>();
|
return _ecsWorld.Enumerate<TComponent>();
|
||||||
|
|||||||
Reference in New Issue
Block a user