This commit is contained in:
Evert Prants 2023-06-10 10:54:51 +03:00
parent 65f84c4d6c
commit 068f7ec2cc
Signed by: evert
GPG Key ID: 1688DA83D222D0B5
10 changed files with 1868 additions and 72 deletions

View File

@ -83,7 +83,6 @@ const changed = ($event: Event) => {
justify-content: flex-start;
align-items: center;
appearance: none;
padding: 4px;
cursor: pointer;
border: 0;
width: 100%;
@ -99,15 +98,15 @@ const changed = ($event: Event) => {
border-radius: 100%;
border: 1px solid #ddd;
margin-right: 4px;
flex-shrink: 0;
}
&-field {
font-family: monospace;
width: 5rem;
width: 100%;
border: 0;
background-color: #ffffff;
padding: 8px;
margin: 2px 0;
font-size: 0.75rem;
border-radius: 4px;
}

View File

@ -61,42 +61,56 @@ const formFields = computed(() => {
const fields: FormItem[] = [];
Object.values(object.editorProperties)
.filter((item) => item.exposed)
.filter((item) => item.definition.exposed)
.forEach((property) => {
if (property.type === String || property.type === Number) {
if (
property.definition.type === String ||
property.definition.type === Number
) {
fields.push({
name: property.name,
label: toCapitalizedWords(property.name),
value: (object as unknown as Record<string, unknown>)[property.name],
type: property.type === String ? 'string' : 'number',
name: property.definition.name,
label: toCapitalizedWords(property.definition.name),
value: (object as unknown as Record<string, unknown>)[
property.definition.name
],
type: property.definition.type === String ? 'string' : 'number',
component: Field,
});
}
if (property.type === Vector3 || property.type === Euler) {
if (
property.definition.type === Vector3 ||
property.definition.type === Euler
) {
fields.push({
name: property.name,
label: toCapitalizedWords(property.name),
value: (object as unknown as Record<string, unknown>)[property.name],
type: property.type === Vector3 ? 'vector' : 'euler',
name: property.definition.name,
label: toCapitalizedWords(property.definition.name),
value: (object as unknown as Record<string, unknown>)[
property.definition.name
],
type: property.definition.type === Vector3 ? 'vector' : 'euler',
component: Vector3Field,
});
}
if (property.type === Color) {
if (property.definition.type === Color) {
fields.push({
name: property.name,
label: toCapitalizedWords(property.name),
value: (object as unknown as Record<string, unknown>)[property.name],
name: property.definition.name,
label: toCapitalizedWords(property.definition.name),
value: (object as unknown as Record<string, unknown>)[
property.definition.name
],
component: ColorPicker,
});
}
if (property.type === Boolean) {
if (property.definition.type === Boolean) {
fields.push({
name: property.name,
label: toCapitalizedWords(property.name),
value: (object as unknown as Record<string, unknown>)[property.name],
name: property.definition.name,
label: toCapitalizedWords(property.definition.name),
value: (object as unknown as Record<string, unknown>)[
property.definition.name
],
component: Checkbox,
});
}

View File

@ -1,3 +1,8 @@
// Contains highly modified code from
// https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/FirstPersonControls.js
// https://github.com/mrdoob/three.js/blob/master/examples/jsm/controls/OrbitControls.js
// MIT Copyright © 2010-2023 three.js authors
import { Euler, EventDispatcher, Object3D, Vector3 } from 'three';
const _changeEvent = { type: 'change' };

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,7 @@ import {
Environment,
EventEmitter,
GameObject,
GameObject3D,
MouseButtonEvent,
Renderer,
Sphere,
@ -30,9 +31,9 @@ import {
SelectionEvent,
TransformModeEvent,
} from '../types/events';
import { TransformControls } from 'three/addons/controls/TransformControls.js';
import { ViewHelper } from 'three/addons/helpers/ViewHelper.js';
import { CameraControls } from './controls';
import { CameraControls } from './controls/camera-controls';
import { TransformControls } from './controls/transform-controls';
/**
* This component does most of the work related to editing the level.
@ -145,6 +146,9 @@ export class WorkspaceComponent extends EngineComponent {
object = object.parent;
}
// Locked is for preventing mouse picking
if ((object as GameObject3D)?.locked) return;
this.events.emit('select', {
object: object as GameObject,
multi: event.control,

View File

@ -98,37 +98,17 @@ export class LevelComponent extends EngineComponent {
await assetManager.loadAll(save.assets);
// Load environment
this.applyProperties(save.environment, this.environment);
this.environment.deserialize(save.environment);
this.events.emit('setEnvironment', this.environment);
// Load world
this.deserializeObject(save.world);
}
private applyProperties(
properties: Record<string, unknown>,
object: Object3D
) {
Object.keys(properties)
.filter((key) => !['children', 'objectType'].includes(key))
.forEach((key) => {
const indexable = object as any;
if (indexable[key]?.fromArray && Array.isArray(properties[key])) {
indexable[key].fromArray(properties[key]);
} else if (indexable[key].isColor) {
indexable[key] = new Color(properties[key] as string);
} else if (indexable[key].copy) {
indexable[key].copy(properties[key]);
} else {
indexable[key] = properties[key];
}
});
}
private recursiveCreate(entry: SerializedObject, setParent?: Object3D) {
const parent = setParent || this.world;
const newObject = this.createObject(entry.objectType, parent);
this.applyProperties(entry, newObject!);
newObject?.deserialize(entry);
entry.children.forEach((child) => this.recursiveCreate(child, newObject));
return newObject;
}

View File

@ -17,9 +17,12 @@ import { AssetInfo } from '../types/asset';
export const brickEditorProperties: EditorProperties = {
...gameObject3DEditorProperties,
color: new Property('color', Color, true, []),
transparency: new Property('transparency', Number, true, []),
texture: new Property('texture', AssetInfo, true, []),
color: new Property({ name: 'color', type: Color }),
transparency: new Property({ name: 'transparency', type: Number }),
texture: new Property({ name: 'texture', type: AssetInfo }),
canCollide: new Property({ name: 'canCollide', type: Boolean }),
anchored: new Property({ name: 'anchored', type: Boolean }),
mass: new Property({ name: 'mass', type: Number }),
};
export class Brick extends GameObject3D {
@ -28,6 +31,10 @@ export class Brick extends GameObject3D {
protected material = new MeshPhongMaterial();
protected mesh: Mesh = new Mesh(this.geometry, this.material);
public canCollide = true;
public anchored = true;
public mass = 1;
constructor(
protected geometry: BufferGeometry = gameObjectGeometries.boxGeometry,
public editorProperties: EditorProperties = brickEditorProperties

View File

@ -8,12 +8,12 @@ import { Property } from '../types/property';
import { environmentDefaults } from '../defaults/environment';
export const environmentEditorProperties: EditorProperties = {
sunColor: new Property('sunColor', Color, true, []),
sunPosition: new Property('sunPosition', Vector3, true, []),
sunStrength: new Property('sunStrength', Number, true, []),
ambientColor: new Property('ambientColor', Color, true, []),
ambientStrength: new Property('ambientStrength', Number, true, []),
clearColor: new Property('clearColor', Color, true, []),
sunColor: new Property({ name: 'sunColor', type: Color }),
sunPosition: new Property({ name: 'sunPosition', type: Vector3 }),
sunStrength: new Property({ name: 'sunStrength', type: Number }),
ambientColor: new Property({ name: 'ambientColor', type: Color }),
ambientStrength: new Property({ name: 'ambientStrength', type: Number }),
clearColor: new Property({ name: 'clearColor', type: Color }),
};
export class Environment extends GameObject {

View File

@ -1,17 +1,18 @@
import { Euler, Object3D, Vector3 } from 'three';
import { Color, Euler, Object3D, Vector3 } from 'three';
import { Property } from './property';
export type EditorProperties = { [x: string]: Property };
export const gameObjectEditorProperties: EditorProperties = {
name: new Property('name', String, true, []),
visible: new Property('visible', Boolean, true, []),
name: new Property({ name: 'name', type: String, exposed: true }),
visible: new Property({ name: 'visible', type: Boolean, exposed: true }),
};
export const gameObject3DEditorProperties: EditorProperties = {
...gameObjectEditorProperties,
position: new Property('position', Vector3, true, []),
scale: new Property('scale', Vector3, true, []),
rotation: new Property('rotation', Euler, true, []),
locked: new Property({ name: 'locked', type: Boolean, exposed: true }),
position: new Property({ name: 'position', type: Vector3, exposed: true }),
scale: new Property({ name: 'scale', type: Vector3, exposed: true }),
rotation: new Property({ name: 'rotation', type: Euler, exposed: true }),
};
export class GameObject extends Object3D {
@ -68,17 +69,31 @@ export class GameObject extends Object3D {
return this;
}
parse(input: SerializedObject) {
/**
* Deserialize a serialized object into properties on this object.
* @param input Serialized information
*/
deserialize(input: SerializedObject) {
Object.keys(input)
.filter((key) => key !== 'children')
.filter((key) => !['children', 'objectType'].includes(key))
.forEach((key) => {
(this as Record<string, unknown>)[key] = input[key];
const indexable = this as any;
if (indexable[key]?.fromArray && Array.isArray(input[key])) {
indexable[key].fromArray(input[key]);
} else if (indexable[key].isColor) {
indexable[key] = new Color(input[key] as string);
} else if (indexable[key].copy) {
indexable[key].copy(input[key]);
} else {
indexable[key] = input[key];
}
});
}
}
export class GameObject3D extends GameObject {
public objectType = 'GameObject3D';
public locked = false;
constructor(public editorProperties = gameObject3DEditorProperties) {
super(editorProperties);
}

View File

@ -1,11 +1,23 @@
import { Material, Object3D } from 'three';
export class Property {
constructor(
public name: string,
public type: any,
public exposed = true,
public validators: Function[] = [],
public postChange?: (obj: Object3D | Material) => void
) {}
export interface PropertyDefinition {
name: string;
type: any;
description?: string;
exposed?: boolean;
validators?: Function[];
postChange?(obj: Object3D | Material): void;
}
export class Property {
public definition!: PropertyDefinition;
constructor(definition: PropertyDefinition) {
this.definition = Object.assign(
{
exposed: true,
validators: [],
},
definition
);
}
}