Skip to content

Commit

Permalink
Merge branch 'feat/shaderloader' into feat/editor_module
Browse files Browse the repository at this point in the history
  • Loading branch information
Sway007 committed Nov 5, 2024
2 parents d15921c + c0e2a60 commit f755ebb
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 71 deletions.
6 changes: 4 additions & 2 deletions packages/core/src/RenderPipeline/BasicRenderPipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ export class BasicRenderPipeline {
if (background.mode === BackgroundMode.Sky) {
background.sky._render(context);
} else if (background.mode === BackgroundMode.Texture && background.texture) {
this._drawBackgroundTexture(engine, background);
this._drawBackgroundTexture(camera, background);
}
}

Expand Down Expand Up @@ -307,7 +307,8 @@ export class BasicRenderPipeline {
}
}

private _drawBackgroundTexture(engine: Engine, background: Background) {
private _drawBackgroundTexture(camera: Camera, background: Background) {
const engine = camera.engine;
const rhi = engine._hardwareRenderer;
const { canvas } = engine;
const { _material: material, _mesh: mesh } = background;
Expand All @@ -324,6 +325,7 @@ export class BasicRenderPipeline {
const program = pass._getShaderProgram(engine, Shader._compileMacros);
program.bind();
program.uploadAll(program.materialUniformBlock, material.shaderData);
program.uploadAll(program.cameraUniformBlock, camera.shaderData);
program.uploadUnGroupTextures();

(pass._renderState || material.renderState)._applyStates(
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/asset/AssetType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ export enum AssetType {
TextureCube = "TextureCube",
/** Material. */
Material = "Material",
/** Shader. */
Shader = "Shader",
/** Mesh. */
Mesh = "Mesh",
/** AnimationClip. */
Expand Down
4 changes: 4 additions & 0 deletions packages/core/src/asset/LoadItem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export type LoadItem = {
* Additional parameters for specified loader.
*/
params?: Record<string, any>;
/**
* Asset path in editor
*/
virtualPath?: string;
} & PickOnlyOne<{
/**
* Loading url.
Expand Down
8 changes: 5 additions & 3 deletions packages/core/src/asset/ResourceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,7 @@ export class ResourceManager {

// Check url mapping
const itemURL = item.url;
let url = this._virtualPathMap[itemURL] ? this._virtualPathMap[itemURL] : itemURL;
let url = this._virtualPathMap[itemURL] ?? itemURL;

// Not absolute and base url is set
if (!Utils.isAbsoluteUrl(url) && this.baseUrl) url = Utils.resolveAbsoluteUrl(this.baseUrl, url);
Expand Down Expand Up @@ -537,15 +537,17 @@ export class ResourceManager {
if (obj) {
promise = Promise.resolve(obj);
} else {
let url = this._editorResourceConfig[refId]?.path;
const resourceConfig = this._editorResourceConfig[refId];
let url = resourceConfig?.path;
if (!url) {
Logger.warn(`refId:${refId} is not find in this._editorResourceConfig.`);
return Promise.resolve(null);
}
url = key ? `${url}${url.indexOf("?") > -1 ? "&" : "?"}q=${key}` : url;
promise = this.load<any>({
url,
type: this._editorResourceConfig[refId].type
virtualPath: resourceConfig.virtualPath,
type: resourceConfig.type
});
}
return promise.then((item) => (isClone ? item.clone() : item));
Expand Down
4 changes: 3 additions & 1 deletion packages/core/src/shaderlib/extra/background-texture.vs.glsl
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
attribute vec3 POSITION;
attribute vec2 TEXCOORD_0;

varying vec2 v_uv;
uniform vec4 camera_ProjectionParams;

void main() {
gl_Position = vec4(POSITION, 1.0);
gl_Position.y *= camera_ProjectionParams.x;

v_uv = TEXCOORD_0;
}
153 changes: 90 additions & 63 deletions packages/loader/src/MaterialLoader.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {
AssetPromise,
AssetType,
Engine,
LoadItem,
Loader,
Material,
Expand All @@ -10,7 +11,15 @@ import {
resourceLoader
} from "@galacean/engine-core";
import { Color, Vector2, Vector3, Vector4 } from "@galacean/engine-math";
import type { IAssetRef, IColor, IMaterialSchema, IVector2, IVector3, IVector4 } from "./resource-deserialize";
import {
MaterialLoaderType,
type IAssetRef,
type IColor,
type IMaterialSchema,
type IVector2,
type IVector3,
type IVector4
} from "./resource-deserialize";

function parseProperty(object: Object, key: string, value: any) {
if (typeof value === "object") {
Expand All @@ -32,73 +41,91 @@ class MaterialLoader extends Loader<Material> {
})
.then((materialSchema: IMaterialSchema) => {
const engine = resourceManager.engine;
const { name, shader, shaderData, macros, renderState } = materialSchema;
const material = new Material(engine, Shader.find(shader));
material.name = name;

const texturePromises = new Array<Promise<Texture2D>>();
const materialShaderData = material.shaderData;
for (let key in shaderData) {
const { type, value } = shaderData[key];
const { shaderRef, shader } = materialSchema;

switch (type) {
case "Vector2":
materialShaderData.setVector2(key, new Vector2((<IVector2>value).x, (<IVector2>value).y));
break;
case "Vector3":
materialShaderData.setVector3(
key,
new Vector3((<IVector3>value).x, (<IVector3>value).y, (<IVector3>value).z)
);
break;
case "Vector4":
materialShaderData.setVector4(
key,
new Vector4((<IVector4>value).x, (<IVector4>value).y, (<IVector4>value).z, (<IVector4>value).w)
);
break;
case "Color":
materialShaderData.setColor(
key,
new Color((<IColor>value).r, (<IColor>value).g, (<IColor>value).b, (<IColor>value).a)
);
break;
case "Float":
materialShaderData.setFloat(key, <number>value);
break;
case "Texture":
texturePromises.push(
// @ts-ignore
resourceManager.getResourceByRef<Texture2D>(<IAssetRef>value).then((texture) => {
materialShaderData.setTexture(key, texture);
})
);
break;
case "Boolean":
materialShaderData.setInt(key, value ? 1 : 0);
break;
case "Integer":
materialShaderData.setInt(key, Number(value));
break;
}
if (shaderRef) {
resolve(
resourceManager
// @ts-ignore
.getResourceByRef<Shader>(<IAssetRef>shaderRef)
.then((shaderObject) => this.getMaterialByShader(materialSchema, shaderObject, engine))
);
} else {
// compatible with 1.2-pre version material schema
const shaderObject = Shader.find(shader);
resolve(this.getMaterialByShader(materialSchema, shaderObject, engine));
}
})
.catch(reject);
});
}

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);
}
}
private getMaterialByShader(materialSchema: IMaterialSchema, shader: Shader, engine: Engine): Promise<Material> {
const { name, shaderData, macros, renderState } = materialSchema;

parseProperty(material, "renderState", renderState);
const material = new Material(engine, shader);
material.name = name;

return Promise.all(texturePromises).then(() => {
resolve(material);
});
})
.catch(reject);
const texturePromises = new Array<Promise<Texture2D>>();
const materialShaderData = material.shaderData;
for (let key in shaderData) {
const { type, value } = shaderData[key];

switch (type) {
case MaterialLoaderType.Vector2:
materialShaderData.setVector2(key, new Vector2((<IVector2>value).x, (<IVector2>value).y));
break;
case MaterialLoaderType.Vector3:
materialShaderData.setVector3(
key,
new Vector3((<IVector3>value).x, (<IVector3>value).y, (<IVector3>value).z)
);
break;
case MaterialLoaderType.Vector4:
materialShaderData.setVector4(
key,
new Vector4((<IVector4>value).x, (<IVector4>value).y, (<IVector4>value).z, (<IVector4>value).w)
);
break;
case MaterialLoaderType.Color:
materialShaderData.setColor(
key,
new Color((<IColor>value).r, (<IColor>value).g, (<IColor>value).b, (<IColor>value).a)
);
break;
case MaterialLoaderType.Float:
materialShaderData.setFloat(key, <number>value);
break;
case MaterialLoaderType.Texture:
texturePromises.push(
// @ts-ignore
engine.resourceManager.getResourceByRef<Texture2D>(<IAssetRef>value).then((texture) => {
materialShaderData.setTexture(key, texture);
})
);
break;
case MaterialLoaderType.Boolean:
materialShaderData.setInt(key, value ? 1 : 0);
break;
case MaterialLoaderType.Integer:
materialShaderData.setInt(key, Number(value));
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);
}
}

parseProperty(material, "renderState", renderState);

return Promise.all(texturePromises).then(() => {
return material;
});
}
}
13 changes: 13 additions & 0 deletions packages/loader/src/PathUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/** @internal */
export class PathUtils {
private static _urlSchema = "files://";
static shaderIncludeRegex = /\s#include\s+"([^\\"]+)"/gm;

static pathResolve(path: string, base: string): string {
return new URL(path, PathUtils._urlSchema + base).href.substring(PathUtils._urlSchema.length);
}

static isRelativePath(path: string): boolean {
return path[0] === ".";
}
}
53 changes: 53 additions & 0 deletions packages/loader/src/ShaderChunkLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
AssetPromise,
LoadItem,
Loader,
ResourceManager,
ShaderFactory,
resourceLoader,
// @ts-ignore
ShaderLib
} from "@galacean/engine-core";
import { PathUtils } from "./PathUtils";

@resourceLoader("ShaderChunk", ["glsl"])
class ShaderChunkLoader extends Loader<void[]> {
load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<void[]> {
const { virtualPath, url } = item;
const shaderVirtualPath = item.params?.shaderVirtualPath ?? "/";
const chunkPath = virtualPath ?? new URL(url).pathname;

return this.request<string>(url, { ...item, type: "text" }).then((code: string) => {
ShaderFactory.registerInclude(chunkPath.substring(1), code);

return _loadChunksInCode(code, shaderVirtualPath, resourceManager);
});
}
}

/** @internal */
export function _loadChunksInCode(
code: string,
shaderVirtualPath: string,
resourceManager: ResourceManager
): Promise<void[]> {
const shaderChunkPaths: string[] = [];
const matches = code.matchAll(PathUtils.shaderIncludeRegex);
for (const match of matches) {
const chunkPath = PathUtils.pathResolve(match[1], shaderVirtualPath);
if (!ShaderLib[chunkPath.substring(1)]) {
shaderChunkPaths.push(chunkPath);
}
}

return Promise.all(
shaderChunkPaths.map((chunkPath) => {
return resourceManager.load<void>({
type: "ShaderChunk",
url: chunkPath,
virtualPath: chunkPath,
params: { shaderVirtualPath }
});
})
);
}
37 changes: 37 additions & 0 deletions packages/loader/src/ShaderLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import {
AssetPromise,
AssetType,
LoadItem,
Loader,
ResourceManager,
Shader,
resourceLoader
} from "@galacean/engine-core";
import { _loadChunksInCode } from "./ShaderChunkLoader";

@resourceLoader(AssetType.Shader, ["gs", "gsl"])
class ShaderLoader extends Loader<Shader> {
private static _builtinRegex = /^\s*\/\/\s*@builtin\s+(\w+)/;

load(item: LoadItem, resourceManager: ResourceManager): AssetPromise<Shader> {
const { virtualPath, url } = item;
const shaderVirtualPath = virtualPath ?? "/";

return this.request<string>(url, { ...item, type: "text" }).then((code: string) => {
const builtinShader = this.getBuiltinShader(code);
// TODO: delete the snippets below when breaking change version released
if (builtinShader) {
return Shader.find(builtinShader);
}

return _loadChunksInCode(code, shaderVirtualPath, resourceManager).then(() => {
return Shader.create(code);
});
});
}

private getBuiltinShader(code: string) {
const match = code.match(ShaderLoader._builtinRegex);
if (match && match[1]) return match[1];
}
}
2 changes: 2 additions & 0 deletions packages/loader/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import "./SpriteLoader";
import "./Texture2DLoader";
import "./TextureCubeLoader";
import "./ktx2/KTX2Loader";
import "./ShaderLoader";
import "./ShaderChunkLoader";

export { GLTFLoader } from "./GLTFLoader";
export type { GLTFParams } from "./GLTFLoader";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,5 @@ export type IBasicType =
export type IAssetRef = { key?: string; refId: string };

export type IEntityRef = { entityId: string };

export type IShaderRef = Omit<IAssetRef, "key">;
Loading

0 comments on commit f755ebb

Please sign in to comment.