Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat add loader for gltf subasset #1071 #1075

Merged
merged 12 commits into from
Oct 17, 2022
2 changes: 1 addition & 1 deletion packages/core/src/animation/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Empty file modified packages/loader/package.json
100755 → 100644
Empty file.
42 changes: 39 additions & 3 deletions packages/loader/src/GLTFLoader.ts
Original file line number Diff line number Diff line change
@@ -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"])
Expand All @@ -9,11 +10,46 @@ export class GLTFLoader extends Loader<GLTFResource> {
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) => {
Expand Down
15 changes: 10 additions & 5 deletions packages/loader/src/MaterialLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ class MaterialLoader extends Loader<string> {
break;
}

const materialShaderData: ShaderData = material.shaderData;
const texturePromises = new Array<Promise<Texture2D | void>>();
const materialShaderData = material.shaderData;
for (let key in shaderData) {
const { type, value } = shaderData[key];

Expand All @@ -62,9 +63,11 @@ class MaterialLoader extends Loader<string> {
materialShaderData.setFloat(key, value);
break;
case "Texture":
resourceManager.getResourceByRef<Texture2D>(value).then((texture) => {
materialShaderData.setTexture(key, texture);
});
texturePromises.push(
resourceManager.getResourceByRef<Texture2D>(value).then((texture) => {
materialShaderData.setTexture(key, texture);
})
);
break;
}
}
Expand All @@ -82,7 +85,9 @@ class MaterialLoader extends Loader<string> {
materialShaderData[key] = renderState[key];
}

resolve(material);
Promise.all(texturePromises).then(() => {
resolve(material);
});
});
});
}
Expand Down
13 changes: 9 additions & 4 deletions packages/loader/src/gltf/GLTFParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)[]) {
Expand All @@ -34,7 +39,7 @@ export class GLTFParser {

parse(context: ParserContext): Promise<GLTFResource> {
const glTFResource = context.glTFResource;
let lastPipe: void | Promise<void>;
let lastPipe;

return new Promise((resolve, reject) => {
this._pipes.forEach((parser: Parser) => {
Expand All @@ -49,8 +54,8 @@ export class GLTFParser {

if (lastPipe) {
lastPipe
.then(() => {
resolve(glTFResource);
.then((customRes) => {
resolve(customRes || glTFResource);
})
.catch(reject);
} else {
Expand Down
1 change: 0 additions & 1 deletion packages/loader/src/gltf/GLTFResource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] }[];

}
49 changes: 49 additions & 0 deletions packages/loader/src/gltf/GLTFUtil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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.
*/
Expand Down
13 changes: 13 additions & 0 deletions packages/loader/src/gltf/extensions/OASIS_materials_remap.ts
Original file line number Diff line number Diff line change
@@ -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<Material> {
const { engine } = context;
return engine.resourceManager.getResourceByRef<Material>(schema);
}
}
6 changes: 6 additions & 0 deletions packages/loader/src/gltf/extensions/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions packages/loader/src/gltf/extensions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ import "./KHR_materials_volume";
import "./KHR_mesh_quantization";
import "./KHR_texture_basisu";
import "./KHR_texture_transform";
import "./OASIS_materials_remap";
18 changes: 16 additions & 2 deletions packages/loader/src/gltf/parser/AnimationParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand Down Expand Up @@ -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(
Expand Down
3 changes: 2 additions & 1 deletion packages/loader/src/gltf/parser/BufferParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}
}
34 changes: 31 additions & 3 deletions packages/loader/src/gltf/parser/MaterialParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,18 @@ 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;

const materials: Material[] = [];

for (let i = 0; i < gltf.materials.length; i++) {
if (materialIndex >= 0 && materialIndex !== i) {
continue;
}
const {
extensions = {},
pbrMetallicRoughness,
Expand All @@ -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;

Expand Down Expand Up @@ -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 {
Expand All @@ -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;
}
}
Loading