import * as THREE from 'three';
import * as ECS from '../../packages/ecs-js/index'
import * as xglsl from '../xutils/xglsl'
import AssetKeepr from '../xutils/assetkeepr.js';
import XSys from './xsys';
import Thrender from './thrender';
import {Visual, Canvas, AssetType, ShaderFlag} from '../component/visual.js'
import {Obj3, Obj3Type} from '../component/obj3.js'
import GlUniform from '../xutils/gluniform.js'
/**
* @property {array} iffall - query condition: {iffall: ['Visual', 'Dynatex', 'Obj3']}
* @member CanvTex#iffall
*/
const iffall = ['Visual', 'Dynatex', 'Obj3'];
const buf = {p0: new THREE.Vector3(), // usually for pos
p : new THREE.Vector3(), // usually for pos
q : new THREE.Quaternion(), // usually for quaternion
q_: new THREE.Quaternion(), // quaternion conjugate
s : new THREE.Vector3() // scale ?
};
/**@class CanvTex
* @classdesc
* Handle dynamic texture with an underlying html canvas. Hidden canvas are
* referenced by component {@link Dynatex}.
*
* Reference: <a href='https://github.com/jeromeetienne/threex.dynamictexture'>
* jeromeetienne/threex.dynamictexture</a>
*/
export default class CanvTex extends XSys {
/**
* @param {ECS} ecs
* @param {x} x x-singleton {options, ...}
* @constructor CanvTex
*/
constructor (ecs, x) {
super(ecs);
this.camera = x.xcam.XCamera.cam;
this.xview = x.xview;
this.refresh = true;
this.ecs = ecs;
// add a plane to Obj3
var ents = ecs.queryEntities({iffall});
this.init(x, ents);
}
/**Create text planes.
* Each plane is rendered with xshader colorArray.
* @param {x} x x-singleton {options, ...}
* @param {array.<{Entity}>} entities
* @member CanvTex.init
* @function
*/
init(x, entities) {
if (!entities) return this;
for (const e of entities) {
var {mesh, vparas} = createTextPlane( e.Obj3, e.Visual, e.Dynatex, x.light );
if (e.Obj3.mesh)
e.Obj3.mesh.add(mesh);
else if (e.Obj3)
e.Obj3.add(mesh);
else //
console.warn('Why parent object is null?');
e.Dynatex.dirty = true;
// force dynatex use colorArray - otherwise it's user's mesh
if (e.Visual.vtype === AssetType.Dynatex) {
e.Visual.shader = ShaderFlag.colorArray;
e.Visual.paras = Object.assign(e.Visual.paras || new Object(), vparas);
}
}
return this;
function createTextPlane( obj3, visual, dynatex, light ) {
var uniforms = GlUniform.init(visual, obj3, light);
var u_tex = dynatex.u_tex ? dynatex.u_tex : [];
// stub: no onLoad callback called by AssetKeepr,
// to stop replacing text later by Thrender
u_tex.push( 'data:application/x-visual+img,stub' );
var vparas = {
colors: [dynatex.hue || [0, 0, 0]],
u_texWeight: dynatex.u_texWeight === undefined ? 1 : dynatex.u_texWeight,
u_tex};
var mat = Thrender.createXShaderMaterial(ShaderFlag.colorArray, uniforms, vparas, obj3);
var w = dynatex.xywh.w;
// using 'h' instead of 'size' is a frequently found mistake
var h = dynatex.xywh.h || (dynatex.xywh.size + (dynatex.xywh.margin || 0) * 2);
var m = Thrender.createObj3mesh({box: [w, h]}, Obj3Type.PLANE, mat);
// transform
var margin = dynatex.xywh.margin || 0;
var y0 = obj3.box[1] || 0;
var valign = dynatex['v-align'];
if (!valign) valign = 'top';
if (valign === 'top')
y0 = y0/2 - h/2;
else if (valign === 'bottom')
y0 = -y0/2 + h/2;
// 'middle'
else y0 = 0;
var transform = [ { translate: [0, y0, 0] } ];
Thrender.applyTransform( m, transform );
Thrender.applyTransform( m, dynatex.transform );
dynatex.textplane = m;
return {mesh: m, vparas: vparas};
}
}
/** Set new text to entity.
* @param {Entity} e
* @param {String} txt
* @member CanvTex.setext
* @function
*/
static setext(e, txt) {
if (e.Dynatex) {
e.Dynatex.dirty = true;
e.Dynatex.text = txt;
}
}
/**Update texture of canvas if Dynatex.dirty is true;
*
* If the Dynatex.lookScreen is true, also turn it to facing screen.
*
* This update checking will dynamically update canvas visual results.
* @param {int} tick
* @param {Array.<Entity>} entities
* @member CanvTex#update
* @function
*/
update(tick, entities) {
if (this.xview.flag <= 0 && !this.refresh)
return;
this.refresh = false;
for (const e of entities) {
if ( e.Dynatex && e.Dynatex.dirty ) {
if ( !e.Dynatex.textplane )
// || !e.Obj3.mesh.material.uniforms
// || !e.Obj3.mesh.material.uniforms.u_tex )
console.error('Dyantex.textplane is not ready when updating?', e);
else {
e.Dynatex.dirty = false;
var tex = AssetKeepr.drawText(e.Dynatex);
// tex = AssetKeepr.loadTexure('data:application/x-visual+img,color-pixel [0.2, 0.0, 0.9]');
tex.needsUpdate = true;
var l = e.Dynatex.textplane.material.uniforms.u_tex.value.length;
e.Dynatex.textplane.material.uniforms.u_tex.value[l - 1] = tex;
}
}
if (e.Dynatex.lookScreen && e.Obj3) {
// TODO merge with D3Pie?
var m = e.Obj3.mesh;
if (m) {
// snapshot q0
if (!e.Obj3.transform)
e.Obj3.transform = new Object();
if (!e.Obj3.transform.q0) {
e.Obj3.transform.q0 = new THREE.Quaternion();
m.matrix.decompose( buf.p, e.Obj3.transform.q0, buf.s );
// collect parent's rotation
var parnt = e.Obj3.group;
const g = this.ecs.getEntity(parnt);
if (g && g.Obj3.mesh && g.Obj3.mesh.matrixWorld) {
g.Obj3.mesh.matrixWorld.decompose( buf.p, buf.q_, buf.s );
e.Obj3.transform.q0.multiply( buf.q_.conjugate() );
}
}
//
m.matrix.decompose( buf.p, buf.q, buf.s );
buf.q.copy(e.Obj3.transform.q0);
buf.q.multiply(this.camera.quaternion);
m.matrix.compose( buf.p0, buf.q, buf.s );// p0: rotate at [0, 0, 0]
m.matrix.setPosition( buf. p );
m.matrixAutoUpdate = false;
}
}
}
}
}
CanvTex.query = {iffall};