_import { getWGSLType } from './data-size.js';
/**
* Constant is a container for const declarations.
* They work in two ways with the `override` attribute.
*
* @class Constant
*/
class Constant {
#name
#value
#type
#override
#shaderStage = GPUShaderStage.VERTEX | GPUShaderStage.FRAGMENT | GPUShaderStage.COMPUTE
/**
* @param {{name:String, value:(Number|Array<Number>), type:String, override:Boolean}} config
*/
constructor({ name, value, type, override = false }) {
this.#validateName(name);
this.#validateType(type);
this.#validateValue(value);
this.#name = name;
this.#type = type || getWGSLType(value);
this.#value = this.#ifTypeVecGetVecValue(this.#type, value);
this.#override = override;
}
#ifTypeVecGetVecValue(type, value) {
let newValue = value;
if (type.indexOf('vec') !== -1) {
newValue = `vec${value.length}f(${value})`
}
return newValue;
}
get name() {
return this.#name;
}
/**
* The name that the Constant will have on the WGSL side.
* @param {String} value name of the Constant. The name is used in the WGSL
* shader.
* @example
* // js
* myConstant.name = 'MYCONST';
*
* // wgsl
* let newVal = MYCONST + 3;
* @memberof Constant
*/
set name(value) {
this.#validateName(value);
this.#name = value;
}
get value() {
return this.#value;
}
/**
* Get or set the value that the constant will have on the WGSL side.
* @warning It can only be assigned once.
* @param {Number|Array<Number>} value
* @memberof Constant
*/
set value(value) {
this.#validateValue(value);
const type = getWGSLType(value);
this.#value = this.#ifTypeVecGetVecValue(type, value);
this.#type = type;
}
get type() {
return this.#type;
}
/**
* Get or set the type of the constant.
* It can be inferred automatically by just passing the value, but if
* something more specific is required, then you should use `type`.
* @param {String} value WGSL data type of the constant
* @example
* myConstant.type = 'u32';
* @memberof Constant
*/
set type(value) {
this.#validateType(value);
this.#type = value;
}
get override() {
return this.#override;
}
/**
* A constant override is a constant you can change per shader.
* By default, POINTS interpolates constant declarations inside the WGSL
* string shader like this:
* ```wgsl
* const MYCONST:u32 = 10;
* ```
* These declarations are added by default to all shaders in the pipeline
* and in all render passes. These can not be changed.
*
* With overrides you can have the same constant in different shaders with
* different values. The default value is passed to each pipeline and then
* it can be overwritten in a specific shader by hand.
* @example
* ```js
* // js side
* constants.PI.setOverride(true).setValue(3.14);
* ```
* ```wgsl
* // wgsl side
* override MYCONST:u32 = 3.1415;
* ```
* @memberof Constant
*/
set override(value) {
this.#override = value;
}
get shaderStage() {
return this.#shaderStage;
}
/**
* Tells WebGPU to which shader it can only be used.
* @param {GPUShaderStage}
* @memberof Constant
*/
set shaderStage(value) {
this.#shaderStage = value;
}
/**
* Sets the value of a Constant
* @param {Number|Array<Number>} value
* @returns {Constant}
* @memberof Constant
*/
setValue(value) {
this.#validateValue(value);
const type = getWGSLType(value);
this.#value = this.#ifTypeVecGetVecValue(type, value);
this.#type = type;
return this;
}
/**
* Set the data type of the Constant.
* @param {String} value WGSL data type of the constant
* @example
* myUniform.setType('u32')
* @memberof Constant
*/
setType(value) {
this.#validateType(value);
this.#type = value;
return this;
}
/**
* A constant override is a constant you can change per shader.
* By default, POINTS interpolates constant declarations inside the WGSL
* string shader like this:
* ```wgsl
* const MYCONST:u32 = 10;
* ```
* These declarations are added by default to all shaders in the pipeline
* and in all render passes. These can not be changed.
*
* With overrides you can have the same constant in different shaders with
* different values. The default value is passed to each pipeline and then
* it can be overwritten in a specific shader by hand.
* @example
* ```js
* // js side
* constants.PI.setOverride(true).setValue(3.14);
* ```
* ```wgsl
* // wgsl side
* override MYCONST:u32 = 3.1415;
* ```
* @memberof Constant
*/
setOverride(value) {
this.#override = value;
return this;
}
/**
* Tells WebGPU to which shader it can only be used.
* @param {GPUShaderStage} value
* @returns {Constant}
* @memberof Constant
*/
setShaderStage(value) {
this.#shaderStage = value;
return this;
}
#validateValue(value) {
if(this.#value){
throw `Constant '${this.#name}': can't update a const after it has been set.`;
}
if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Uint8Array)) {
throw `Constant '${this.#name}' value:'${value}' can't be an Object.`
}
if (typeof value === 'string') {
throw `Constant '${this.#name}' value: '${value}' can't be an String.`
}
const isArray = Array.isArray(value);
if (isArray) {
const { length } = value;
if (length < 2) {
throw `Constant named '${this.#name}': Size of the array is lower than 2. There's no vec1`;
}
if (Array.isArray(this.#value)) {
if (length != this.#value.length) {
throw `Constant named '${this.#name}': Size of the array value has changed from ${this.#value.length} to ${length}.`
}
}
}
}
#validateName(value) {
if (typeof value === 'number') {
throw `Constant name '${this.#name}' can't be an Number.`
}
if (typeof value === 'string') {
const valNumber = +value;
if (!Number.isNaN(valNumber) && typeof valNumber === 'number') {
throw `Constant name '${this.#name}' can't be an Number.`
}
}
}
#validateType(value) {
}
}
export default Constant;