import { x } from '../xapp/xworld';
import { ShaderFlag, ShaderAlpha, AssetType } from '../component/visual';
import * as xutils from './xcommon';
import AssetKeepr from './assetkeepr';
import { randomParticl } from './shaders/random-particl.glsl';
import { cubeVoxels, cubeVoxelGeom } from './shaders/cube-voxels.glsl';
import { colorLine } from './shaders/color-line.glsl';
import { phongMorph2 } from './shaders/color-array.glsl';
import { scaleOrb } from './shaders/orb-scale.glsl';
import { orbGroups } from './shaders/orb-groups.glsl';
import { worldOrbs } from './shaders/orb-world.glsl';
import { thermalTile } from './shaders/tile-thermal.glsl';
import { texPrism } from './shaders/texprism.glsl';
import { boxLayers, xyzLayer2 } from './shaders/shape-layers.glsl';
import { blinkStar, pointGeom } from './shaders/blink-star.glsl';
import { reflectex } from './shaders/reflectex.glsl';
import { testPoint } from './shaders/testpoint.glsl';
import { fragShape } from './shaders/frag-shape.glsl';
import { glConfig } from './shaders/glx.glsl';
export default
/**X-visual helper for handling shader material's unforms.
* @class gluniform
*/
class GlUniform {
/** @constructor gluniform */
constructor (config) {
this.cfg = Object.assign(glConfig, config);
}
/**
* export function formatUniforms(visual, obj3, light)
*
* Merge Visual.paras.uniforms, load texture according to Visual.paras.u_tex.
*
* Scince v0.3.36, this method also modified obj3.lightened = true if the shader
* using xlight's parameter. Only material of x-visual extension is possible, any
* light effects of THEE.Materials is updated by xlight.hemisphereLight (instance
* of THREE.HemisphereLight).
* @param {Visual} visual Entities Visual component
* @param {Obj3} obj3 not used?
* @param {object=} ligth (optinal) light options
* @return {object} uniforms for THREE.Mesh - properties are in format of name: {value}
* @member gluniform.init
* @function
*/
static init(visual, obj3, light) {
var uniforms = new Object();// make sure uniforms are not shared between materials
// common section
// u_tex
if (visual.paras && visual.paras.uniforms && visual.paras.uniforms.u_tex) {
console.warn ( "XMaterials.init(): ignoring wrong paras: Visual.paras.uniforms.u_tex = ",
visual.paras.uniforms.u_tex,
"Should been used as Visual.paras.u_tex." );
visual.paras = Object.assign(visual.paras || {}, {u_tex: visual.paras.uniforms.u_tex});
delete visual.paras.uniforms.u_tex;
}
// u_tex
if (visual.paras && visual.paras.u_tex) {
if (typeof visual.paras.u_tex === 'string')
uniforms.u_tex = { value: AssetKeepr.loadTexure(visual.paras.u_tex) };
else if (Array.isArray(visual.paras.u_tex)) {
// var texs = [];
for (var i = 0; i < visual.paras.u_tex.length && i < 8; i++) {
var tex = new xutils.ramTexture(i + 1, i + 1, {alpha: 0.5});
// suppress RanderPass warning - but not working
tex.needsUpdate = false;
uniforms[`u_tex${i}`] = {value: tex};
AssetKeepr.loadTexure(visual.paras.u_tex[i],
function() {
var j = i;
return (texture) => {
texture.needsUpdate = true;
uniforms[`u_tex${j}`] = {value: texture};
}
}());
}
}
}
else if (visual.paras && visual.paras.uniforms
&& typeof visual.paras.uniforms.u_tex === 'string') {
uniforms.u_tex = { value: AssetKeepr.loadTexure(visual.paras.uniforms.u_tex) };
delete visual.paras.uniforms.u_tex;
}
else if ((visual.shader & ShaderFlag.defaultex) === ShaderFlag.defaultex) {
uniforms.u_tex = { value: AssetKeepr.cheapixelTex() };
}
// u_color
// Desgin Memo v0.3.33
// This must been set before uniPhongs() handling, which override u_color = light.skyColor
if (visual.paras && visual.paras.color) {
uniforms.u_color = {value: new THREE.Vector4(...visual.paras.color)};
}
uniforms.side = {value: !visual.paras || visual.paras.side === undefined
? THREE.FrontSide : visual.paras.side};
uniforms.u_alpha = {value: visual.paras !== undefined && visual.paras.tex_alpha !== undefined
? visual.paras.tex_alpha : 1};
// setup a default texture, a grey pixel to avoid this error:
// ERROR :GL_INVALID_OPERATION : glDrawElements: Source and destination textures of the draw are the same.
// Cause: It is not sufficient to bind the texture to a texture unit,
// the index of the texture unit has to be set to the texture sampler uniform, too.
// comments on https://stackoverflow.com/questions/50777793/gldrawelements-source-and-destination-textures-of-the-draw-are-the-same
if (!uniforms.u_tex &&
( visual.shader === ShaderFlag.worldOrbs
|| visual.shader === ShaderFlag.scaleOrb
|| visual.shader === ShaderFlag.orbGroups
|| visual.shader === ShaderFlag.tiledOrbs) ) {
if (visual.asset)
uniforms.u_tex = { value: AssetKeepr.loadTexure(visual.asset) };
else
uniforms.u_tex = { value: AssetKeepr.cheapixelTex() };
}
// switch of shader types
if (visual.paras && visual.paras.uniforms)
Object.assign(uniforms, GlUniform.obj2uniforms(visual.paras.uniforms));
if (visual.shader === ShaderFlag.colorArray) {
phongMorph2.initUniform(uniforms, light, visual.paras);
if (visual.paras && visual.paras.colors) {
// TODO should we use uniform array ?
for (var i = 0; i < visual.paras.colors.length; i++)
uniforms[`u_colors${i}`] = {value: new THREE.Vector4(...visual.paras.colors[i])};
}
}
else if (visual.shader === ShaderFlag.scaleOrb) {
uniforms.wpos = {value: visual.paras.wpos ?
new THREE.Vector3(...p) : new THREE.Vector3(0, 0, 0)};
uniforms.r = {value: visual.paras.orbR === undefined
? 20 : visual.paras.orbR};
uniforms.whiteAlpha = {value: visual.paras.whiteAlpha === undefined
? 0 : visual.paras.whiteAlpha};
var os = visual.paras.orbScale;
uniforms.orbScale = {value: os === undefined
? new THREE.Vector3(1, 0.2, 0.2)
: new THREE.Vector3(os[0], os[1], os[2])};
}
else if (visual.shader === ShaderFlag.orbGroups) {
// orbs in a group (always have 1 orb)
var offsets = visual.paras.offsets || [0];
var orbs = visual.paras.offsets ? visual.paras.offsets.length : 1;
var rs = [];
for (var r of visual.paras.orbR || [10]) {
rs.push(r);
}
var orbColors = [];
for (var c of visual.paras.colors) {
orbColors.push(new THREE.Vector3(...c));
}
uniforms.orbs = { value: orbs };
uniforms.offsets = { value: offsets };
uniforms.orbColors = { value: orbColors };
uniforms.r = { value: rs };
// groups
if ( ! Array.isArray(visual.paras.follows)
|| visual.paras.follows.length <= 0) {
console.error(visual);
throw new XError('Paras.follows for orbGroups\'s groups are not correct!');
}
uniforms.u_t = { value: 0 };
uniforms.tmin = { value: visual.paras.t_range ? visual.paras.t_range[0] : 0 };
uniforms.tmax = { value: visual.paras.t_range ? visual.paras.t_range[1] : 1 };
var wpos = [];
var wtan = [];
var flws = [];
for (var follow of visual.paras.follows) {
wpos.push(new THREE.Vector3(0));
wtan.push(new THREE.Vector3(1, 0, 0));
flws.push(follow);
}
uniforms.wpos = { value: wpos };
uniforms.wtan = { value: wtan };
uniforms.follows = { value: flws };
uniforms.whiteAlpha = { value: visual.paras.whiteAlpha === undefined
? 0 : visual.paras.whiteAlpha };
var os = visual.paras.orbScale;
uniforms.orbScale = { value: os === undefined
? new THREE.Vector3(1, 0.2, 0.2)
: new THREE.Vector3(os[0], os[1], os[2]) };
}
else if (visual.shader === ShaderFlag.worldOrbs
|| visual.shader === ShaderFlag.tiledOrbs) {
var poses = [];
var orbs = 0;
if (visual.paras.offsets === undefined)
visual.paras.offsets = [[0, 0, 0]];
for (var p of visual.paras.offsets) {
poses.push(new THREE.Vector3(...p));
orbs++;
}
var rs = [];
var orbRs = typeof visual.paras.orbR === 'number'
? [visual.paras.orbR] : visual.paras.orbR || [10];
for (var r of orbRs) {
rs.push(r);
}
var orbColors = [];
for (var c of visual.paras.colors) {
orbColors.push(new THREE.Vector3(...c));
}
var wpos = visual.paras.wpos
? new THREE.Vector3(...visual.paras.wpos) : new THREE.Vector3(0, 0, 0);
uniforms.orbs = { value: orbs };
uniforms.wpos = { value: wpos };
uniforms.offsets = { value: poses };
uniforms.orbColors = { value: orbColors };
uniforms.r = { value: rs };
uniforms.whiteAlpha = { value: visual.paras.whiteAlpha === undefined
? 0 : visual.paras.whiteAlpha };
var os = visual.paras.orbScale;
uniforms.orbScale = { value: os === undefined
? new THREE.Vector3(1, 0.2, 0.2)
: new THREE.Vector3(os[0], os[1], os[2]) };
if (visual.shader === ShaderFlag.tiledOrbs) {
uniforms.now = { value: 0 };
var groups = visual.paras.geostyle.groups || 1;
uniforms.speedVert = { value: new Array(groups).fill(0.01) };
uniforms.speedFrag = { value: new Array(groups).fill(0.01) };
var colrs = new Array();
var pcolrs = visual.paras.thermalColors || [[1, 0, 0], [0, 1, 0], [0, 0, 1]];
for (var c of pcolrs)
colrs.push(new THREE.Vector3(...c));
uniforms.thermalColors = { value: colrs };
uniforms.maxHeight = { value: visual.paras.geostyle.maxHeight
|| visual.paras.geostyle.height || 1 };
}
}
else if (visual.shader === ShaderFlag.texPrism
|| visual.shader === ShaderFlag.boxLayers) {
if (visual.shader === ShaderFlag.texPrism)
texPrism.initUniform(uniforms, light, visual.paras);
else
xyzLayer2.initUniform(uniforms, light, visual.paras);
// xz, xy, yz layers center
uniforms.wpos = { value: visual.paras.wpos ?
new THREE.Vector3(...visual.paras.wpos) : new THREE.Vector3(0, 0, 0) };
// xz, xy, yz layers offset
var offsetxyz = [0, 0, 0];
if (visual.paras.yztile)
offsetxyz[0] += visual.paras.yztile.x0 || 0;
if (visual.paras.xztile)
offsetxyz[1] += visual.paras.xztile.y0 || 0;
if (visual.paras.xytile)
offsetxyz[2] += visual.paras.xytile.z0 || 0;
uniforms.u_offsetxyz = {value: new THREE.Vector3(...offsetxyz)};
uniforms.now = { value: 0 };
var groups = visual.paras.geostyle ? visual.paras.geostyle.groups || 1 : 1;
uniforms.speedVert = { value: new Array(groups).fill(0.001) };
uniforms.speedFrag = { value: new Array(groups).fill(0.001) };
if (visual.paras.u_tex) {
uniforms.u_tex = { value: AssetKeepr.loadTexure(visual.paras.u_tex[0]) };
uniforms.u_basetex = { value: AssetKeepr.loadTexure(visual.paras.u_tex[1]) };
}
if (visual.paras.tile && visual.paras.tile.hue) {
uniforms.u_hue = { value: new THREE.Vector4(...visual.paras.tile.hue) };
}
else
uniforms.u_hue = { value: new THREE.Vector4(0.4, 0.2, 0.8, 1.0) };
uniforms.u_north = { value: visual.paras.north || 0 };
}
else if (visual.shader === ShaderFlag.reflectex
|| visual.vtype === AssetType.reflectex) {
reflectex.initUniform(uniforms, light, visual.paras);
}
if (visual && (visual.shader & ShaderFlag.lightened) === ShaderFlag.lightened )
obj3.lightened = true;
return uniforms;
}
/**
* check uniforms exists<br>
* update uniforms in light if light.dirty = true;<br>
* update paras
* @param {object} uniforms
* @param {object} vparas paras in the same structure of Visual.paras
* @param {object} ligth light options
* @param {ShaderFlag=} shader Visual.shader, override vparas.shader
* ({@link XMaterial} doesn't have it)
* @return {object} uniforms
* @member gluniform.update
* @function
*/
static update(uniforms, light, vparas, shader) {
if (typeof vparas.paras === "object") {
if (vparas.side !== undefined || vparas.tex_alpha !== undefined) {
uniforms.side.value = vparas.side;
}
if (vparas.tex_alpha !== undefined) {
uniforms.u_alpha.value = vparas.tex_alpha;
}
}
shader = shader || vparas.shader;
if (shader !== undefined) {
if (shader === ShaderFlag.colorArray) {
phongMorph2.updateUniform(uniforms, light, vparas);
if (vparas.colors) {
// TODO should we use uniform array ?
for (let i = 0; i < vparas.colors.length; i++)
uniforms[`u_colors${i}`] = {
value: new THREE.Vector4(...vparas.colors[i])
};
}
}
else if (shader === ShaderFlag.scaleOrb) {
// TODO
}
else if (shader === ShaderFlag.orbGroups) {
// TODO
}
if (shader === ShaderFlag.tiledOrbs) {
// TODO
}
else if (shader === ShaderFlag.texPrism ||
shader === ShaderFlag.boxLayers) {
if (shader === ShaderFlag.texPrism)
texPrism.updateUniform(uniforms, light, vparas);
else
xyzLayer2.updateUniform(uniforms, light, vparas);
if (Array.isArray(vparas.wpos) && vparas.wpos.length > 0) {
uniforms.wpos = new THREE.Vector3(...vparas.wpos);
}
// xz, xy, yz layers offset
let offsetxyz = uniforms.u_offsetxyz.value;
if (vparas.yztile !== undefined) {
offsetxyz.x = vparas.yztile.x0;
}
if (vparas.xztile !== undefined) {
offsetxyz.y = vparas.xztile.y0;
}
if (vparas.xytile !== undefined) {
offsetxyz.z = vparas.xytile.z0;
}
if (typeof vparas.geostyle === "object" && vparas.geostyle.groups >= 1) {
let groups = vparas.geostyle.groups;
uniforms.speedVert.value = new Array(groups).fill(0.001);
uniforms.speedFrag.value = new Array(groups).fill(0.001);
}
if (vparas.u_tex) {
uniforms.u_tex = {
value: AssetKeepr.loadTexure(vparas.u_tex[0])
};
uniforms.u_basetex = {
value: AssetKeepr.loadTexure(vparas.u_tex[1])
};
}
if (vparas.tile && vparas.tile.hue) {
uniforms.u_hue.value = new THREE.Vector4(...vparas.tile.hue);
}
if (vparas.north !== undefined) {
uniforms.u_north.value = vparas.north
}
}
else if (visual.shader === ShaderFlag.reflectex
|| visual.vtype === AssetType.reflectex) {
reflectex.updateUniform(uniforms, light, vparas);
}
}
}
/**
* Convert object into THREE.Mesh.uniforms format (properties are {value} object).<br>
* x-visual v.s. Three.js material variable name mapping:<pre>
three.js -> x-visual shader
opacity - u_alpha
</pre>
* @param {object} properties
* @param {THREE.Uniforms} uniforms
* @return {object} uniforms for THREE.Mesh - properties are in format of name: {value}
* @member GlUniform.obj2uniforms
* @function
*/
static obj2uniforms(properties, uniforms) {
var u = new Object();
for (var p in properties) {
if (p === 'opacity')
u.u_alpha = {value: properties[p]};
else
u[p] = {value: properties[p]};
}
return Object.assign(uniforms || new Object(), u);
}
}