@needle-tools/engine
Version:
Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.
297 lines (262 loc) • 11.4 kB
text/typescript
import { Object3D, Quaternion, Vector3 } from "three";
import { TransformControlsGizmo } from "three/examples/jsm/controls/TransformControls.js";
import { addComponent, addNewComponent, getComponent, getComponentInChildren, getComponentInParent, getComponents, getComponentsInChildren, getComponentsInParent, getOrAddComponent, removeComponent } from "../../engine/engine_components.js";
import { destroy, isActiveSelf, setActive } from "../../engine/engine_gameobject.js";
import {
getTempVector,
getWorldPosition,
getWorldQuaternion,
getWorldRotation,
getWorldScale,
setWorldPosition,
setWorldQuaternion,
setWorldRotation,
setWorldScale
}
from "../../engine/engine_three_utils.js";
import type { ComponentInit, Constructor, ConstructorConcrete, HideFlags,IComponent as Component, IComponent } from "../../engine/engine_types.js";
import { applyPrototypeExtensions, registerPrototypeExtensions } from "./ExtensionUtils.js";
// NOTE: keep in sync with method declarations below
declare module 'three' {
export interface Object3D {
get guid(): string | undefined;
set guid(value: string | undefined);
/**
* Allows to control e.g. if an object should be exported
*/
hideFlags: HideFlags;
/**
* Add a Needle Engine component to the {@link Object3D}.
* @param comp The component instance or constructor to add.
* @param init Optional initialization data for the component.
* @returns The added component instance.
* @example Directly pass in constructor and properties:
* ```ts
* const obj = new Object3D();
* obj.addComponent(MyComponent, { myProperty: 42 });
* ```
* @example Create a component instance, assign properties and then add it:
* ```ts
* const obj = new Object3D();
* const comp = new MyComponent();
* comp.myProperty = 42;
* obj.addComponent(comp);
* ```
*/
addComponent<T extends IComponent>(comp: T | ConstructorConcrete<T>, init?: ComponentInit<T>) : T;
/**
* Remove a Needle Engine component from the {@link Object3D}.
*/
removeComponent(inst: IComponent) : IComponent;
/**
* Get or add a Needle Engine component to the Object3D.
* If the component already exists, it will be returned. Otherwise, a new component will be added.
* @param typeName The component constructor to get or add.
* @param init Optional initialization data for the component.
* @returns The component instance.
*/
getOrAddComponent<T extends IComponent>(typeName: ConstructorConcrete<T>, init?: ComponentInit<T>): T;
/**
* Get a Needle Engine component from the {@link Object3D}.
* @returns The component instance or null if not found.
*/
getComponent<T extends IComponent>(type: Constructor<T>): T | null;
/**
* Get all components of a specific type from the {@link Object3D}.
* @param arr Optional array to fill with the found components.
* @returns An array of components.
*/
getComponents<T extends IComponent>(type: Constructor<T>, arr?: []): T[];
/**
* Get a Needle Engine component from the {@link Object3D} or its children. This will search on the current Object and all its children.
* @returns The component instance or null if not found.
*/
getComponentInChildren<T extends IComponent>(type: Constructor<T>): T | null;
/**
* Get all components of a specific type from the {@link Object3D} or its children. This will search on the current Object and all its children.
* @param arr Optional array to fill with the found components.
* @returns An array of components.
*/
getComponentsInChildren<T extends IComponent>(type: Constructor<T>, arr?: []): T[];
/**
* Get a Needle Engine component from the {@link Object3D} or its parents. This will search on the current Object and all its parents.
* @returns The component instance or null if not found.
*/
getComponentInParent<T extends IComponent>(type: Constructor<T>): T | null;
/**
* Get all Needle Engine components of a specific type from the {@link Object3D} or its parents. This will search on the current Object and all its parents.
* @param arr Optional array to fill with the found components.
* @returns An array of components.
*/
getComponentsInParent<T extends IComponent>(type: Constructor<T>, arr?: []): T[];
/**
* Destroys the {@link Object3D} and all its Needle Engine components.
*/
destroy(): void;
/**
* Get or set the world position of the {@link Object3D}.
* Added by Needle Engine.
*/
worldPosition: Vector3;
/**
* Get or set the world quaternion of the {@link Object3D}.
* Added by Needle Engine.
*/
worldQuaternion: Quaternion;
/**
* Get or set the world rotation of the {@link Object3D}.
* Added by Needle Engine.
*/
worldRotation: Vector3;
/**
* Get or set the world scale of the {@link Object3D}.
* Added by Needle Engine.
*/
worldScale: Vector3;
/**
* Get the world forward vector of the {@link Object3D}.
* Added by Needle Engine.
*/
get worldForward(): Vector3;
/**
* Get the world right vector of the {@link Object3D}.
* Added by Needle Engine.
*/
get worldRight(): Vector3;
/**
* Get the world up vector of the {@link Object3D}.
* Added by Needle Engine.
*/
get worldUp(): Vector3;
}
}
/**
* @internal
* used to decorate cloned object3D objects with the same added components defined above
**/
export function apply(object: Object3D) {
if (object && object.isObject3D === true) {
applyPrototypeExtensions(object, Object3D);
}
}
Object3D.prototype["SetActive"] = function (active: boolean) {
this.visible = active;
}
// e.g. when called via a UnityEvent
Object3D.prototype["setActive"] = function (active: boolean) {
this.visible = active;
}
Object3D.prototype["destroy"] = function () {
destroy(this);
}
Object3D.prototype["addComponent"] = function <T extends IComponent>(comp: T | ConstructorConcrete<T>, init?: ComponentInit<T>) {
return addComponent(this, comp, init);
}
Object3D.prototype["addNewComponent"] = function <T extends Component>(type: ConstructorConcrete<T>, init?: ComponentInit<T>) {
return addComponent(this, type, init);
}
Object3D.prototype["removeComponent"] = function (inst: Component) {
return removeComponent(this, inst);
}
Object3D.prototype["getOrAddComponent"] = function <T extends IComponent>(typeName: ConstructorConcrete<T>, init?: ComponentInit<T>): T {
return getOrAddComponent<T>(this, typeName, init);
}
Object3D.prototype["getComponent"] = function <T extends IComponent>(type: Constructor<T>) {
return getComponent(this, type);
}
Object3D.prototype["getComponents"] = function <T extends IComponent>(type: Constructor<T>, arr?: []) {
return getComponents(this, type, arr);
}
Object3D.prototype["getComponentInChildren"] = function <T extends IComponent>(type: Constructor<T>) {
return getComponentInChildren(this, type);
}
Object3D.prototype["getComponentsInChildren"] = function <T extends IComponent>(type: Constructor<T>, arr?: []) {
return getComponentsInChildren(this, type, arr);
}
Object3D.prototype["getComponentInParent"] = function <T extends IComponent>(type: Constructor<T>) {
return getComponentInParent(this, type);
}
Object3D.prototype["getComponentsInParent"] = function <T extends IComponent>(type: Constructor<T>, arr?: []) {
return getComponentsInParent(this, type, arr);
}
// this is a fix to allow gameObject active animation be applied to a three object
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "activeSelf")) {
Object.defineProperty(Object3D.prototype, "activeSelf", {
get: function () {
return isActiveSelf(this)
},
set: function (val: boolean | number) {
setActive(this, val);
}
});
}
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldPosition")) {
Object.defineProperty(Object3D.prototype, "worldPosition", {
get: function () {
// TODO: would be great to remove this - just a workaround because the TransformControlsGizmo also defines this
if (this instanceof TransformControlsGizmo) {
return getWorldPosition(this["object"]);
}
return getWorldPosition(this);
},
set: function (val: Vector3) {
setWorldPosition(this, val)
}
});
}
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldQuaternion")) {
Object.defineProperty(Object3D.prototype, "worldQuaternion", {
get: function () {
if (this instanceof TransformControlsGizmo) {
return getWorldQuaternion(this["object"]);
}
return getWorldQuaternion(this);
},
set: function (val: Quaternion) {
setWorldQuaternion(this, val)
}
});
}
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldRotation")) {
Object.defineProperty(Object3D.prototype, "worldRotation", {
get: function () {
return getWorldRotation(this);
},
set: function (val: Vector3) {
setWorldRotation(this, val)
}
});
}
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldScale")) {
Object.defineProperty(Object3D.prototype, "worldScale", {
get: function () {
return getWorldScale(this);
},
set: function (val: Vector3) {
setWorldScale(this, val)
}
});
}
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldForward")) {
Object.defineProperty(Object3D.prototype, "worldForward", {
get: function () {
return getTempVector().set(0, 0, 1).applyQuaternion(getWorldQuaternion(this));
},
});
}
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldRight")) {
Object.defineProperty(Object3D.prototype, "worldRight", {
get: function () {
return getTempVector().set(1, 0, 0).applyQuaternion(getWorldQuaternion(this));
},
});
}
if (!Object.getOwnPropertyDescriptor(Object3D.prototype, "worldUp")) {
Object.defineProperty(Object3D.prototype, "worldUp", {
get: function () {
return getTempVector().set(0, 1, 0).applyQuaternion(getWorldQuaternion(this));
},
});
}
// do this after adding the component extensions
registerPrototypeExtensions(Object3D);