Skip to content

Commit

Permalink
Merge branch 'main' into fix-grid-update
Browse files Browse the repository at this point in the history
  • Loading branch information
marklundin authored Jan 20, 2025
2 parents 6a41063 + 68db2b1 commit 112007d
Show file tree
Hide file tree
Showing 40 changed files with 514 additions and 275 deletions.
56 changes: 56 additions & 0 deletions examples/src/examples/test/global-shader-properties.controls.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import * as pc from 'playcanvas';

/**
* @param {import('../../app/components/Example.mjs').ControlOptions} options - The options.
* @returns {JSX.Element} The returned JSX Element.
*/
export const controls = ({ observer, ReactPCUI, React, jsx, fragment }) => {
const { BindingTwoWay, BooleanInput, LabelGroup, Panel, SelectInput } = ReactPCUI;
return fragment(
jsx(
Panel,
{ headerText: 'Settings' },
jsx(
LabelGroup,
{ text: 'Tonemapping' },
jsx(SelectInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'data.tonemapping' },
type: 'number',
options: [
{ v: pc.TONEMAP_LINEAR, t: 'LINEAR' },
{ v: pc.TONEMAP_FILMIC, t: 'FILMIC' },
{ v: pc.TONEMAP_HEJL, t: 'HEJL' },
{ v: pc.TONEMAP_ACES, t: 'ACES' },
{ v: pc.TONEMAP_ACES2, t: 'ACES2' },
{ v: pc.TONEMAP_NEUTRAL, t: 'NEUTRAL' }
]
})
),
jsx(
LabelGroup,
{ text: 'Fog' },
jsx(SelectInput, {
binding: new BindingTwoWay(),
link: { observer, path: 'data.fog' },
type: 'string',
options: [
{ v: pc.FOG_NONE, t: 'NONE' },
{ v: pc.FOG_LINEAR, t: 'LINEAR' },
{ v: pc.FOG_EXP, t: 'EXP' },
{ v: pc.FOG_EXP2, t: 'EXP2' }
]
})
),
jsx(
LabelGroup,
{ text: 'Gamma' },
jsx(BooleanInput, {
type: 'toggle',
binding: new BindingTwoWay(),
link: { observer, path: 'data.gamma' }
})
)
)
);
};
252 changes: 252 additions & 0 deletions examples/src/examples/test/global-shader-properties.example.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
// @config HIDDEN
import { data } from 'examples/observer';
import { deviceType, rootPath, fileImport } from 'examples/utils';
import * as pc from 'playcanvas';
const { createGoochMaterial } = await fileImport(`${rootPath}/static/assets/scripts/misc/gooch-material.mjs`);

const canvas = /** @type {HTMLCanvasElement} */ (document.getElementById('application-canvas'));
window.focus();

const assets = {
script: new pc.Asset('script', 'script', { url: `${rootPath}/static/scripts/camera/orbit-camera.js` }),
terrain: new pc.Asset('terrain', 'container', { url: `${rootPath}/static/assets/models/terrain.glb` }),
biker: new pc.Asset('gsplat', 'gsplat', { url: `${rootPath}/static/assets/splats/biker.ply` }),
helipad: new pc.Asset(
'helipad-env-atlas',
'texture',
{ url: `${rootPath}/static/assets/cubemaps/table-mountain-env-atlas.png` },
{ type: pc.TEXTURETYPE_RGBP, mipmaps: false }
)
};

const gfxOptions = {
deviceTypes: [deviceType],
glslangUrl: `${rootPath}/static/lib/glslang/glslang.js`,
twgslUrl: `${rootPath}/static/lib/twgsl/twgsl.js`
};

const device = await pc.createGraphicsDevice(canvas, gfxOptions);
device.maxPixelRatio = Math.min(window.devicePixelRatio, 2);

const createOptions = new pc.AppOptions();
createOptions.graphicsDevice = device;
createOptions.mouse = new pc.Mouse(document.body);
createOptions.touch = new pc.TouchDevice(document.body);

createOptions.componentSystems = [
pc.RenderComponentSystem,
pc.CameraComponentSystem,
pc.LightComponentSystem,
pc.ScriptComponentSystem,
pc.GSplatComponentSystem,
pc.ParticleSystemComponentSystem
];
createOptions.resourceHandlers = [
pc.TextureHandler,
pc.ContainerHandler,
pc.ScriptHandler,
pc.GSplatHandler
];

const app = new pc.AppBase(canvas);
app.init(createOptions);

const assetListLoader = new pc.AssetListLoader(Object.values(assets), app.assets);
assetListLoader.load(() => {
app.start();

// Set the canvas to fill the window and automatically change resolution to be the same as the canvas size
app.setCanvasFillMode(pc.FILLMODE_FILL_WINDOW);
app.setCanvasResolution(pc.RESOLUTION_AUTO);

// Ensure canvas is resized when window changes size
const resize = () => app.resizeCanvas();
window.addEventListener('resize', resize);
app.on('destroy', () => {
window.removeEventListener('resize', resize);
});

// setup skydome
app.scene.skyboxMip = 0;
app.scene.envAtlas = assets.helipad.resource;
app.scene.skyboxRotation = new pc.Quat().setFromEulerAngles(0, -70, 0);

// for params
app.scene.fog.color = new pc.Color(0.8, 0.8, 0.8);
app.scene.fog.start = 400;
app.scene.fog.end = 800;
app.scene.fog.density = 0.001;

// STANDARD MATERIAL ----------

/** @type {pc.Entity} */
const terrain = assets.terrain.resource.instantiateRenderEntity();
terrain.setLocalScale(30, 30, 30);
app.root.addChild(terrain);

// GSPLAT MATERIAL ----------

const biker = new pc.Entity();
biker.addComponent('gsplat', {
asset: assets.biker
});
biker.setLocalPosition(0, 0, 150);
biker.setLocalEulerAngles(180, 90, 0);
biker.setLocalScale(20, 20, 20);
app.root.addChild(biker);

// SHADER MATERIAL ----------

const box = new pc.Entity('ShaderMaterial');
const boxMaterial = createGoochMaterial(null, [0.13, 0.55, 0.13]);
box.addComponent('render', {
type: 'box',
material: boxMaterial
});
box.setLocalScale(30, 30, 30);
box.setLocalPosition(-70, 30, 130);
app.root.addChild(box);

// LIT MATERIAL ----------

const material = new pc.LitMaterial();
material.setParameter('texture_envAtlas', assets.helipad.resource);
material.setParameter('material_reflectivity', 1.0);
material.useSkybox = true;
material.hasSpecular = true;
material.hasSpecularityFactor = true;
material.hasNormals = true;
material.hasMetalness = true;
material.occludeSpecular = pc.SPECOCC_AO;

const argumentsChunk = `
void evaluateFrontend() {
litArgs_emission = vec3(0.7, 0.4, 0);
litArgs_metalness = 0.5;
litArgs_specularity = vec3(0.5, 0.5, 0.5);
litArgs_specularityFactor = 1.0;
litArgs_gloss = 0.5;
litArgs_ior = 0.1;
litArgs_ao = 0.0;
litArgs_opacity = 1.0;
}`;
material.shaderChunk = argumentsChunk;
material.update();

// create primitive
const primitive = new pc.Entity();
primitive.addComponent('render', {
type: 'sphere',
material: material
});

primitive.setLocalScale(30, 30, 30);
primitive.setLocalPosition(-170, 30, 130);
app.root.addChild(primitive);

// PARTICLE SYSTEM ----------

const localVelocityCurve = new pc.CurveSet([
[0, 0, 0.5, 30],
[0, 0, 0.5, 30],
[0, 0, 0.5, 30]
]);
const localVelocityCurve2 = new pc.CurveSet([
[0, 0, 0.5, -30],
[0, 0, 0.5, -30],
[0, 0, 0.5, -30]
]);
const worldVelocityCurve = new pc.CurveSet([
[0, 0],
[0, 0, 0.2, 6, 1, 300],
[0, 0]
]);

// Create entity for particle system
const entity = new pc.Entity('ParticleSystem');
app.root.addChild(entity);
entity.setLocalPosition(0, 20, 0);

// add particlesystem component to entity
entity.addComponent('particlesystem', {
numParticles: 200,
lifetime: 1,
rate: 0.01,
scaleGraph: new pc.Curve([0, 10]),
velocityGraph: worldVelocityCurve,
localVelocityGraph: localVelocityCurve,
localVelocityGraph2: localVelocityCurve2,
colorGraph: new pc.CurveSet([
[0, 1, 0.25, 1],
[0, 0, 0.25, 0.3],
[0, 0, 1, 0]
])
});

// --------

// create an Entity with a camera component
const camera = new pc.Entity();
camera.addComponent('camera', {
clearColor: new pc.Color(0.9, 0.9, 0.9),
farClip: 1000,
toneMapping: pc.TONEMAP_ACES
});

// and position it in the world
camera.setLocalPosition(-500, 60, 300);

// add orbit camera script with a mouse and a touch support
camera.addComponent('script');
camera.script.create('orbitCamera', {
attributes: {
inertiaFactor: 0.2,
distanceMax: 500
}
});
camera.script.create('orbitCameraInputMouse');
camera.script.create('orbitCameraInputTouch');
app.root.addChild(camera);

// Create a directional light casting soft shadows
const dirLight = new pc.Entity('Cascaded Light');
dirLight.addComponent('light', {
type: 'directional',
color: pc.Color.WHITE,
shadowBias: 0.3,
normalOffsetBias: 0.2,
intensity: 1.0,

// enable shadow casting
castShadows: true,
shadowType: pc.SHADOW_PCF3_32F,
shadowDistance: 1000,
shadowResolution: 2048
});
app.root.addChild(dirLight);
dirLight.setLocalEulerAngles(75, 120, 20);

// handle HUD changes
data.on('*:set', (path, value) => {
const propertyName = path.split('.')[1];
if (propertyName === 'tonemapping') {
// set up selected tone-mapping
camera.camera.toneMapping = value;
}
if (propertyName === 'fog') {
app.scene.fog.type = value;
}
if (propertyName === 'gamma') {
camera.camera.gammaCorrection = value ? pc.GAMMA_SRGB : pc.GAMMA_NONE;
}
});

// initial values
data.set('data', {
tonemapping: pc.TONEMAP_ACES,
fog: pc.FOG_LINEAR,
gamma: true
});
});

export { app };
Binary file not shown.
Binary file not shown.
1 change: 0 additions & 1 deletion scripts/esm/camera-frame.mjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// Camera Frame v 1.0

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

/** @enum {number} */
Expand Down
3 changes: 3 additions & 0 deletions scripts/utils/cubemap-renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ CubemapRenderer.prototype.initialize = function () {
farClip: camera.farClip,
nearClip: camera.nearClip,
frustumCulling: camera.frustumCulling,
gammaCorrection: camera.gammaCorrection,
toneMapping: camera.toneMapping,
fog: camera.fog,

// this camera renders into texture target
renderTarget: renderTarget
Expand Down
7 changes: 6 additions & 1 deletion src/core/preprocessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ const INCLUDE = /include[ \t]+"([\w-]+)"\r?(?:\n|$)/g;
* inspired by: https://github.com/dcodeIO/Preprocessor.js
*/
class Preprocessor {
static sourceName;

/**
* Run c-like preprocessor on the source code, and resolves the code based on the defines and ifdefs
*
Expand All @@ -53,10 +55,13 @@ class Preprocessor {
* @param {object} [options] - Optional parameters.
* @param {boolean} [options.stripUnusedColorAttachments] - If true, strips unused color attachments.
* @param {boolean} [options.stripDefines] - If true, strips all defines from the source.
* @param {string} [options.sourceName] - The name of the source file.
* @returns {string|null} Returns preprocessed source code, or null in case of error.
*/
static run(source, includes = new Map(), options = {}) {

Preprocessor.sourceName = options.sourceName;

// strips comments, handles // and many cases of /*
source = this.stripComments(source);

Expand Down Expand Up @@ -364,7 +369,7 @@ class Preprocessor {
// process the just included test
KEYWORD.lastIndex = include.index;
} else {
console.error(`Include "${identifier}" not resolved while preprocessing a shader`, { source: originalSource });
console.error(`Include "${identifier}" not resolved while preprocessing ${Preprocessor.sourceName}`, { source: originalSource });
error = true;
}
}
Expand Down
Loading

0 comments on commit 112007d

Please sign in to comment.