import { Flags, ObjectAttributes, RefCountObject } from '@uino/base-thing';
import { Utils } from '../common/Utils';
import { MathUtils } from '../math/MathUtils';
import { StyleAttributeKeys, EventType, ImageSlotType, Empty } from '../const';
const __ = {
private: Symbol('private'),
}
// #region Internal Variables
const cAnimationTagName = '__Style_AnimationTagName__';
const Flag = {
// #region Diff
// Type
BlendingType: 1 << 0,
SideType: 1 << 1,
// Number
Opacity: 1 << 2,
Metalness: 1 << 3,
Roughness: 1 << 4,
// Color
Color: 1 << 5,
Emissive: 1 << 6,
OutlineColor: 1 << 7,
Transparent: 1 << 8,
// Object
ClippingPlanes: 1 << 9,
Edge: 1 << 10,
Effect: 1 << 11,
Images: 1 << 12,
UVMatrix: 1 << 13,
// Boolean
Wireframe: 1 << 14,
DepthTest: 1 << 15,
DepthWrite: 1 << 16,
EnvMapping: 1 << 17,
// External
Animation: 1 << 18,
Attributes: 1 << 19,
Uniforms: 1 << 20,
Macros: 1 << 21,
LgithGroup: 1 << 22,
// #endregion
// #region State
DelayBegin: 1 << 23,
HookAnimationEvent: 1 << 24,
ChangingDefaultValues: 1 << 25
// #endregion
};
const cBaseValueFlags =
Flag.BlendingType |
Flag.SideType |
Flag.Opacity |
Flag.Metalness |
Flag.Roughness |
Flag.Color |
Flag.Emissive |
Flag.OutlineColor |
Flag.Transparent |
Flag.ClippingPlanes |
Flag.Wireframe |
Flag.DepthTest |
Flag.DepthWrite |
Flag.EnvMapping;
const cDiffValueFlags =
cBaseValueFlags |
Flag.Edge |
Flag.Effect |
Flag.Images |
Flag.UVMatrix |
Flag.Animation |
Flag.Attributes |
Flag.Uniforms |
Flag.Macros;
const cDefaultValues = {
// Type
blendingType: null,
sideType: null,
// Number
opacity: null,
metalness: null,
roughness: null,
// Color
color: null,
emissive: null,
outlineColor: null,
transparent: null,
// Object
clippingPlanes: null,
// Boolean
wireframe: null,
depthTest: null,
depthWrite: null,
envMapping: null
};
const cDefaultValueKeys = Object.keys(cDefaultValues);
/**
* 样式的边框效果。
* @public
* @typedef {object} StyleEdgeResult
* @property {boolean} enable 是否开启边框。
* @property {Array<number>} color 边框的颜色。
* @property {number} opacity 边框的不透明度。
* @property {boolean} glow 边框是否具有发光效果。
* @property {number} thresholdAngle 边框的阈值角度。
*/
const cEdgeDefaultValues = {
enable: false,
color: [1, 1, 1],
opacity: 1,
glow: false,
thresholdAngle: 0.5,
}
const cEdgeDefaultValueKeys = Object.keys(cEdgeDefaultValues);
/**
* @typedef {object} StyleEffectResult
* @property {number} glow 发光强度(null表示恢复原始效果)
* @property {boolean} innerGlow 是否开启内发光(null表示恢复原始效果)
* @property {boolean} lineBloom 是否开启辉光(null表示恢复原始效果)
* @property {boolean} tailing 是否开启拖尾效果(null表示恢复原始效果)
* @property {boolean} radial 是否开启扩散效果(null表示恢复原始效果)
* @property {boolean} ghosting 是否开启残影效果(null表示恢复原始效果)
* @public
*/
const cEffectDefaultValues = {
glow: null,
innerGlow: null,
lineBloom: null,
tailing: null,
radial: null,
ghosting: null
};
const cEffectDefaultValueKeys = Object.keys(cEffectDefaultValues);
/**
* @typedef {object} StyleImagesResult
* @property {object} map 漫反射贴图(最基础的贴图)
* @property {object} envMap 环境光贴图
* @property {object} alphaMap 透明贴图
* @property {object} emissiveMap 自发光贴图
* @property {object} normalMap 发现贴图
* @property {object} colorMapping 颜色映射
* @property {object} aoMap 环境光遮蔽贴图
* @public
*/
const cImagesDefaultValues = {
map: null,
envMap: null,
alphaMap: null,
emissiveMap: null,
normalMap: null,
colorMapping: null,
aoMap: null,
};
const cImagesDefaultValueKeys = Object.keys(cImagesDefaultValues);
function _createUVMatrixDefaultValues() {
return {
offset: [0, 0],
repeat: [1, 1],
center: [0, 0],
rotation: 0
};
}
/**
* @typedef {object} StyleUVMatrixResult
* @property {Array<number>} offset 偏移 通过调整偏移可以使贴图移动
* @property {Array<number>} repeat default: [1, 1] 贴图重复度
* @property {Array<number>} center default: [0, 0] 纹理变换的中心位置,偏移 重复 旋转都基于该位置
* @property {number} rotation 旋转角度
* @public
*/
const cUVMatrixDefaultValues = _createUVMatrixDefaultValues();
const cValueKeys = [
'attributes',
'uniforms',
'macros'
];
// #endregion
// #region Private Functions
// Fast to compare values.
function _equals(v1, v2) {
if (v1 && v2 && Utils.isArray(v1) && Utils.isArray(v2)) {
if (v1.length != v2.length) {
return false;
}
switch (v1.length) {
case 1: return v1[0] == v2[0];
case 2: return v1[0] == v2[0] && v1[1] == v2[1];
case 3: return v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2];
case 4: return v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2] && v1[3] == v2[3];
default:
for (let i = 0; i < v1.length; i++) {
if (v1[i] != v2[i]) {
return false;
}
}
break;
}
}
else if (v1 !== v2) {
return false;
}
return true;
}
function _equalsEx(v1, v2) {
if (_equals(v1, v2)) {
return true;
}
if (Utils.isObject(v1) && Utils.isObject(v2) && Utils.equalsObject(v1, v2)) {
return true;
}
else if (v1 != v2) {
return false;
}
return true;
}
function _assignArray(value) {
return value;
}
function _toImageType(type) {
switch (type) {
case 'Map': return 'map';
case 'EnvMap': return 'envMap';
case 'AlphaMap': return 'alphaMap';
case 'EmissiveMap': return 'emissiveMap';
case 'NormalMap': return 'normalMap';
case 'ColorMapping': return 'colorMapping';
case 'AOMap': return 'aoMap';
default:
return type;
}
}
function _toUImageType(type) {
switch (type) {
case 'map': return 'Map';
case 'envMap': return 'EnvMap';
case 'alphaMap': return 'AlphaMap';
case 'emissiveMap': return 'EmissiveMap';
case 'normalMap': return 'NormalMap';
case 'colorMapping': return 'ColorMapping';
case 'aoMap': return 'AOMap';
default:
return type;
}
}
function _needProxy(prop, value) {
if (value && value.isBaseObject) {
return false;
}
return true;
}
function _assignObject(data, target) {
target = target || {};
return Object.assign(target, data);
}
function _cloneObject(data, target) {
target = target || {};
for (let key in data) {
let value = data[key];
if (value !== null && value !== undefined) {
if (Utils.isArray(value)) {
target[key] = value.slice(0);
}
else {
target[key] = value;
}
}
else {
target[key] = value;
}
}
return target;
}
// #endregion
/**
* @class Style
* 物体的样式效果
* @memberof THING
* @extends THING.RefCountObject
* @public
*/
class Style extends RefCountObject {
static cDefaultValues = cDefaultValues;
static cEdgeDefaultValues = cEdgeDefaultValues;
static cEffectDefaultValues = cEffectDefaultValues;
static cImagesDefaultValues = cImagesDefaultValues;
static cUVMatrixDefaultValues = cUVMatrixDefaultValues;
/**
* 对象的样式,可以改变对象的颜色、轮廓颜色等。
* @param {object} modifier 修改器。
*/
constructor(modifier) {
super();
this[__.private] = {};
let _private = this[__.private];
_private.modifier = modifier;
// The current image slot type
_private.curImageSlotType = ImageSlotType.Map;
// Attributes
_private.edgeData = null;
_private.effectData = null;
_private.imagesData = null;
_private.uvData = null; // Use image type as key
_private.data = new ObjectAttributes({
data: cDefaultValues,
keys: cDefaultValueKeys,
onNeedProxy: _needProxy,
onCloneObject: _cloneObject,
onEquals: _equals
});
// If reach to 0 then indicates use the default original style in Diff flags
_private.state = new Flags();
// The values for attributes/uniforms/macros
_private.values = {
attributes: null,
uniforms: null,
macros: null,
};
// Watch GC to check memory leak
if (_DEBUG) {
this._uuid = MathUtils.generateUUID();
let app = _private.modifier.app;
if (!Style.gcObjects) {
Style.gcObjects = new Set();
app.global.gc.registerDestroyObjectCallback((key) => {
Style.gcObjects.delete(key);
});
}
let key = `type: 'Style', uuid: ${this._uuid}`;
Style.gcObjects.add(key);
app.global.gc.register(this, key);
}
}
// #region Private
// #region Private-Attributes
/**
* Get proxy values or original values.
* @param {string} valueKey 值的键。
* @returns {object} 返回值的对象。
* @private
* @example
* let style = new THING.Style();
* let values = style._getValues('data');
* // @expect(values.data === style.data);
*/
_getValues(valueKey) {
let _private = this[__.private];
let values = {
data: _private.data[valueKey]
};
if (_private.edgeData) {
values['edge'] = _private.edgeData[valueKey];
}
if (_private.effectData) {
values['effect'] = _private.effectData[valueKey];
}
if (_private.imagesData) {
values['images'] = _private.imagesData[valueKey];
}
if (_private.uvData) {
values['uvData'] = _private.uvData[valueKey];
}
return values;
}
_updateDiffSlots(flag, value, onChange) {
let _private = this[__.private];
let diffFlags = _private.state;
// Update flags
let oldValues = diffFlags.values & cDiffValueFlags;
diffFlags.enable(flag, value);
// Begin to change body node style
if (oldValues === 0 && diffFlags.values) {
_private.modifier.onBegin();
if (_private.state.has(Flag.DelayBegin)) {
_private.modifier.resource.begin();
_private.state.enable(Flag.DelayBegin, false);
}
}
// Restore the default original style
else if (oldValues && ((diffFlags.values & cDiffValueFlags) === 0)) {
onChange && onChange();
_private.modifier.onEnd();
}
onChange && onChange();
}
_createColorOpCallback(callback) {
if (!callback) {
return null;
}
let opCallback = function (baseValue) {
let color = callback(baseValue, this);
return Utils.parseColor(color, baseValue);
}
if (callback.isConstantValue) {
opCallback.isConstantValue = true;
}
return opCallback;
}
_createOpCallback(callback) {
if (!callback) {
return null;
}
let opCallback = function (baseValue) {
return callback(baseValue, this);
}
if (callback.isConstantValue) {
opCallback.isConstantValue = true;
}
return opCallback;
}
_createColorCallbackWrapper(key) {
let _private = this[__.private];
return this._createColorOpCallback(baseValue => {
return _private.data.getValueByDefault(key);
});
}
_createOpCallbackWrapper(key, callback) {
let _private = this[__.private];
if (callback) {
return this._createOpCallback(baseValue => {
return callback(baseValue, _private.data.getValueByDefault(key));
});
}
else {
return this._createOpCallback(baseValue => {
return _private.data.getValueByDefault(key);
});
}
}
_updateAttributeValue(flag, key, value, onFormatValue, onChange) {
let _private = this[__.private];
// Clear value
if (Utils.isNull(value)) {
if (!_private.data.setValue(key, null, _equals)) {
return;
}
}
// Set value
else {
// Try to format/convert value
if (onFormatValue) {
value = onFormatValue(value);
}
if (!_private.data.setValue(key, value, _equals, null, _assignArray)) {
return;
}
}
// Update the diff slots of current and base values
let isBaseValue = _private.data.isBaseValue(key, _equals);
this._updateDiffSlots(flag, !isBaseValue, () => {
// Get the value again(the default value could be returned here)
value = _private.data.getValue(key);
// Update renderer style attribute value
onChange(key, value);
});
}
_updateAttributeOpFunc(flag, key, value, onFormatValue, onCreateOpCallback, onCreateCallbackWrapper) {
let _private = this[__.private];
// Clear it
if (Utils.isNull(value)) {
if (!_private.data.setValue(key, null, _equals)) {
return;
}
// Get the default value to set as latest attribute
if (_private.data.hasValue(key)) {
value = onCreateCallbackWrapper(key);
}
}
// Set it by function
else if (Utils.isFunction(value)) {
value = onCreateOpCallback(value);
if (!_private.data.setValue(key, value, _equals)) {
return;
}
// If we are changing default values and have value already then do not change attribute of style
if (_private.state.has(Flag.ChangingDefaultValues)) {
if (_private.data.hasValue(key, true)) {
return;
}
}
}
// Set it by value(need to wrap function)
else {
// Try to format/convert value
if (onFormatValue) {
value = onFormatValue(value);
if (!value) {
return;
}
}
if (!_private.data.setValue(key, value, _equals, null, _assignArray)) {
return;
}
// If we are changing default values and have value already then do not change attribute of style
if (_private.state.has(Flag.ChangingDefaultValues)) {
if (_private.data.hasValue(key, true)) {
return;
}
}
// Create callback function wrapper due to renderer use callback function as argument
value = onCreateCallbackWrapper(key);
}
// Update the diff slots of current and base values
let isBaseValue = _private.data.isBaseValue(key, _equals);
this._updateDiffSlots(flag, !isBaseValue);
// Update renderer callback function
_private.modifier.onChangeOpFunc(key, value);
}
_updateOutlineColor(flag, key, value) {
this._updateAttributeValue(flag, key, value,
(value) => {
return Utils.parseColor(value, null);
},
(key, value) => {
let _private = this[__.private];
_private.modifier.onChangeOutlineColor(key, value);
}
);
}
_updateValue(flag, key, value) {
this._updateAttributeValue(flag, key, value, null,
(key, value) => {
let _private = this[__.private];
_private.modifier.onChangeValue(key, value);
}
);
}
_updateColorOpFunc(flag, key, value) {
this._updateAttributeOpFunc(flag, key, value,
(v) => {
return Utils.parseColor(v, null);
},
// By function
(v) => {
return this._createColorOpCallback(v);
},
// By value(we need to wrap function)
(key) => {
return this._createColorCallbackWrapper(key);
}
);
}
_updateValueOpFunc(flag, key, value, callback) {
this._updateAttributeOpFunc(flag, key, value, null,
// By function
(v) => {
return this._createOpCallback(v);
},
// By value(we need to wrap function)
(key) => {
return this._createOpCallbackWrapper(key, callback);
}
);
}
_updateValues(flag, key, value, values) {
values.setValue(key, value, _equals, (key, value) => {
let isBaseValue = _equals(values.getValue(key), value);
this._updateDiffSlots(flag, !isBaseValue);
});
}
_changeValue(flag, funcName, ev) {
let _private = this[__.private];
let key = ev.key;
let value = Utils.parseValue(ev.value, null);
// Update the diff slots of current and base values
let isBaseValue = _private.data.isBaseValue(key, _equals);
this._updateDiffSlots(flag, !isBaseValue);
_private.modifier[funcName](key, value);
}
_createAttributes() {
let _private = this[__.private];
if (!_private.values['attributes']) {
_private.values['attributes'] = new ObjectAttributes({
onChange: (ev) => {
this._changeValue(Flag.Attributes, 'onChangeAttribute', ev);
}
});
}
return _private.values['attributes'];
}
_createUniforms() {
let _private = this[__.private];
if (!_private.values['uniforms']) {
_private.values['uniforms'] = new ObjectAttributes({
onNeedProxy: function (prop, value) {
if (value && value.isImageTexture) {
return false;
}
return true;
},
onChange: (ev) => {
this._changeValue(Flag.Uniforms, 'onChangeUniformValue', ev);
}
});
}
return _private.values['uniforms'];
}
_createMacros() {
let _private = this[__.private];
if (!_private.values['macros']) {
_private.values['macros'] = new ObjectAttributes({
onChange: (ev) => {
this._changeValue(Flag.Macros, 'onChangeMacroValue', ev);
}
});
}
return _private.values['macros'];
}
// #endregion
// #region Private-Image
_updateImage(type, image, isChangingDefaultValues) {
let _private = this[__.private];
// If we had destroyed object then modifier would be null
let modifier = _private.modifier;
if (!modifier) {
return;
}
// If it's default values then need to check the current image state, if it's existing then do not update resource image now
if (isChangingDefaultValues) {
let values = _private.imagesData.getValues().data;
if (values[type]) {
return;
}
}
// We must check the image here again to prevent the image loading order problem
else {
if (_private.imagesData.data[type] != image) {
return;
}
}
// Update diff slots
this._updateDiffSlots(Flag.Images, true);
// Get the U-Image type
let uImageType = _toUImageType(type);
// Update image resource
if (image) {
image.addRef();
modifier.onChangeImage(uImageType, image.getTextureResource());
}
// Check whether need to use default image
else {
let defaultImage = _private.imagesData.defaultValues[type];
if (defaultImage) {
modifier.onChangeImage(uImageType, defaultImage.getTextureResource());
}
else {
modifier.onChangeImage(uImageType, null);
}
}
}
_createImagesData() {
let _private = this[__.private];
if (!_private.imagesData) {
_private.imagesData = new ObjectAttributes({
data: cImagesDefaultValues,
keys: cImagesDefaultValueKeys,
onCloneObject: (data) => {
return {
map: data.map,
envMap: data.envMap,
alphaMap: data.alphaMap,
emissiveMap: data.emissiveMap,
normalMap: data.normalMap,
colorMapping: data.colorMapping,
aoMap: data.aoMap
};
},
onFilterProperty: () => {
// We skip to proxy images here
return false;
},
onChange: (ev) => {
},
onNeedProxy: _needProxy,
onCloneObject: _assignObject,
onEquals: _equalsEx
});
}
return _private.imagesData;
}
// #endregion
// #region Private-UV
_updateImageUVMatrix(type, uv) {
let _private = this[__.private];
let offset = uv.offset;
let repeat = uv.repeat;
let center = uv.center;
let rotation = MathUtils.degToRad(uv.rotation);
const c = Math.cos(rotation);
const s = Math.sin(rotation);
let uvMatrix = [
repeat[0] * c, -repeat[1] * s, 0,
repeat[0] * s, repeat[1] * c, 0,
-repeat[0] * (c * center[0] + s * center[1]) + center[0] + offset[0], -repeat[1] * (-s * center[0] + c * center[1]) + center[1] + offset[1], 1
]
this._updateDiffSlots(Flag.UVMatrix, true);
_private.modifier.onChangeUVMatrix(type, uvMatrix);
}
_createUVMatrixData() {
let _private = this[__.private];
if (!_private.uvData) {
_private.uvData = new ObjectAttributes({
data: {
map: cUVMatrixDefaultValues,
envMap: cUVMatrixDefaultValues,
alphaMap: cUVMatrixDefaultValues,
emissiveMap: cUVMatrixDefaultValues,
normalMap: cUVMatrixDefaultValues,
colorMapping: cUVMatrixDefaultValues,
aoMap: cUVMatrixDefaultValues,
},
keys: cImagesDefaultValueKeys,
onCloneObject: (data) => {
return {
map: _createUVMatrixDefaultValues(),
envMap: _createUVMatrixDefaultValues(),
alphaMap: _createUVMatrixDefaultValues(),
emissiveMap: _createUVMatrixDefaultValues(),
normalMap: _createUVMatrixDefaultValues(),
colorMapping: _createUVMatrixDefaultValues(),
aoMap: _createUVMatrixDefaultValues()
};
},
onChange: (ev) => {
let type = _private.curImageSlotType;
let data = ev.data[_toImageType(type)];
if (data) {
this._updateImageUVMatrix(type, data);
}
},
onEquals: _equalsEx
});
}
return _private.uvData;
}
_getImageUVMatrixByType(type) {
let _private = this[__.private];
type = _toImageType(type);
this._createUVMatrixData();
let uvData = _private.uvData.getValueByDefault(type);
if (!uvData) {
_private.uvData.setValue(type, {
'offset': [0, 0],
'repeat': [1, 1],
'center': [0, 0],
'rotation': 0
}, _equalsEx);
uvData = _private.uvData.getValueByDefault(type);
}
return uvData;
}
// #endregion
// #region Private-Edge
_createEdgeData() {
let _private = this[__.private];
if (!_private.edgeData) {
_private.edgeData = new ObjectAttributes({
data: cEdgeDefaultValues,
keys: cEdgeDefaultValueKeys,
onCloneObject: (data) => {
return {
enable: data.enable,
color: data.color.slice(0),
opacity: data.opacity,
glow: data.glow,
thresholdAngle: data.thresholdAngle
};
},
onConvertValue: (type, value) => {
if (type == 'color') {
return Utils.parseColor(value, null);
}
},
onChange: (ev) => {
this._updateDiffSlots(Flag.Edge, true);
_private.modifier.onChangeEdge(ev.data);
},
onEquals: _equalsEx
});
}
return _private.edgeData;
}
// #endregion
// #region Private-Effect
_createEffectData() {
let _private = this[__.private];
if (!_private.effectData) {
_private.effectData = new ObjectAttributes({
data: cEffectDefaultValues,
keys: cEffectDefaultValueKeys,
onCloneObject: (data) => {
return {
glow: data.glow,
innerGlow: data.innerGlow,
lineBloom: data.lineBloom,
tailing: data.tailing,
radial: data.radial,
ghosting: data.ghosting
};
},
onChange: (ev) => {
this._updateDiffSlots(Flag.Effect, true);
_private.modifier.onChangeEffect(ev.data);
},
onCloneObject: _assignObject,
onEquals: _equalsEx
});
}
return _private.effectData;
}
// #endregion
// #endregion
getValues() {
let _private = this[__.private];
let values = {
data: _private.data.getValues().data
};
if (_private.edgeData) {
values['edge'] = _private.edgeData.getValues().data;
}
if (_private.effectData) {
values['effect'] = _private.effectData.getValues().data;
}
if (_private.imagesData) {
values['images'] = _private.imagesData.getValues().data;
}
if (_private.uvData) {
values['uvData'] = _private.uvData.getValues().data;
}
cValueKeys.forEach(key => {
if (_private.values[key]) {
values[key] = _private.values[key].data;
}
});
return values;
}
makeCurrentAsBaseValue() {
let _private = this[__.private];
let diffValues = _private.state.values & cDiffValueFlags;
if (!diffValues) {
return;
}
if (diffValues & cBaseValueFlags) {
_private.data.makeCurrentAsBaseValue();
}
if (_private.edgeData && _private.edgeData.isChanged()) {
_private.edgeData.makeCurrentAsBaseValue();
}
if (_private.effectData && _private.effectData.isChanged()) {
_private.effectData.makeCurrentAsBaseValue();
}
if (_private.imagesData && _private.imagesData.isChanged()) {
_private.imagesData.makeCurrentAsBaseValue();
}
if (_private.uvData && _private.uvData.isChanged()) {
_private.uvData.makeCurrentAsBaseValue();
}
cValueKeys.forEach(key => {
if (_private.values[key]) {
_private.values[key].makeCurrentAsBaseValue();
}
});
_private.state.clear(cDiffValueFlags);
}
dispose() {
let _private = this[__.private];
if (_private.edgeData) {
_private.edgeData.dispose();
_private.edgeData = null;
}
if (_private.effectData) {
_private.effectData.dispose();
_private.effectData = null;
}
if (_private.imagesData) {
_private.imagesData.dispose();
_private.imagesData = null;
}
if (_private.uvData) {
_private.uvData.dispose();
_private.uvData = null;
}
// TODO ? Really need this ?!
// Remember: we should not clear any values here, due to style use the callback functions what reply on it
// if (_private.data) {
// _private.data.dispose();
// _private.data = null;
// }
this.onDisposeModifier()
cValueKeys.forEach(key => {
if (_private.values[key]) {
_private.values[key].dispose();
_private.values[key] = null;
}
});
}
onDisposeModifier() {
let _private = this[__.private];
if (_private.modifier) {
let object = _private.modifier.object;
object.off(EventType.PlayAnimation, cAnimationTagName);
object.off(EventType.StopAnimation, cAnimationTagName);
_private.modifier.dispose();
_private.modifier = null;
}
}
updateBaseValues(values) {
let _private = this[__.private];
let diffValues = _private.state.values & cDiffValueFlags;
if (!diffValues) {
return;
}
if (diffValues & cBaseValueFlags) {
_private.data.updateBaseValues(values.data);
}
if (values.edge) {
this._createEdgeData();
_private.edgeData.updateBaseValues(values.edge);
}
if (values.effect) {
this._createEffectData();
_private.effectData.updateBaseValues(values.effect);
}
if (values.images) {
this._createImagesData();
_private.imagesData.updateBaseValues(values.images);
}
if (values.uv) {
this._createUVMatrixData();
_private.uvData.updateBaseValues(values.uv);
}
cValueKeys.forEach(key => {
if (_private.values[key]) {
_private.values[key].makeCurrentAsBaseValue();
}
});
_private.state.clear(cDiffValueFlags);
}
initResource(resource) {
let _private = this[__.private];
_private.modifier.initResource(resource);
}
updateResource(resource) {
let _private = this[__.private];
_private.modifier.updateResource(resource);
}
replaceResource(resource) {
let _private = this[__.private];
_private.modifier.replaceResource(resource);
}
getOringialValues() {
return this._getValues('data');
}
getValuesWithDefaults() {
let _private = this[__.private];
let values = {
data: _private.data.getValuesWithDefaults(_equals)
};
if (_private.edgeData) {
values['edge'] = _private.edgeData.getValuesWithDefaults(_equalsEx);
}
if (_private.effectData) {
values['effect'] = _private.effectData.getValuesWithDefaults(_equalsEx);
}
if (_private.imagesData) {
values['images'] = _private.imagesData.getValuesWithDefaults(_equalsEx);
}
if (_private.uvData) {
values['uvData'] = _private.uvData.getValuesWithDefaults(_equalsEx);
}
return values;
}
getData(key) {
if (key == 'edge') {
return this._createEdgeData().data;
}
else if (key == 'effect') {
return this._createEffectData().data;
}
else if (key == 'images') {
return this._createImagesData().data;
}
else if (key == 'uv') {
return this._createUVMatrixData().data;
}
else if (key == 'attributes') {
return this._createAttributes().data;
}
else if (key == 'macros') {
return this._createMacros().data;
}
else if (key == 'uniforms') {
return this._createUniforms().data;
}
return null;
}
copyFromValues(values) {
let data = values['data'];
for (let key in data) {
this[key] = data[key];
}
let edge = values['edge'];
if (edge) {
this.edge = edge;
}
let effect = values['effect'];
if (effect) {
this.effect = effect;
}
let images = values['images'];
if (images) {
for (let key in images) {
let image = images[key];
if (image) {
this.setImage(key, image);
}
}
}
let uvData = values['uvData'];
if (uvData) {
let prevImageSlotType = this.imageSlotType;
for (let key in uvData) {
this.imageSlotType = key;
let uv = uvData[key];
this.uv.offset = uv.offset;
this.uv.repeat = uv.repeat;
this.uv.center = uv.center;
this.uv.rotation = uv.rotation;
}
this.imageSlotType = prevImageSlotType;
}
}
copyFromProperties(values) {
for (let key in values) {
if (key === 'edge') {
let edge = values['edge'];
if (edge) {
this.edge = edge;
}
}
else if (key === 'effect') {
let effect = values['effect'];
if (effect) {
this.effect = effect;
}
}
else if (key === 'images') {
let images = values['images'];
if (images) {
for (let key in images) {
let image = images[key];
if (image) {
this.setImage(key, image);
}
}
}
}
else if (key === 'uvData') {
let uvData = values['uvData'];
if (uvData) {
let prevImageSlotType = this.imageSlotType;
for (let key in uvData) {
this.imageSlotType = key;
let uv = uvData[key];
this.uv.offset = uv.offset;
this.uv.repeat = uv.repeat;
this.uv.center = uv.center;
this.uv.rotation = uv.rotation;
}
this.imageSlotType = prevImageSlotType;
}
}
else {
this[key] = values[key];
}
}
}
/**
* Copy from another style or object.
* @param {object|THING.Style} style The style or object.
* @returns {THING.Style} 返回当前样式。
* @private
* @example
* let style = new THING.Style();
* style.copy(anotherStyle);
*/
copy(style) {
if (!style) {
return this;
}
if (style.isStyle) {
this.copyFromValues(style.getOringialValues());
}
else {
if (Utils.isObject(style)) {
for (let key in style) {
// `app` 只有 getter 没有 setter,复制时跳过避免只读属性赋值报错。
if (key === 'app') {
continue;
}
this[key] = style[key];
}
}
}
return this;
}
/**
* Get image.
* @param {ImageSlotType} type The image slot type.
* @returns {THING.ImageTexture} 返回图片资源。
* @private
* @example
* let style = new THING.Style();
* let image = style.getImage(THING.ImageSlotType.Map);
* // @expect(image === style.images.map);
*/
getImage(type) {
let _private = this[__.private];
if (!_private.imagesData) {
return null;
}
return _private.imagesData.data[_toImageType(type)];
}
/**
* Set image.
* @param {ImageSlotType} type The image slot type.
* @param {THING.ImageTexture} value The image texture resource.
* If you want to use a `canvas` as texture, wrap it first:
* `new THING.ImageTexture({ resource: canvas })`.
* @private
* @example
* let style = new THING.Style();
* style.setImage(THING.ImageSlotType.Map, myImageTexture);
*
* // Use a canvas as texture
* let canvas = document.createElement('canvas');
* // draw pixels to canvas...
* let canvasTexture = new THING.ImageTexture({ resource: canvas });
* style.setImage(THING.ImageSlotType.Map, canvasTexture);
* // 设置 Map 槽位的图片资源
*
* style.setImage(THING.ImageSlotType.Normal, THING.Empty.Texture);
* // 将 Normal 槽位图片清空为默认空贴图
*/
setImage(type, value) {
// Check value
if (value) {
// Check whether need to clear all textures.
if (value == Empty.ImageTexture) {
value = this.app.global.cache.images['empty_image_texture'];
console.warn('THING.Empty.ImageTexture will not be supported soon. Use THING.Empty.Texture instead.')
}
if (value == Empty.Texture) {
value = this.app.global.cache.images['empty_image_texture'];
}
if (!Utils.isValidTexture(value)) {
Utils.error(`Set style image failed, due to image is unknown`, value);
return;
}
}
// Convert image type
type = _toImageType(type);
// Create images data
let imagesData = this._createImagesData();
// Use the right image resource to release
let isChangingDefaultValues = imagesData.isChangingDefaultValues();
// Check whether it's same image resource
let imageResource = isChangingDefaultValues ? imagesData.defaultValues[type] : imagesData.data[type];
if (imageResource == value) {
return;
}
// Release previous image
if (imageResource) {
imageResource.release();
}
// Update image data
imagesData.updateValue(type, value);
this._updateImage(type, value, isChangingDefaultValues);
}
/**
* Get image UV.
* @param {ImageSlotType} type The image slot type.
* @returns {StyleUVMatrixResult} 返回 UV 矩阵结果。
* @private
* @example
* let style = new THING.Style();
* let uv = style.getUV(THING.ImageSlotType.Map);
* // @expect(uv === style.uvs.map);
*/
getUV(type) {
let uvData = this._getImageUVMatrixByType(type);
if (!uvData) {
return null;
}
return uvData;
}
/**
* Set image UV.
* @param {ImageSlotType} type The image slot type.
* @param {string} key The UV key.
* @param {StyleUVMatrixResult} value The UV value.
* @private
* @example
* let style = new THING.Style();
* // 设置 'map' 类型的 UV 信息
* style.setUV(THING.ImageSlotType.Map, "uv", { rotation: 0, scale: [1, 1], offset: [0, 0] });
* // 设置 'alphaMap' 类型的 UV 信息
* style.setUV(THING.ImageSlotType.AlphaMap, "uv", { rotation: 0.5, scale: [0.5, 0.5], offset: [0.1, 0.1] });
*/
setUV(type, key, value) {
let uvData = this._getImageUVMatrixByType(type);
if (!uvData) {
return;
}
let _private = this[__.private];
let curImageSlotType = _private.curImageSlotType;
_private.curImageSlotType = type;
uvData[key] = value;
_private.curImageSlotType = curImageSlotType;
}
/**
* Begin to set default values.
* @example
* style.beginDefaultValues();
* @public
*/
beginDefaultValues() {
let _private = this[__.private];
_private.state.enable(Flag.ChangingDefaultValues, true);
this._createEdgeData();
this._createEffectData();
this._createImagesData();
this._createUVMatrixData();
_private.edgeData.beginDefaultValues();
_private.effectData.beginDefaultValues();
_private.imagesData.beginDefaultValues();
_private.uvData.beginDefaultValues();
_private.data.beginDefaultValues();
cValueKeys.forEach(key => {
if (_private.values[key]) {
_private.values[key].beginDefaultValues();
}
});
let attributes = this._createAttributes();
attributes.beginDefaultValues();
let uniforms = this._createUniforms();
uniforms.beginDefaultValues();
let macros = this._createMacros();
macros.beginDefaultValues();
}
/**
* End to set default values.
* @example
* style.endDefaultValues();
* @public
*/
endDefaultValues() {
let _private = this[__.private];
_private.edgeData.endDefaultValues();
_private.effectData.endDefaultValues();
_private.imagesData.endDefaultValues();
_private.uvData.endDefaultValues();
_private.data.endDefaultValues();
cValueKeys.forEach(key => {
if (_private.values[key]) {
_private.values[key].endDefaultValues();
}
});
let attributes = this._createAttributes();
attributes.endDefaultValues();
let uniforms = this._createUniforms();
uniforms.endDefaultValues();
let macros = this._createMacros();
macros.endDefaultValues();
_private.state.enable(Flag.ChangingDefaultValues, false);
}
getDiffValues() {
let _private = this[__.private];
let values = _private.data.getDiffValues();
if (_private.edgeData) {
let diffValues = _private.edgeData.getDiffValues();
if (!Utils.isEmptyObject(diffValues)) {
values['edge'] = diffValues;
}
}
if (_private.effectData) {
let diffValues = _private.effectData.getDiffValues();
if (!Utils.isEmptyObject(diffValues)) {
values['effect'] = diffValues;
}
}
if (_private.imagesData) {
let diffValues = _private.imagesData.getDiffValues();
if (!Utils.isEmptyObject(diffValues)) {
values['images'] = diffValues;
}
}
if (_private.uvData) {
let diffValues = _private.uvData.getDiffValues();
if (!Utils.isEmptyObject(diffValues)) {
values['uvData'] = diffValues;
}
}
return values;
}
getDiffAttributes() {
let that = this;
let diffAttributes = {};
if (that.resource) {
for (let key in StyleAttributeKeys) {
let attrKey = StyleAttributeKeys[key];
if (that.resource.isAttributeModified(attrKey)) {
let value = that.getAttribute(attrKey)
diffAttributes[attrKey] = value;
}
}
}
return diffAttributes;
}
bindAnimationEvents(object) {
let _private = this[__.private];
if (_private.state.has(Flag.HookAnimationEvent)) {
return;
}
_private.state.enable(Flag.HookAnimationEvent, true);
// Hook animation events
let animationNames = object.animationNames;
if (animationNames && animationNames.length) {
object.on(EventType.PlayAnimation, (ev) => {
this._updateDiffSlots(Flag.Animation, true);
}, cAnimationTagName);
object.on(EventType.StopAnimation, (ev) => {
// let animations = object.animations;
// for (let i = 0; i < animations.length; i++) {
// let animation = animations[i];
// if (animation.state != PlayStateType.Ready || animation.state != PlayStateType.Stopped) {
// return;
// }
// }
this._updateDiffSlots(Flag.Animation, false);
}, cAnimationTagName);
}
}
/**
* 开始样式设置。
* @example
* style.begin();
*/
begin() {
if (this.resource) {
this.resource.begin();
}
else {
let _private = this[__.private];
_private.state.enable(Flag.DelayBegin, true);
}
}
/**
* 结束样式设置。
* @example
* style.end();
*/
end() {
if (this.resource) {
this.resource.end();
}
else {
let _private = this[__.private];
_private.state.enable(Flag.DelayBegin, false);
}
}
getProperties() {
let _private = this[__.private];
let values = _private.data['data'];
if (_private.edgeData) {
values['edge'] = _private.edgeData['data'];
}
if (_private.effectData) {
values['effect'] = _private.effectData['data'];
}
if (_private.imagesData) {
values['images'] = _private.imagesData['data'];
}
if (_private.uvData) {
values['uvData'] = _private.uvData['data'];
}
return values;
}
/**
* Set attribute.
* @param {string} type The type.
* @param {*} value The value.
* @private
* @example
* let style = new THING.Style();
* style.setAttribute('color', '#ff0000');
* style.setAttribute('opacity', 0.8);
* // @expect(style.getAttribute('color') === '#ff0000');
* // @expect(style.getAttribute('opacity') === 0.8);
*/
setAttribute(type, value) {
let attributes = this._createAttributes();
// Try to sync base value with resource default attribute value
let currentValue = attributes.getValue(type);
if (currentValue === null && this.resource) {
currentValue = this.resource.getAttribute(type);
if (Utils.isValid(currentValue)) {
attributes.updateBaseValue(type, currentValue);
}
}
this._updateValues(Flag.Attributes, type, value, attributes);
}
/**
* Get attribute.
* @param {string} type The type.
* @returns {*} 返回 attribute 的值。
* @private
* @example
* let style = new THING.Style();
* let value = style.getAttribute('name');
* // @expect(value === style.attributes.name);
*/
getAttribute(type) {
let attributes = this._createAttributes();
return attributes.getValue(type, true);
}
/**
* Set uniform value.
* @param {string} name The name.
* @param {*} value The value.
* @private
* @example
* let style = new THING.Style();
* style.setUniform('u_color', [1, 0, 0]);
* style.setUniform('u_opacity', 0.8);
* // @expect(style.getUniform('u_color') === [1, 0, 0]);
* // @expect(style.getUniform('u_opacity') === 0.8);
*/
setUniform(name, value) {
let uniforms = this._createUniforms();
this._updateValues(Flag.Uniforms, name, value, uniforms);
}
/**
* Get uniform value.
* @param {string} name The name.
* @returns {*} 返回 uniform 的值。
* @private
* @example
* let style = new THING.Style();
* let value = style.getUniform('name');
* // @expect(value === style.uniforms.name);
*/
getUniform(name) {
let uniforms = this._createUniforms();
return uniforms.getValue(name, true);
}
/**
* Set macro value.
* @param {string} name The name.
* @param {*} value The value.
* @private
* @example
* let style = new THING.Style();
* style.setMacro('u_color', [1, 0, 0]);
* style.setMacro('u_opacity', 0.8);
* // @expect(style.getMacro('u_color') === [1, 0, 0]);
* // @expect(style.getMacro('u_opacity') === 0.8);
*/
setMacro(name, value) {
let macros = this._createMacros();
this._updateValues(Flag.Macros, name, value, macros);
}
/**
* Get macro value.
* @param {string} name The name.
* @returns {*} 返回宏的值。
* @private
* @example
* let style = new THING.Style();
* let value = style.getMacro('name');
* // @expect(value === style.macros.name);
*/
getMacro(name) {
let macros = this._createMacros();
return macros.getValue(name, true);
}
toJSON() {
let style = {};
function _export(values, keys, callback) {
keys.forEach(key => {
let value = values[key];
if (Utils.isNull(value)) {
return;
}
if (Utils.isFunction(value)) {
return;
}
callback(key, value)
});
}
_export(this, cDefaultValueKeys, (key, value) => {
style[key] = value;
});
_export(this.edge, cEdgeDefaultValueKeys, (key, value) => {
style.edge = style.edge || {};
style.edge[key] = value;
});
_export(this.effect, cEffectDefaultValueKeys, (key, value) => {
style.effect = style.effect || {};
style.effect[key] = value;
});
return style;
}
// #region Accessor
get originalResource() {
return this[__.private].modifier.originalResource;
}
get resource() {
return this[__.private].modifier.resource;
}
get modifier() {
return this[__.private].modifier;
}
set modifier(value) {
this[__.private].modifier = value;
}
get values() {
return this._getValues('values');
}
get data() {
return this._getValues('data');
}
get isInstancedDrawing() {
let _private = this[__.private];
let style = _private.modifier.object.bodyNode.getStyle();
return style.isEnabled('InstancedDrawing');
}
get isChanged() {
let _private = this[__.private];
if (_private.data.isChanged()) {
return true;
}
if (_private.edgeData && _private.edgeData.isChanged()) {
return true;
}
if (_private.effectData && _private.effectData.isChanged()) {
return true;
}
if (_private.imagesData && _private.imagesData.isChanged()) {
return true;
}
if (_private.uvData && _private.uvData.isChanged()) {
return true;
}
for (let i = 0; i < cValueKeys.length; i++) {
const vk = cValueKeys[i];
const vv = _private.values[vk]
if (vv && vv.isChanged()) {
return true
}
}
return false;
}
// #region Common
/**
* 设置/获取 叠加类型
* @type {THING.BlendingType}
* @public
* @example
* style.blendingType = THING.BlendingType.Additive;
*/
get blendingType() {
return this.values.data.blendingType;
}
set blendingType(value) {
this._updateValue(Flag.BlendingType, 'blendingType', value);
}
/**
* 设置/获取 正面|背面|双面渲染
* @type {THING.SideType}
* @public
* @example
* style.sideType = THING.SideType.Double;
*/
get sideType() {
return this.values.data.sideType;
}
set sideType(value) {
this._updateValue(Flag.SideType, 'sideType', value);
}
/**
* 设置/获取 是否透明(即我们平时说的透明物体还是不透明物体,渲染排序时这两种物体在不同的队列中)
* 如果transparent为false,即便设置透明度,或者对象是用的贴图是透明的,仍然没有透明效果
* @type {boolean}
* @public
* @example
* style.transparent = true;
*/
get transparent() {
return this.values.data.transparent;
}
set transparent(value) {
this._updateValue(Flag.Transparent, 'transparent', value);
}
/**
* 是否开启线框模式(绘制时使用组成mesh的三角面线框)
* @type {boolean}
* @public
* @example
* style.wireframe = true;
*/
get wireframe() {
return this.values.data.wireframe;
}
set wireframe(value) {
this._updateValue(Flag.Wireframe, 'wireframe', value);
}
/**
* 是否开启深度测试
* @type {boolean}
* @public
* @example
* style.depthTest = true;
*/
get depthTest() {
return this.values.data.depthTest;
}
set depthTest(value) {
this._updateValue(Flag.DepthTest, 'depthTest', value);
}
/**
* 是否写入深度
* @type {boolean}
* @public
* @example
* style.depthWrite = true;
*/
get depthWrite() {
return this.values.data.depthWrite;
}
set depthWrite(value) {
this._updateValue(Flag.DepthWrite, 'depthWrite', value);
}
/**
* 是否开启环境贴图
* @type {boolean}
* @public
* @example
* style.envMapping = true;
*/
get envMapping() {
return this.values.data.envMapping;
}
set envMapping(value) {
this._updateValue(Flag.EnvMapping, 'envMapping', value);
}
// #endregion
// #region Image
/**
* 设置当前的纹理类型,主要在设置纹理参数时使用
* @type {ImageSlotType}
* @public
* @example
* style.imageSlotType = THING.ImageSlotType.Map;
*/
get imageSlotType() {
let _private = this[__.private];
return _private.curImageSlotType;
}
set imageSlotType(value) {
let _private = this[__.private];
_private.curImageSlotType = _toUImageType(value);
}
/**
* 设置/获取当前贴图类型下的图片资源(需要传 `THING.ImageTexture`)。
* 若要使用 `canvas` 作为纹理,请先包装成 `new THING.ImageTexture({ resource: canvas })`。
* @type {THING.ImageTexture}
* @public
* @example
* style.image = new THING.ImageTexture('path/to/texture.png');
*
* // Use a canvas as texture
* let canvas = document.createElement('canvas');
* // draw pixels to canvas...
* style.image = new THING.ImageTexture({ resource: canvas });
*/
get image() {
let _private = this[__.private];
return this.getImage(_private.curImageSlotType);
}
set image(value) {
let _private = this[__.private];
this.setImage(_private.curImageSlotType, value);
}
/**
* 设置/获取 基础纹理
* @type {THING.ImageTexture}
* @public
* @example
* // 设置基础纹理
* style.map = new THING.ImageTexture('path/to/texture.png');
*
* // Use a canvas as texture
* let canvas = document.createElement('canvas');
* // draw pixels to canvas...
* style.map = new THING.ImageTexture({ resource: canvas });
*/
get map() {
return this.getImage(ImageSlotType.Map);
}
set map(value) {
this.setImage(ImageSlotType.Map, value);
}
/**
* 设置/获取 环境光纹理
* @type {THING.ImageTexture}
* @public
* @example
* // 设置环境光纹理
* style.envMap = new THING.ImageTexture('path/to/envMap.png');
* // 或使用 canvas:
* // style.envMap = new THING.ImageTexture({ resource: canvas });
*/
get envMap() {
return this.getImage(ImageSlotType.EnvMap);
}
set envMap(value) {
this.setImage(ImageSlotType.EnvMap, value);
}
/**
* 设置/获取 透明纹理
* @type {THING.ImageTexture}
* @public
* @example
* // 设置透明纹理
* style.alphaMap = new THING.ImageTexture('path/to/alphaMap.png');
* // 或使用 canvas:
* // style.alphaMap = new THING.ImageTexture({ resource: canvas });
*/
get alphaMap() {
return this.getImage(ImageSlotType.AlphaMap);
}
set alphaMap(value) {
this.setImage(ImageSlotType.AlphaMap, value);
}
/**
* 设置/获取 自发光纹理
* @type {THING.ImageTexture}
* @public
* @example
* // 设置自发光纹理
* style.emissiveMap = new THING.ImageTexture('path/to/emissiveMap.png');
* // 或使用 canvas:
* // style.emissiveMap = new THING.ImageTexture({ resource: canvas });
*/
get emissiveMap() {
return this.getImage(ImageSlotType.EmissiveMap);
}
set emissiveMap(value) {
this.setImage(ImageSlotType.EmissiveMap, value);
}
/**
* 设置/获取 法线纹理
* @type {THING.ImageTexture}
* @public
* @example
* // 设置法线纹理
* style.normalMap = new THING.ImageTexture('path/to/normalMap.png');
* // 或使用 canvas:
* // style.normalMap = new THING.ImageTexture({ resource: canvas });
*/
get normalMap() {
return this.getImage(ImageSlotType.NormalMap);
}
set normalMap(value) {
this.setImage(ImageSlotType.NormalMap, value);
}
/**
* 设置/获取 颜色映射纹理
* @type {THING.ImageTexture}
* @public
* @example
* // 设置颜色映射纹理
* style.colorMapping = new THING.ImageTexture('path/to/colorMapping.png');
* // 或使用 canvas:
* // style.colorMapping = new THING.ImageTexture({ resource: canvas });
*/
get colorMapping() {
return this.getImage(ImageSlotType.ColorMapping);
}
set colorMapping(value) {
this.setImage(ImageSlotType.ColorMapping, value);
}
/**
* 设置/获取 环境光遮蔽纹理
* @type {THING.ImageTexture}
* @public
* @example
* // 设置环境光遮蔽纹理
* style.aoMap = new THING.ImageTexture('path/to/aoMap.png');
* // 或使用 canvas:
* // style.aoMap = new THING.ImageTexture({ resource: canvas });
*/
get aoMap() {
return this.getImage(ImageSlotType.AOMap);
}
set aoMap(value) {
this.setImage(ImageSlotType.AOMap, value);
}
// #endregion
// #region UV
/**
* 设置/获取 纹理坐标矩阵
* @type {StyleUVMatrixResult}
* @public
* @example
* // 获取当前纹理坐标矩阵
* let uvMatrix = style.uv;
* // 设置纹理坐标矩阵
* style.uv = {
* offset: [0, 0],
* repeat: [1, 1],
* center: [0, 0],
* rotation: 0
* };
*/
get uv() {
let _private = this[__.private];
return this.getUV(_private.curImageSlotType);
}
set uv(value) {
let _private = this[__.private];
let uvData = this._getImageUVMatrixByType(_private.curImageSlotType);
if (!uvData) {
return;
}
for (let key in value) {
uvData[key] = value[key];
}
}
// #endregion
// #region Color
/**
* 当更新值时调用的函数。
* @callback StyleValueOperationCallback
* @param {number} baseValue 基础值。
* @param {THING.Style} style 样式。
* @public
*/
/**
* 当更新颜色时调用的函数。
* @callback StyleColorOperationCallback
* @param {Array<number>} baseValue 基础颜色。
* @param {THING.Style} style 样式。
* @public
*/
/**
* 设置/获取 不透明度 如果该属性是一个方法,将该方法发返回值作为不透明度
* @type {number|StyleValueOperationCallback}
* @public
* @example
* // 设置不透明度为0.5
* style.opacity = 0.5;
* // 使用函数设置不透明度
* style.opacity = function(baseValue) {
* return baseValue * 0.5;
* };
*/
get opacity() {
return this.values.data.opacity;
}
set opacity(value) {
this._updateValueOpFunc(Flag.Opacity, 'opacity', value, (baseValue, v) => {
return baseValue * v;
});
}
/**
* 设置/获取 颜色值 如果该属性是一个方法,将该方法发返回值作为颜色值
* @type {number|string|Array<number>|StyleColorOperationCallback}
* @public
* @example
* // 设置颜色为红色
* style.color = [1, 0, 0]; // RGB
* // 使用函数设置颜色
* style.color = function(baseValue) {
* return [baseValue[0], baseValue[1], baseValue[2]];
* };
*/
get color() {
let _private = this[__.private];
return _private.data.values['color'];
}
set color(value) {
this._updateColorOpFunc(Flag.Color, 'color', value);
}
/**
* 设置/获取 金属度 如果该属性是一个方法,将该方法发返回值作为金属度
* </br>取值范围0~1 具有较高金属度的材质表面通常具有金属的光泽和反射特性
* </br>可以控制材质表面的反射特性,从而影响光照效果和最终的视觉呈现
* @type {number|StyleValueOperationCallback}
* @public
* @example
* // 设置金属度为0.8
* style.metalness = 0.8;
* // 使用函数设置金属度
* style.metalness = function(baseValue) {
* return baseValue * 0.8;
* };
*/
get metalness() {
return this.values.data.metalness;
}
set metalness(value) {
this._updateValueOpFunc(Flag.Metalness, 'metalness', value);
}
/**
* 设置/获取 粗糙度 如果该属性是一个方法,将该方法发返回值作为粗糙度
* </br>取值范围0~1 具有较高粗糙度的材质表面通常是不规则、粗糙和光滑度较低的,如砂纸、混凝土等材质。
* </br>可以控制材质表面的细节和光滑程度,从而影响光照效果和最终的视觉呈现
* </br>
* @type {number|StyleValueOperationCallback}
* @public
* @example
* // 设置粗糙度为0.6
* style.roughness = 0.6;
* // 使用函数设置粗糙度
* style.roughness = function(baseValue) {
* return baseValue * 0.6;
* };
*/
get roughness() {
return this.values.data.roughness;
}
set roughness(value) {
this._updateValueOpFunc(Flag.Roughness, 'roughness', value);
}
/**
* 设置/获取 自发光颜色值 值越高,颜色越亮(越白)
* @type {number|string|Array<number>|StyleColorOperationCallback}
* @public
* @example
* // 设置自发光颜色为白色
* style.emissive = [1, 1, 1]; // RGB
* // 使用函数设置自发光颜色
* style.emissive = function(baseValue) {
* return [baseValue[0], baseValue[1], baseValue[2]];
* };
*/
get emissive() {
let _private = this[__.private];
return _private.data.values['emissive'];
}
set emissive(value) {
this._updateColorOpFunc(Flag.Emissive, 'emissive', value);
}
/**
* 设置/获取 勾边颜色
* @type {number|string|Array<number>}
* @public
* @example
* // 设置勾边颜色为黑色
* style.outlineColor = [0, 0, 0]; // RGB
*/
get outlineColor() {
let _private = this[__.private];
return _private.data.values['outlineColor'];
}
set outlineColor(value) {
this._updateOutlineColor(Flag.OutlineColor, 'outlineColor', value);
}
// #endregion
// #region Object
/**
* 设置/获取 裁剪平面 在范围内的片元会被裁剪
* @type {Array<THING.ClippingPlanes>}
* @example
* let clippingPlanes = [
* { direction: [0, -1, 0], height: 10 }, // top
* { direction: [0, 1, 0], height: 10 }, // bottom
* { direction: [-1, 0, 0], height: 10 }, // right
* { direction: [1, 0, 0], height: 10 }, // left
* { direction: [0, 0, -1], height: 10 }, // front
* { direction: [0, 0, 1], height: 10 } // back
* ];
*
* let clippingPlanesObject = new THING.ClippingPlanes({
* parent: object,
* planes: clippingPlanes,
* });
*
* object.query('*', { includeSelf: true }).style.clippingPlanes = clippingPlanesObject;
* @public
*/
get clippingPlanes() {
return this.values.data.clippingPlanes;
}
set clippingPlanes(value) {
this._updateValue(Flag.ClippingPlanes, 'clippingPlanes', value);
}
/**
* 设置/获取 边框效果
* @type {StyleEdgeResult}
* @public
* @example
* // 设置边框效果
* style.edge = {
* enable: true, // 是否开启边框
* color: [1, 1, 1], // 边框颜色
* opacity: 1, // 边框不透明度
* glow: false, // 边框是否开启发光效果
* thresholdAngle: 0.5 // 边框阈值角度
* };
*/
get edge() {
let _private = this[__.private];
this._createEdgeData();
return _private.edgeData.values;
}
set edge(value) {
if (!value) {
return;
}
let _private = this[__.private];
this._createEdgeData();
_private.edgeData.mergeValues(value);
}
/**
* 设置/获取 物体级后期配置 需配合app.camera.effect中的属性配置生效
* @type {StyleEffectResult}
* @public
* @example
* // 设置物体级后期配置
* style.effect = {
* glow: true, // 是否开启发光效果
* innerGlow: true, // 是否开启内发光效果
* lineBloom: true, // 是否开启线性发光效果
* tailing: true, // 是否开启拖尾效果
* radial: true, // 是否开启扩散效果
* ghosting: true // 是否开启残影效果
* };
*/
get effect() {
let _private = this[__.private];
this._createEffectData();
return _private.effectData.values;
}
set effect(value) {
if (!value) {
return;
}
let _private = this[__.private];
this._createEffectData();
_private.effectData.mergeValues(value);
}
// #endregion
// #region Attributes
/**
* Get/Set attributes.
* @type {object}
* @private
*/
get attributes() {
let attributes = this._createAttributes();
return attributes.values;
}
set attributes(value) {
for (let key in value) {
this.setAttribute(key, value[key]);
}
}
/**
* Get/Set uniforms.
* @type {object}
* @private
*/
get uniforms() {
let uniforms = this._createUniforms();
return uniforms.values;
}
set uniforms(value) {
for (let key in value) {
this.setUniform(key, value[key]);
}
}
/**
* Get/Set macros.
* @type {object}
* @private
*/
get macros() {
let macros = this._createMacros();
return macros.values;
}
set macros(value) {
for (let key in value) {
this.setMacro(key, value[key]);
}
}
// #endregion
get app() {
let _private = this[__.private];
return _private.modifier.app;
}
/**
* Check class type.
* @type {boolean}
* @private
*/
get isStyle() {
return true;
}
// #endregion
}
export { Style }