Skip to content

Commit 61ce060

Browse files
mvaligurskyMartin Valigurskywilleastcott
authored
Unified handling of core shader generation defines (playcanvas#7298)
* Unified handling of core shader generation defines * lint + thumbnails * examples lint * Update scripts/esm/camera-frame.mjs Co-authored-by: Will Eastcott <will@playcanvas.com> --------- Co-authored-by: Martin Valigursky <mvaligursky@snapchat.com> Co-authored-by: Will Eastcott <will@playcanvas.com>
1 parent 43984b1 commit 61ce060

39 files changed

+512
-273
lines changed
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as pc from 'playcanvas';
2+
3+
/**
4+
* @param {import('../../app/components/Example.mjs').ControlOptions} options - The options.
5+
* @returns {JSX.Element} The returned JSX Element.
6+
*/
7+
export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
8+
const { BindingTwoWay, BooleanInput, LabelGroup, Panel, SelectInput } = ReactPCUI;
9+
return fragment(
10+
jsx(
11+
Panel,
12+
{ headerText: 'Settings' },
13+
jsx(
14+
LabelGroup,
15+
{ text: 'Tonemapping' },
16+
jsx(SelectInput, {
17+
binding: new BindingTwoWay(),
18+
link: { observer, path: 'data.tonemapping' },
19+
type: 'number',
20+
options: [
21+
{ v: pc.TONEMAP_LINEAR, t: 'LINEAR' },
22+
{ v: pc.TONEMAP_FILMIC, t: 'FILMIC' },
23+
{ v: pc.TONEMAP_HEJL, t: 'HEJL' },
24+
{ v: pc.TONEMAP_ACES, t: 'ACES' },
25+
{ v: pc.TONEMAP_ACES2, t: 'ACES2' },
26+
{ v: pc.TONEMAP_NEUTRAL, t: 'NEUTRAL' }
27+
]
28+
})
29+
),
30+
jsx(
31+
LabelGroup,
32+
{ text: 'Fog' },
33+
jsx(SelectInput, {
34+
binding: new BindingTwoWay(),
35+
link: { observer, path: 'data.fog' },
36+
type: 'string',
37+
options: [
38+
{ v: pc.FOG_NONE, t: 'NONE' },
39+
{ v: pc.FOG_LINEAR, t: 'LINEAR' },
40+
{ v: pc.FOG_EXP, t: 'EXP' },
41+
{ v: pc.FOG_EXP2, t: 'EXP2' }
42+
]
43+
})
44+
),
45+
jsx(
46+
LabelGroup,
47+
{ text: 'Gamma' },
48+
jsx(BooleanInput, {
49+
type: 'toggle',
50+
binding: new BindingTwoWay(),
51+
link: { observer, path: 'data.gamma' }
52+
})
53+
)
54+
)
55+
);
56+
};
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// @config HIDDEN
2+
import { data } from 'examples/observer';
3+
import { deviceType, rootPath, fileImport } from 'examples/utils';
4+
import * as pc from 'playcanvas';
5+
const { createGoochMaterial } = await fileImport(`${rootPath}/static/assets/scripts/misc/gooch-material.mjs`);
6+
7+
const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas'));
8+
window.focus();
9+
10+
const assets = {
11+
script: new pc.Asset('script', 'script', { url: `${rootPath}/static/scripts/camera/orbit-camera.js` }),
12+
terrain: new pc.Asset('terrain', 'container', { url: `${rootPath}/static/assets/models/terrain.glb` }),
13+
biker: new pc.Asset('gsplat', 'gsplat', { url: `${rootPath}/static/assets/splats/biker.ply` }),
14+
helipad: new pc.Asset(
15+
'helipad-env-atlas',
16+
'texture',
17+
{ url: `${rootPath}/static/assets/cubemaps/table-mountain-env-atlas.png` },
18+
{ type: pc.TEXTURETYPE_RGBP, mipmaps: false }
19+
)
20+
};
21+
22+
const gfxOptions = {
23+
deviceTypes: [deviceType],
24+
glslangUrl: `${rootPath}/static/lib/glslang/glslang.js`,
25+
twgslUrl: `${rootPath}/static/lib/twgsl/twgsl.js`
26+
};
27+
28+
const device = await pc.createGraphicsDevice(canvas, gfxOptions);
29+
device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);
30+
31+
const createOptions = new pc.AppOptions();
32+
createOptions.graphicsDevice = device;
33+
createOptions.mouse = new pc.Mouse(document.body);
34+
createOptions.touch = new pc.TouchDevice(document.body);
35+
36+
createOptions.componentSystems = [
37+
pc.RenderComponentSystem,
38+
pc.CameraComponentSystem,
39+
pc.LightComponentSystem,
40+
pc.ScriptComponentSystem,
41+
pc.GSplatComponentSystem,
42+
pc.ParticleSystemComponentSystem
43+
];
44+
createOptions.resourceHandlers = [
45+
pc.TextureHandler,
46+
pc.ContainerHandler,
47+
pc.ScriptHandler,
48+
pc.GSplatHandler
49+
];
50+
51+
const app = new pc.AppBase(canvas);
52+
app.init(createOptions);
53+
54+
const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets);
55+
assetListLoader.load(() => {
56+
app.start();
57+
58+
// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
59+
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
60+
app.setCanvasResolution(pc.RESOLUTION_AUTO);
61+
62+
// Ensure canvas is resized when window changes size
63+
const resize = () => app.resizeCanvas();
64+
window.addEventListener('resize', resize);
65+
app.on('destroy', () => {
66+
window.removeEventListener('resize', resize);
67+
});
68+
69+
// setup skydome
70+
app.scene.skyboxMip = 0;
71+
app.scene.envAtlas = assets.helipad.resource;
72+
app.scene.skyboxRotation = new pc.Quat().setFromEulerAngles(0, -70, 0);
73+
74+
// for params
75+
app.scene.fog.color = new pc.Color(0.8, 0.8, 0.8);
76+
app.scene.fog.start = 400;
77+
app.scene.fog.end = 800;
78+
app.scene.fog.density = 0.001;
79+
80+
// STANDARD MATERIAL ----------
81+
82+
/** @type {pc.Entity} */
83+
const terrain = assets.terrain.resource.instantiateRenderEntity();
84+
terrain.setLocalScale(30, 30, 30);
85+
app.root.addChild(terrain);
86+
87+
// GSPLAT MATERIAL ----------
88+
89+
const biker = new pc.Entity();
90+
biker.addComponent('gsplat', {
91+
asset: assets.biker
92+
});
93+
biker.setLocalPosition(0, 0, 150);
94+
biker.setLocalEulerAngles(180, 90, 0);
95+
biker.setLocalScale(20, 20, 20);
96+
app.root.addChild(biker);
97+
98+
// SHADER MATERIAL ----------
99+
100+
const box = new pc.Entity('ShaderMaterial');
101+
const boxMaterial = createGoochMaterial(null, [0.13, 0.55, 0.13]);
102+
box.addComponent('render', {
103+
type: 'box',
104+
material: boxMaterial
105+
});
106+
box.setLocalScale(30, 30, 30);
107+
box.setLocalPosition(-70, 30, 130);
108+
app.root.addChild(box);
109+
110+
// LIT MATERIAL ----------
111+
112+
const material = new pc.LitMaterial();
113+
material.setParameter('texture_envAtlas', assets.helipad.resource);
114+
material.setParameter('material_reflectivity', 1.0);
115+
material.useSkybox = true;
116+
material.hasSpecular = true;
117+
material.hasSpecularityFactor = true;
118+
material.hasNormals = true;
119+
material.hasMetalness = true;
120+
material.occludeSpecular = pc.SPECOCC_AO;
121+
122+
const argumentsChunk = `
123+
void evaluateFrontend() {
124+
litArgs_emission = vec3(0.7, 0.4, 0);
125+
litArgs_metalness = 0.5;
126+
litArgs_specularity = vec3(0.5, 0.5, 0.5);
127+
litArgs_specularityFactor = 1.0;
128+
litArgs_gloss = 0.5;
129+
litArgs_ior = 0.1;
130+
litArgs_ao = 0.0;
131+
litArgs_opacity = 1.0;
132+
}`;
133+
material.shaderChunk = argumentsChunk;
134+
material.update();
135+
136+
// create primitive
137+
const primitive = new pc.Entity();
138+
primitive.addComponent('render', {
139+
type: 'sphere',
140+
material: material
141+
});
142+
143+
primitive.setLocalScale(30, 30, 30);
144+
primitive.setLocalPosition(-170, 30, 130);
145+
app.root.addChild(primitive);
146+
147+
// PARTICLE SYSTEM ----------
148+
149+
const localVelocityCurve = new pc.CurveSet([
150+
[0, 0, 0.5, 30],
151+
[0, 0, 0.5, 30],
152+
[0, 0, 0.5, 30]
153+
]);
154+
const localVelocityCurve2 = new pc.CurveSet([
155+
[0, 0, 0.5, -30],
156+
[0, 0, 0.5, -30],
157+
[0, 0, 0.5, -30]
158+
]);
159+
const worldVelocityCurve = new pc.CurveSet([
160+
[0, 0],
161+
[0, 0, 0.2, 6, 1, 300],
162+
[0, 0]
163+
]);
164+
165+
// Create entity for particle system
166+
const entity = new pc.Entity('ParticleSystem');
167+
app.root.addChild(entity);
168+
entity.setLocalPosition(0, 20, 0);
169+
170+
// add particlesystem component to entity
171+
entity.addComponent('particlesystem', {
172+
numParticles: 200,
173+
lifetime: 1,
174+
rate: 0.01,
175+
scaleGraph: new pc.Curve([0, 10]),
176+
velocityGraph: worldVelocityCurve,
177+
localVelocityGraph: localVelocityCurve,
178+
localVelocityGraph2: localVelocityCurve2,
179+
colorGraph: new pc.CurveSet([
180+
[0, 1, 0.25, 1],
181+
[0, 0, 0.25, 0.3],
182+
[0, 0, 1, 0]
183+
])
184+
});
185+
186+
// --------
187+
188+
// create an Entity with a camera component
189+
const camera = new pc.Entity();
190+
camera.addComponent('camera', {
191+
clearColor: new pc.Color(0.9, 0.9, 0.9),
192+
farClip: 1000,
193+
toneMapping: pc.TONEMAP_ACES
194+
});
195+
196+
// and position it in the world
197+
camera.setLocalPosition(-500, 60, 300);
198+
199+
// add orbit camera script with a mouse and a touch support
200+
camera.addComponent('script');
201+
camera.script.create('orbitCamera', {
202+
attributes: {
203+
inertiaFactor: 0.2,
204+
distanceMax: 500
205+
}
206+
});
207+
camera.script.create('orbitCameraInputMouse');
208+
camera.script.create('orbitCameraInputTouch');
209+
app.root.addChild(camera);
210+
211+
// Create a directional light casting soft shadows
212+
const dirLight = new pc.Entity('Cascaded Light');
213+
dirLight.addComponent('light', {
214+
type: 'directional',
215+
color: pc.Color.WHITE,
216+
shadowBias: 0.3,
217+
normalOffsetBias: 0.2,
218+
intensity: 1.0,
219+
220+
// enable shadow casting
221+
castShadows: true,
222+
shadowType: pc.SHADOW_PCF3_32F,
223+
shadowDistance: 1000,
224+
shadowResolution: 2048
225+
});
226+
app.root.addChild(dirLight);
227+
dirLight.setLocalEulerAngles(75, 120, 20);
228+
229+
// handle HUD changes
230+
data.on('*:set', (path, value) => {
231+
const propertyName = path.split('.')[1];
232+
if (propertyName === 'tonemapping') {
233+
// set up selected tone-mapping
234+
camera.camera.toneMapping = value;
235+
}
236+
if (propertyName === 'fog') {
237+
app.scene.fog.type = value;
238+
}
239+
if (propertyName === 'gamma') {
240+
camera.camera.gammaCorrection = value ? pc.GAMMA_SRGB : pc.GAMMA_NONE;
241+
}
242+
});
243+
244+
// initial values
245+
data.set('data', {
246+
tonemapping: pc.TONEMAP_ACES,
247+
fog: pc.FOG_LINEAR,
248+
gamma: true
249+
});
250+
});
251+
252+
export { app };
5.05 KB
Loading
714 Bytes
Loading

scripts/esm/camera-frame.mjs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Camera Frame v 1.0
22

3-
/* eslint-disable-next-line import/no-unresolved */
43
import { CameraFrame as EngineCameraFrame, Script, Color } from 'playcanvas';
54

65
/** @enum {number} */

scripts/utils/cubemap-renderer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,9 @@ CubemapRenderer.prototype.initialize = function () {
100100
farClip: camera.farClip,
101101
nearClip: camera.nearClip,
102102
frustumCulling: camera.frustumCulling,
103+
gammaCorrection: camera.gammaCorrection,
104+
toneMapping: camera.toneMapping,
105+
fog: camera.fog,
103106

104107
// this camera renders into texture target
105108
renderTarget: renderTarget

src/core/preprocessor.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const INCLUDE = /include[ \t]+"([\w-]+)"\r?(?:\n|$)/g;
4444
* inspired by: https://github.com/dcodeIO/Preprocessor.js
4545
*/
4646
class Preprocessor {
47+
static sourceName;
48+
4749
/**
4850
* Run c-like preprocessor on the source code, and resolves the code based on the defines and ifdefs
4951
*
@@ -53,10 +55,13 @@ class Preprocessor {
5355
* @param {object} [options] - Optional parameters.
5456
* @param {boolean} [options.stripUnusedColorAttachments] - If true, strips unused color attachments.
5557
* @param {boolean} [options.stripDefines] - If true, strips all defines from the source.
58+
* @param {string} [options.sourceName] - The name of the source file.
5659
* @returns {string|null} Returns preprocessed source code, or null in case of error.
5760
*/
5861
static run(source, includes = new Map(), options = {}) {
5962

63+
Preprocessor.sourceName = options.sourceName;
64+
6065
// strips comments, handles // and many cases of /*
6166
source = this.stripComments(source);
6267

@@ -364,7 +369,7 @@ class Preprocessor {
364369
// process the just included test
365370
KEYWORD.lastIndex = include.index;
366371
} else {
367-
console.error(`Include "${identifier}" not resolved while preprocessing a shader`, { source: originalSource });
372+
console.error(`Include "${identifier}" not resolved while preprocessing ${Preprocessor.sourceName}`, { source: originalSource });
368373
error = true;
369374
}
370375
}

0 commit comments

Comments
 (0)