Skip to content

Commit

Permalink
ESM script base class (#6367)
Browse files Browse the repository at this point in the history
* refactored script type

* removed attribute check on esm scripts

* linting fixes

* Delete redundant scripts

* Remove engines field from package-lock.json

* Fix event name in ScriptType class

* Add export to rawToValue function and update script schema in ScriptHandler

* linting

* Update script attribute assignment in ScriptComponent

* Fix script initialization and enablement

* Fix missing semicolon in ScriptRegistry class

* Refactor script component and handler

* GS reorder fix (#6410)

* USDZ / GLTF exporter supports exposing of compressed textures on WebGPU (#6394)

* Support for Picker on WebGPU

* Gizmos example on webgpu

* USDZ / GLTF exporter supports exposing of compressed textures on WebGPU

* tiny comment update

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* restore default color when setting axis shape disable to false (#6587)

* [Fix] Add missing exposure handling from the Neutral tonemapping (#6592)

* SSAO engine example (#6586)

* SSAO engine example

* lint

* draco + texture resize: 17MB -> 6MB

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* [BREAKING] Removed backwards compatibility for few functions (#6408)

* [BREAKING] Removed backwards compatibility for few functions

* lint

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* Examples app bundle optimizations (#6588)

* treeshake smallest enabled

* ignore playcanvas PCUI from being treeshaken

* treeshake ignore only pcui

* added device type constants directly (no playcanvas dependency)

* format rollup

* word wrap fix

* Updated PCUI

* removed PCUI aliasing

* Fix to just submitted AO example to disabled baked in occlusion maps (#6593)

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* added publint to ci; removed build:publish (#6363)

* Fix types for calcAabb functions (#6596)

* vercel setup (#6600)

* replaces slashes for windows (#6601)

* fixed using query params with iframe example (#6604)

* New texture formats: R8 and RG8 (#6602)

* New texture formats: R8 and RG8

* Apply suggestions from code review

Co-authored-by: Will Eastcott <will@playcanvas.com>

* deprecate the old constants

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
Co-authored-by: Will Eastcott <will@playcanvas.com>

* Added orthogonal facing translation to gizmo (#6607)

* set shader chunk type to record (#6621)

* Gizmo example update (#6622)

* separated out grid code

* split out selector and disabled deselection on orbiting

* added type for observer set callback

* fixed example page

* Exports and types fixes (#6623)

* cleaned up ts and js config; adds debug and profiler to exports

* set module resolution to node

* added package json for linting of example modules and renamed @examples to examples

* PR commit (#6626)

* fixed fallback options (#6628)

* fixed example types (#6634)

* fixed selection bug (#6637)

* Fix URL handling in ScriptHandler (#6639)

* Fix mesh collider creation (#6630)

* Docs update (#6631)

* docs update

* Update README.md

---------

Co-authored-by: Will Eastcott <willeastcott@gmail.com>

* Update script component to use getSchema method instead of tSchema

* Refactor script component attribute initialization

* merge fix

* linting

* Update import statement in script.js

* Refactor script attribute assignment

* linting

* Refactor script component initialization

* Refactor script component and script handler

* linting + comments

* Fix the definition of sheenTint Standard Material property (#6665)

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* Update to skybox shader to map it closer to the infinity (#6664)

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* [Fix] Particle system handle different IB formats of incoming mesh (#6662)

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* [BREAKING] Remove deprecated AudioSourceComponent component (#6407)

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* Cubemap fix for webgpu (#6669)

* [BREAKING] Remove support for legacy scripts (#6584)

* [BREAKING] Remove support for legaxy scripts

* removed pc.script.attribute

---------

Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>

* Fix shader chunk error for tonemapped compressed GS (#6670)

* Remove support for legacy scripts

* Remove legacy script system

* Add getScriptName function and toLowerCamelCase utility function

* jsdoc improvements

* Update src/framework/script/script.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* Update src/framework/script/script-attributes.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* linting + feedback

* Update src/framework/script/script.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* removed redundant error link

* Update src/framework/script/script.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* Update src/framework/script/script.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* Update src/framework/script/script.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* Update src/framework/script/script-attributes.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* Update src/framework/script/script-type.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* Update src/framework/components/script/component.js

Co-authored-by: Will Eastcott <will@playcanvas.com>

* Refactor attributeToValue function

---------

Co-authored-by: Donovan Hutchence <slimbuck7@gmail.com>
Co-authored-by: Martin Valigursky <59932779+mvaligursky@users.noreply.github.com>
Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com>
Co-authored-by: KPal <48248865+kpal81xd@users.noreply.github.com>
Co-authored-by: Will Eastcott <will@playcanvas.com>
Co-authored-by: kpal81xd <kpal81xd@gmail.com>
Co-authored-by: Alex <LeXXik@users.noreply.github.com>
Co-authored-by: Will Eastcott <willeastcott@gmail.com>
  • Loading branch information
9 people committed Jun 18, 2024
1 parent 795aca3 commit fd40f29
Show file tree
Hide file tree
Showing 9 changed files with 659 additions and 469 deletions.
84 changes: 74 additions & 10 deletions src/framework/components/script/component.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { Debug } from '../../../core/debug.js';
import { SortedLoopArray } from '../../../core/sorted-loop-array.js';

import { ScriptAttributes } from '../../script/script-attributes.js';
import { ScriptAttributes, assignAttributesToScript } from '../../script/script-attributes.js';
import {
SCRIPT_INITIALIZE, SCRIPT_POST_INITIALIZE, SCRIPT_UPDATE,
SCRIPT_POST_UPDATE, SCRIPT_SWAP
} from '../../script/constants.js';

import { Component } from '../component.js';
import { Entity } from '../../entity.js';
import { ScriptType } from '../../script/script-type.js';
import { getScriptName } from '../../script/script.js';

const toLowerCamelCase = str => str[0].toLowerCase() + str.substring(1);

/**
* The ScriptComponent allows you to extend the functionality of an Entity by attaching your own
Expand All @@ -18,6 +22,14 @@ import { Entity } from '../../entity.js';
* @category Script
*/
class ScriptComponent extends Component {
/**
* A map of script name to initial component data.
*
* @type {Map<string, object>}
* @private
*/
_attributeDataMap = new Map();

/**
* Fired when a {@link ScriptType} instance is created and attached to the script component.
* This event is available in two forms. They are as follows:
Expand Down Expand Up @@ -191,7 +203,7 @@ class ScriptComponent extends Component {
* Sets the array of all script instances attached to an entity. This array is read-only and
* should not be modified by developer.
*
* @type {import('../../script/script-type.js').ScriptType[]}
* @type {import('../../script/script.js').Script[]}
*/
set scripts(value) {
this._scriptsData = value;
Expand All @@ -205,8 +217,14 @@ class ScriptComponent extends Component {
// existing script

// enabled
if (typeof value[key].enabled === 'boolean')
if (typeof value[key].enabled === 'boolean') {

// Before a script is initialized, initialize any attributes
script.once('preInitialize', () => {
this.initializeAttributes(script);
});
script.enabled = !!value[key].enabled;
}

// attributes
if (typeof value[key].attributes === 'object') {
Expand Down Expand Up @@ -330,6 +348,9 @@ class ScriptComponent extends Component {

for (let i = 0, len = this.scripts.length; i < len; i++) {
const script = this.scripts[i];
script.once('preInitialize', () => {
this.initializeAttributes(script);
});
script.enabled = script._enabled;
}

Expand Down Expand Up @@ -368,8 +389,40 @@ class ScriptComponent extends Component {
}

_onInitializeAttributes() {
for (let i = 0, len = this.scripts.length; i < len; i++)
this.scripts[i].__initializeAttributes();
for (let i = 0, len = this.scripts.length; i < len; i++) {
const script = this.scripts[i];
this.initializeAttributes(script);
}
}

initializeAttributes(script) {

// if script has __initializeAttributes method assume it has a runtime schema
if (script instanceof ScriptType) {

script.__initializeAttributes();

} else {

// otherwise we need to manually initialize attributes from the schema
const name = script.__scriptType.__name;
const data = this._attributeDataMap.get(name);

// If not data exists return early
if (!data) {
return;
}

// Fetch schema and warn if it doesn't exist
const schema = this.system.app.scripts?.getSchema(name);
if (!schema) {
Debug.warnOnce(`No schema exists for the script '${name}'. A schema must exist for data to be instantiated on the script.`);
}

// Assign the attributes to the script instance based on the attribute schema
assignAttributesToScript(this.system.app, schema.attributes, data, script);

}
}

_scriptMethod(script, method, arg) {
Expand Down Expand Up @@ -599,8 +652,8 @@ class ScriptComponent extends Component {
/**
* Create a script instance and attach to an entity script component.
*
* @param {string|typeof import('../../script/script-type.js').ScriptType} nameOrType - The
* name or type of {@link ScriptType}.
* @param {string|typeof import('../../script/script.js').Script} nameOrType - The
* name or type of {@link Script}.
* @param {object} [args] - Object with arguments for a script.
* @param {boolean} [args.enabled] - If script instance is enabled after creation. Defaults to
* true.
Expand Down Expand Up @@ -631,7 +684,7 @@ class ScriptComponent extends Component {
if (typeof scriptType === 'string') {
scriptType = this.system.app.scripts.get(scriptType);
} else if (scriptType) {
scriptName = scriptType.__name;
scriptName = scriptType.__name ?? toLowerCamelCase(getScriptName(scriptType));
}

if (scriptType) {
Expand All @@ -644,6 +697,15 @@ class ScriptComponent extends Component {
attributes: args.attributes
});


// If the script is not a ScriptType then we must store attribute data on the component
if (!(scriptInstance instanceof ScriptType)) {

// Store the Attribute data
this._attributeDataMap.set(scriptName, args.attributes);

}

const len = this._scripts.length;
let ind = -1;
if (typeof args.ind === 'number' && args.ind !== -1 && len > args.ind)
Expand All @@ -661,7 +723,7 @@ class ScriptComponent extends Component {
this[scriptName] = scriptInstance;

if (!args.preloading)
scriptInstance.__initializeAttributes();
this.initializeAttributes(scriptInstance);

this.fire('create', scriptName, scriptInstance);
this.fire('create:' + scriptName, scriptInstance);
Expand Down Expand Up @@ -725,6 +787,8 @@ class ScriptComponent extends Component {
delete this._scriptsIndex[scriptName];
if (!scriptData) return false;

this._attributeDataMap.delete(scriptName);

const scriptInstance = scriptData.instance;
if (scriptInstance && !scriptInstance._destroyed) {
scriptInstance.enabled = false;
Expand Down Expand Up @@ -793,7 +857,7 @@ class ScriptComponent extends Component {
if (!scriptInstance.swap)
return false;

scriptInstance.__initializeAttributes();
this.initializeAttributes(scriptInstance);

// add to component
this._scripts[ind] = scriptInstance;
Expand Down
26 changes: 13 additions & 13 deletions src/framework/handlers/script.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { platform } from '../../core/platform.js';
import { script } from '../script.js';
import { ScriptType } from '../script/script-type.js';
import { ScriptTypes } from '../script/script-types.js';
import { registerScript } from '../script/script.js';
import { registerScript } from '../script/script-create.js';
import { ResourceLoader } from './loader.js';

import { ResourceHandler } from './handler.js';
import { ScriptAttributes } from '../script/script-attributes.js';
import { Script } from '../script/script.js';

const toLowerCamelCase = str => str[0].toLowerCase() + str.substring(1);

Expand Down Expand Up @@ -134,21 +133,22 @@ class ScriptHandler extends ResourceHandler {
// @ts-ignore
import(importUrl.toString()).then((module) => {

const filename = importUrl.pathname.split('/').pop();
const scriptSchema = this._app.assets.find(filename, 'script').data.scripts;

for (const key in module) {
const scriptClass = module[key];
const extendsScriptType = scriptClass.prototype instanceof ScriptType;
const extendsScriptType = scriptClass.prototype instanceof Script;

if (extendsScriptType) {

// Check if attributes is defined directly on the class and not inherited
if (scriptClass.hasOwnProperty('attributes')) {
const attributes = new ScriptAttributes(scriptClass);
for (const key in scriptClass.attributes) {
attributes.add(key, scriptClass.attributes[key]);
}
scriptClass.attributes = attributes;
}
registerScript(scriptClass, toLowerCamelCase(scriptClass.name));
const scriptName = toLowerCamelCase(scriptClass.name);

// Register the script name
registerScript(scriptClass, scriptName);

// Store any schema associated with the script
this._app.scripts.addSchema(scriptName, scriptSchema[scriptName]);
}
}

Expand Down
48 changes: 46 additions & 2 deletions src/framework/script/script-attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,7 @@ import { CurveSet } from '../../core/math/curve-set.js';
import { Vec2 } from '../../core/math/vec2.js';
import { Vec3 } from '../../core/math/vec3.js';
import { Vec4 } from '../../core/math/vec4.js';

import { GraphNode } from '../../scene/graph-node.js';

import { Asset } from '../asset/asset.js';

const components = ['x', 'y', 'z', 'w'];
Expand Down Expand Up @@ -148,6 +146,50 @@ function rawToValue(app, args, value, old) {
return value;
}

/**
* @typedef {Object} AttributeSchema
* @property {"boolean"|"number"|"string"|"json"|"asset"|"entity"|"rgb"|"rgba"|"vec2"|"vec3"|"vec4"|"curve"} type - The Attribute type
* @property {boolean} [array] - True if this attribute is an array of `type`
*/

/**
* Takes an attribute schema, a value and current value, and return a new value.
*
* @param {import('../../framework/application.js').Application} app - The working application
* @param {AttributeSchema} schema - The attribute schema used to resolve properties
* @param {*} value - The raw value to create
* @param {*} current - The existing value
* @returns {*} The return value
*/
function attributeToValue(app, schema, value, current) {
if (schema.array) {
return value.map((item, index) => rawToValue(app, schema, item, current ? current[index] : null));
}

return rawToValue(app, schema, value, current);
}

/**
* Assigns values to a script instance based on a map of attributes schemas
* and a corresponding map of data.
*
* @param {import('../../framework/application.js').Application} app - The application instance
* @param {Object<string, AttributeSchema>} attributeSchemaMap - A map of names to Schemas
* @param {Object<string, *>} data - A Map of data to assign to the Script instance
* @param {import('../../framework/script/script.js').Script} script - A Script instance to assign values on
*/
export function assignAttributesToScript(app, attributeSchemaMap, data, script) {

// Iterate over the schema and assign corresponding data
for (const attributeName in attributeSchemaMap) {
const attributeSchema = attributeSchemaMap[attributeName];
const dataToAssign = data[attributeName];

// Assign the value to the script based on the attribute schema
script[attributeName] = attributeToValue(app, attributeSchema, dataToAssign, script);
}
}

/**
* Container of Script Attribute definitions. Implements an interface to add/remove attributes and
* store their definition for a {@link ScriptType}. Note: An instance of ScriptAttributes is
Expand All @@ -156,6 +198,8 @@ function rawToValue(app, args, value, old) {
* @category Script
*/
class ScriptAttributes {
static assignAttributesToScript = assignAttributesToScript;

/**
* Create a new ScriptAttributes instance.
*
Expand Down
Loading

0 comments on commit fd40f29

Please sign in to comment.