diff --git a/packages/core/src/RenderPipeline/RenderQueue.ts b/packages/core/src/RenderPipeline/RenderQueue.ts index 55c173e0f..d517c127e 100644 --- a/packages/core/src/RenderPipeline/RenderQueue.ts +++ b/packages/core/src/RenderPipeline/RenderQueue.ts @@ -115,7 +115,7 @@ export class RenderQueue { } else if (switchProgram) { program.uploadTextures(program.cameraUniformBlock, cameraData); } - + if (program._uploadRenderer !== renderer) { program.uploadAll(program.rendererUniformBlock, rendererData); program._uploadRenderer = renderer; diff --git a/packages/core/src/asset/AssetType.ts b/packages/core/src/asset/AssetType.ts index 28bbbd1f4..18ffbb6f4 100644 --- a/packages/core/src/asset/AssetType.ts +++ b/packages/core/src/asset/AssetType.ts @@ -33,10 +33,14 @@ export enum AssetType { KTX = "ktx", /** Cube Compress Texture. */ KTXCube = "ktx-cube", + /** Sprite. */ + Sprite = "sprite", /** Sprite Atlas. */ SpriteAtlas = "sprite-atlas", /** ambient light */ Env = "environment", + /** scene */ + Scene = "scene", /** HDR to cube */ HDR = "HDR" } diff --git a/packages/core/src/asset/Loader.ts b/packages/core/src/asset/Loader.ts index caf233f00..bfc67d4c0 100644 --- a/packages/core/src/asset/Loader.ts +++ b/packages/core/src/asset/Loader.ts @@ -6,6 +6,25 @@ import { ResourceManager } from "./ResourceManager"; * Loader abstract class. */ export abstract class Loader { + /** + * Register a class with a string name for serialization and deserialization. + * @param key - class name + * @param obj - class object + */ + public static registerClass(className: string, classDefine: { new (): Object }) { + this._engineObjects[className] = classDefine; + } + + /** + * Get the class object by class name. + * @param key - class name + * @returns class object + */ + public static getClass(className: string): { new (): Object } { + return this._engineObjects[className]; + } + + private static _engineObjects: { [key: string]: any } = {}; request: (url: string, config: RequestConfig) => AssetPromise = request; abstract load(item: LoadItem, resourceManager: ResourceManager): AssetPromise; constructor(public readonly useCache: boolean) {} diff --git a/packages/core/src/asset/ResourceManager.ts b/packages/core/src/asset/ResourceManager.ts index 473edba70..8e18052e9 100644 --- a/packages/core/src/asset/ResourceManager.ts +++ b/packages/core/src/asset/ResourceManager.ts @@ -5,6 +5,8 @@ import { Loader } from "./Loader"; import { LoadItem } from "./LoadItem"; import { RefObject } from "./RefObject"; +type EditorResourceItem = { virtualPath: string; path: string; type: string; id: string }; +type EditorResourceConfig = Record; /** * ResourceManager */ @@ -34,6 +36,12 @@ export class ResourceManager { retryInterval: number = 0; /** The default timeout period for loading assets, in milliseconds. */ timeout: number = 20000; + /** @internal */ + _objectPool: { [key: string]: any } = Object.create(null); + /** @internal */ + _editorResourceConfig: EditorResourceConfig = Object.create(null); + /** @internal */ + _virtualPathMap: Record = Object.create(null); /** Asset path pool, key is asset ID, value is asset path */ private _assetPool: { [key: number]: string } = Object.create(null); @@ -136,6 +144,29 @@ export class ResourceManager { return this._assetPool[instanceId]; } + /** + * @beta Just for internal editor, not recommended for developers. + */ + getResourceByRef(ref: { refId: string; key?: string; isClone?: boolean }): Promise { + const { refId, key, isClone } = ref; + const obj = this._objectPool[refId]; + const promise = obj + ? Promise.resolve(obj) + : this.load({ type: this._editorResourceConfig[refId].type, url: this._editorResourceConfig[refId].path }); + return promise.then((res) => (key ? res[key] : res)).then((item) => (isClone ? item.clone() : item)); + } + + /** + * @internal + * @beta Just for internal editor, not recommended for developers. + */ + initVirtualResources(config: EditorResourceItem[]): void { + config.forEach((element) => { + this._virtualPathMap[element.virtualPath] = element.path; + this._editorResourceConfig[element.id] = element; + }); + } + /** * @internal */ @@ -196,7 +227,9 @@ export class ResourceManager { private _loadSingleItem(item: LoadItem | string): AssetPromise { const info = this._assignDefaultOptions(typeof item === "string" ? { url: item } : item); - const url = info.url; + const infoUrl = info.url; + // check url mapping + const url = this._virtualPathMap[infoUrl] ? this._virtualPathMap[infoUrl] : infoUrl; // has cache if (this._assetUrlPool[url]) { return new AssetPromise((resolve) => { @@ -208,6 +241,10 @@ export class ResourceManager { return this._loadingPromises[info.url]; } const loader = ResourceManager._loaders[info.type]; + if (!loader) { + throw `loader not found: ${info.type}`; + } + info.url = url; const promise = loader.load(info, this); this._loadingPromises[url] = promise; promise @@ -216,7 +253,7 @@ export class ResourceManager { delete this._loadingPromises[url]; }) .catch((err: Error) => { - Promise.reject(err) + Promise.reject(err); delete this._loadingPromises[url]; }); return promise; diff --git a/packages/core/src/graphic/index.ts b/packages/core/src/graphic/index.ts index 6e95b2051..cd78e9e93 100644 --- a/packages/core/src/graphic/index.ts +++ b/packages/core/src/graphic/index.ts @@ -10,4 +10,4 @@ export { IndexBufferBinding } from "./IndexBufferBinding"; export { Mesh } from "./Mesh"; export { SubMesh } from "./SubMesh"; export { VertexBufferBinding } from "./VertexBufferBinding"; -export { VertexElement } from "./VertexElement"; +export { VertexElement } from "./VertexElement"; \ No newline at end of file diff --git a/packages/loader/package.json b/packages/loader/package.json index 235abec40..17538928e 100755 --- a/packages/loader/package.json +++ b/packages/loader/package.json @@ -21,6 +21,7 @@ "@oasis-engine/core": "0.8.0-alpha.5", "@oasis-engine/draco": "0.8.0-alpha.5", "@oasis-engine/math": "0.8.0-alpha.5", + "@oasis-engine/resource-process": "0.8.0-alpha.5", "@oasis-engine/rhi-webgl": "0.8.0-alpha.5" } } diff --git a/packages/loader/src/MaterialLoader.ts b/packages/loader/src/MaterialLoader.ts index e69de29bb..60d1d026b 100644 --- a/packages/loader/src/MaterialLoader.ts +++ b/packages/loader/src/MaterialLoader.ts @@ -0,0 +1,89 @@ +import { + AssetPromise, + AssetType, + BlinnPhongMaterial, + Loader, + LoadItem, + PBRMaterial, + PBRSpecularMaterial, + resourceLoader, + ResourceManager, + ShaderData, + Texture2D, + UnlitMaterial +} from "@oasis-engine/core"; +import { Color, Vector2, Vector3, Vector4 } from "@oasis-engine/math"; + +@resourceLoader(AssetType.Material, ["json"]) +class MaterialLoader extends Loader { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return new AssetPromise((resolve, reject) => { + this.request(item.url, { + ...item, + type: "json" + }).then((json: { [key: string]: any }) => { + const engine = resourceManager.engine; + const { shader, shaderData, macros, renderState } = json; + + let material; + switch (shader) { + case "pbr": + material = new PBRMaterial(engine); + break; + case "pbr-specular": + material = new PBRSpecularMaterial(engine); + break; + case "unlit": + material = new UnlitMaterial(engine); + break; + case "blinn-phong": + material = new BlinnPhongMaterial(engine); + break; + } + + const materialShaderData: ShaderData = material.shaderData; + for (let key in shaderData) { + const { type, value } = shaderData[key]; + + switch (type) { + case "Vector2": + materialShaderData.setVector2(key, new Vector2(value.x, value.y)); + break; + case "Vector3": + materialShaderData.setVector3(key, new Vector3(value.x, value.y, value.z)); + break; + case "Vector4": + materialShaderData.setVector4(key, new Vector4(value.x, value.y, value.z, value.w)); + break; + case "Color": + materialShaderData.setColor(key, new Color(value.r, value.g, value.b, value.a)); + break; + case "Float": + materialShaderData.setFloat(key, value); + break; + case "Texture": + resourceManager.getResourceByRef(value).then((texture) => { + materialShaderData.setTexture(key, texture); + }); + break; + } + } + + for (let i = 0, length = macros.length; i < length; i++) { + const { name, value } = macros[i]; + if (value == undefined) { + materialShaderData.enableMacro(name); + } else { + materialShaderData.enableMacro(name, value); + } + } + + for (let key in renderState) { + materialShaderData[key] = renderState[key]; + } + + resolve(material); + }); + }); + } +} diff --git a/packages/loader/src/PrefabLoader.ts b/packages/loader/src/PrefabLoader.ts deleted file mode 100644 index e69de29bb..000000000 diff --git a/packages/loader/src/SceneLoader.ts b/packages/loader/src/SceneLoader.ts new file mode 100644 index 000000000..0f6bd1aff --- /dev/null +++ b/packages/loader/src/SceneLoader.ts @@ -0,0 +1,65 @@ +import { + AssetPromise, + Loader, + LoadItem, + resourceLoader, + ResourceManager, + AssetType, + Scene, + BackgroundMode, + SkyBoxMaterial, + PrimitiveMesh +} from "@oasis-engine/core"; +import { SceneParser } from "@oasis-engine/resource-process"; + +@resourceLoader(AssetType.Scene, ["prefab"], true) +class SceneLoader extends Loader { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + const { engine } = resourceManager; + return new AssetPromise((resolve, reject) => { + return this.request(item.url, { type: "json" }).then((data) => { + // @ts-ignore + engine.resourceManager.initVirtualResources(data.files); + return SceneParser.parse(engine, data).then((scene) => { + const ambient = data.scene.ambient; + if (ambient.ambientLight) { + resourceManager.getResourceByRef(data.scene.ambient.ambientLight).then((light) => { + scene.ambientLight = light; + }); + } + scene.ambientLight.diffuseIntensity = ambient.diffuseIntensity; + scene.ambientLight.specularIntensity = ambient.specularIntensity; + + const background = data.scene.background; + scene.background.mode = background.mode; + switch (scene.background.mode) { + case BackgroundMode.SolidColor: + scene.background.solidColor.copyFrom(background.color); + break; + case BackgroundMode.Sky: + if (background.sky) { + resourceManager.getResourceByRef(background.sky).then((light) => { + const sky = scene.background.sky; + const skyMaterial = new SkyBoxMaterial(engine); + skyMaterial.textureCubeMap = light.specularTexture; + skyMaterial.textureDecodeRGBM = true; + sky.material = skyMaterial; + sky.mesh = PrimitiveMesh.createCuboid(engine, 1, 1, 1); + }); + } + break; + case BackgroundMode.Texture: + if (background.texture) { + resourceManager.getResourceByRef(background.texture).then((texture) => { + scene.background.texture = texture; + }); + } + break; + } + resolve(scene); + }); + }); + // + }); + } +} diff --git a/packages/loader/src/SpriteLoader.ts b/packages/loader/src/SpriteLoader.ts new file mode 100644 index 000000000..4c8d84cb5 --- /dev/null +++ b/packages/loader/src/SpriteLoader.ts @@ -0,0 +1,29 @@ +import { + resourceLoader, + Loader, + AssetPromise, + AssetType, + LoadItem, + Sprite, + Texture2D, + ResourceManager +} from "@oasis-engine/core"; + +@resourceLoader(AssetType.Sprite, ["sprite"], false) +class SpriteLoader extends Loader { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return new AssetPromise((resolve, reject) => { + this.request(item.url, { + ...item, + type: "json" + }).then((data) => { + resourceManager.getResourceByRef(data.texture).then((texture) => { + const sprite = new Sprite(resourceManager.engine, texture); + sprite.region = data.region; + sprite.pivot = data.pivot; + resolve(sprite); + }); + }); + }); + } +} diff --git a/packages/loader/src/index.ts b/packages/loader/src/index.ts index 243948b8f..f431f2c82 100644 --- a/packages/loader/src/index.ts +++ b/packages/loader/src/index.ts @@ -5,13 +5,15 @@ import "./KTXCubeLoader"; import "./KTXLoader"; import "./Texture2DLoader"; import "./TextureCubeLoader"; +import "./SpriteLoader"; import "./SpriteAtlasLoader"; import "./EnvLoader"; import "./HDRLoader"; import "./gltf/extensions/index"; +import "./MaterialLoader" +export * from "@oasis-engine/resource-process"; export { GLTFResource } from "./gltf/GLTFResource"; -export { GLTFModel } from "./scene-loader/GLTFModel"; -export { Model } from "./scene-loader/Model"; -export * from "./scene-loader/index"; +export * from "./SceneLoader"; export { parseSingleKTX } from "./compressed-texture"; +export type { IScene } from "@oasis-engine/resource-process"; diff --git a/packages/loader/src/scene-loader/AbilityManager.ts b/packages/loader/src/scene-loader/AbilityManager.ts deleted file mode 100644 index 4e558308f..000000000 --- a/packages/loader/src/scene-loader/AbilityManager.ts +++ /dev/null @@ -1,129 +0,0 @@ -import { Component, Logger } from "@oasis-engine/core"; -import { Model } from "./Model"; -import { Oasis } from "./Oasis"; -import { Parser } from "./Parser"; -import { pluginHook } from "./plugins/PluginManager"; -import { scriptAbility } from "./resources"; -import { ComponentConfig, Props } from "./types"; -import { switchElementsIndex } from "./utils"; -import { colliderConfigure } from "./ColliderConfigure"; - -export class AbilityManager { - private abilityMap: { [id: string]: Component } = {}; - - constructor(private oasis: Oasis) {} - - @pluginHook({ after: "abilityAdded", before: "beforeAbilityAdded" }) - public add(abilityConfig: ComponentConfig) { - const { type, node: nodeId, props, id, index } = abilityConfig; - - const node = this.oasis.nodeManager.get(nodeId); - const AbilityConstructor = this.getCompConstructor(type); - if (!AbilityConstructor) { - Logger.error(`${type} ability is not defined`); - return; - } - - const abilityProps = this.mixPropsToExplicitProps(props); - const ability = node.addComponent(AbilityConstructor); - const { enabled } = abilityProps; - if (enabled !== undefined) { - ability.enabled = enabled; - } - - if (type === "GLTFModel") { - // TODO - (ability as any).init(abilityProps); - } else if (type === "Model") { - // TODO - (ability as any).setProps(abilityProps); - if (abilityProps.material) { - (ability as any).material = abilityProps.material; - } - } else if (type === "StaticCollider" || type === "DynamicCollider") { - colliderConfigure(ability as any, abilityProps); - } else { - for (let k in abilityProps) { - if (abilityProps[k] !== null) { - ability[k] = abilityProps[k]; - } - } - } - - //@ts-ignore - const abilityArray = node._components; - const currentIndex = abilityArray.length - 1; - switchElementsIndex(abilityArray, currentIndex, index); - (ability as any).id = id; - this.abilityMap[id] = ability; - return ability; - } - - @pluginHook({ before: "beforeAbilityUpdated", after: "abilityUpdated" }) - public update(id: string, key: string, value: any) { - if (value && this.checkIsAsset(value)) { - this.get(id)[key] = this.oasis.resourceManager.get(value.id).resource; - } else { - if (this.get(id).constructor === Model) { - (this.get(id) as any).updateProp(key, value); - } else { - this.get(id)[key] = value; - } - } - - return { id, key, value }; - } - - public addRuntimeComponent(componentId: string, component: Component) { - (component as any).id = componentId; - this.abilityMap[componentId] = component; - return component; - } - - public get(id: string): Component { - return this.abilityMap[id]; - } - - @pluginHook({ after: "abilityDeleted", before: "beforeAbilityDeleted" }) - public delete(id: string) { - const ability = this.abilityMap[id]; - ability.destroy(); - delete this.abilityMap[id]; - return id; - } - - private getCompConstructor(type: string) { - const splits = type.split("."); - // script - if (splits[0] === "script") { - return scriptAbility[splits[1]]; - } - - const constructor = Parser._components["o3"][type]; - if (!constructor) { - console.warn(`${type} is not defined`); - } - return constructor; - } - - private mixPropsToExplicitProps(props: Props) { - const explicitProps = { ...props }; - for (let k in props) { - const prop = props[k]; - if (prop && this.checkIsAsset(prop)) { - const res = this.oasis.resourceManager.get(prop.id); - if (res) { - explicitProps[k] = res.resource; - } else { - explicitProps[k] = null; - Logger.warn(`AbilityManager: can't get asset "${k}", which id is ${prop.id}`); - } - } - } - return explicitProps; - } - - private checkIsAsset(prop: any): boolean { - return prop.type === "asset"; - } -} diff --git a/packages/loader/src/scene-loader/ColliderConfigure.ts b/packages/loader/src/scene-loader/ColliderConfigure.ts deleted file mode 100644 index 9369d3d16..000000000 --- a/packages/loader/src/scene-loader/ColliderConfigure.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { - BoxColliderShape, - CapsuleColliderShape, - Collider, - ColliderShapeUpAxis, - DynamicCollider, - PlaneColliderShape, - SphereColliderShape -} from "@oasis-engine/core"; -import { Vector3 } from "@oasis-engine/math"; - -// 根据Schema构造Component -export function colliderConfigure(collider: Collider, props: any) { - (collider).isShowCollider = props.isShowCollider; - - const shapes = props.colliderShapes; - for (let i = 0; i < shapes.length; i++) { - const shape = shapes[i]; - switch (shape._shapes) { - case "BoxColliderShape": { - const box = new BoxColliderShape(); - shape.size && box.setSize(shape.size[0], shape.size[1], shape.size[2]); - shape.position && box.setPosition(shape.position[0], shape.position[1], shape.position[2]); - shape.isTrigger && (box.isTrigger = shape.isTrigger); - collider.addShape(box); - break; - } - case "CapsuleColliderShape": { - const capsule = new CapsuleColliderShape(); - shape.radius && (capsule.radius = shape.radius); - shape.height && (capsule.height = shape.height); - if (shape.upAxis) { - switch (shape.upAxis) { - case "X": - capsule.upAxis = ColliderShapeUpAxis.X; - break; - case "Y": - capsule.upAxis = ColliderShapeUpAxis.Y; - break; - case "Z": - capsule.upAxis = ColliderShapeUpAxis.Z; - break; - } - } - shape.position && capsule.setPosition(shape.position[0], shape.position[1], shape.position[2]); - shape.isTrigger && (capsule.isTrigger = shape.isTrigger); - collider.addShape(capsule); - break; - } - case "PlaneColliderShape": { - const plane = new PlaneColliderShape(); - shape.rotation && plane.setRotation(shape.rotation[0], shape.rotation[1], shape.rotation[2]); - shape.position && plane.setPosition(shape.position[0], shape.position[1], shape.position[2]); - shape.isTrigger && (plane.isTrigger = shape.isTrigger); - collider.addShape(plane); - break; - } - case "SphereColliderShape": { - const sphere = new SphereColliderShape(); - shape.radius && (sphere.radius = shape.radius); - shape.position && sphere.setPosition(shape.position[0], shape.position[1], shape.position[2]); - shape.isTrigger && (sphere.isTrigger = shape.isTrigger); - collider.addShape(sphere); - break; - } - } - } - - if (collider instanceof DynamicCollider) { - const force = props.force; - if (force) { - (collider).applyForce(new Vector3(force[0], force[1], force[2])); - } - - const torque = props.torque; - if (torque) { - (collider).applyTorque(new Vector3(torque[0], torque[1], torque[2])); - } - } -} diff --git a/packages/loader/src/scene-loader/GLTFModel.ts b/packages/loader/src/scene-loader/GLTFModel.ts deleted file mode 100644 index eb93ea2b4..000000000 --- a/packages/loader/src/scene-loader/GLTFModel.ts +++ /dev/null @@ -1,197 +0,0 @@ -import { Animator, AnimatorController, Component, Entity } from "@oasis-engine/core"; -import { BoolUpdateFlag } from "@oasis-engine/core/types/BoolUpdateFlag"; -import { GLTFResource } from "../gltf/GLTFResource"; - -/** - * @deprecated - * Temporarily only for editor use. - * Remove when editor finish change from gltf to prefab. - */ -export class GLTFModel extends Component { - private _animatorController: AnimatorController; - private _speed: number = 1.0; - private _animator: Animator; - private _asset: GLTFResource; - private _glTFEntity: Entity; - private _clipPreview: string; - private _hasBuiltNode: boolean = false; - private _controllerUpdateFlag: BoolUpdateFlag; - - get asset() { - return this._asset; - } - - set asset(value: GLTFResource) { - const { _animatorController: animatorController, _speed: speed } = this; - const entity = this._glTFEntity; - if (value && value.defaultSceneRoot === this._glTFEntity) { - return; - } - if (!this._hasBuiltNode) { - entity.clearChildren(); - if (value !== null) { - entity?.destroy(); - const gltfEntity = value.defaultSceneRoot.clone(); - this._animator = gltfEntity.getComponent(Animator); - this.entity.addChild(gltfEntity); - gltfEntity.isActive = this.enabled; - this._glTFEntity = gltfEntity; - } - } - if (animatorController) { - this._animator.animatorController = animatorController; - this._animator.speed = speed; - this._playState(); - } - this._asset = value; - } - - get animatorController(): AnimatorController { - return this._animatorController; - } - - set animatorController(animatorController: AnimatorController) { - const { _animator: animator } = this; - if (animatorController !== this._animatorController) { - this._controllerUpdateFlag && this._controllerUpdateFlag.clearFromManagers(); - // @ts-ignore - this._controllerUpdateFlag = animatorController && animatorController._registerChangeFlag(); - this._animatorController = animatorController; - if (animator) { - animator.animatorController = animatorController; - this._playState(); - } - } - } - - get speed(): number { - return this._speed; - } - - set speed(speed: number) { - const { _animator: animator } = this; - this._speed = speed; - if (animator) { - animator.speed = speed; - this._playState(); - } - } - - get animator() { - return this._animator; - } - - get clipPreview() { - return this._clipPreview; - } - - set clipPreview(value: string) { - if (this._animator) { - if (value) { - if (value === "_default") { - this._playDefaultState(); - } else { - this._animator.play(value, 0); - } - } else { - // @ts-ignore - this._animator._reset(); - } - } - this._clipPreview = value; - } - - constructor(entity) { - super(entity); - } - - /** - * Init. - * @param props - Init props - */ - init(props): void { - const { asset = null, speed, animatorController, clipPreview, isClone } = props; - if (isClone) { - const rootName = (props as any).gltfRootName; - if (rootName) { - this._glTFEntity = this.entity.findByName(rootName); - } - } - if (!this._glTFEntity) { - const rootName = `GLTF-${Date.now()}`; - (props as any).gltfRootName = rootName; - this._glTFEntity = this.entity.createChild(rootName); - this._hasBuiltNode = false; - } else { - this._hasBuiltNode = true; - } - - this.asset = asset; - this.animatorController = animatorController; - this.speed = speed; - this.clipPreview = clipPreview; - } - - update() { - if (this._animator) { - if (this._controllerUpdateFlag?.flag) { - this._playState(); - } - } - } - - /** - * @override - */ - _onEnable(): void { - this._glTFEntity && (this._glTFEntity.isActive = true); - this.engine._componentsManager.addOnUpdateAnimations(this); - } - - /** - * @override - */ - _onDisable(): void { - this._glTFEntity && (this._glTFEntity.isActive = false); - this.engine._componentsManager.removeOnUpdateAnimations(this); - } - - _playState() { - const playStateName = this._clipPreview; - if (playStateName) { - if (playStateName === "_default") { - this._playDefaultState(); - } else { - this._animator.play(playStateName, 0); - } - } else { - // @ts-ignore - this._animator._reset(); - } - if (this._controllerUpdateFlag?.flag) { - this._controllerUpdateFlag.flag = false; - } - } - - _playDefaultState() { - const { _animatorController: animatorController, _animator: animator } = this; - if (!animator) return; - if (animatorController) { - const { layers } = animatorController; - for (let i = 0, length = layers.length; i < length; ++i) { - //@ts-ignore - const defaultState = layers[i]?.stateMachine?._defaultState; - const defaultStateName = defaultState?.name; - if (defaultStateName) { - animator.play(defaultStateName, i); - } else { - // @ts-ignore - animator._reset(); - } - if (this._controllerUpdateFlag?.flag) { - this._controllerUpdateFlag.flag = false; - } - } - } - } -} diff --git a/packages/loader/src/scene-loader/Model.ts b/packages/loader/src/scene-loader/Model.ts deleted file mode 100644 index c29c1a119..000000000 --- a/packages/loader/src/scene-loader/Model.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { BlinnPhongMaterial, Entity, MeshRenderer, PrimitiveMesh } from "@oasis-engine/core"; - -// Only for editor -export class Model extends MeshRenderer { - private _props: Object = null; - - constructor(entity: Entity) { - super(entity); - this.setMaterial(new BlinnPhongMaterial(this.engine)); - } - - get material(): any { - return this.getMaterial(); - } - - set material(mtl: any) { - this.setMaterial(mtl); - } - - setProps(props: any = {}) { - if (this._props !== props) { - this._props = props; - } - - switch (props.geometryType) { - case "Sphere": - this.mesh = PrimitiveMesh.createSphere(this._engine, props.sphereRadius, props.sphereSegments); - break; - - case "Cylinder": - this.mesh = PrimitiveMesh.createCylinder( - this._engine, - props.cylinderRadiusTop, - props.cylinderRadiusBottom, - props.cylinderHeight, - props.cylinderRadialSegments, - props.cylinderHeightSegments - ); - break; - - case "Plane": - this.mesh = PrimitiveMesh.createPlane( - this._engine, - props.planeWidth, - props.planeHeight, - props.planeHorizontalSegments, - props.planeVerticalSegments - ); - break; - - case "Box": - this.mesh = PrimitiveMesh.createCuboid(this._engine, props.boxWidth, props.boxHeight, props.boxDepth); - break; - } - } - - updateProp(key: string, value: string | number) { - const props = this._props; - props[key] = value; - this.setProps(props); - } -} diff --git a/packages/loader/src/scene-loader/NodeManager.ts b/packages/loader/src/scene-loader/NodeManager.ts deleted file mode 100644 index 397359f81..000000000 --- a/packages/loader/src/scene-loader/NodeManager.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { Entity } from "@oasis-engine/core"; -import { Vector3 } from "@oasis-engine/math"; -import { Oasis } from "./Oasis"; -import { pluginHook } from "./plugins/PluginManager"; -import { NodeConfig } from "./types"; -import { switchElementsIndex } from "./utils"; - -export class NodeManager { - private nodeMap: { [id: string]: Entity } = {}; - private readonly root: Entity; - - constructor(private oasis: Oasis) { - this.root = new Entity(this.oasis.engine, "root"); - } - - public addRootEntity() { - this.oasis.engine.sceneManager.activeScene.addRootEntity(this.root); - } - - @pluginHook({ after: "nodeAdded" }) - public add(nodeConfig: NodeConfig) { - this.create(nodeConfig); - this.append(nodeConfig.id, nodeConfig.parent, nodeConfig.index); - return this.get(nodeConfig.id); - } - - @pluginHook({ before: "beforeNodeUpdated", after: "nodeUpdated" }) - public update(id: string, key: string, value: any) { - this.get(id)[key] = value; - return { id, key, value }; - } - - public get(id: string): Entity { - return this.nodeMap[id]; - } - - public reset() { - this.nodeMap = {}; - } - - @pluginHook({ before: "beforeNodeDeleted" }) - public delete(id: string) { - this.nodeMap[id].destroy(); - delete this.nodeMap[id]; - } - - private create(nodeConfig: NodeConfig): Entity { - const { isActive, position, rotation, scale, id, name } = nodeConfig; - const entity = new Entity(this.oasis.engine, name); - entity.isActive = isActive; - entity.transform.position = new Vector3(position[0], position[1], position[2]); - entity.transform.rotation = new Vector3(rotation[0], rotation[1], rotation[2]); - entity.transform.scale = new Vector3(scale[0], scale[1], scale[2]); - (entity as any).id = id; - this.nodeMap[id] = entity; - return entity; - } - - private append(childId: string, parentId: string, index: number) { - const child = this.nodeMap[childId]; - const parent = this.nodeMap[parentId] || this.root; - parent.addChild(child); - //@ts-ignore - const children = parent._children; - const currentIndex = children.length - 1; - switchElementsIndex(children, currentIndex, index); - } -} diff --git a/packages/loader/src/scene-loader/Oasis.ts b/packages/loader/src/scene-loader/Oasis.ts deleted file mode 100644 index 61c306c66..000000000 --- a/packages/loader/src/scene-loader/Oasis.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { Engine, EventDispatcher, ObjectValues } from "@oasis-engine/core"; -import { AbilityManager } from "./AbilityManager"; -import { NodeManager } from "./NodeManager"; -import { SceneManager } from "./SceneManager"; -import { pluginHook, PluginManager } from "./plugins/PluginManager"; -import { RESOURCE_CLASS, SchemaResourceManager } from "./ResourceManager"; -import { Options, Schema } from "./types"; - -export class Oasis extends EventDispatcher { - public readonly nodeManager: NodeManager; - public readonly abilityManager: AbilityManager; - public readonly sceneManager: SceneManager; - public resourceManager: SchemaResourceManager; - public _canvas: HTMLCanvasElement; - private schema: Schema; - public timeout: number; - private oasis = this; - public engine: Engine; - - private constructor(private _options: Options, public readonly pluginManager: PluginManager) { - super(); - this.engine = _options.engine; - this.schema = _options.config; - this.timeout = _options.timeout; - _options.scripts = _options.scripts ?? {}; - this.nodeManager = new NodeManager(this); - this.abilityManager = new AbilityManager(this); - this.nodeManager.add = this.nodeManager.add.bind(this.nodeManager); - this.abilityManager.add = this.abilityManager.add.bind(this.abilityManager); - this.resourceManager = new SchemaResourceManager(this); - this.sceneManager = new SceneManager(this); - if (_options.fps) { - this.engine.targetFrameRate = _options.fps; - this.engine.vSyncCount = 0; - } - } - - public get canvas(): HTMLCanvasElement { - return this._options.canvas; - } - - public get options(): Readonly { - return this._options; - } - - public updateConfig(config: Schema): void { - this.schema = config; - - this.init(); - } - - @pluginHook({ after: "schemaParsed" }) - private init(): Promise { - return this.loadResources().then(() => { - this.bindResources(); - this.parseEntities(); - this.attach(); - this.nodeManager.addRootEntity(); - this.sceneManager.init(); - this.parseNodeAbilities(); - this.pluginManager.boot(this); - }); - } - - private loadResources(): Promise { - const { assets = {} } = this.schema; - - const loadingPromises = ObjectValues(assets) - .filter((asset) => { - if (RESOURCE_CLASS[asset.type]) { - return true; - } - console.warn(`${asset.type} loader is not defined. the ${asset.type} type will be ignored.`); - return false; - }) - .map((asset) => this.resourceManager.load(asset)); - - return Promise.all(loadingPromises); - } - - private bindResources() { - this.resourceManager.getAll().forEach((resource) => { - resource.bind(); - }); - } - - private parseEntities(): void { - const { nodes } = this.schema; - const indices = this.bfsNodes(); - indices.map((index) => nodes[index]).forEach(this.nodeManager.add); - } - - private parseNodeAbilities(): void { - const { abilities } = this.schema; - Object.keys(abilities) - .map((id) => ({ id, ...abilities[id] })) - .forEach(this.abilityManager.add); - } - - private bfsNodes(): number[] { - const { nodes } = this.schema; - const roots = ObjectValues(nodes) - .filter((node) => !nodes[node.parent]) - .map((node) => node.id); - - let result = []; - const traverseChildren = (roots: string[]) => { - result = result.concat(roots); - roots.forEach((id) => { - const children = nodes[id].children; - children && traverseChildren(children); - }); - }; - traverseChildren(roots); - return result; - } - - private attach() { - this.resourceManager.getAll().forEach((resource) => { - resource.attach(); - }); - } - - static create(options: Options, pluginManager: PluginManager): Promise { - const oasis = new Oasis(options, pluginManager); - return oasis.init().then(() => { - options.autoPlay && oasis.engine.run(); - return oasis; - }); - } -} diff --git a/packages/loader/src/scene-loader/Parser.ts b/packages/loader/src/scene-loader/Parser.ts deleted file mode 100644 index 52309bbb4..000000000 --- a/packages/loader/src/scene-loader/Parser.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { Component } from "@oasis-engine/core"; -import { Oasis } from "./Oasis"; -import { Plugin } from "./plugins/Plugin"; -import { PluginManager } from "./plugins/PluginManager"; -import { Options } from "./types"; -import { compatibleToV2 } from "./temp.compatible"; - -const CURRENT_SCHEMA_VERSION = 3; - -export class Parser { - private pluginManager: PluginManager = new PluginManager(); - /** - * Parse a scene config. - * @param options - Options of scene - */ - public parse(options: Options): Promise { - if (options?.config?.version !== CURRENT_SCHEMA_VERSION) { - console.warn( - `schema-parser: schema version "${options?.config?.version}" is out of date, please re-pull the latest version (version ${CURRENT_SCHEMA_VERSION}) of the schema` - ); - } - compatibleToV2(options.config); - return Oasis.create(options, this.pluginManager); - } - - register(plugin: Plugin) { - this.pluginManager.register(plugin); - } - - resetPlugins() { - this.pluginManager.reset(); - } - - private constructor() {} - - static create(): Parser { - const parser = new Parser(); - return parser; - } - - /** @internal */ - public static _components: { [namespace: string]: { [compName: string]: Component } } = {}; - /** - * Register parsing component - * @param namespace - Namespace - * @param components - Components - */ - static registerComponents(namespace: string, components: { [key: string]: any }) { - if (!this._components[namespace]) { - this._components[namespace] = {}; - } - Object.assign(this._components[namespace], components); - } -} - -export const parser = Parser.create(); diff --git a/packages/loader/src/scene-loader/ResourceManager.ts b/packages/loader/src/scene-loader/ResourceManager.ts deleted file mode 100644 index 1a90c9dd3..000000000 --- a/packages/loader/src/scene-loader/ResourceManager.ts +++ /dev/null @@ -1,181 +0,0 @@ -import { ObjectValues, ResourceManager } from "@oasis-engine/core"; -import { Oasis } from "./Oasis"; -import { pluginHook } from "./plugins/PluginManager"; -import { - BaseResource, - BlinnPhongMaterialResource, - GLTFResource, - PBRMaterialResource, - PBRSpecularMaterialResource, - SchemaResource, - ScriptResource, - SpriteResource, - TextureCubeMapResource, - TextureResource, - UnlitMaterialResource, - AnimatorControllerResource, - AnimationClipResource, - AmbientLightResource -} from "./resources"; -import { SpriteAtlasResource } from "./resources/SpriteAtlasResource"; -import { AssetConfig } from "./types"; - -export const RESOURCE_CLASS = { - script: ScriptResource, - gltf: GLTFResource, - texture: TextureResource, - // 'image': TextureResource, - cubeTexture: TextureCubeMapResource, - PBRMaterial: PBRMaterialResource, - PBRSpecularMaterial: PBRSpecularMaterialResource, - UnlitMaterial: UnlitMaterialResource, - BlinnPhongMaterial: BlinnPhongMaterialResource, - base: BaseResource, - sprite: SpriteResource, - SpriteAtlas: SpriteAtlasResource, - animatorController: AnimatorControllerResource, - animationClip: AnimationClipResource, - environment: AmbientLightResource -}; - -const RESOURCE_TYPE: Map = new Map(); -for (const key in RESOURCE_CLASS) { - if (RESOURCE_CLASS.hasOwnProperty(key)) { - const element = RESOURCE_CLASS[key]; - RESOURCE_TYPE.set(element, key); - } -} - -const resourceFactory = { - createResource(resourceManager: SchemaResourceManager, type: string): SchemaResource { - return new RESOURCE_CLASS[type](resourceManager); - } -}; - -export function registerResource(type: string, resource: any) { - if (!RESOURCE_CLASS.hasOwnProperty(type)) { - RESOURCE_CLASS[type] = resource; - RESOURCE_TYPE.set(resource, type); - } -} - -export class SchemaResourceManager { - private resourceMap: { [id: string]: SchemaResource } = {}; - private resourceIdMap: WeakMap = new WeakMap(); - private maxId = 0; - private readonly engineResourceManager: ResourceManager; - - constructor(private oasis: Oasis) { - this.engineResourceManager = this.oasis.engine.resourceManager; - } - - load(asset: AssetConfig): Promise { - const resource = resourceFactory.createResource(this, asset.type); - const loadPromise = resource.load(this.oasis.engine.resourceManager, asset, this.oasis); - this.maxId = Math.max(+asset.id, this.maxId); - loadPromise.then(() => { - this.resourceMap[asset.id] = resource; - this.resourceIdMap.set(resource, asset.id); - }); - return loadPromise; - } - - add(asset: AssetConfig): Promise { - const resource = resourceFactory.createResource(this, asset.type); - return new Promise((resolve) => { - resource.loadWithAttachedResources(this.oasis.engine.resourceManager, asset, this.oasis).then((result) => { - resolve(this.getAddResourceResult(result.resources, result.structure)); - }); - }); - } - - @pluginHook({ before: "beforeResourceRemove" }) - remove(id: string): Promise> { - return new Promise((resolve) => { - const resource = this.resourceMap[id]; - const result = [id]; - let hasAttachedResource = false; - delete this.resourceMap[id]; - if (resource) { - const attached = resource.attachedResources; - for (let index = 0; index < attached.length; index++) { - const attachedResource = attached[index]; - const attachedResourceId = this.resourceIdMap.get(attachedResource); - if (attachedResourceId) { - hasAttachedResource = true; - this.remove(attachedResourceId).then((attachedResourceRemoveResult) => { - result.push(...attachedResourceRemoveResult); - resolve(result); - }); - } - } - } - if (!hasAttachedResource) { - resolve(result); - } - }); - } - - @pluginHook({ after: "resourceUpdated", before: "beforeResourceUpdate" }) - update(id: string, key: string, value: any) { - const resource = this.get(id); - if (resource) { - resource.update(key, value); - } - return { - resource, - id, - key, - value - }; - } - - updateMeta(id: string, key: string, value: any) { - const resource = this.get(id); - if (resource) { - resource.updateMeta(key, value); - } - } - - get(id: string): SchemaResource { - return this.resourceMap[id]; - } - - getAll(): Array { - return ObjectValues(this.resourceMap); - } - - private getAddResourceResult(resources, structure) { - const addResourceResult: any = {}; - const resource = resources[structure.index]; - const id = `${++this.maxId}`; - this.resourceMap[id] = resource; - this.resourceIdMap.set(resource, id); - - addResourceResult.id = this.maxId; - addResourceResult.type = RESOURCE_TYPE.get(resource.constructor); - addResourceResult.meta = resource.meta; - addResourceResult.props = {}; - for (const key in structure.props) { - if (structure.props.hasOwnProperty(key)) { - const element = structure.props[key]; - if (element) { - if (Array.isArray(element)) { - addResourceResult.props[key] = element.map((child) => this.getAddResourceResult(resources, child)); - } else { - addResourceResult.props[key] = this.getAddResourceResult(resources, element); - } - } - } - } - return addResourceResult; - } - - get isLocal(): boolean { - return this.oasis.options.local; - } - - get useCompressedTexture(): boolean { - return this.oasis.options.useCompressedTexture ?? true; - } -} diff --git a/packages/loader/src/scene-loader/SceneManager.ts b/packages/loader/src/scene-loader/SceneManager.ts deleted file mode 100644 index 59ea3c1a6..000000000 --- a/packages/loader/src/scene-loader/SceneManager.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { AmbientLight, DiffuseMode, PrimitiveMesh, SkyBoxMaterial } from "@oasis-engine/core"; -import { Oasis } from "./Oasis"; -import { pluginHook } from "./plugins/PluginManager"; - -export class SceneManager { - constructor(private oasis: Oasis) {} - - init() { - const { scene } = this.oasis.options.config; - if (scene) { - Object.keys(scene).forEach((field) => { - const fieldConfig = scene[field]; - Object.keys(fieldConfig.props).forEach((key) => { - const prop = fieldConfig.props[key]; - this.setProp(field, key, prop); - }); - }); - } - } - - @pluginHook({ before: "beforeSceneUpdated", after: "sceneUpdated" }) - public update(field: string, key: string, value: any) { - this.setProp(field, key, value); - return { field, key, value }; - } - - private setProp(field, key, prop) { - const scene = this.oasis.engine.sceneManager.activeScene; - if (field === "background" && key === "skyboxTexture") { - const sky = scene.background.sky; - if (prop) { - sky.mesh = PrimitiveMesh.createCuboid(scene.engine, 2, 2, 2); - const skyMaterial = new SkyBoxMaterial(scene.engine); - skyMaterial.textureCubeMap = this.oasis.resourceManager.get(prop.id).resource; - sky.material = skyMaterial; - } else { - sky.mesh = null; - sky.material = null; - } - } else if (scene[field] && field === "ambientLight" && key === "specularTexture") { - if (prop && prop.type === "asset") { - const ambientLight: AmbientLight = this.oasis.resourceManager.get(prop.id).resource; - scene.ambientLight.specularTexture = ambientLight.specularTexture; - scene.ambientLight.diffuseSphericalHarmonics = ambientLight.diffuseSphericalHarmonics; - scene.ambientLight.diffuseMode = DiffuseMode.SphericalHarmonics; - scene.ambientLight.specularTextureDecodeRGBM = true; - } else { - scene.ambientLight.specularTexture = null; - scene.ambientLight.diffuseMode = DiffuseMode.SolidColor; - } - } else if (scene[field]) { - if (prop && prop.type === "asset") { - scene[field][key] = this.oasis.resourceManager.get(prop.id).resource; - } else { - scene[field][key] = prop; - } - } - } -} diff --git a/packages/loader/src/scene-loader/index.ts b/packages/loader/src/scene-loader/index.ts deleted file mode 100644 index 0e999e8c5..000000000 --- a/packages/loader/src/scene-loader/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export { GLTFModel } from "./GLTFModel"; -export { Model } from "./Model"; -export { Oasis } from "./Oasis"; -export { parser, Parser } from "./Parser"; -export { registerResource } from "./ResourceManager"; -export { SchemaResource, script, SpritePivotType } from "./resources"; -export * from "./types"; diff --git a/packages/loader/src/scene-loader/plugins/Plugin.ts b/packages/loader/src/scene-loader/plugins/Plugin.ts deleted file mode 100644 index f164c5172..000000000 --- a/packages/loader/src/scene-loader/plugins/Plugin.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { PluginHook } from "./PluginManager"; -import { Oasis } from "../Oasis"; - -export type Plugin = ((oasis: Oasis) => PluginHook) | PluginHook; diff --git a/packages/loader/src/scene-loader/plugins/PluginManager.ts b/packages/loader/src/scene-loader/plugins/PluginManager.ts deleted file mode 100644 index 08ce5a5ed..000000000 --- a/packages/loader/src/scene-loader/plugins/PluginManager.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { Component, Entity } from "@oasis-engine/core"; -import { Oasis } from "../Oasis"; -import { SchemaResource } from "../resources"; -import { Plugin } from "./Plugin"; -export class PluginManager implements PluginHook { - private registeredPlugins: Set = new Set(); - private plugins: PluginHook[] = []; - - register(plugin: Plugin) { - this.registeredPlugins.add(plugin); - } - - boot(oasis: Oasis) { - for (let plugin of this.registeredPlugins.values()) { - if (typeof plugin === "function") { - plugin = plugin(oasis); - } - this.plugins.push(plugin); - } - } - - reset() { - this.registeredPlugins.clear(); - this.plugins = []; - } - - nodeAdded(entity: Entity) { - this.delegateMethod("nodeAdded", entity); - } - - private delegateMethod(name: keyof PluginHook, ...args) { - this.plugins.forEach((plugin) => plugin[name] && (plugin[name] as any)(...args)); - } -} - -export interface PluginHook { - oasis?: Oasis; - nodeAdded?(entity: Entity): any; - beforeNodeUpdated?(id: string, key: string, value: any): any; - nodeUpdated?(updateConfig?: { id: string; key: string; value: any }): any; - abilityAdded?(ability: Component): any; - beforeAbilityAdded?(config: any): any; - beforeAbilityUpdated?(id: string, key: string, value: any): any; - abilityUpdated?(updateConfig?: { id: string; key: string; value: any }): any; - schemaParsed?(): any; - abilityDeleted?(id: string): any; - beforeAbilityDeleted?(id: string): any; - beforeNodeDeleted?(config: any): any; - beforeResourceRemove?(id: string): any; - resourceUpdated?(info: { resource: SchemaResource; id: string; key: string; value: any }): any; - beforeResourceUpdate?(id: string, key: string, value: any): any; - // todo type - beforeResourceAdd?(resource: any): any; - resourceAdded?(resource: any): any; - beforeSceneUpdated?(field: string, key: string, value: any): any; - sceneUpdated?(updateConfig?: { type: string; key: string; value: any }): any; -} - -export function pluginHook(options: Partial<{ before: keyof PluginHook; after: keyof PluginHook }>): MethodDecorator { - return function (target: any, propertyName: string, descriptor: TypedPropertyDescriptor) { - const method = descriptor.value; - - descriptor.value = function (...args: any[]) { - options.before && this.oasis.pluginManager.delegateMethod(options.before, ...args); - return Promise.resolve(method.apply(this, arguments)).then((returnObj) => { - options.after && this.oasis.pluginManager.delegateMethod(options.after, returnObj); - return returnObj; - }); - }; - }; -} diff --git a/packages/loader/src/scene-loader/resources/AmbientLightResource.ts b/packages/loader/src/scene-loader/resources/AmbientLightResource.ts deleted file mode 100644 index 22bf8ba8b..000000000 --- a/packages/loader/src/scene-loader/resources/AmbientLightResource.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { AssetType, ResourceManager } from "@oasis-engine/core"; -import { Oasis } from "../Oasis"; -import { AssetConfig } from "../types"; -import { SchemaResource } from "./SchemaResource"; - -export class AmbientLightResource extends SchemaResource { - load(resourceManager: ResourceManager, assetConfig: AssetConfig, oasis: Oasis): Promise { - return new Promise((resolve, reject) => { - const { url } = assetConfig; - resourceManager - .load({ url, type: AssetType.Env }) - .then((res) => { - this._resource = res; - resolve(this); - }) - .catch((e) => { - reject(e); - }); - }); - } - - setMeta() { - if (this.resource) { - this._meta.name = this.resource.name; - } - } -} diff --git a/packages/loader/src/scene-loader/resources/AnimationClipResource.ts b/packages/loader/src/scene-loader/resources/AnimationClipResource.ts deleted file mode 100644 index 5e86e70d6..000000000 --- a/packages/loader/src/scene-loader/resources/AnimationClipResource.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { - ResourceManager, -} from "@oasis-engine/core"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { SchemaResource } from "./SchemaResource"; - -export class AnimationClipResource extends SchemaResource { - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - this._resource = assetConfig.props || {}; - this.setMeta(); - resolve(this); - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig - ): Promise { - return new Promise((resolve, reject) => { - let loadPromise; - if (assetConfig.props) { - loadPromise = this.load(resourceManager, assetConfig); - } else { - reject("Load AnimationClip Error"); - } - if (loadPromise) { - loadPromise.then(() => { - const result: any = { - resources: [this], - structure: { - index: 0, - props: {} - } - }; - resolve(result); - }); - } - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } - - getProps() { - return this._resource; - } -} diff --git a/packages/loader/src/scene-loader/resources/AnimatorControllerResource.ts b/packages/loader/src/scene-loader/resources/AnimatorControllerResource.ts deleted file mode 100644 index 6946d4b78..000000000 --- a/packages/loader/src/scene-loader/resources/AnimatorControllerResource.ts +++ /dev/null @@ -1,214 +0,0 @@ -import { AnimationClipResource } from "./AnimationClipResource"; -import { - AnimatorController, - AnimatorControllerLayer, - AnimatorStateMachine, - AnimatorStateTransition -} from "@oasis-engine/core"; -import { ResourceManager } from "@oasis-engine/core"; -import { SchemaResource } from "./SchemaResource"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; - -export class AnimatorControllerResource extends SchemaResource { - public gltf; - private animatorControllerData; - private animationClipAssets: any[]; - private animationsIndices: { - name: string; - index: number; - }[]; - - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - const { animatorController, animationClips: animationClipAssets, animationsIndices, gltf } = - assetConfig.props || {}; - this._resource = new AnimatorController(); - this.animatorControllerData = animatorController; - this.animationsIndices = animationsIndices || []; - this.animationClipAssets = animationClipAssets || []; - this.gltf = gltf; - !animatorController && this._setDefaultDataByAnimationClip(); - this.setMetaData("name", assetConfig.name); - resolve(this); - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig - ): Promise { - return new Promise((resolve, reject) => { - const clipLoadPromises = []; - this.load(resourceManager, assetConfig).then(() => { - const result: any = { - resources: [this], - structure: { - index: 0, - props: { - animationClips: [] - } - } - }; - - const animations = this.animationsIndices; - for (let i = 0, length = animations.length; i < length; ++i) { - const clip = animations[i]; - const clipResourse = new AnimationClipResource(this.resourceManager); - this.attachedResources.push(clipResourse); - clipLoadPromises.push( - clipResourse.loadWithAttachedResources(resourceManager, { - type: "animationClip", - name: clip.name, - props: clip - }) - ); - } - - Promise.all(clipLoadPromises).then((res) => { - const { animationClips } = result.structure.props; - res.forEach((clip) => { - const clipStructure = clip.structure; - const clipResource = clip.resources[clipStructure.index]; - result.resources.push(clipResource); - clipStructure.index = result.resources.length - 1; - animationClips.push(clipStructure); - }); - resolve(result); - }); - }); - }); - } - - setMetaData(key, value) { - this._meta[key] = value; - } - - update(key: string, value: any) { - this._initAnimatorController(value); - } - - bind() { - const { animatorControllerData, animationClipAssets } = this; - this._bindClips(animationClipAssets); - if (animatorControllerData) { - this._initAnimatorController(animatorControllerData); - } else { - this._setDefaultDataByAnimationClipAsset(); - } - } - - _initAnimatorController(animatorControllerData) { - const { animations } = this.gltf || {}; - const { layers } = animatorControllerData; - if (!animations || !layers) return; - this._resource.clearLayers(); - for (let i = 0, length = layers.length; i < length; ++i) { - const { name, blending, weight, stateMachine: stateMachineData } = layers[i]; - if (!stateMachineData) continue; - const layer = new AnimatorControllerLayer(name); - layer.blendingMode = blending; - layer.weight = weight; - const { states } = stateMachineData; - const stateMachine = new AnimatorStateMachine(); - let stateMachineTransitions = []; - for (let j = 0, length = states.length; j < length; ++j) { - const stateData = states[j]; - const { - name, - transitions, - clip, - speed, - wrapMode, - clipStartNormalizedTime, - clipEndNormalizedTime, - isDefaultState - } = stateData; - const { id: clipAssetId } = clip || {}; - if (!clipAssetId) continue; - const uniqueName = stateMachine.makeUniqueStateName(name); - if (uniqueName !== name) { - console.warn(`AnimatorState name is existed, name: ${name} reset to ${uniqueName}`); - } - const state = stateMachine.addState(uniqueName); - state.speed = speed; - state.wrapMode = wrapMode; - const animationIndex = this.resourceManager.get(clipAssetId).resource; - const animationClip = animations[animationIndex.index]; - if (!animationClip) continue; - state.clip = animationClip; - state.clipStartTime = clipStartNormalizedTime; - state.clipEndTime = clipEndNormalizedTime; - for (let j = 0, length = transitions.length; j < length; ++j) { - const transition = transitions[j]; - transitions[j].srcState = state; - stateMachineTransitions.push(transition); - } - - if (isDefaultState) { - //@ts-ignore - stateMachine._defaultState = state; - } - } - for (let j = 0, length = stateMachineTransitions.length; j < length; ++j) { - const transitionData = stateMachineTransitions[j]; - const transition = new AnimatorStateTransition(); - transition.duration = transitionData.duration; - transition.offset = transitionData.offset; - transition.exitTime = transitionData.exitTime; - transition.destinationState = stateMachine.findStateByName(transitionData.targetStateName); - transitionData.srcState.addTransition(transition); - delete transitionData.srcState; - } - layer.stateMachine = stateMachine; - this._resource.addLayer(layer); - } - } - - _bindClips(animationClips) { - for (let i = 0, length = animationClips.length; i < length; i++) { - const clipAsset = animationClips[i]; - const clipResource = this.resourceManager.get(clipAsset.id); - if (clipResource) { - this._attachedResources.push(clipResource); - } else { - `AnimatorResource: ${this.meta.name} can't find asset "animationClip", which id is: ${clipAsset.id}`; - } - } - } - - _setDefaultDataByAnimationClipAsset() { - const { animationClipAssets } = this; - if (!animationClipAssets.length) { - return; - } - let clips = []; - for (let i = 0, length = animationClipAssets.length; i < length; i++) { - const clipAsset = this.resourceManager.get(animationClipAssets[i].id); - clips.push(clipAsset.resource); - } - this.animationsIndices = clips; - this._setDefaultDataByAnimationClip(); - } - - _setDefaultDataByAnimationClip() { - const { animationsIndices, _resource: animatorController, gltf } = this; - if (!animationsIndices.length || !gltf) { - return; - } - const { animations } = gltf - const layer = new AnimatorControllerLayer("layer"); - const animatorStateMachine = new AnimatorStateMachine(); - animatorController.addLayer(layer); - layer.stateMachine = animatorStateMachine; - for (let i = 0, length = animationsIndices.length; i < length; i++) { - const animationIndex = animationsIndices[i]; - const { name, index} = animationIndex - const uniqueName = animatorStateMachine.makeUniqueStateName(name); - if (uniqueName !== name) { - console.warn(`AnimatorState name is existed, name: ${name} reset to ${uniqueName}`); - } - const animatorState = animatorStateMachine.addState(uniqueName); - animatorState.clip = animations[index]; - } - } -} diff --git a/packages/loader/src/scene-loader/resources/BaseResource.ts b/packages/loader/src/scene-loader/resources/BaseResource.ts deleted file mode 100644 index a90ee1096..000000000 --- a/packages/loader/src/scene-loader/resources/BaseResource.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { SchemaResource } from "./SchemaResource"; -import { AssetConfig } from "../types"; - -export class BaseResource extends SchemaResource { - load(resourceLoader, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - this._resource = assetConfig; - this.setMetaData("name", this.resource.name); - this.setMetaData("url", this.resource.url); - resolve(this); - }); - } - - setMetaData(key, value) { - this._meta[key] = value; - } -} diff --git a/packages/loader/src/scene-loader/resources/BlinnPhongMaterialResource.ts b/packages/loader/src/scene-loader/resources/BlinnPhongMaterialResource.ts deleted file mode 100644 index f01e4c056..000000000 --- a/packages/loader/src/scene-loader/resources/BlinnPhongMaterialResource.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { BlinnPhongMaterial, Logger, ResourceManager, Texture } from "@oasis-engine/core"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { getAllGetters, isAsset } from "../utils"; -import { SchemaResource } from "./SchemaResource"; -import { TextureResource } from "./TextureResource"; - -export class BlinnPhongMaterialResource extends SchemaResource { - private configProps; - - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - const assetObj = new BlinnPhongMaterial(resourceManager.engine); - this.configProps = assetConfig.props; - this._resource = assetObj; - - for (let k in this.configProps) { - if (!isAsset(this.configProps[k])) { - assetObj[k] = this.configProps[k]; - } - } - this.setMeta(); - resolve(this); - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig - ): Promise { - return new Promise((resolve, reject) => { - let loadPromise; - if (assetConfig.resource instanceof BlinnPhongMaterial) { - loadPromise = new Promise((resolve) => { - this._resource = assetConfig.resource; - this.setMeta(); - resolve(this); - }); - } else if (assetConfig.props) { - loadPromise = this.load(resourceManager, assetConfig); - } else { - reject("Load BlinnPhongMaterial Error"); - } - if (loadPromise) { - loadPromise.then(() => { - const result: any = { - resources: [this], - structure: { - index: 0, - props: {} - } - }; - - const material = this._resource; - getAllGetters(this._resource).forEach((attr) => { - if (!(material[attr] instanceof Texture)) return; - const textureResource = new TextureResource(this.resourceManager, material[attr]); - this.attachedResources.push(textureResource); - result.resources.push(textureResource); - result.structure.props[attr] = { - index: result.resources.length - 1 - }; - }); - resolve(result); - }); - } - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } - - bind() { - const resource = this._resource; - Object.keys(this.configProps).forEach((attr) => { - const value = this.configProps[attr]; - if (isAsset(value)) { - const textureResource = this.resourceManager.get(value.id); - if (textureResource && textureResource instanceof TextureResource) { - resource[attr] = textureResource.resource; - this._attachedResources.push(textureResource); - } else { - resource[attr] = null; - Logger.warn( - `BlinnPhongMaterialResource: ${this.meta.name} can't find asset "${attr}", which id is: ${value.id}` - ); - } - } else { - resource[attr] = value; - } - }); - } -} diff --git a/packages/loader/src/scene-loader/resources/GLTFResource.ts b/packages/loader/src/scene-loader/resources/GLTFResource.ts deleted file mode 100644 index 856dfd90e..000000000 --- a/packages/loader/src/scene-loader/resources/GLTFResource.ts +++ /dev/null @@ -1,216 +0,0 @@ -import { AnimatorControllerResource } from "./AnimatorControllerResource"; -import { - AssetType, - Entity, - Logger, - MeshRenderer, - PBRMaterial, - PBRSpecularMaterial, - ResourceManager, - UnlitMaterial -} from "@oasis-engine/core"; -import { Oasis } from "../Oasis"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { BlinnPhongMaterialResource } from "./BlinnPhongMaterialResource"; -import { PBRMaterialResource } from "./PBRMaterialResource"; -import { PBRSpecularMaterialResource } from "./PBRSpecularMaterialResource"; -import { SchemaResource } from "./SchemaResource"; -import { UnlitMaterialResource } from "./UnlitMaterialResource"; - -export class GLTFResource extends SchemaResource { - load(resourceManager: ResourceManager, assetConfig: AssetConfig, oasis: Oasis): Promise { - return resourceManager.load({ url: assetConfig.url, type: AssetType.Prefab }).then((res) => { - const gltf = res; - if (assetConfig.props) { - gltf.newMaterial = (assetConfig.props as any).newMaterial; - gltf.animatorControllers = (assetConfig.props as any).animatorControllers; - } - this._resource = gltf; - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig, - oasis: Oasis - ): Promise { - return new Promise((resolve) => { - this.load(resourceManager, assetConfig, oasis).then(() => { - const gltf = this.resource; - const { materials = [], _animationsIndices = [] } = gltf; - const materialLoadPromises = []; - const clipLoadPromises = []; - let animatorControllerLoadPromise: Promise; - const result = { - resources: [this], - structure: { - index: 0, - props: { - newMaterial: [], - animatorControllers: [] - } - } - }; - if (materials?.length) { - for (let i = 0; i < materials.length; i++) { - const material = materials[i]; - let materialResource = null; - let type = ""; - - if (material instanceof PBRMaterial) { - materialResource = new PBRMaterialResource(this.resourceManager); - type = "PBRMaterial"; - } else if (material instanceof UnlitMaterial) { - materialResource = new UnlitMaterialResource(this.resourceManager); - type = "UnlitMaterial"; - } else if (material instanceof PBRSpecularMaterial) { - materialResource = new PBRSpecularMaterialResource(this.resourceManager); - type = "PBRSpecularMaterial"; - } else { - materialResource = new BlinnPhongMaterialResource(this.resourceManager); - type = "BlinnPhongMaterial"; - } - - this._attachedResources.push(materialResource); - materialLoadPromises.push( - materialResource.loadWithAttachedResources(resourceManager, { - type, - name: material.name, - resource: material - }) - ); - } - } - - if (_animationsIndices.length) { - const animatorControllerResource = new AnimatorControllerResource(this.resourceManager); - this._attachedResources.push(animatorControllerResource); - animatorControllerLoadPromise = animatorControllerResource.loadWithAttachedResources(resourceManager, { - type: "animatorController", - name: "AnimatorController", - props: { - animationsIndices: _animationsIndices, - gltf: this._resource - } - }); - } - - const loadAttachedMaterial = Promise.all(materialLoadPromises).then((res) => { - const newMaterial = result.structure.props.newMaterial; - res.forEach((mat) => { - const matStructure = mat.structure; - const matResource = mat.resources[matStructure.index]; - result.resources.push(matResource); - matStructure.index = result.resources.length - 1; - for (const key in matStructure.props) { - if (matStructure.props.hasOwnProperty(key)) { - const textureStructure = matStructure.props[key]; - const textureResource = mat.resources[textureStructure.index]; - result.resources.push(textureResource); - textureStructure.index = result.resources.length - 1; - } - } - newMaterial.push(matStructure); - }); - }); - const loadAttachedController = animatorControllerLoadPromise - ? animatorControllerLoadPromise.then((res) => { - const { animatorControllers } = result.structure.props; - const controllerStructure = res.structure; - const controllerResource = res.resources[controllerStructure.index]; - result.resources.push(controllerResource as any); - controllerStructure.index = result.resources.length - 1; - const { animationClips } = controllerStructure.props; - if (animationClips) { - for (let i = 0, length = animationClips.length; i < length; ++i) { - const clipStructure = animationClips[i]; - const clipResource = res.resources[clipStructure.index]; - result.resources.push(clipResource); - clipStructure.index = result.resources.length - 1; - } - } - animatorControllers.push(controllerStructure); - }) - : Promise.resolve(); - Promise.all([loadAttachedMaterial, loadAttachedController]).then(() => { - resolve(result); - }); - }); - }); - } - - setMeta(assetConfig?: AssetConfig) { - if (assetConfig) { - this.meta.name = assetConfig.name; - } - } - - bind() { - const resource = this._resource; - this.bindMaterials(resource.newMaterial); - this.bindAnimatorControllers(resource.animatorControllers); - } - - update(key: string, value: any) { - if (key === "newMaterial") { - this.bindMaterials(value); - } else { - this._resource[key] = value; - } - } - - private bindMaterials(newMaterialsConfig) { - const newMaterialCount = newMaterialsConfig.length; - if (!newMaterialsConfig || !newMaterialsConfig.length) { - return; - } - - const gltf = this._resource; - - const newMaterials = new Array(newMaterialCount); - gltf.newMaterial = newMaterials; - - for (let i = 0; i < newMaterialsConfig.length; i++) { - const mtlResource = this.resourceManager.get(newMaterialsConfig[i].id); - if (mtlResource) { - this._attachedResources.push(mtlResource); - newMaterials[i] = mtlResource.resource; - } else { - Logger.warn( - `GLTFResource: ${this.meta.name} can't find asset "material", which id is: ${newMaterialsConfig[i].id}` - ); - } - } - - const gltfRoot = gltf.defaultSceneRoot as Entity; - const originMaterials = gltf.materials; - const meshRenderers: MeshRenderer[] = gltfRoot.getComponentsIncludeChildren(MeshRenderer, []); - - for (let i = 0; i < newMaterialCount; i++) { - const newMaterial = newMaterials[i]; - const originMaterial = originMaterials[i]; - for (let j = 0; j < meshRenderers.length; j++) { - const meshRenderer = meshRenderers[j]; - const meshMaterials = meshRenderer.getMaterials(); - for (let k = 0; k < meshMaterials.length; k++) { - if (originMaterial === meshMaterials[k]) { - meshRenderer.setMaterial(k, newMaterial); - } - } - } - } - } - - private bindAnimatorControllers(animatorControllers) { - for (let i = 0, length = animatorControllers.length; i < length; i++) { - const animatorControllerAsset = animatorControllers[i]; - const controllerResource = this.resourceManager.get(animatorControllerAsset.id); - controllerResource.gltf = this._resource; - if (controllerResource) { - this._attachedResources.push(controllerResource); - } else { - `GLTFResource: ${this.meta.name} can't find asset "animatorController", which id is: ${animatorControllerAsset.id}`; - } - } - } -} diff --git a/packages/loader/src/scene-loader/resources/PBRMaterialResource.ts b/packages/loader/src/scene-loader/resources/PBRMaterialResource.ts deleted file mode 100644 index 542054316..000000000 --- a/packages/loader/src/scene-loader/resources/PBRMaterialResource.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { Logger, PBRMaterial, ResourceManager, Texture } from "@oasis-engine/core"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { isAsset } from "../utils"; -import { SchemaResource } from "./SchemaResource"; -import { TextureResource } from "./TextureResource"; - -const attrs = [ - "metallic", - "roughness", - "roughnessMetallicTexture", - - // pbr base - "tilingOffset", - "baseColor", - "normalTextureIntensity", - "emissiveColor", - "occlusionTextureIntensity", - "baseTexture", - "normalTexture", - "emissiveTexture", - "occlusionTexture", - - // base material - "isTransparent", - "alphaCutoff", - "renderFace", - "blendMode" -]; -export class PBRMaterialResource extends SchemaResource { - private configProps; - - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - const assetObj = new PBRMaterial(resourceManager.engine); - this.configProps = assetConfig.props; - - for (let k in this.configProps) { - if (!isAsset(this.configProps[k])) { - assetObj[k] = this.configProps[k]; - } - } - this._resource = assetObj; - this.setMeta(); - resolve(this); - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig - ): Promise { - return new Promise((resolve, reject) => { - let loadPromise; - if (assetConfig.resource instanceof PBRMaterial) { - loadPromise = new Promise((resolve) => { - this._resource = assetConfig.resource; - this.setMeta(); - resolve(this); - }); - } else if (assetConfig.props) { - loadPromise = this.load(resourceManager, assetConfig); - } else { - reject("Load PBRMaterial Error"); - } - if (loadPromise) { - loadPromise.then(() => { - const result: any = { - resources: [this], - structure: { - index: 0, - props: {} - } - }; - - const material = this._resource; - attrs.forEach((attr) => { - if (!(material[attr] instanceof Texture)) return; - const textureResource = new TextureResource(this.resourceManager, material[attr]); - this.attachedResources.push(textureResource); - result.resources.push(textureResource); - result.structure.props[attr] = { - index: result.resources.length - 1 - }; - }); - resolve(result); - }); - } - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } - - getProps() { - const result = {}; - attrs.forEach((prop) => (result[prop] = this.resource[prop])); - return result; - } - - bind() { - const resource = this._resource; - Object.keys(this.configProps).forEach((attr) => { - const value = this.configProps[attr]; - if (isAsset(value)) { - const textureResource = this.resourceManager.get(value.id); - if (textureResource && textureResource instanceof TextureResource) { - resource[attr] = textureResource.resource; - this._attachedResources.push(textureResource); - } else { - resource[attr] = null; - Logger.warn(`PBRMaterialResource: ${this.meta.name} can't find asset "${attr}", which id is: ${value.id}`); - } - } else { - resource[attr] = value; - } - }); - } -} diff --git a/packages/loader/src/scene-loader/resources/PBRSpecularMaterialResource.ts b/packages/loader/src/scene-loader/resources/PBRSpecularMaterialResource.ts deleted file mode 100644 index 9fb572803..000000000 --- a/packages/loader/src/scene-loader/resources/PBRSpecularMaterialResource.ts +++ /dev/null @@ -1,124 +0,0 @@ -import { Logger, PBRSpecularMaterial, ResourceManager, Texture } from "@oasis-engine/core"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { isAsset } from "../utils"; -import { SchemaResource } from "./SchemaResource"; -import { TextureResource } from "./TextureResource"; - -const attrs = [ - "specularColor", - "glossiness", - "specularGlossinessTexture", - - // pbr base - "tilingOffset", - "baseColor", - "normalTextureIntensity", - "emissiveColor", - "occlusionTextureIntensity", - "baseTexture", - "normalTexture", - "emissiveTexture", - "occlusionTexture", - - // base material - "isTransparent", - "alphaCutoff", - "renderFace", - "blendMode" -]; - -export class PBRSpecularMaterialResource extends SchemaResource { - private configProps; - - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - const assetObj = new PBRSpecularMaterial(resourceManager.engine); - this.configProps = assetConfig.props; - this._resource = assetObj; - - for (let k in this.configProps) { - if (!isAsset(this.configProps[k])) { - assetObj[k] = this.configProps[k]; - } - } - this.setMeta(); - resolve(this); - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig - ): Promise { - return new Promise((resolve, reject) => { - let loadPromise; - if (assetConfig.resource instanceof PBRSpecularMaterial) { - loadPromise = new Promise((resolve) => { - this._resource = assetConfig.resource; - this.setMeta(); - resolve(this); - }); - } else if (assetConfig.props) { - loadPromise = this.load(resourceManager, assetConfig); - } else { - reject("Load PBRSpecularMaterial Error"); - } - if (loadPromise) { - loadPromise.then(() => { - const result: any = { - resources: [this], - structure: { - index: 0, - props: {} - } - }; - - const material = this._resource; - Object.keys(this._resource).forEach((attr) => { - if (!(material[attr] instanceof Texture)) return; - const textureResource = new TextureResource(this.resourceManager, material[attr]); - this.attachedResources.push(textureResource); - result.resources.push(textureResource); - result.structure.props[attr] = { - index: result.resources.length - 1 - }; - }); - resolve(result); - }); - } - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } - - getProps() { - const result = {}; - attrs.forEach((prop) => (result[prop] = this.resource[prop])); - return result; - } - - bind() { - const resource = this._resource; - Object.keys(this.configProps).forEach((attr) => { - const value = this.configProps[attr]; - if (isAsset(value)) { - const textureResource = this.resourceManager.get(value.id); - if (textureResource && textureResource instanceof TextureResource) { - resource[attr] = textureResource.resource; - this._attachedResources.push(textureResource); - } else { - resource[attr] = null; - Logger.warn( - `PBRSpecularMaterialResource: ${this.meta.name} can't find asset "${attr}", which id is: ${value.id}` - ); - } - } else { - resource[attr] = value; - } - }); - } -} diff --git a/packages/loader/src/scene-loader/resources/SchemaResource.ts b/packages/loader/src/scene-loader/resources/SchemaResource.ts deleted file mode 100644 index 8e861b136..000000000 --- a/packages/loader/src/scene-loader/resources/SchemaResource.ts +++ /dev/null @@ -1,87 +0,0 @@ -import { Logger, ResourceManager } from "@oasis-engine/core"; -import { Oasis } from "../Oasis"; -import { SchemaResourceManager } from "../ResourceManager"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { isAsset } from "../utils"; - -interface IResourceMeta { - name?: string; - url?: string; - size?: number; - source?: string; -} - -export abstract class SchemaResource { - protected _meta: IResourceMeta = {}; - protected _attachedResources: Array = []; - - /** - * Resource - */ - get resource() { - return this._resource; - } - - get meta(): IResourceMeta { - return this._meta; - } - - get attachedResources() { - return this._attachedResources; - } - - protected setMeta() {} - - constructor(protected resourceManager: SchemaResourceManager, protected _resource?: any) { - this.setMeta(); - } - - abstract load(resourceManager: ResourceManager, assetConfig: AssetConfig, oasis: Oasis): Promise; - loadWithAttachedResources( - resourceLoader: any, - assetConfig: AssetConfig, - oasis: Oasis - ): Promise { - return new Promise((resolve, reject) => { - this.load(resourceLoader, assetConfig, oasis) - .then(() => { - resolve({ - resources: [this], - structure: { - index: 0, - props: {} - } - }); - }) - .catch((e) => { - reject(e); - }); - }); - } - - getProps(): any { - return {}; - } - - bind(): void {} - attach(): void {} - - update(key: string, value: any) { - if (isAsset(value)) { - const resource = this.resourceManager.get(value.id); - if (resource) { - this._resource[key] = resource.resource; - } else { - Logger.warn(`SchemaResource: ${this.meta.name} can't find asset, which id is: ${value.id}`); - } - } else { - this._resource[key] = value; - } - } - - updateMeta(key: string, value: any) { - this._meta[key] = value; - } - - onDestroy() {} -} diff --git a/packages/loader/src/scene-loader/resources/ScriptResource.ts b/packages/loader/src/scene-loader/resources/ScriptResource.ts deleted file mode 100644 index d849672cc..000000000 --- a/packages/loader/src/scene-loader/resources/ScriptResource.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { SchemaResource } from "./SchemaResource"; -import { AssetConfig } from "../types"; -import { Oasis } from "../Oasis"; -import { Parser } from "../Parser"; - -export const scriptAbility = {}; -export function script(name: string) { - return (target: any) => { - scriptAbility[name] = target; - }; -} -export class ScriptResource extends SchemaResource { - private isInit = false; - - private initScriptContext() { - if (this.isInit) { - return; - } - this.isInit = true; - (window as any).__o3_script_context__ = { - o3: Parser._components["o3"], - script: (name: string) => { - return (target: any) => { - scriptAbility[name] = target; - }; - } - }; - } - - load(resourceLoader, assetConfig: AssetConfig, oasis: Oasis): Promise { - this.initScriptContext(); - return new Promise((resolve) => { - const config = assetConfig as any; - const scripts = config.props.scripts; - - if (!this.resourceManager.isLocal) { - const scriptDom = document.createElement("script"); - scriptDom.crossOrigin = "anonymous"; - this.setMeta(assetConfig); - scriptDom.onload = () => { - const o3Scripts = (window as any).o3Scripts; - for (let i = 0; i < scripts.length; i++) { - const name = scripts[i].name; - this._resource = o3Scripts && o3Scripts[name]; - scriptAbility[name] = this._resource; - } - resolve(this); - }; - scriptDom.src = assetConfig.url; - document.body.appendChild(scriptDom); - } else { - for (let i = 0; i < scripts.length; i++) { - const name = scripts[i].name; - scriptAbility[name] = oasis.options?.scripts[name]; - } - resolve(this); - } - }); - } - - setMeta(assetConfig?: AssetConfig) { - if (assetConfig) { - this._meta.name = assetConfig.name; - this._meta.url = assetConfig.url; - this._meta.source = assetConfig.source; - } - } -} diff --git a/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts b/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts deleted file mode 100644 index 10e04b407..000000000 --- a/packages/loader/src/scene-loader/resources/SpriteAtlasResource.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { AssetType, ResourceManager, SpriteAtlas } from "@oasis-engine/core"; -import { AssetConfig } from "../types"; -import { getAllGetters } from "../utils"; -import { SchemaResource } from "./SchemaResource"; -import { SpriteResource } from "./SpriteResource"; - -export class SpriteAtlasResource extends SchemaResource { - static defaultAtlas: SpriteAtlas; - - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - this.setMeta(); - if (assetConfig.source) { - resourceManager - .load({ - url: assetConfig.source, - type: AssetType.SpriteAtlas - }) - .then((spriteAtlas) => { - this._resource = spriteAtlas; - const { sprites } = spriteAtlas; - const schemaResourceManager = this.resourceManager; - for (let index = sprites.length - 1; index >= 0; index--) { - const sprite = sprites[index]; - const spriteResource = new SpriteResource(schemaResourceManager, sprite); - // @ts-ignore - const assetID = sprite._assetID; - // @ts-ignore - schemaResourceManager.maxId = Math.max(assetID, schemaResourceManager.maxId); - // @ts-ignore - schemaResourceManager.resourceMap[assetID] = spriteResource; - // @ts-ignore - schemaResourceManager.resourceIdMap.set(spriteResource, "" + assetID); - } - resolve(this); - }); - } else { - if (!SpriteAtlasResource.defaultAtlas) { - SpriteAtlasResource.defaultAtlas = new SpriteAtlas(resourceManager.engine); - } - this._resource = SpriteAtlasResource.defaultAtlas; - resolve(this); - } - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } - - getProps() { - const result = {}; - const props = getAllGetters(this.resource); - props.forEach((prop) => (result[prop] = this.resource[prop])); - return result; - } - - update() {} -} diff --git a/packages/loader/src/scene-loader/resources/SpriteResource.ts b/packages/loader/src/scene-loader/resources/SpriteResource.ts deleted file mode 100644 index 915f5216a..000000000 --- a/packages/loader/src/scene-loader/resources/SpriteResource.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { Logger, ResourceManager, Sprite, Texture } from "@oasis-engine/core"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { getAllGetters, isAsset } from "../utils"; -import { SchemaResource } from "./SchemaResource"; -import { TextureResource } from "./TextureResource"; - -export class SpriteResource extends SchemaResource { - private configProps; - - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - const assetObj = new Sprite(resourceManager.engine); - this.configProps = assetConfig.props; - const { configProps } = this; - const { pivotType, pivot } = configProps; - - if (typeof pivot !== "undefined" && typeof pivotType !== "undefined" && pivotType !== SpritePivotType.Custom) { - switch (pivotType) { - case SpritePivotType.Center: - pivot.x = 0.5; - pivot.y = 0.5; - break; - case SpritePivotType.TopLeft: - pivot.x = 0; - pivot.y = 1; - break; - case SpritePivotType.Top: - pivot.x = 0.5; - pivot.y = 1; - break; - case SpritePivotType.TopRight: - pivot.x = 1; - pivot.y = 1; - break; - case SpritePivotType.Left: - pivot.x = 0; - pivot.y = 0.5; - break; - case SpritePivotType.Right: - pivot.x = 1; - pivot.y = 0.5; - break; - case SpritePivotType.BottomLeft: - pivot.x = 0; - pivot.y = 0; - break; - case SpritePivotType.Bottom: - pivot.x = 0.5; - pivot.y = 0; - break; - case SpritePivotType.BottomRight: - pivot.x = 1; - pivot.y = 0; - break; - default: - break; - } - } - - for (let k in configProps) { - if (!isAsset(configProps[k]) && typeof configProps[k] !== "undefined") { - assetObj[k] = configProps[k]; - } - } - - this._resource = assetObj; - this.setMeta(); - resolve(this); - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig - ): Promise { - return new Promise((resolve, reject) => { - let loadPromise; - if (assetConfig.resource instanceof SpriteResource) { - loadPromise = new Promise((resolve) => { - this._resource = assetConfig.resource; - this.setMeta(); - resolve(this); - }); - } else if (assetConfig.props) { - loadPromise = this.load(resourceManager, assetConfig); - } else { - reject("Load Sprite Error"); - } - if (loadPromise) { - loadPromise.then(() => { - const result: any = { - resources: [this], - structure: { - index: 0, - props: {} - } - }; - - const sprite = this._resource; - getAllGetters(this._resource).forEach((attr) => { - if (!(sprite[attr] instanceof Texture)) return; - const textureResource = new TextureResource(this.resourceManager, sprite[attr]); - this.attachedResources.push(textureResource); - result.resources.push(textureResource); - result.structure.props[attr] = { - index: result.resources.length - 1 - }; - }); - resolve(result); - }); - } - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } - - getProps() { - const result = {}; - const props = getAllGetters(this.resource); - props.forEach((prop) => (result[prop] = this.resource[prop])); - return result; - } - - bind() { - const resource = this._resource; - this.configProps && - Object.keys(this.configProps).forEach((attr) => { - const value = this.configProps[attr]; - if (isAsset(value)) { - const textureResource = this.resourceManager.get(value.id); - if (textureResource && textureResource instanceof TextureResource) { - resource[attr] = textureResource.resource; - this._attachedResources.push(textureResource); - } else { - resource[attr] = null; - Logger.warn(`SpriteResource: ${this.meta.name} can't find asset "${attr}", which id is: ${value.id}`); - } - } else { - resource[attr] = value; - } - }); - } -} - -export enum SpritePivotType { - Center = 0, - TopLeft = 1, - Top = 2, - TopRight = 3, - Left = 4, - Right = 5, - BottomLeft = 6, - Bottom = 7, - BottomRight = 8, - Custom = 9 -} diff --git a/packages/loader/src/scene-loader/resources/TextureCubeMapResource.ts b/packages/loader/src/scene-loader/resources/TextureCubeMapResource.ts deleted file mode 100644 index 2166d1e1c..000000000 --- a/packages/loader/src/scene-loader/resources/TextureCubeMapResource.ts +++ /dev/null @@ -1,68 +0,0 @@ -import { AssetType, GLCapabilityType, ResourceManager } from "@oasis-engine/core"; -import { Oasis } from "../Oasis"; -import { AssetConfig } from "../types"; -import { SchemaResource } from "./SchemaResource"; - -const imageOrderMap = { - px: 0, - nx: 1, - py: 2, - ny: 3, - pz: 4, - nz: 5 -}; - -export class TextureCubeMapResource extends SchemaResource { - load(resourceManager: ResourceManager, assetConfig: AssetConfig, oasis: Oasis): Promise { - return new Promise((resolve, reject) => { - const imageUrls = []; - let type = AssetType.TextureCube; - if (this.resourceManager.useCompressedTexture && assetConfig?.props?.compression?.compressions.length) { - const rhi = oasis.engine._hardwareRenderer; - const compressions = assetConfig.props.compression.compressions; - for (let i = 0; i < compressions.length; i++) { - const compression = compressions[i]; - if (compression.container === "ktx" && rhi.canIUse(GLCapabilityType[compression.type])) { - for (const key in compression.files) { - if (compression.files.hasOwnProperty(key)) { - const image = compression.files[key]; - imageUrls[imageOrderMap[key]] = image.url; - } - } - console.warn(compression.type); - type = AssetType.KTXCube; - break; - } - } - } - - if (type === AssetType.TextureCube) { - for (const key in assetConfig.props.images) { - if (assetConfig.props.images.hasOwnProperty(key)) { - const image = assetConfig.props.images[key]; - imageUrls[imageOrderMap[key]] = image.url; - } - } - } - - resourceManager - .load({ - urls: imageUrls, - type: type - }) - .then((res) => { - this._resource = res; - resolve(this); - }) - .catch((e) => { - reject(e); - }); - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } -} diff --git a/packages/loader/src/scene-loader/resources/TextureResource.ts b/packages/loader/src/scene-loader/resources/TextureResource.ts deleted file mode 100644 index 8691b963a..000000000 --- a/packages/loader/src/scene-loader/resources/TextureResource.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { AssetType, GLCapabilityType, ResourceManager } from "@oasis-engine/core"; -import { Oasis } from "../Oasis"; -import { AssetConfig } from "../types"; -import { SchemaResource } from "./SchemaResource"; - -export class TextureResource extends SchemaResource { - load(resourceManager: ResourceManager, assetConfig: AssetConfig, oasis: Oasis): Promise { - return new Promise((resolve, reject) => { - let url: string; - let assetType = AssetType.Texture2D; - if (this.resourceManager.useCompressedTexture && assetConfig?.props?.compression?.compressions.length) { - const rhi = oasis.engine._hardwareRenderer; - const compressions = assetConfig.props.compression.compressions; - for (let i = 0; i < compressions.length; i++) { - const compression = compressions[i]; - if (compression.container === "ktx" && rhi.canIUse(GLCapabilityType[compression.type])) { - url = compression.url; - assetType = AssetType.KTX; - break; - } - } - } - - url = url ?? assetConfig.url; - - resourceManager - .load({ url, type: assetType }) - .then((res) => { - this._resource = res; - resolve(this); - }) - .catch((e) => { - reject(e); - }); - }); - } - - setMeta() { - if (this.resource) { - this._meta.name = this.resource.name; - } - } -} diff --git a/packages/loader/src/scene-loader/resources/UnlitMaterialResource.ts b/packages/loader/src/scene-loader/resources/UnlitMaterialResource.ts deleted file mode 100644 index 5c7bc9b46..000000000 --- a/packages/loader/src/scene-loader/resources/UnlitMaterialResource.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { Logger, UnlitMaterial, ResourceManager, Texture } from "@oasis-engine/core"; -import { AssetConfig, LoadAttachedResourceResult } from "../types"; -import { getAllGetters, isAsset } from "../utils"; -import { SchemaResource } from "./SchemaResource"; -import { TextureResource } from "./TextureResource"; - -export class UnlitMaterialResource extends SchemaResource { - private configProps; - - load(resourceManager: ResourceManager, assetConfig: AssetConfig): Promise { - return new Promise((resolve) => { - const assetObj = new UnlitMaterial(resourceManager.engine); - this.configProps = assetConfig.props; - - for (let k in this.configProps) { - if (!isAsset(this.configProps[k])) { - assetObj[k] = this.configProps[k]; - } - } - this._resource = assetObj; - this.setMeta(); - resolve(this); - }); - } - - loadWithAttachedResources( - resourceManager: ResourceManager, - assetConfig: AssetConfig - ): Promise { - return new Promise((resolve, reject) => { - let loadPromise; - if (assetConfig.resource instanceof UnlitMaterial) { - loadPromise = new Promise((resolve) => { - this._resource = assetConfig.resource; - this.setMeta(); - resolve(this); - }); - } else if (assetConfig.props) { - loadPromise = this.load(resourceManager, assetConfig); - } else { - reject("Load PBRMaterial Error"); - } - if (loadPromise) { - loadPromise.then(() => { - const result: any = { - resources: [this], - structure: { - index: 0, - props: {} - } - }; - - const material = this._resource; - getAllGetters(this._resource).forEach((attr) => { - if (!(material[attr] instanceof Texture)) return; - const textureResource = new TextureResource(this.resourceManager, material[attr]); - this.attachedResources.push(textureResource); - result.resources.push(textureResource); - result.structure.props[attr] = { - index: result.resources.length - 1 - }; - }); - resolve(result); - }); - } - }); - } - - setMeta() { - if (this.resource) { - this.meta.name = this.resource.name; - } - } - - getProps() { - const result = {}; - const props = getAllGetters(this.resource); - props.forEach((prop) => (result[prop] = this.resource[prop])); - return result; - } - - bind() { - const resource = this._resource; - Object.keys(this.configProps).forEach((attr) => { - const value = this.configProps[attr]; - if (isAsset(value)) { - const textureResource = this.resourceManager.get(value.id); - if (textureResource && textureResource instanceof TextureResource) { - resource[attr] = textureResource.resource; - this._attachedResources.push(textureResource); - } else { - resource[attr] = null; - Logger.warn(`PBRMaterialResource: ${this.meta.name} can't find asset "${attr}", which id is: ${value.id}`); - } - } else { - resource[attr] = value; - } - }); - } -} diff --git a/packages/loader/src/scene-loader/resources/index.ts b/packages/loader/src/scene-loader/resources/index.ts deleted file mode 100644 index 8d303c1e9..000000000 --- a/packages/loader/src/scene-loader/resources/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export { SchemaResource } from "./SchemaResource"; -export { GLTFResource } from "./GLTFResource"; -export { PBRMaterialResource } from "./PBRMaterialResource"; -export { PBRSpecularMaterialResource } from "./PBRSpecularMaterialResource"; -export { UnlitMaterialResource } from "./UnlitMaterialResource"; -export { TextureResource } from "./TextureResource"; -export { ScriptResource } from "./ScriptResource"; -export { SpriteResource, SpritePivotType } from "./SpriteResource"; -export { BlinnPhongMaterialResource } from "./BlinnPhongMaterialResource"; -export { TextureCubeMapResource } from "./TextureCubeMapResource"; -export * from "./ScriptResource"; -export { BaseResource } from "./BaseResource"; -export { AnimatorControllerResource } from "./AnimatorControllerResource"; -export { AnimationClipResource } from "./AnimationClipResource"; -export { AmbientLightResource } from "./AmbientLightResource"; diff --git a/packages/loader/src/scene-loader/temp.compatible.ts b/packages/loader/src/scene-loader/temp.compatible.ts deleted file mode 100644 index 1a40ad2e3..000000000 --- a/packages/loader/src/scene-loader/temp.compatible.ts +++ /dev/null @@ -1,94 +0,0 @@ -import { Color, Vector2, Vector3, Vector4 } from "@oasis-engine/math"; - -/** - * temp compa - * @param config - */ -export function compatibleToV2(config) { - const { abilities = {}, assets = {}, scene = {} } = config; - const ids = Object.keys(abilities); - const assetKeys = Object.keys(assets); - const sceneKeys = Object.keys(scene || {}); - - for (let i = 0, l = ids.length; i < l; ++i) { - handleComponents(abilities[ids[i]].props); - } - - for (let i = 0, l = assetKeys.length; i < l; ++i) { - handleAssets(assets[assetKeys[i]].props); - } - - for (let i = 0, l = sceneKeys.length; i < l; ++i) { - handleSceneProps(scene[sceneKeys[i]].props); - } - - return config; -} - -// TODO temp -function handleComponents(props) { - const keys = Object.keys(props); - for (let i = 0, l = keys.length; i < l; ++i) { - const k = keys[i]; - const v = props[k]; - - if (Array.isArray(v) && typeof v[0] !== "object") { - if (["color", "diffuseColor", "specularColor"].indexOf(k) !== -1) { - props[k] = new Color(v[0], v[1], v[2], v[3]); - } else if (v.length === 4) { - props[k] = new Vector4(v[0], v[1], v[2], v[3]); - } else if (v.length === 3) { - props[k] = new Vector3(v[0], v[1], v[2]); - } else if (v.length === 2) { - props[k] = new Vector2(v[0], v[1]); - } - } - } -} - -function handleSceneProps(props) { - const keys = Object.keys(props); - for (let i = 0, l = keys.length; i < l; ++i) { - const k = keys[i]; - const v = props[k]; - - if (Array.isArray(v) && typeof v[0] !== "object") { - if (/color/i.test(k)) { - props[k] = new Color(v[0], v[1], v[2], v[3]); - } else if (v.length === 4) { - props[k] = new Vector4(v[0], v[1], v[2], v[3]); - } else if (v.length === 3) { - props[k] = new Vector3(v[0], v[1], v[2]); - } else if (v.length === 2) { - props[k] = new Vector2(v[0], v[1]); - } - } - } -} - -function handleAssets(props: any = {}) { - if (!props) { - return; - } - const keys = Object.keys(props); - for (let i = 0, l = keys.length; i < l; i++) { - const key = keys[i]; - const value = props[key]; - - if (key === "newMaterial" || key === "scripts") { - continue; - } - - if (Array.isArray(value) && typeof value[0] !== "object") { - if (["emissiveColor", "diffuseColor", "specularColor", "baseColor"].indexOf(key) !== -1) { - props[key] = new Color(value[0], value[1], value[2], value[3]); - } else if (value.length === 4) { - props[key] = new Vector4(value[0], value[1], value[2], value[3]); - } else if (value.length === 3) { - props[key] = new Vector3(value[0], value[1], value[2]); - } else if (value.length === 2) { - props[key] = new Vector2(value[0], value[1]); - } - } - } -} diff --git a/packages/loader/src/scene-loader/types.ts b/packages/loader/src/scene-loader/types.ts deleted file mode 100644 index 08449fd31..000000000 --- a/packages/loader/src/scene-loader/types.ts +++ /dev/null @@ -1,164 +0,0 @@ -import { Engine } from "@oasis-engine/core"; -import { SchemaResource } from "./resources"; - -export interface NodeConfig { - /** - * Node id - */ - id: string; - /** - * Index of parent's children - */ - index: number; - /** - * Name of node - */ - name: string; - /** - * Position of node - */ - position: [number, number, number]; - /** - * Euler rotation of node - */ - rotation: [number, number, number]; - /** - * Scale of node - */ - scale: [number, number, number]; - /** - * Parent node - */ - parent: string | undefined; - /** - * Children of node - */ - children: string[]; - /** - * Components of node - */ - abilities?: string[]; - /** - * Is active of - */ - isActive?: boolean; -} - -export interface Props { - [key: string]: any | AssetProp; -} - -export interface AssetProp { - type: "asset"; - /** - * asset id - */ - id: string; -} -export interface ComponentConfig { - /** - * ComponentId id - */ - id: string; - /** - * Node id of component - */ - node: string; - /** - * Ability type - */ - type: string; - /** - * Ability props - */ - props: Props; - /** - * Index of components - */ - index: number; -} - -// todo -export interface AssetConfig { - /** - * Asset id - */ - id?: string; - /** - * Asset name - */ - name: string; - /** - * Asset type - */ - type: string; - /** - * Asset props - */ - props?: any; - /** - * Asset url - */ - url?: string; - /** - * Asset source - */ - source?: string; - /** - * Asset resource - */ - resource?: any; -} - -export interface Schema { - nodes: { - [nodeId: string]: NodeConfig; - }; - abilities: { - [abiliId: string]: ComponentConfig; - }; - assets: { - [assetId: string]: AssetConfig; - }; - version: number; - [name: string]: any; -} - -export interface ClassType extends Function { - new (...args: any[]): T; -} - -export interface Options { - /** Engine of scene */ - engine: Engine; - /** HTMLCanvasElement */ - canvas?: HTMLCanvasElement; - /** Scene data */ - config?: Schema; - /** Call engine.run() automatically */ - autoPlay?: boolean; - onProgress?: () => {}; - local?: boolean; - scripts?: { [name: string]: any }; - /** Attributes of GLContext */ - rhiAttr?: WebGLContextAttributes & { enableCollect?: boolean }; - /** Timeout of loading */ - timeout?: number; - /** Fps of engine run */ - fps?: number; - /** Whether to use compress texture */ - useCompressedTexture?: boolean; - /** Engine */ - // engine?: Engine; -} - -export interface LoadAttachedResourceResult { - resources: Array; - structure: LoadAttachedResourceResultStructure; -} -interface LoadAttachedResourceResultStructure { - index: number; - props?: { - [propName: string]: LoadAttachedResourceResultStructure | Array; - }; -} diff --git a/packages/loader/src/scene-loader/utils.ts b/packages/loader/src/scene-loader/utils.ts deleted file mode 100644 index 64067c1a1..000000000 --- a/packages/loader/src/scene-loader/utils.ts +++ /dev/null @@ -1,40 +0,0 @@ -export function switchElementsIndex(elements: any[], currentIndex: number, targetIndex: number) { - if (currentIndex === targetIndex || targetIndex === null || targetIndex === undefined) { - return; - } - [elements[currentIndex], elements[targetIndex]] = [elements[targetIndex], elements[currentIndex]]; -} - -export function isAsset(config: any): boolean { - return config && config.type === "asset"; -} - -export function getAllGetters(obj: any): Array { - const result = []; - const prototype = Object.getPrototypeOf(obj); - const prototype_property_descriptors = Object.getOwnPropertyDescriptors(prototype); - for (const [property, descriptor] of Object.entries(prototype_property_descriptors)) { - if (typeof descriptor.get === "function") { - result.push(property); - } - } - return result; -} - -export function union(arr1: Array, arr2: Array): Array { - return arr1.concat(arr2.filter((v) => !(arr1.indexOf(v) > -1))); -} - -// https://github.com/BabylonJS/Babylon.js/blob/d780145531ac1b1cee85cbfba4d836dcc24ab58e/src/Engines/Extensions/engine.textureSelector.ts#L70 -// Intelligently add supported compressed formats in order to check for. -// Check for ASTC support first as it is most powerful and to be very cross platform. -// Next PVRTC & DXT, which are probably superior to ETC1/2. -// Likely no hardware which supports both PVR & DXT, so order matters little. -// ETC2 is newer and handles ETC1 (no alpha capability), so check for first. -export const compressedTextureLoadOrder = { - astc: 1, - s3tc: 2, - pvrtc: 3, - etc: 4, - etc1: 5 -}; diff --git a/packages/oasis-engine/src/index.ts b/packages/oasis-engine/src/index.ts index 4fcf81b3a..c34df546b 100644 --- a/packages/oasis-engine/src/index.ts +++ b/packages/oasis-engine/src/index.ts @@ -1,41 +1,16 @@ -export * from "@oasis-engine/core"; -export * from "@oasis-engine/loader"; -export * from "@oasis-engine/math"; -export * from "@oasis-engine/rhi-webgl"; -import { - AmbientLight, - Camera, - Component, - DirectLight, - ParticleRenderer, - PointLight, - SpriteRenderer, - SpriteMask, - TextRenderer, - Animator, - StaticCollider, - DynamicCollider -} from "@oasis-engine/core"; -import { GLTFModel, Parser, Model } from "@oasis-engine/loader"; - -Parser.registerComponents("o3", { - GLTFModel, - SpriteRenderer, - SpriteMask, - TextRenderer, - PointLight, - AmbientLight, - DirectLight, - ParticleRenderer, - Camera, - Model, - Component, - StaticCollider, - DynamicCollider, - Animator -}); - +import * as CoreObjects from "@oasis-engine/core"; +import { Loader } from "@oasis-engine/core"; //@ts-ignore export const version = `__buildVersion`; console.log(`oasis engine version: ${version}`); + +export * from "@oasis-engine/core"; +export * from "@oasis-engine/loader"; +export * from "@oasis-engine/math"; +export * from "@oasis-engine/rhi-webgl"; + +for (let key in CoreObjects) { + Loader.registerClass(key, CoreObjects[key]); +} + diff --git a/packages/resource-process/package.json b/packages/resource-process/package.json new file mode 100755 index 000000000..ec427e97d --- /dev/null +++ b/packages/resource-process/package.json @@ -0,0 +1,21 @@ +{ + "name": "@oasis-engine/resource-process", + "license": "MIT", + "version": "0.8.0-alpha.5", + "scripts": { + "b:types": "tsc" + }, + "types": "types/index.d.ts", + "debug": "src/index.ts", + "main": "dist/browser.js", + "module": "dist/module.js", + "browser": "dist/browser.js", + "files": [ + "dist/**/*", + "types/**/*" + ], + "dependencies": { + "@oasis-engine/core": "0.8.0-alpha.5", + "@oasis-engine/math": "0.8.0-alpha.5" + } +} diff --git a/packages/resource-process/src/index.ts b/packages/resource-process/src/index.ts new file mode 100644 index 000000000..ba4ee892b --- /dev/null +++ b/packages/resource-process/src/index.ts @@ -0,0 +1,32 @@ +import { Engine } from "@oasis-engine/core"; +import { BufferReader } from "./utils/BufferReader"; +import { decoderMap, decoder } from "./utils/Decorator"; +import { FileHeader } from "./utils/FileHeader"; + +export { MeshDecoder } from "./resources/mesh/MeshDecoder"; +export { Texture2DDecoder } from "./resources/texture2D/TextureDecoder"; +export { ReflectionParser } from "./resources/prefab/ReflectionParser"; +export { PrefabParser } from "./resources/prefab/PrefabParser"; +export type { IModelMesh } from "./resources/mesh/IModelMesh"; +export type { IAnimationClipAsset } from "./resources/animationClip/type"; +export type { IAnimatorControllerAsset } from "./resources/animatorController/type"; + +/** + * Decode engine binary resource. + * @param arrayBuffer - array buffer of decode binary file + * @param engine - engine + * @returns + */ +export function decode(arrayBuffer: ArrayBuffer, engine: Engine): Promise { + const header = FileHeader.decode(arrayBuffer); + const bufferReader = new BufferReader(arrayBuffer, header.headerLength, header.dataLength); + return decoderMap[header.type].decode(engine, bufferReader).then((object) => { + object.name = header.name; + return object; + }); +} + +export * from "./resources/prefab/PrefabDesign"; +export * from "./resources/scene/SceneParser"; +export * from "./resources/scene/MeshLoader"; +export * from "./resources/scene/EditorTextureLoader"; diff --git a/packages/resource-process/src/resources/animationClip/AnimationClipDecoder.ts b/packages/resource-process/src/resources/animationClip/AnimationClipDecoder.ts new file mode 100644 index 000000000..006a71d14 --- /dev/null +++ b/packages/resource-process/src/resources/animationClip/AnimationClipDecoder.ts @@ -0,0 +1,162 @@ +import { Quaternion } from "@oasis-engine/math"; +import { + AnimationClip, + AnimationCurve, + AnimationEvent, + Component, + Engine, + Entity, + InterpolableKeyframe, + InterpolableValueType, + SkinnedMeshRenderer, + Transform +} from "@oasis-engine/core"; +import { Vector2, Vector3, Vector4 } from "@oasis-engine/math"; +import type { BufferReader } from "../../utils/BufferReader"; +import { decoder } from "../../utils/Decorator"; +import { ComponentClass, PropertyNameMap } from "./type"; + +@decoder("AnimationClip") +export class AnimationClipDecoder { + public static decode(engine: Engine, bufferReader: BufferReader): Promise { + return new Promise((resolve) => { + const objectId = bufferReader.nextStr(); + const name = bufferReader.nextStr(); + const clip = new AnimationClip(name); + const eventsLen = bufferReader.nextUint16(); + for (let i = 0; i < eventsLen; ++i) { + const event = new AnimationEvent(); + event.time = bufferReader.nextFloat32(); + event.functionName = bufferReader.nextStr(); + event.parameter = JSON.parse(bufferReader.nextStr()).val; + clip.addEvent(event); + } + + const curveBindingsLen = bufferReader.nextUint16(); + for (let i = 0; i < curveBindingsLen; ++i) { + const relativePath = bufferReader.nextStr(); + const componentClass: ComponentClass = bufferReader.nextUint8(); + let compType: new (entity: Entity) => Component; + switch (componentClass) { + case ComponentClass.Transform: + compType = Transform; + break; + case ComponentClass.SkinnedMeshRenderer: + compType = SkinnedMeshRenderer; + break; + } + const property = bufferReader.nextUint8(); + const curve = new AnimationCurve(); + curve.interpolation = bufferReader.nextUint8(); + const keysLen = bufferReader.nextUint16(); + for (let j = 0; j < keysLen; ++j) { + const type = bufferReader.nextUint8(); + switch (type) { + case InterpolableValueType.Float: { + const keyframe = new InterpolableKeyframe(); + keyframe.time = bufferReader.nextFloat32(); + keyframe.value = bufferReader.nextFloat32(); + keyframe.inTangent = bufferReader.nextFloat32(); + keyframe.outTangent = bufferReader.nextFloat32(); + curve.addKey(keyframe); + break; + } + case InterpolableValueType.FloatArray: { + const keyframe = new InterpolableKeyframe(); + keyframe.time = bufferReader.nextFloat32(); + const len = bufferReader.nextUint16(); + keyframe.value = bufferReader.nextFloat32Array(len); + keyframe.inTangent = bufferReader.nextFloat32Array(len); + keyframe.outTangent = bufferReader.nextFloat32Array(len); + curve.addKey(keyframe); + break; + } + case InterpolableValueType.Vector2: { + const keyframe = new InterpolableKeyframe(); + keyframe.time = bufferReader.nextFloat32(); + keyframe.value = new Vector2(bufferReader.nextFloat32(), bufferReader.nextFloat32()); + keyframe.inTangent = new Vector2(bufferReader.nextFloat32(), bufferReader.nextFloat32()); + keyframe.outTangent = new Vector2(bufferReader.nextFloat32(), bufferReader.nextFloat32()); + curve.addKey(keyframe); + break; + } + case InterpolableValueType.Vector3: { + const keyframe = new InterpolableKeyframe(); + keyframe.time = bufferReader.nextFloat32(); + keyframe.value = new Vector3( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + keyframe.inTangent = new Vector3( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + keyframe.outTangent = new Vector3( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + curve.addKey(keyframe); + break; + } + case InterpolableValueType.Vector4: { + const keyframe = new InterpolableKeyframe(); + keyframe.time = bufferReader.nextFloat32(); + keyframe.value = new Vector4( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + keyframe.inTangent = new Vector4( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + keyframe.outTangent = new Vector4( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + curve.addKey(keyframe); + break; + } + case InterpolableValueType.Quaternion: { + const keyframe = new InterpolableKeyframe(); + keyframe.time = bufferReader.nextFloat32(); + keyframe.value = new Quaternion( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + keyframe.inTangent = new Vector4( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + keyframe.outTangent = new Vector4( + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32(), + bufferReader.nextFloat32() + ); + curve.addKey(keyframe); + break; + } + } + } + clip.addCurveBinding(relativePath, compType, PropertyNameMap[property], curve); + } + + // @ts-ignore + engine.resourceManager._objectPool[objectId] = clip; + resolve(clip); + }); + } +} diff --git a/packages/resource-process/src/resources/animationClip/type.ts b/packages/resource-process/src/resources/animationClip/type.ts new file mode 100644 index 000000000..fb635b703 --- /dev/null +++ b/packages/resource-process/src/resources/animationClip/type.ts @@ -0,0 +1,33 @@ +import { AnimationProperty, InterpolableValueType } from "@oasis-engine/core"; + +export enum ComponentClass { + Transform, + SkinnedMeshRenderer, + Other +} + +export const PropertyNameMap = ['position', 'rotation', 'scale', 'blendShapeWeights']; + +export interface IAnimationClipAsset { + objectId: string; + name: string; + events: Array<{ + time: number; + functionName: string; + parameter: string; + }>; + curveBindings: Array<{ + relativePath: string; + property: AnimationProperty; + curve: { + interpolation: number; + keys: Array<{ + time: number; + value: any; // 详细查看 KeyframeValueType的映射 + inTangent: any; // 详细查看 KeyframeValueType的映射 + outTangent: any; // 详细查看 KeyframeValueType的映射 + }>; + valueType: InterpolableValueType; + }; + }>; +} diff --git a/packages/resource-process/src/resources/animatorController/AnimatorControllerDecoder.ts b/packages/resource-process/src/resources/animatorController/AnimatorControllerDecoder.ts new file mode 100644 index 000000000..b4e68aa78 --- /dev/null +++ b/packages/resource-process/src/resources/animatorController/AnimatorControllerDecoder.ts @@ -0,0 +1,89 @@ +import { + Engine, + AnimatorController, + AnimatorControllerLayer, + AnimatorStateMachine, + AnimationClip, + AnimatorStateTransition, + AssetType +} from "@oasis-engine/core"; +import type { BufferReader } from "../../utils/BufferReader"; +import { decoder } from "../../utils/Decorator"; + +@decoder("AnimatorController") +export class AnimatorControllerDecoder { + public static decode(engine: Engine, bufferReader: BufferReader): Promise { + return new Promise(async (resolve) => { + const animatorController = new AnimatorController(); + const objectId = bufferReader.nextStr(); + const layersLen = bufferReader.nextUint16(); + const clipLoadPromises = []; + + for (let i = 0; i < layersLen; ++i) { + const name = bufferReader.nextStr(); + const layer = new AnimatorControllerLayer(name); + layer.blendingMode = bufferReader.nextUint8(); + layer.weight = bufferReader.nextFloat32(); + const stateMachine = new AnimatorStateMachine(); + const statesLen = bufferReader.nextUint16(); + for (let j = 0; j < statesLen; ++j) { + const stateName = bufferReader.nextStr(); + const state = stateMachine.addState(stateName); + state.speed = bufferReader.nextFloat32(); + state.wrapMode = bufferReader.nextUint8(); + const isDefaultState = bufferReader.nextUint8() ? true : false; + const clipStartNormalizedTime = bufferReader.nextFloat32(); + const clipEndNormalizedTime = bufferReader.nextFloat32(); + const clipPath = bufferReader.nextStr(); + const clipObjectId = bufferReader.nextStr(); + clipLoadPromises.push( + AnimatorControllerDecoder.loadAndSetClip(engine, clipPath, clipObjectId).then((clip) => { + state.clip = clip; + state.clipStartTime = clip.length * clipStartNormalizedTime; + state.clipEndTime = clip.length * clipEndNormalizedTime; + }) + ); + // @ts-ignore + isDefaultState && (stateMachine._defaultState = state); + + const transitionsLen = bufferReader.nextUint16(); + for (let k = 0; k < transitionsLen; ++k) { + const transition = new AnimatorStateTransition(); + transition.duration = bufferReader.nextFloat32(); + transition.offset = bufferReader.nextFloat32(); + transition.exitTime = bufferReader.nextFloat32(); + transition.exitTime = bufferReader.nextFloat32(); + transition.destinationState = stateMachine.findStateByName(bufferReader.nextStr()); + state.addTransition(transition); + } + } + layer.stateMachine = stateMachine; + animatorController.addLayer(layer); + } + + Promise.all(clipLoadPromises).then(() => { + // @ts-ignore + engine.resourceManager._objectPool[objectId] = animatorController; + resolve(animatorController); + }); + }); + } + + public static loadAndSetClip(engine: Engine, path: string, objectId: string): Promise { + // @ts-ignore + return Promise.resolve(engine.resourceManager._objectPool[objectId]); + // return new Promise((resolve) => { + // engine.resourceManager + // .load({ + // url: path, + // // @ts-ignore + // type: AssetType.Oasis + // }) + // .then(() => { + // // 从缓存池获取对象 + // // @ts-ignore + // resolve(engine.resourceManager._objectPool[objectId]); + // }); + // }); + } +} diff --git a/packages/resource-process/src/resources/animatorController/type.ts b/packages/resource-process/src/resources/animatorController/type.ts new file mode 100644 index 000000000..b42c66187 --- /dev/null +++ b/packages/resource-process/src/resources/animatorController/type.ts @@ -0,0 +1,27 @@ +import { AnimatorState, AnimatorLayerBlendingMode, WrapMode } from "@oasis-engine/core"; + +export interface IAnimatorControllerAsset { + objectId: string; + layers: Array<{ + name: string; + blending: AnimatorLayerBlendingMode; + weight: number; + stateMachine: { + states: Array<{ + name: string; + speed: number; + wrapMode: WrapMode; + isDefaultState: boolean; + clipStartNormalizedTime: number; + clipEndNormalizedTime: number; + clip: { path: string; objectId: string }; + transitions: Array<{ + duration: number; + offset: number; + exitTime: number; + targetStateName: string; + }>; + }>; + }; + }>; +} diff --git a/packages/resource-process/src/resources/mesh/IModelMesh.d.ts b/packages/resource-process/src/resources/mesh/IModelMesh.d.ts new file mode 100644 index 000000000..ba7bef521 --- /dev/null +++ b/packages/resource-process/src/resources/mesh/IModelMesh.d.ts @@ -0,0 +1,141 @@ +import { MeshTopology } from "@oasis-engine/core"; + +export interface IVector3 { + x: number; + y: number; + z: number; +} +export interface IVector2 { + x: number; + y: number; +} + +export interface IVector4 { + x: number; + y: number; + z: number; + w: number; +} + +export interface IColor { + r: number; + g: number; + b: number; + a: number; +} + +export interface IEncodedModelMesh { + positions: { + start: number; + end: number; + }; + normals?: { + start: number; + end: number; + }; + uvs?: { + start: number; + end: number; + }; + uv1?: { + start: number; + end: number; + }; + uv2?: { + start: number; + end: number; + }; + uv3?: { + start: number; + end: number; + }; + uv4?: { + start: number; + end: number; + }; + uv5?: { + start: number; + end: number; + }; + uv6?: { + start: number; + end: number; + }; + uv7?: { + start: number; + end: number; + }; + colors?: { + start: number; + end: number; + }; + tangents?: { + start: number; + end: number; + }; + boneWeights?: { + start: number; + end: number; + }; + boneIndices?: { + start: number; + end: number; + }; + blendShapes?: { + name: string; + frames: { + weight: number; + deltaPosition: { start: number; end: number }; + deltaNormals?: { + start: number; + end: number; + }; + deltaTangents?: { + start: number; + end: number; + }; + }[]; + }[]; + indices?: { + type: number; + start: number; + end: number; + }; + subMeshes: { + start: number; + topology: MeshTopology; + count: number; + }[]; +} + +export interface IModelMesh { + positions: IVector3[]; + normals?: IVector3[]; + uvs?: IVector2[]; + uv1?: IVector2[]; + uv2?: IVector2[]; + uv3?: IVector2[]; + uv4?: IVector2[]; + uv5?: IVector2[]; + uv6?: IVector2[]; + uv7?: IVector2[]; + colors?: IColor[]; + tangents?: IVector4[]; + boneWeights?: IVector4[]; + boneIndices?: IVector4[]; + blendShapes?: { + name: string; + frames: { + weight: number; + deltaPositions: IVector3[]; + deltaNormals: IVector3[]; + deltaTangents: IVector4[]; + }[]; + }[]; + indices?: number[]; + subMeshes: { + start: number; + topology: MeshTopology; + count: number; + }[]; +} diff --git a/packages/resource-process/src/resources/mesh/MeshDecoder.ts b/packages/resource-process/src/resources/mesh/MeshDecoder.ts new file mode 100644 index 000000000..0c640f999 --- /dev/null +++ b/packages/resource-process/src/resources/mesh/MeshDecoder.ts @@ -0,0 +1,225 @@ +import { + ModelMesh, + BlendShape} from "@oasis-engine/core"; +import { decoder } from "../../utils/Decorator"; +import type { Engine } from "@oasis-engine/core"; +import type { BufferReader } from "../../utils/BufferReader"; +import { IEncodedModelMesh } from "./IModelMesh"; +import { Color, Vector2, Vector3, Vector4 } from "@oasis-engine/math"; + +@decoder("Mesh") +export class MeshDecoder { + public static decode(engine: Engine, bufferReader: BufferReader): Promise { + return new Promise((resolve) => { + const modelMesh = new ModelMesh(engine); + const jsonDataString = bufferReader.nextStr(); + const encodedMeshData: IEncodedModelMesh = JSON.parse(jsonDataString); + + const offset = Math.ceil(bufferReader.offset / 4) * 4; + + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.positions.start + offset, + (encodedMeshData.positions.end - encodedMeshData.positions.start) / 4 + ); + const vertexCount = float32Array.length / 3; + const positions = float32ArrayToVector3(float32Array, vertexCount); + modelMesh.setPositions(positions); + if (encodedMeshData.normals) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.normals.start + offset, + (encodedMeshData.normals.end - encodedMeshData.normals.start) / 4 + ); + const normals = float32ArrayToVector3(float32Array, vertexCount); + modelMesh.setNormals(normals); + } + if (encodedMeshData.uvs) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uvs.start + offset, + (encodedMeshData.uvs.end - encodedMeshData.uvs.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount)); + } + if (encodedMeshData.uv1) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uv1.start + offset, + (encodedMeshData.uv1.end - encodedMeshData.uv1.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount), 1); + } + if (encodedMeshData.uv2) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uv2.start + offset, + (encodedMeshData.uv2.end - encodedMeshData.uv2.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount), 2); + } + if (encodedMeshData.uv3) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uv3.start + offset, + (encodedMeshData.uv3.end - encodedMeshData.uv3.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount), 3); + } + if (encodedMeshData.uv4) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uv4.start + offset, + (encodedMeshData.uv4.end - encodedMeshData.uv4.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount), 4); + } + if (encodedMeshData.uv5) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uv5.start + offset, + (encodedMeshData.uv5.end - encodedMeshData.uv5.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount), 5); + } + if (encodedMeshData.uv6) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uv6.start + offset, + (encodedMeshData.uv6.end - encodedMeshData.uv6.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount), 6); + } + if (encodedMeshData.uv7) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.uv7.start + offset, + (encodedMeshData.uv7.end - encodedMeshData.uv7.start) / 4 + ); + modelMesh.setUVs(float32ArrayToVector2(float32Array, vertexCount), 7); + } + if (encodedMeshData.colors) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.colors.start + offset, + (encodedMeshData.colors.end - encodedMeshData.colors.start) / 4 + ); + modelMesh.setColors(float32ArrayToVColor(float32Array, vertexCount)); + } + if (encodedMeshData.boneWeights) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.boneWeights.start + offset, + (encodedMeshData.boneWeights.end - encodedMeshData.boneWeights.start) / 4 + ); + modelMesh.setBoneWeights(float32ArrayToVector4(float32Array, vertexCount)); + } + if (encodedMeshData.boneIndices) { + const float32Array = new Float32Array( + bufferReader.buffer, + encodedMeshData.boneIndices.start + offset, + (encodedMeshData.boneIndices.end - encodedMeshData.boneIndices.start) / 4 + ); + modelMesh.setBoneIndices(float32ArrayToVector4(float32Array, vertexCount)); + } + if (encodedMeshData.blendShapes) { + encodedMeshData.blendShapes.forEach((blendShapeData) => { + const blendShape = new BlendShape(blendShapeData.name); + blendShapeData.frames.forEach((frameData) => { + const positionArray = new Float32Array( + bufferReader.buffer, + frameData.deltaPosition.start + offset, + (frameData.deltaPosition.end - frameData.deltaPosition.start) / 4 + ); + const count = positionArray.length / 3; + const deltaPosition = float32ArrayToVector3(positionArray, count); + let deltaNormals: Vector3[] | null = null; + if (frameData.deltaNormals) { + const normalsArray = new Float32Array( + bufferReader.buffer, + frameData.deltaNormals.start + offset, + (frameData.deltaNormals.end - frameData.deltaNormals.start) / 4 + ); + deltaNormals = float32ArrayToVector3(normalsArray, count); + } + let deltaTangents: Vector4[] | null = null; + if (frameData.deltaTangents) { + const tangentsArray = new Float32Array( + bufferReader.buffer, + frameData.deltaTangents.start + offset, + (frameData.deltaTangents.end - frameData.deltaTangents.start) / 4 + ); + deltaTangents = float32ArrayToVector4(tangentsArray, count); + } + blendShape.addFrame(frameData.weight, deltaPosition); + }); + modelMesh.addBlendShape(blendShape); + }); + } + if (encodedMeshData.indices) { + let indices: Uint16Array | Uint32Array = null; + if (encodedMeshData.indices.type === 0) { + indices = new Uint16Array( + bufferReader.buffer, + encodedMeshData.indices.start + offset, + (encodedMeshData.indices.end - encodedMeshData.indices.start) / 2 + ); + } else { + indices = new Uint32Array( + bufferReader.buffer, + encodedMeshData.indices.start + offset, + (encodedMeshData.indices.end - encodedMeshData.indices.start) / 4 + ); + } + modelMesh.setIndices(indices); + } + encodedMeshData.subMeshes.forEach((subMesh) => { + modelMesh.addSubMesh(subMesh); + }); + modelMesh.uploadData(false); + resolve(modelMesh); + }); + } +} + +function float32ArrayToVColor(float32Array: Float32Array, vertexCount: number) { + const array = new Array(vertexCount); + for (let i = 0; i < vertexCount; i++) { + array[i] = new Color( + float32Array[i * 4], + float32Array[i * 4 + 1], + float32Array[i * 4 + 2], + float32Array[i * 4 + 3] + ); + } + return array; +} + +function float32ArrayToVector4(float32Array: Float32Array, vertexCount: number) { + const array = new Array(vertexCount); + for (let i = 0; i < vertexCount; i++) { + array[i] = new Vector4( + float32Array[i * 4], + float32Array[i * 4 + 1], + float32Array[i * 4 + 2], + float32Array[i * 4 + 3] + ); + } + return array; +} + +function float32ArrayToVector3(float32Array: Float32Array, vertexCount: number) { + const array = new Array(vertexCount); + for (let i = 0; i < vertexCount; i++) { + array[i] = new Vector3(float32Array[i * 3], float32Array[i * 3 + 1], float32Array[i * 3 + 2]); + } + return array; +} + +function float32ArrayToVector2(float32Array: Float32Array, vertexCount: number) { + const array = new Array(vertexCount); + for (let i = 0; i < vertexCount; i++) { + array[i] = new Vector2(float32Array[i * 2], float32Array[i * 2 + 1]); + } + return array; +} diff --git a/packages/resource-process/src/resources/mesh/type.ts b/packages/resource-process/src/resources/mesh/type.ts new file mode 100644 index 000000000..b6f01bc39 --- /dev/null +++ b/packages/resource-process/src/resources/mesh/type.ts @@ -0,0 +1,31 @@ +type uint8 = number; +type uint16 = number; +type uint32 = number; + +export interface IMeshData { + objectId: string; + name: string; + vertexElements: Array<{ + semantic: string; + offset: uint32; + format: uint8; + bindingIndex: uint8; + instanceStepRate: uint8; + }>; + subMeshes: Array<{ + start: uint32; + count: uint32; + topology: uint8; + }>; + vertexBuffer: { + bufferUsage: uint8; + buffer: ArrayBuffer; + stride: uint16; + }; + hasIndexBuffer: boolean; + indexBuffer?: { + bufferUsage: uint8; + buffer: ArrayBuffer; + format: uint8; + }; +} diff --git a/packages/resource-process/src/resources/prefab/PrefabDesign.ts b/packages/resource-process/src/resources/prefab/PrefabDesign.ts new file mode 100644 index 000000000..af5f09d70 --- /dev/null +++ b/packages/resource-process/src/resources/prefab/PrefabDesign.ts @@ -0,0 +1,44 @@ +export interface IPrefabFile { + entities: Array; +} + +export interface IScene extends IPrefabFile {} + +export interface IVector3 { + x: number; + y: number; + z: number; +} +export interface IBasicEntity { + name?: string; + id?: string; + components?: Array; + isActive?: boolean; + position?: IVector3; + rotation?: IVector3; + scale?: IVector3; + children?: Array; + parent?: string; +} + +export type IEntity = IBasicEntity | IRefEntity; + +export interface IRefEntity extends IBasicEntity { + assetRefId: string; + key?: string; +} + +export type IComponent = { id: string; refId?: string } & IClassObject; + +export type IMethodParams = Array; + +export type IClassObject = { + class: string; + constructParams?: IMethodParams; + methods?: { [methodName: string]: Array }; + props?: { [key: string]: IBasicType | IMethodParams }; +}; + +export type IBasicType = string | number | boolean | null | undefined | IReferenceType | IClassObject | IMethodParams; + +export type IReferenceType = { key?: string; refId: string }; diff --git a/packages/resource-process/src/resources/prefab/PrefabParser.ts b/packages/resource-process/src/resources/prefab/PrefabParser.ts new file mode 100644 index 000000000..db3bf60ce --- /dev/null +++ b/packages/resource-process/src/resources/prefab/PrefabParser.ts @@ -0,0 +1,44 @@ +import { Engine, Entity } from "@oasis-engine/core"; +import type { IEntity, IPrefabFile } from "./PrefabDesign"; +import { ReflectionParser } from "./ReflectionParser"; + +export class PrefabParser { + constructor(private _engine: Engine) {} + + parse(data: IPrefabFile): Promise { + const entitiesMap: Record = {}; + const entitiesConfigMap: Record = {}; + const promises: Promise[] = []; + const entitiesConfig = data.entities; + for (const entity of entitiesConfig) { + entitiesConfigMap[entity.id] = entity; + promises.push(ReflectionParser.parseEntity(entity, this._engine)); + } + + return Promise.all(promises).then((entities) => { + const rootId = entitiesConfig[0].id; + entities.forEach((entity, index) => { + entitiesMap[entitiesConfig[index].id] = entity; + }); + PrefabParser.parseChildren(entitiesConfigMap, entitiesMap, rootId); + return entitiesMap[rootId]; + }); + } + + static parseChildren( + entitiesConfig: { [key: string]: IEntity }, + entities: { [key: string]: Entity }, + parentId: string + ) { + const children = entitiesConfig[parentId].children; + if (children && children.length > 0) { + const parent = entities[parentId]; + for (let i = 0; i < children.length; i++) { + const childId = children[i]; + const entity = entities[childId]; + parent.addChild(entity); + this.parseChildren(entitiesConfig, entities, childId); + } + } + } +} diff --git a/packages/resource-process/src/resources/prefab/ReflectionParser.ts b/packages/resource-process/src/resources/prefab/ReflectionParser.ts new file mode 100644 index 000000000..8620dbf96 --- /dev/null +++ b/packages/resource-process/src/resources/prefab/ReflectionParser.ts @@ -0,0 +1,132 @@ +import { AssetType, Engine, Entity, Loader } from "@oasis-engine/core"; +import { IBasicType, IClassObject, IEntity, IReferenceType } from "./PrefabDesign"; + +export class ReflectionParser { + static parseEntity(entityConfig: IEntity, engine: Engine): Promise { + return ReflectionParser.getEntityByConfig(entityConfig, engine).then((entity) => { + entity.isActive = entityConfig.isActive ?? true; + const { position, rotation, scale } = entityConfig; + if (position) { + entity.transform.setPosition(position.x, position.y, position.z); + } + if (rotation) { + entity.transform.setRotation(rotation.x, rotation.y, rotation.z); + } + if (scale) { + entity.transform.setScale(scale.x, scale.y, scale.z); + } + const promises = []; + for (let i = 0; i < entityConfig.components.length; i++) { + const componentConfig = entityConfig.components[i]; + const key = !componentConfig.refId ? componentConfig.class : componentConfig.refId; + const component = entity.addComponent(Loader.getClass(key)); + const promise = this.parsePropsAndMethods(component, componentConfig, engine); + promises.push(promise); + } + return Promise.all(promises).then(() => { + return entity; + }); + }); + } + + private static getEntityByConfig(entityConfig: IEntity, engine: Engine): Promise { + // @ts-ignore + const assetRefId: string = entityConfig.assetRefId; + if (assetRefId) { + // @ts-ignore + return engine.resourceManager.getResourceByRef({ refId: assetRefId, key: entityConfig.key }); + } else { + const entity = new Entity(engine, entityConfig.name); + return Promise.resolve(entity); + } + } + + static parseClassObject( + item: IClassObject, + engine: Engine, + resourceManager: any = engine.resourceManager + ): Promise { + const Class = Loader.getClass(item.class); + const params = item.constructParams ?? []; + const instance = new Class(...params); + return this.parsePropsAndMethods(instance, item, engine, resourceManager); + } + + static parseBasicType( + value: IBasicType, + engine: Engine, + resourceManager: any = engine.resourceManager + ): Promise { + if (Array.isArray(value)) { + return Promise.all(value.map((item) => this.parseBasicType(item, engine, resourceManager))); + } else if (typeof value === "object") { + if (this._isClass(value)) { + // 类对象 + return this.parseClassObject(value, engine, resourceManager); + } else if (this._isRef(value)) { + // 引用对象 + return resourceManager.getResourceByRef(value); + } else { + // 基础类型 + return Promise.resolve(value); + } + } else { + return Promise.resolve(value); + } + } + + static parsePropsAndMethods( + instance: any, + item: Omit, + engine: Engine, + resourceManager: any = engine.resourceManager + ) { + const promises = []; + if (item.methods) { + for (let methodName in item.methods) { + const methodParams = item.methods[methodName]; + for (let i = 0, count = methodParams.length; i < count; i++) { + const params = methodParams[i]; + const promise = this.parseMethod(instance, methodName, params, engine, resourceManager); + promises.push(promise); + } + } + } + + if (item.props) { + for (let key in item.props) { + const value = item.props[key]; + const promise = this.parseBasicType(value, engine).then((v) => { + return (instance[key] = v); + }); + promises.push(promise); + } + } + + return Promise.all(promises).then(() => { + return instance; + }); + } + + static parseMethod( + instance: any, + methodName: string, + methodParams: Array, + engine: Engine, + resourceManager: any = engine.resourceManager + ) { + return Promise.all(methodParams.map((param) => this.parseBasicType(param, engine, resourceManager))).then( + (result) => { + return instance[methodName](...result); + } + ); + } + + private static _isClass(value: any): value is IClassObject { + return "class" in value; + } + + private static _isRef(value: any): value is IReferenceType { + return "refId" in value; + } +} diff --git a/packages/resource-process/src/resources/scene/EditorTextureLoader.ts b/packages/resource-process/src/resources/scene/EditorTextureLoader.ts new file mode 100644 index 000000000..1ed0240d5 --- /dev/null +++ b/packages/resource-process/src/resources/scene/EditorTextureLoader.ts @@ -0,0 +1,21 @@ +import { + AssetPromise, + Loader, + LoadItem, + resourceLoader, + ResourceManager, + Texture2D} from "@oasis-engine/core"; +import { decode } from "../.."; + +@resourceLoader("EditorTexture2D", ["prefab"], true) +export class EditorTextureLoader extends Loader { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return new AssetPromise((resolve) => { + this.request(item.url, { type: "arraybuffer" }).then((data) => { + decode(data, resourceManager.engine).then((texture) => { + resolve(texture); + }); + }); + }); + } +} diff --git a/packages/resource-process/src/resources/scene/MeshLoader.ts b/packages/resource-process/src/resources/scene/MeshLoader.ts new file mode 100644 index 000000000..48ddb03a0 --- /dev/null +++ b/packages/resource-process/src/resources/scene/MeshLoader.ts @@ -0,0 +1,24 @@ +import { + AssetPromise, + Loader, + LoadItem, + Mesh, + ModelMesh, + resourceLoader, + ResourceManager, + UnlitMaterial +} from "@oasis-engine/core"; +import { decode } from "../.."; + +@resourceLoader("Mesh", ["prefab"], true) +export class MeshLoader extends Loader { + load(item: LoadItem, resourceManager: ResourceManager): AssetPromise { + return new AssetPromise((resolve, reject) => { + this.request(item.url, { type: "arraybuffer" }).then((data) => { + decode(data, resourceManager.engine).then((mesh) => { + resolve(mesh); + }); + }); + }); + } +} diff --git a/packages/resource-process/src/resources/scene/SceneParser.ts b/packages/resource-process/src/resources/scene/SceneParser.ts new file mode 100644 index 000000000..a32524839 --- /dev/null +++ b/packages/resource-process/src/resources/scene/SceneParser.ts @@ -0,0 +1,36 @@ +import { Engine, Entity, Scene } from "@oasis-engine/core"; +import { IEntity, IScene } from "../prefab/PrefabDesign"; +import { PrefabParser } from "../prefab/PrefabParser"; +import { ReflectionParser } from "../prefab/ReflectionParser"; + +export class SceneParser { + static parse(engine: Engine, sceneData: IScene): Promise { + const scene = new Scene(engine); + const entitiesMap: Record = {}; + const entitiesConfigMap: Record = {}; + const promises: Promise[] = []; + const entitiesConfig = sceneData.entities; + for (const entity of entitiesConfig) { + entitiesConfigMap[entity.id] = entity; + promises.push(ReflectionParser.parseEntity(entity, engine)); + } + + return Promise.all(promises).then((entities) => { + const rootIds = []; + entities.forEach((entity, index) => { + entitiesMap[entitiesConfig[index].id] = entity; + if (!entitiesConfig[index].parent) { + rootIds.push(entitiesConfig[index].id); + } + }); + for (const rootId of rootIds) { + PrefabParser.parseChildren(entitiesConfigMap, entitiesMap, rootId); + } + const rootEntities = rootIds.map((id) => entitiesMap[id]); + for (let i = 0; i < rootEntities.length; i++) { + scene.addRootEntity(rootEntities[i]); + } + return scene; + }); + } +} diff --git a/packages/resource-process/src/resources/texture2D/TextureDecoder.ts b/packages/resource-process/src/resources/texture2D/TextureDecoder.ts new file mode 100644 index 000000000..496060a66 --- /dev/null +++ b/packages/resource-process/src/resources/texture2D/TextureDecoder.ts @@ -0,0 +1,72 @@ +import { Engine, Texture2D } from "@oasis-engine/core"; +import { BufferReader } from "../../utils/BufferReader"; +import { decoder } from "../../utils/Decorator"; + +@decoder("Texture2D") +export class Texture2DDecoder { + static decode(engine: Engine, bufferReader: BufferReader): Promise { + return new Promise((resolve, reject) => { + const objectId = bufferReader.nextStr(); + const mipmap = !!bufferReader.nextUint8(); + const filterMode = bufferReader.nextUint8(); + const anisoLevel = bufferReader.nextUint8(); + const wrapModeU = bufferReader.nextUint8(); + const wrapModeV = bufferReader.nextUint8(); + const format = bufferReader.nextUint8(); + const width = bufferReader.nextUint16(); + const height = bufferReader.nextUint16(); + const isPixelBuffer = bufferReader.nextUint8(); + + const mipCount = bufferReader.nextUint8(); + const imagesData = bufferReader.nextImagesData(mipCount); + + const texture2D = new Texture2D(engine, width, height, format, mipmap); + texture2D.filterMode = filterMode; + texture2D.anisoLevel = anisoLevel; + texture2D.wrapModeU = wrapModeU; + texture2D.wrapModeV = wrapModeV; + + if (isPixelBuffer) { + const pixelBuffer = new Uint8Array(imagesData[0]); + texture2D.setPixelBuffer(pixelBuffer); + if (mipmap) { + texture2D.generateMipmaps(); + for (let i = 1; i < mipCount; i++) { + const pixelBuffer = new Uint8Array(imagesData[i]); + texture2D.setPixelBuffer(pixelBuffer, i); + } + } + // @ts-ignore + engine.resourceManager._objectPool[objectId] = texture2D; + resolve(texture2D); + } else { + const blob = new window.Blob([imagesData[0]]); + const img = new Image(); + img.src = URL.createObjectURL(blob); + img.onload = () => { + texture2D.setImageSource(img); + let completedCount = 0; + const onComplete = () => { + completedCount++; + if (completedCount >= mipCount) { + resolve(texture2D); + } + }; + onComplete(); + if (mipmap) { + texture2D.generateMipmaps(); + for (let i = 1; i < mipCount; i++) { + const blob = new window.Blob([imagesData[i]]); + const img = new Image(); + img.src = URL.createObjectURL(blob); + img.onload = () => { + texture2D.setImageSource(img, i); + onComplete(); + }; + } + } + }; + } + }); + } +} diff --git a/packages/resource-process/src/utils/BufferReader.ts b/packages/resource-process/src/utils/BufferReader.ts new file mode 100644 index 000000000..585057080 --- /dev/null +++ b/packages/resource-process/src/utils/BufferReader.ts @@ -0,0 +1,145 @@ +import { ab2str } from "./Utils"; + +class ImageData { + type: "image/png" | "image/jpg" | "image/webp" | "ktx"; + buffer: ArrayBuffer; +} + +const textDecode = new TextDecoder(); + +export class BufferReader { + private _dataView: DataView; + private _littleEndian: boolean; + private _offset: number; + + public static imageMapping = { + 0: "image/png", + 1: "image/jpg", + 2: "image/webp", + 3: "ktx" + }; + + constructor(public buffer: ArrayBuffer, byteOffset: number = 0, byteLength?: number, littleEndian: boolean = true) { + // byteLength = byteLength ?? _buffer.byteLength; + this._dataView = new DataView(buffer); + this._littleEndian = littleEndian; + this._offset = byteOffset; + } + + get offset() { + return this._offset; + } + + nextUint8() { + const value = this._dataView.getUint8(this._offset); + this._offset += 1; + return value; + } + + nextUint16() { + const value = this._dataView.getUint16(this._offset, this._littleEndian); + this._offset += 2; + return value; + } + + nextUint32() { + const value = this._dataView.getUint32(this._offset, this._littleEndian); + this._offset += 4; + return value; + } + + nextInt32() { + const value = this._dataView.getInt32(this._offset, this._littleEndian); + this._offset += 4; + return value; + } + + nextInt32Array(len: number) { + const value = new Int32Array(this.buffer, this._offset, len); + this._offset += 4 * len; + return value; + } + + nextFloat32() { + const value = this._dataView.getFloat32(this._offset, this._littleEndian); + this._offset += 4; + return value; + } + + nextFloat32Array(len: number) { + const value = new Float32Array(this.buffer, this._offset, len); + this._offset += 4 * len; + return value; + } + + nextUint32Array(len: number) { + const value = new Uint32Array(this.buffer, this._offset, len); + this._offset += 4 * len; + return value; + } + + nextUint8Array(len: number) { + const value = new Uint8Array(this.buffer, this._offset, len); + this._offset += len; + return value; + } + + nextUint64() { + const left = this._dataView.getUint32(this._offset, this._littleEndian); + const right = this._dataView.getUint32(this._offset + 4, this._littleEndian); + const value = left + 2 ** 32 * right; + this._offset += 8; + return value; + } + + nextStr(): string { + const strByteLength = this.nextUint16(); + const uint8Array = new Uint8Array(this.buffer, this._offset, strByteLength); + this._offset += strByteLength; + return textDecode.decode(uint8Array); + } + + /** + * image data 放在最后 + */ + nextImageData(count: number = 0): ArrayBuffer { + return this.buffer.slice(this._offset); + } + + nextImagesData(count: number): ArrayBuffer[] { + const imagesLen = new Array(count); + // Start offset of Uint32Array should be a multiple of 4. ref: https://stackoverflow.com/questions/15417310/why-typed-array-constructors-require-offset-to-be-multiple-of-underlying-type-si + for (let i = 0; i < count; i++) { + const len = this._dataView.getUint32(this._offset, this._littleEndian); + imagesLen[i] = len; + this._offset += 4; + } + const imagesData: ArrayBuffer[] = []; + + for (let i = 0; i < count; i++) { + const len = imagesLen[i]; + const buffer = this.buffer.slice(this._offset, this._offset + len); + this._offset += len; + imagesData.push(buffer); + } + return imagesData; + } + + skip(bytes: number) { + this._offset += bytes; + return this; + } + + scan(maxByteLength: number, term: number = 0x00): Uint8Array { + const byteOffset = this._offset; + let byteLength = 0; + while (this._dataView.getUint8(this._offset) !== term && byteLength < maxByteLength) { + byteLength++; + this._offset++; + } + + if (byteLength < maxByteLength) this._offset++; + + return new Uint8Array(this._dataView.buffer, this._dataView.byteOffset + byteOffset, byteLength); + } +} diff --git a/packages/resource-process/src/utils/Decorator.ts b/packages/resource-process/src/utils/Decorator.ts new file mode 100644 index 000000000..bf6496c35 --- /dev/null +++ b/packages/resource-process/src/utils/Decorator.ts @@ -0,0 +1,20 @@ +import { Engine } from "@oasis-engine/core"; +import type { BufferReader } from "./BufferReader"; + +export const decoderMap: Record< + string, + { + decode: (engine: Engine, bufferReader: BufferReader) => Promise; + } +> = {}; + +/** + * Decoder decorator generator. + * @param type - resource file type. + * @returns Decoder decorator + */ +export function decoder(type: string): ClassDecorator { + return (target: any) => { + decoderMap[type] = target; + }; +} \ No newline at end of file diff --git a/packages/resource-process/src/utils/FileHeader.ts b/packages/resource-process/src/utils/FileHeader.ts new file mode 100644 index 000000000..ba535f87d --- /dev/null +++ b/packages/resource-process/src/utils/FileHeader.ts @@ -0,0 +1,33 @@ +const textDecode = new TextDecoder(); + +export class FileHeader { + totalLength: number = 0; + version: number = 0; + type: string = ""; + name: string = ""; + headerLength: number = 0; + + static decode(arrayBuffer: ArrayBuffer): FileHeader { + const dataView = new DataView(arrayBuffer); + const totalLen = dataView.getUint32(0, true); + const fileVersion = dataView.getUint8(4); + const typeLen = dataView.getUint16(5, true); + const typeUint8Array = new Uint8Array(arrayBuffer, 7, typeLen); + const nameLen = dataView.getUint16(7 + typeLen, true); + const nameUint8Array = new Uint8Array(arrayBuffer, 9 + typeLen, nameLen); + + const name = textDecode.decode(nameUint8Array); + const type = textDecode.decode(typeUint8Array); + const header = new FileHeader(); + header.totalLength = totalLen; + header.name = name; + header.type = type; + header.version = fileVersion; + header.headerLength = nameUint8Array.byteLength + typeUint8Array.byteLength + 9; + return header; + } + + public get dataLength() { + return this.totalLength - this.headerLength; + } +} diff --git a/packages/resource-process/src/utils/Utils.ts b/packages/resource-process/src/utils/Utils.ts new file mode 100644 index 000000000..cd320d6e4 --- /dev/null +++ b/packages/resource-process/src/utils/Utils.ts @@ -0,0 +1,7 @@ +/** + * Array buffer to string. + * @param buf + */ +export function ab2str(buf: ArrayBuffer) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); +} diff --git a/packages/resource-process/tests/AnimatorController/animatorController.json b/packages/resource-process/tests/AnimatorController/animatorController.json new file mode 100644 index 000000000..5a3551cb1 --- /dev/null +++ b/packages/resource-process/tests/AnimatorController/animatorController.json @@ -0,0 +1,30 @@ +{ + "objectId": "0", + "layers": [ + { + "name": "Base", + "weight": 1, + "blending": 0, + "stateMachine": { + "states": [ + { + "name": "animation_AnimatedCube", + "clip": { + "objectId": "1", + "path": "./" + }, + "wrapMode": 1, + "speed": 1, + "clipStartNormalizedTime": 0, + "clipEndNormalizedTime": 1, + "transitions": [], + "isEntryState": false, + "isExitState": false, + "isAnyState": false, + "isDefaultState": true + } + ] + } + } + ] +} diff --git a/packages/resource-process/tests/AnimatorController/index.test.ts b/packages/resource-process/tests/AnimatorController/index.test.ts new file mode 100644 index 000000000..9ad4e1cc4 --- /dev/null +++ b/packages/resource-process/tests/AnimatorController/index.test.ts @@ -0,0 +1,37 @@ +import { AnimationClipDecoder } from './../../src/resources/animationClip/AnimationClipDecoder'; +import { AnimationClipEncoder } from './../../src/resources/animationClip/AnimationClipEncoder'; +import { AnimatorControllerDecoder } from './../../src/resources/animatorController/AnimatorControllerDecoder'; +import { AnimatorControllerEncoder } from './../../src/resources/animatorController/AnimatorControllerEncoder'; +import { BufferReader } from '../../src/utils/BufferReader'; +import { WebGLEngine } from '../../../rhi-webgl/src/WebGLEngine'; +import { BufferWriter } from '../../src/utils/BufferWriter'; +import testClipData from '../animationClip/animationClip.json' +import testControllerData from './animatorController.json' +describe("AnimatorControllerEncoderAndDecoder Test", () => { + it("parser", async () => { + const engine = new WebGLEngine(document.createElement("canvas")); + const clipBuffer = new ArrayBuffer(10000); + const clipBufferWriter = new BufferWriter(clipBuffer); + const clipBufferReader = new BufferReader(clipBuffer); + AnimationClipEncoder.encode(clipBufferWriter, testClipData); + await AnimationClipDecoder.decode(engine, clipBufferReader) + const controllerBuffer = new ArrayBuffer(10000); + const controllerBufferWriter = new BufferWriter(controllerBuffer); + const controllerBufferReader = new BufferReader(controllerBuffer); + + AnimatorControllerEncoder.encode(controllerBufferWriter, testControllerData); + const controller = await AnimatorControllerDecoder.decode(engine, controllerBufferReader) + const { clip } = controller.layers[0].stateMachine.states[0] + expect(controller.layers.length).toEqual(1); + expect(controller.layers[0].name).toEqual('Base'); + expect(controller.layers[0].blendingMode).toEqual(0); + expect(controller.layers[0].weight).toEqual(1); + expect(controller.layers[0].stateMachine.states.length).toEqual(1); + expect(controller.layers[0].stateMachine.states[0].name).toEqual('animation_AnimatedCube'); + expect(controller.layers[0].stateMachine.states[0].clip.name).toEqual('animation_AnimatedCube'); + expect(controller.layers[0].stateMachine.states[0].wrapMode).toEqual(1); + expect(controller.layers[0].stateMachine.states[0].clipStartTime).toEqual(0); + expect(controller.layers[0].stateMachine.states[0].clipEndTime).toEqual(clip.length); + expect(controller.layers[0].stateMachine.states[0].transitions.length).toEqual(0); + }); +}); diff --git a/packages/resource-process/tests/animationClip/animationClip.json b/packages/resource-process/tests/animationClip/animationClip.json new file mode 100644 index 000000000..16dd8fe9f --- /dev/null +++ b/packages/resource-process/tests/animationClip/animationClip.json @@ -0,0 +1,86 @@ +{ + "objectId": "1", + "name": "animation_AnimatedCube", + "curveBindings": [ + { + "relativePath": "", + "property": 1, + "curve": { + "keys": [ + { + "time": 0, + "value": { + "x": 0, + "y": 0, + "z": 0, + "w": 1 + }, + "inTangent": { + "x": 0, + "y": 0, + "z": 0, + "w": 0 + }, + "outTangent": { + "x": 0, + "y": 0, + "z": 0, + "w": 0 + } + }, + { + "time": 1, + "value": { + "x": 0, + "y": 1, + "z": 0, + "w": -4.371138828673793e-8 + }, + "inTangent": { + "x": 0, + "y": 0, + "z": 0, + "w": 0 + }, + "outTangent": { + "x": 0, + "y": 0, + "z": 0, + "w": 0 + } + }, + { + "time": 2, + "value": { + "x": 0, + "y": -8.742277657347586e-8, + "z": 0, + "w": -1 + }, + "inTangent": { + "x": 0, + "y": 0, + "z": 0, + "w": 0 + }, + "outTangent": { + "x": 0, + "y": 0, + "z": 0, + "w": 0 + } + } + ], + "interpolation": 0, + "valueType": 5 + } + } + ], + "events": [ + { + "time": 0.5, + "functionName": "test", + "parameter": "param" + } + ] +} diff --git a/packages/resource-process/tests/animationClip/index.test.ts b/packages/resource-process/tests/animationClip/index.test.ts new file mode 100644 index 000000000..673a8b695 --- /dev/null +++ b/packages/resource-process/tests/animationClip/index.test.ts @@ -0,0 +1,25 @@ +import { AnimationClipDecoder } from '../../src/resources/animationClip/AnimationClipDecoder'; +import { BufferReader } from '../../src/utils/BufferReader'; +import { WebGLEngine } from '../../../rhi-webgl/src/WebGLEngine'; +import { BufferWriter } from '../../src/utils/BufferWriter'; +import { AnimationClipEncoder } from '../../src/resources/animationClip/AnimationClipEncoder'; +import testData from './animationClip.json' +describe("AnimationClipEncoderAndDecoder Test", () => { + it("parser", async () => { + const engine = new WebGLEngine(document.createElement("canvas")); + const buffer = new ArrayBuffer(10000); + const bufferWriter = new BufferWriter(buffer); + const bufferReader = new BufferReader(buffer); + AnimationClipEncoder.encode(bufferWriter, testData); + const clip = await AnimationClipDecoder.decode(engine, bufferReader) + expect(clip.name).toEqual('animation_AnimatedCube'); + expect(clip.events.length).toEqual(1); + expect(clip.events[0].time).toEqual(0.5); + expect(clip.events[0].parameter).toEqual('param'); + expect(clip.events[0].functionName).toEqual('test'); + expect(clip.curveBindings.length).toEqual(1); + expect(clip.curveBindings[0].relativePath).toEqual(''); + expect(clip.curveBindings[0].property).toEqual(1); + expect(clip.curveBindings[0].curve.keys.length).toEqual(3); + }); +}); diff --git a/packages/resource-process/tsconfig.json b/packages/resource-process/tsconfig.json new file mode 100644 index 000000000..1dc8cad30 --- /dev/null +++ b/packages/resource-process/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "module": "esnext", + "target": "esnext", + "declaration": true, + "moduleResolution": "node", + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "declarationDir": "types", + "emitDeclarationOnly": true, + "sourceMap": true, + "skipLibCheck": true, + "incremental": false + }, + "include": [ + "src/**/*" + ] +} \ No newline at end of file diff --git a/tests/package.json b/tests/package.json index 8656137d8..e5db82038 100644 --- a/tests/package.json +++ b/tests/package.json @@ -15,12 +15,9 @@ "types/**/*" ], "dependencies": { - "@oasis-engine/core": "0.7.0-beta.4", - "@oasis-engine/math": "0.7.0-beta.4", - "@oasis-engine/physics-lite": "0.7.0-beta.4", - "@oasis-engine/rhi-webgl": "0.7.0-beta.4" - }, - "devDependencies": { - "@oasis-engine/design": "0.7.0-beta.4" + "@oasis-engine/core": "0.8.0-alpha.5", + "@oasis-engine/math": "0.8.0-alpha.5", + "@oasis-engine/rhi-webgl": "0.8.0-alpha.5", + "@oasis-engine/design": "0.8.0-alpha.5" } }