From 34c456106311801e372cf136f45786a292b07906 Mon Sep 17 00:00:00 2001 From: zhuxudong Date: Mon, 17 Oct 2022 18:50:45 +0800 Subject: [PATCH] Feat add loader for gltf subasset #1071 (#1075) *feat: add loader for gltf subasset --- packages/core/src/animation/index.ts | 2 +- packages/loader/package.json | 0 packages/loader/src/GLTFLoader.ts | 42 ++++- packages/loader/src/MaterialLoader.ts | 15 +- packages/loader/src/gltf/GLTFParser.ts | 13 +- packages/loader/src/gltf/GLTFResource.ts | 1 - packages/loader/src/gltf/GLTFUtil.ts | 49 ++++++ .../gltf/extensions/OASIS_materials_remap.ts | 13 ++ packages/loader/src/gltf/extensions/Schema.ts | 6 + packages/loader/src/gltf/extensions/index.ts | 1 + .../loader/src/gltf/parser/AnimationParser.ts | 18 ++- .../loader/src/gltf/parser/BufferParser.ts | 3 +- .../loader/src/gltf/parser/MaterialParser.ts | 34 +++- packages/loader/src/gltf/parser/MeshParser.ts | 149 ++++++++++-------- packages/loader/src/gltf/parser/Parser.ts | 2 +- .../loader/src/gltf/parser/ParserContext.ts | 9 +- .../loader/src/gltf/parser/SceneParser.ts | 42 +++-- .../loader/src/gltf/parser/TextureParser.ts | 17 +- packages/oasis-engine/package.json | 0 packages/rhi-webgl/package.json | 0 20 files changed, 315 insertions(+), 101 deletions(-) mode change 100755 => 100644 packages/loader/package.json create mode 100644 packages/loader/src/gltf/extensions/OASIS_materials_remap.ts mode change 100755 => 100644 packages/oasis-engine/package.json mode change 100755 => 100644 packages/rhi-webgl/package.json diff --git a/packages/core/src/animation/index.ts b/packages/core/src/animation/index.ts index 9f85881d52..a7fe88a475 100644 --- a/packages/core/src/animation/index.ts +++ b/packages/core/src/animation/index.ts @@ -12,5 +12,5 @@ export { AnimatorConditionMode } from "./enums/AnimatorConditionMode"; export { AnimatorLayerBlendingMode } from "./enums/AnimatorLayerBlendingMode"; export { InterpolationType } from "./enums/InterpolationType"; export { WrapMode } from "./enums/WrapMode"; -export * from "./KeyFrame"; +export * from "./Keyframe"; export { StateMachineScript } from "./StateMachineScript"; diff --git a/packages/loader/package.json b/packages/loader/package.json old mode 100755 new mode 100644 diff --git a/packages/loader/src/GLTFLoader.ts b/packages/loader/src/GLTFLoader.ts index 859f38c67a..5fdce42e64 100644 --- a/packages/loader/src/GLTFLoader.ts +++ b/packages/loader/src/GLTFLoader.ts @@ -1,6 +1,7 @@ import { AssetPromise, AssetType, Loader, LoadItem, resourceLoader, ResourceManager } from "@oasis-engine/core"; import { GLTFParser } from "./gltf/GLTFParser"; import { GLTFResource } from "./gltf/GLTFResource"; +import { GLTFUtil } from "./gltf/GLTFUtil"; import { ParserContext } from "./gltf/parser/ParserContext"; @resourceLoader(AssetType.Prefab, ["gltf", "glb"]) @@ -9,11 +10,46 @@ export class GLTFLoader extends Loader { const url = item.url; return new AssetPromise((resolve, reject) => { const context = new ParserContext(); - context.glTFResource = new GLTFResource(resourceManager.engine); - context.glTFResource.url = url; + const glTFResource = new GLTFResource(resourceManager.engine); + context.glTFResource = glTFResource; + glTFResource.url = url; context.keepMeshData = item.params?.keepMeshData ?? false; - GLTFParser.instance + let pipeline = GLTFParser.defaultPipeline; + + const query = GLTFUtil.getQuery(url); + if (query.q) { + const path = GLTFUtil.stringToPath(query.q); + const key = path[0]; + const value1 = Number(path[1]) || 0; + const value2 = Number(path[2]) || 0; + + switch (key) { + case "textures": + pipeline = GLTFParser.texturePipeline; + context.textureIndex = value1; + break; + case "materials": + pipeline = GLTFParser.materialPipeline; + context.materialIndex = value1; + break; + case "animations": + pipeline = GLTFParser.animationPipeline; + context.animationIndex = value1; + break; + case "meshes": + pipeline = GLTFParser.meshPipeline; + context.meshIndex = value1; + context.subMeshIndex = value2; + break; + case "defaultSceneRoot": + pipeline = GLTFParser.defaultPipeline; + context.defaultSceneRootOnly = true; + break; + } + } + + pipeline .parse(context) .then(resolve) .catch((e) => { diff --git a/packages/loader/src/MaterialLoader.ts b/packages/loader/src/MaterialLoader.ts index 60d1d026b9..8037bba4d9 100644 --- a/packages/loader/src/MaterialLoader.ts +++ b/packages/loader/src/MaterialLoader.ts @@ -41,7 +41,8 @@ class MaterialLoader extends Loader { break; } - const materialShaderData: ShaderData = material.shaderData; + const texturePromises = new Array>(); + const materialShaderData = material.shaderData; for (let key in shaderData) { const { type, value } = shaderData[key]; @@ -62,9 +63,11 @@ class MaterialLoader extends Loader { materialShaderData.setFloat(key, value); break; case "Texture": - resourceManager.getResourceByRef(value).then((texture) => { - materialShaderData.setTexture(key, texture); - }); + texturePromises.push( + resourceManager.getResourceByRef(value).then((texture) => { + materialShaderData.setTexture(key, texture); + }) + ); break; } } @@ -82,7 +85,9 @@ class MaterialLoader extends Loader { materialShaderData[key] = renderState[key]; } - resolve(material); + Promise.all(texturePromises).then(() => { + resolve(material); + }); }); }); } diff --git a/packages/loader/src/gltf/GLTFParser.ts b/packages/loader/src/gltf/GLTFParser.ts index cd60bce10b..39473a0494 100644 --- a/packages/loader/src/gltf/GLTFParser.ts +++ b/packages/loader/src/gltf/GLTFParser.ts @@ -12,7 +12,7 @@ import { TextureParser } from "./parser/TextureParser"; import { Validator } from "./parser/Validator"; export class GLTFParser { - static instance = new GLTFParser([ + static defaultPipeline = new GLTFParser([ BufferParser, Validator, TextureParser, @@ -24,6 +24,11 @@ export class GLTFParser { SceneParser ]); + static texturePipeline = new GLTFParser([BufferParser, TextureParser]); + static materialPipeline = new GLTFParser([BufferParser, TextureParser, MaterialParser]); + static animationPipeline = new GLTFParser([BufferParser, EntityParser, AnimationParser]); + static meshPipeline = new GLTFParser([BufferParser, MeshParser]); + private _pipes: Parser[] = []; private constructor(pipes: (new () => Parser)[]) { @@ -34,7 +39,7 @@ export class GLTFParser { parse(context: ParserContext): Promise { const glTFResource = context.glTFResource; - let lastPipe: void | Promise; + let lastPipe; return new Promise((resolve, reject) => { this._pipes.forEach((parser: Parser) => { @@ -49,8 +54,8 @@ export class GLTFParser { if (lastPipe) { lastPipe - .then(() => { - resolve(glTFResource); + .then((customRes) => { + resolve(customRes || glTFResource); }) .catch(reject); } else { diff --git a/packages/loader/src/gltf/GLTFResource.ts b/packages/loader/src/gltf/GLTFResource.ts index c98cf36261..97de45519b 100644 --- a/packages/loader/src/gltf/GLTFResource.ts +++ b/packages/loader/src/gltf/GLTFResource.ts @@ -44,5 +44,4 @@ export class GLTFResource extends EngineObject { defaultSceneRoot: Entity; /** Renderer can replace material by `renderer.setMaterial` if gltf use plugin-in KHR_materials_variants. */ variants?: { renderer: Renderer; material: Material; variants: string[] }[]; - } diff --git a/packages/loader/src/gltf/GLTFUtil.ts b/packages/loader/src/gltf/GLTFUtil.ts index 9501e0cf38..95178d7d4c 100644 --- a/packages/loader/src/gltf/GLTFUtil.ts +++ b/packages/loader/src/gltf/GLTFUtil.ts @@ -2,6 +2,26 @@ import { IndexFormat, TypedArray, VertexElement, VertexElementFormat } from "@oa import { Color, Vector2, Vector3, Vector4 } from "@oasis-engine/math"; import { AccessorComponentType, AccessorType, IAccessor, IBufferView, IGLTF } from "./Schema"; +const charCodeOfDot = ".".charCodeAt(0); +const reEscapeChar = /\\(\\)?/g; +const rePropName = RegExp( + // Match anything that isn't a dot or bracket. + "[^.[\\]]+" + + "|" + + // Or match property names within brackets. + "\\[(?:" + + // Match a non-string expression. + "([^\"'][^[]*)" + + "|" + + // Or match strings (supports escaping characters). + "([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2" + + ")\\]" + + "|" + + // Or match "" as the space between consecutive dots or empty brackets. + "(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|$))", + "g" +); + /** * @internal */ @@ -308,6 +328,35 @@ export class GLTFUtil { return baseUrl.substring(0, baseUrl.lastIndexOf("/") + 1) + relativeUrl; } + static getQuery(path: string): { [key: string]: string } { + const url = new URL(path); + const entries = url.searchParams.entries(); + const data = {}; + + for (const i of entries) { + data[i[0]] = i[1]; + } + return data; + } + + // "meshes[0][0]" => ["meshes","0","0"] + static stringToPath(string): string[] { + const result = []; + if (string.charCodeAt(0) === charCodeOfDot) { + result.push(""); + } + string.replace(rePropName, (match, expression, quote, subString) => { + let key = match; + if (quote) { + key = subString.replace(reEscapeChar, "$1"); + } else if (expression) { + key = expression.trim(); + } + result.push(key); + }); + return result; + } + /** * Parse the glb format. */ diff --git a/packages/loader/src/gltf/extensions/OASIS_materials_remap.ts b/packages/loader/src/gltf/extensions/OASIS_materials_remap.ts new file mode 100644 index 0000000000..3eff7c8f7c --- /dev/null +++ b/packages/loader/src/gltf/extensions/OASIS_materials_remap.ts @@ -0,0 +1,13 @@ +import { Material } from "@oasis-engine/core"; +import { GLTFResource } from "../GLTFResource"; +import { registerExtension } from "../parser/Parser"; +import { ExtensionParser } from "./ExtensionParser"; +import { IOasisMaterialRemap } from "./Schema"; + +@registerExtension("OASIS_materials_remap") +class OasisMaterialsRemap extends ExtensionParser { + createEngineResource(schema: IOasisMaterialRemap, context: GLTFResource): Promise { + const { engine } = context; + return engine.resourceManager.getResourceByRef(schema); + } +} diff --git a/packages/loader/src/gltf/extensions/Schema.ts b/packages/loader/src/gltf/extensions/Schema.ts index 59d67fd649..53793d5462 100644 --- a/packages/loader/src/gltf/extensions/Schema.ts +++ b/packages/loader/src/gltf/extensions/Schema.ts @@ -153,6 +153,12 @@ export interface IKHRXmp_Node { packet: number; } +export interface IOasisMaterialRemap { + refId: string; + key?: string; + isClone?: boolean; +} + export type ExtensionSchema = | IKHRLightsPunctual_Light | IKHRDracoMeshCompression diff --git a/packages/loader/src/gltf/extensions/index.ts b/packages/loader/src/gltf/extensions/index.ts index 7ef0b8892b..a9ee9e029c 100644 --- a/packages/loader/src/gltf/extensions/index.ts +++ b/packages/loader/src/gltf/extensions/index.ts @@ -11,3 +11,4 @@ import "./KHR_materials_volume"; import "./KHR_mesh_quantization"; import "./KHR_texture_basisu"; import "./KHR_texture_transform"; +import "./OASIS_materials_remap"; diff --git a/packages/loader/src/gltf/parser/AnimationParser.ts b/packages/loader/src/gltf/parser/AnimationParser.ts index 56bd6b4aa3..28b0a0281e 100644 --- a/packages/loader/src/gltf/parser/AnimationParser.ts +++ b/packages/loader/src/gltf/parser/AnimationParser.ts @@ -18,8 +18,8 @@ import { Parser } from "./Parser"; import { ParserContext } from "./ParserContext"; export class AnimationParser extends Parser { - parse(context: ParserContext): void { - const glTFResource = context.glTFResource; + parse(context: ParserContext) { + const { animationIndex, glTFResource } = context; const { gltf, buffers, entities } = glTFResource; const { animations, accessors } = gltf; if (!animations) { @@ -33,6 +33,9 @@ export class AnimationParser extends Parser { }>(animationClipCount); for (let i = 0; i < animationClipCount; i++) { + if (animationIndex >= 0 && animationIndex !== i) { + continue; + } const gltfAnimation = animations[i]; const { channels, samplers, name = `AnimationClip${i}` } = gltfAnimation; const animationClip = new AnimationClip(name); @@ -121,7 +124,18 @@ export class AnimationParser extends Parser { index: i }; } + + if (animationIndex >= 0) { + const animationClip = animationClips[animationIndex]; + if (animationClip) { + return animationClip; + } else { + throw `animation index not find in: ${animationIndex}`; + } + } glTFResource.animations = animationClips; + // @ts-ignore for editor + glTFResource._animationsIndices = animationsIndices; } private _addCurve( diff --git a/packages/loader/src/gltf/parser/BufferParser.ts b/packages/loader/src/gltf/parser/BufferParser.ts index 6e5ed29638..5fb1deb6c5 100644 --- a/packages/loader/src/gltf/parser/BufferParser.ts +++ b/packages/loader/src/gltf/parser/BufferParser.ts @@ -43,6 +43,7 @@ export class BufferParser extends Parser { } private _isGLB(url: string): boolean { - return url.substring(url.lastIndexOf(".") + 1) === "glb"; + const index = url.lastIndexOf("."); + return url.substring(index + 1, index + 4) === "glb"; } } diff --git a/packages/loader/src/gltf/parser/MaterialParser.ts b/packages/loader/src/gltf/parser/MaterialParser.ts index b83d807c68..9149b901a7 100644 --- a/packages/loader/src/gltf/parser/MaterialParser.ts +++ b/packages/loader/src/gltf/parser/MaterialParser.ts @@ -22,8 +22,8 @@ export class MaterialParser extends Parser { } } - parse(context: ParserContext): void { - const glTFResource = context.glTFResource; + parse(context: ParserContext) { + const { glTFResource, materialIndex } = context; const { gltf, engine, textures } = glTFResource; if (!gltf.materials) return; @@ -31,6 +31,9 @@ export class MaterialParser extends Parser { const materials: Material[] = []; for (let i = 0; i < gltf.materials.length; i++) { + if (materialIndex >= 0 && materialIndex !== i) { + continue; + } const { extensions = {}, pbrMetallicRoughness, @@ -44,7 +47,12 @@ export class MaterialParser extends Parser { name = "" } = gltf.materials[i]; - const { KHR_materials_unlit, KHR_materials_pbrSpecularGlossiness, KHR_materials_clearcoat } = extensions; + const { + KHR_materials_unlit, + KHR_materials_pbrSpecularGlossiness, + KHR_materials_clearcoat, + OASIS_materials_remap + } = extensions; let material: UnlitMaterial | PBRMaterial | PBRSpecularMaterial = null; @@ -136,6 +144,17 @@ export class MaterialParser extends Parser { } } + if (OASIS_materials_remap) { + glTFResource.gltf.extensions = glTFResource.gltf.extensions ?? {}; + glTFResource.gltf.extensions["OASIS_materials_remap"] = + glTFResource.gltf.extensions["OASIS_materials_remap"] ?? {}; + glTFResource.gltf.extensions["OASIS_materials_remap"][i] = Parser.createEngineResource( + "OASIS_materials_remap", + OASIS_materials_remap, + glTFResource + ); + } + if (doubleSided) { material.renderFace = RenderFace.Double; } else { @@ -157,6 +176,15 @@ export class MaterialParser extends Parser { materials[i] = material; } + if (materialIndex >= 0) { + const material = materials[materialIndex]; + if (material) { + return material; + } else { + throw `material index not find in: ${materialIndex}`; + } + } + glTFResource.materials = materials; } } diff --git a/packages/loader/src/gltf/parser/MeshParser.ts b/packages/loader/src/gltf/parser/MeshParser.ts index 62988c0a32..b975a42dc3 100644 --- a/packages/loader/src/gltf/parser/MeshParser.ts +++ b/packages/loader/src/gltf/parser/MeshParser.ts @@ -8,95 +8,108 @@ import { ParserContext } from "./ParserContext"; export class MeshParser extends Parser { private static _tempVector3 = new Vector3(); - parse(context: ParserContext): Promise { - const glTFResource = context.glTFResource; + parse(context: ParserContext) { + const { meshIndex, subMeshIndex, glTFResource } = context; const { engine, gltf, buffers } = glTFResource; if (!gltf.meshes) return; const meshPromises: Promise[] = []; for (let i = 0; i < gltf.meshes.length; i++) { + if (meshIndex >= 0 && meshIndex !== i) { + continue; + } const gltfMesh = gltf.meshes[i]; const primitivePromises: Promise[] = []; for (let j = 0; j < gltfMesh.primitives.length; j++) { + if (subMeshIndex >= 0 && subMeshIndex !== j) { + continue; + } + const gltfPrimitive = gltfMesh.primitives[j]; const { extensions = {} } = gltfPrimitive; const { KHR_draco_mesh_compression } = extensions; - primitivePromises.push( - new Promise((resolve) => { - const mesh = new ModelMesh(engine, gltfMesh.name || j + ""); - - if (KHR_draco_mesh_compression) { - (>( - Parser.createEngineResource( - "KHR_draco_mesh_compression", - KHR_draco_mesh_compression, - glTFResource, - gltfPrimitive - ) - )) - .then((decodedGeometry: any) => { - return this._parseMeshFromGLTFPrimitive( - mesh, - gltfMesh, - gltfPrimitive, - gltf, - (attributeSemantic) => { - for (let j = 0; j < decodedGeometry.attributes.length; j++) { - if (decodedGeometry.attributes[j].name === attributeSemantic) { - return decodedGeometry.attributes[j].array; - } + primitivePromises[j] = new Promise((resolve) => { + const mesh = new ModelMesh(engine, gltfMesh.name || j + ""); + + if (KHR_draco_mesh_compression) { + (>( + Parser.createEngineResource( + "KHR_draco_mesh_compression", + KHR_draco_mesh_compression, + glTFResource, + gltfPrimitive + ) + )) + .then((decodedGeometry: any) => { + return this._parseMeshFromGLTFPrimitive( + mesh, + gltfMesh, + gltfPrimitive, + gltf, + (attributeSemantic) => { + for (let j = 0; j < decodedGeometry.attributes.length; j++) { + if (decodedGeometry.attributes[j].name === attributeSemantic) { + return decodedGeometry.attributes[j].array; } - return null; - }, - (attributeSemantic, shapeIndex) => { - throw "BlendShape animation is not supported when using draco."; - }, - () => { - return decodedGeometry.index.array; - }, - context.keepMeshData - ); - }) - .then(resolve); - } else { - this._parseMeshFromGLTFPrimitive( - mesh, - gltfMesh, - gltfPrimitive, - gltf, - (attributeSemantic) => { - const accessorIdx = gltfPrimitive.attributes[attributeSemantic]; - const accessor = gltf.accessors[accessorIdx]; - return GLTFUtil.getAccessorData(gltf, accessor, buffers); - }, - (attributeName, shapeIndex) => { - const shapeAccessorIdx = gltfPrimitive.targets[shapeIndex]; - const attributeAccessorIdx = shapeAccessorIdx[attributeName]; - if (attributeAccessorIdx) { - const accessor = gltf.accessors[attributeAccessorIdx]; - return GLTFUtil.getAccessorData(gltf, accessor, buffers); - } else { + } return null; - } - }, - () => { - const indexAccessor = gltf.accessors[gltfPrimitive.indices]; - return GLTFUtil.getAccessorData(gltf, indexAccessor, buffers); - }, - context.keepMeshData - ).then(resolve); - } - }) - ); + }, + (attributeSemantic, shapeIndex) => { + throw "BlendShape animation is not supported when using draco."; + }, + () => { + return decodedGeometry.index.array; + }, + context.keepMeshData + ); + }) + .then(resolve); + } else { + this._parseMeshFromGLTFPrimitive( + mesh, + gltfMesh, + gltfPrimitive, + gltf, + (attributeSemantic) => { + const accessorIdx = gltfPrimitive.attributes[attributeSemantic]; + const accessor = gltf.accessors[accessorIdx]; + return GLTFUtil.getAccessorData(gltf, accessor, buffers); + }, + (attributeName, shapeIndex) => { + const shapeAccessorIdx = gltfPrimitive.targets[shapeIndex]; + const attributeAccessorIdx = shapeAccessorIdx[attributeName]; + if (attributeAccessorIdx) { + const accessor = gltf.accessors[attributeAccessorIdx]; + return GLTFUtil.getAccessorData(gltf, accessor, buffers); + } else { + return null; + } + }, + () => { + const indexAccessor = gltf.accessors[gltfPrimitive.indices]; + return GLTFUtil.getAccessorData(gltf, indexAccessor, buffers); + }, + context.keepMeshData + ).then(resolve); + } + }); } - meshPromises.push(Promise.all(primitivePromises)); + meshPromises[i] = Promise.all(primitivePromises); } return Promise.all(meshPromises).then((meshes: ModelMesh[][]) => { + if (meshIndex >= 0) { + const mesh = meshes[meshIndex]?.[subMeshIndex]; + if (mesh) { + return mesh; + } else { + throw `meshIndex-subMeshIndex index not find in: ${meshIndex}-${subMeshIndex}`; + } + } glTFResource.meshes = meshes; }); } diff --git a/packages/loader/src/gltf/parser/Parser.ts b/packages/loader/src/gltf/parser/Parser.ts index b3e4b4094a..fc44298445 100644 --- a/packages/loader/src/gltf/parser/Parser.ts +++ b/packages/loader/src/gltf/parser/Parser.ts @@ -61,7 +61,7 @@ export abstract class Parser { Parser._extensionParsers[extensionName].push(extensionParser); } - abstract parse(context: ParserContext): void | Promise; + abstract parse(context: ParserContext); } /** diff --git a/packages/loader/src/gltf/parser/ParserContext.ts b/packages/loader/src/gltf/parser/ParserContext.ts index 05cdeb6711..9577f9b35b 100644 --- a/packages/loader/src/gltf/parser/ParserContext.ts +++ b/packages/loader/src/gltf/parser/ParserContext.ts @@ -6,5 +6,12 @@ import { GLTFResource } from "../GLTFResource"; export class ParserContext { glTFResource: GLTFResource; keepMeshData: boolean; - createAnimator: boolean; + hasSkinned: boolean = false; + /** adapter subAsset */ + textureIndex?: number; + materialIndex?: number; + animationIndex?: number; + meshIndex?: number; + subMeshIndex?: number; + defaultSceneRootOnly?: boolean; } diff --git a/packages/loader/src/gltf/parser/SceneParser.ts b/packages/loader/src/gltf/parser/SceneParser.ts index 7e86ee2a39..b7927e4bfe 100644 --- a/packages/loader/src/gltf/parser/SceneParser.ts +++ b/packages/loader/src/gltf/parser/SceneParser.ts @@ -27,8 +27,8 @@ export class SceneParser extends Parser { return SceneParser._defaultMaterial; } - parse(context: ParserContext): void { - const glTFResource = context.glTFResource; + parse(context: ParserContext) { + const { defaultSceneRootOnly, glTFResource } = context; const { gltf: { nodes, cameras: gltfCameras }, entities @@ -36,6 +36,8 @@ export class SceneParser extends Parser { if (!nodes) return; + const promises = []; + for (let i = 0; i < nodes.length; i++) { const gltfNode = nodes[i]; const { camera: cameraID, mesh: meshID, extensions = {} } = gltfNode; @@ -47,7 +49,7 @@ export class SceneParser extends Parser { } if (meshID !== undefined) { - this._createRenderer(context, gltfNode, entity); + promises.push(this._createRenderer(context, gltfNode, entity)); } if (KHR_lights_punctual) { @@ -61,6 +63,18 @@ export class SceneParser extends Parser { if (glTFResource.defaultSceneRoot) { this._createAnimator(context); } + + glTFResource.gltf.extensions && delete glTFResource.gltf.extensions["OASIS_materials_remap"]; + + if (defaultSceneRootOnly) { + if (glTFResource.defaultSceneRoot) { + return glTFResource.defaultSceneRoot; + } else { + throw `defaultSceneRoot is not find in this gltf`; + } + } + + return Promise.all(promises).then(() => null); } private _createCamera(context: GLTFResource, cameraSchema: ICamera, entity: Entity): void { @@ -103,7 +117,7 @@ export class SceneParser extends Parser { camera.enabled = false; } - private _createRenderer(context: ParserContext, gltfNode: INode, entity: Entity): void { + private _createRenderer(context: ParserContext, gltfNode: INode, entity: Entity) { const glTFResource = context.glTFResource; const { engine, @@ -117,12 +131,13 @@ export class SceneParser extends Parser { const gltfMeshPrimitives = glTFMesh.primitives; const blendShapeWeights = gltfNode.weights || glTFMesh.weights; + const promises = []; for (let i = 0; i < gltfMeshPrimitives.length; i++) { const mesh = meshes[meshID][i]; let renderer: MeshRenderer | SkinnedMeshRenderer; if (skinID !== undefined || blendShapeWeights) { - context.createAnimator = true; + context.hasSkinned = true; const skinRenderer = entity.addComponent(SkinnedMeshRenderer); skinRenderer.mesh = mesh; if (skinID !== undefined) { @@ -133,14 +148,22 @@ export class SceneParser extends Parser { } renderer = skinRenderer; } else { - context.createAnimator = false; renderer = entity.addComponent(MeshRenderer); renderer.mesh = mesh; } const materialIndex = gltfMeshPrimitives[i].material; - const material = materials?.[materialIndex] || SceneParser._getDefaultMaterial(engine); - renderer.setMaterial(material); + const remapMaterials = glTFResource.gltf.extensions && glTFResource.gltf.extensions["OASIS_materials_remap"]; + if (remapMaterials && remapMaterials[materialIndex]) { + promises.push( + remapMaterials[materialIndex].then((mtl) => { + renderer.setMaterial(mtl); + }) + ); + } else { + const material = materials?.[materialIndex] || SceneParser._getDefaultMaterial(engine); + renderer.setMaterial(material); + } const { extensions = {} } = gltfMeshPrimitives[i]; const { KHR_materials_variants } = extensions; @@ -148,10 +171,11 @@ export class SceneParser extends Parser { Parser.parseEngineResource("KHR_materials_variants", KHR_materials_variants, renderer, glTFResource); } } + return Promise.all(promises); } private _createAnimator(context: ParserContext): void { - if (!context.createAnimator) { + if (!context.hasSkinned && !context.glTFResource.animations) { return; } diff --git a/packages/loader/src/gltf/parser/TextureParser.ts b/packages/loader/src/gltf/parser/TextureParser.ts index ddfb1c4545..6f42ddae54 100644 --- a/packages/loader/src/gltf/parser/TextureParser.ts +++ b/packages/loader/src/gltf/parser/TextureParser.ts @@ -1,4 +1,5 @@ import { AssetType, Logger, Texture2D, TextureWrapMode } from "@oasis-engine/core"; +import { GLTFResource } from "../GLTFResource"; import { GLTFUtil } from "../GLTFUtil"; import { ISampler } from "../Schema"; import { Parser } from "./Parser"; @@ -11,13 +12,17 @@ export class TextureParser extends Parser { 10497: TextureWrapMode.Repeat }; - parse(context: ParserContext): void | Promise { - const glTFResource = context.glTFResource; + parse(context: ParserContext) { + const { textureIndex, glTFResource } = context; const { gltf, buffers, engine, url } = glTFResource; if (gltf.textures) { return Promise.all( gltf.textures.map(({ sampler, source = 0, name: textureName }, index) => { + if (textureIndex >= 0 && textureIndex !== index) { + return; + } + const { uri, bufferView: bufferViewIndex, mimeType, name: imageName } = gltf.images[source]; if (uri) { @@ -51,6 +56,14 @@ export class TextureParser extends Parser { } }) ).then((textures: Texture2D[]) => { + if (textureIndex >= 0) { + const texture = textures[textureIndex]; + if (texture) { + return texture; + } else { + throw `texture index not find in: ${textureIndex}`; + } + } glTFResource.textures = textures; }); } diff --git a/packages/oasis-engine/package.json b/packages/oasis-engine/package.json old mode 100755 new mode 100644 diff --git a/packages/rhi-webgl/package.json b/packages/rhi-webgl/package.json old mode 100755 new mode 100644