mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
270 lines (224 loc) • 7.03 kB
JavaScript
import {vec3, quat} from 'gl-matrix';
import {VEC3_ZERO, VEC3_ONE, QUAT_DEFAULT} from '../../../common/gl-matrix-addon';
import stringHash from '../../../common/stringhash';
import unique from '../../../common/arrayunique';
import AnimatedObject from './animatedobject';
import {layerFilterMode} from './filtermode';
/**
* An MDX layer.
*/
export default class Layer extends AnimatedObject {
/**
* @param {Model} model
* @param {ModelViewer.parser.mdlx.Layer} layer
* @param {number} layerId
* @param {number} priorityPlane
*/
constructor(model, layer, layerId, priorityPlane) {
let filterMode = layer.filterMode;
let textureAnimationId = layer.textureAnimationId;
let gl = model.viewer.gl;
super(model, layer);
this.index = layerId;
this.priorityPlane = priorityPlane;
this.filterMode = filterMode;
this.textureId = layer.textureId;
this.coordId = layer.coordId;
this.alpha = layer.alpha;
let flags = layer.flags;
this.unshaded = flags & 0x1;
this.sphereEnvironmentMap = flags & 0x2;
this.twoSided = flags & 0x10;
this.unfogged = flags & 0x20;
this.noDepthTest = flags & 0x40;
this.noDepthSet = flags & 0x80;
this.depthMaskValue = (filterMode === 0 || filterMode === 1) ? 1 : 0;
this.blendSrc = 0;
this.blendDst = 0;
this.blended = (filterMode > 1) ? true : false;
if (this.blended) {
[this.blendSrc, this.blendDst] = layerFilterMode(filterMode, gl);
}
this.uvDivisor = new Float32Array([1, 1]);
if (textureAnimationId !== -1) {
let textureAnimation = model.textureAnimations[textureAnimationId];
if (textureAnimation) {
this.textureAnimation = textureAnimation;
}
}
let variants = {
alpha: [],
slot: [],
translation: [],
rotation: [],
scale: [],
};
let hasSlotAnim = false;
let hasTranslationAnim = false;
let hasRotationAnim = false;
let hasScaleAnim = false;
for (let i = 0, l = model.sequences.length; i < l; i++) {
let alpha = this.isAlphaVariant(i);
let slot = this.isTextureIdVariant(i);
let translation = this.isTranslationVariant(i);
let rotation = this.isRotationVariant(i);
let scale = this.isScaleVariant(i);
variants.alpha[i] = alpha;
variants.slot[i] = slot;
variants.translation[i] = translation;
variants.rotation[i] = rotation;
variants.scale[i] = scale;
hasSlotAnim = hasSlotAnim || slot;
hasTranslationAnim = hasTranslationAnim || translation;
hasRotationAnim = hasRotationAnim || rotation;
hasScaleAnim = hasScaleAnim || scale;
}
this.variants = variants;
this.hasSlotAnim = hasSlotAnim;
this.hasTranslationAnim = hasTranslationAnim;
this.hasRotationAnim = hasRotationAnim;
this.hasScaleAnim = hasScaleAnim;
// Handle sprite animations
if (this.animations.KMTF) {
// Get all unique texture IDs used by this layer
let textureIds = unique(this.animations.KMTF.getValues().map((array) => array[0]));
if (textureIds.length > 1) {
let hash = stringHash(textureIds.join(''));
let textures = [];
// Grab all of the textures
for (let i = 0, l = textureIds.length; i < l; i++) {
textures[i] = model.textures[textureIds[i]];
}
let atlas = model.viewer.loadTextureAtlas(hash, textures);
atlas.whenLoaded()
.then(() => {
atlas.wrapMode(gl.REPEAT, gl.REPEAT);
model.textures.push(atlas);
this.textureId = model.textures.length - 1;
this.uvDivisor.set([atlas.columns, atlas.rows]);
});
}
}
}
/**
* @param {ShaderProgram} shader
*/
bind(shader) {
let gl = this.model.viewer.gl;
// gl.uniform1f(shader.uniforms.u_unshaded, this.unshaded);
gl.uniform1f(shader.uniforms.u_filterMode, this.filterMode);
if (this.blended) {
gl.enable(gl.BLEND);
gl.blendFunc(this.blendSrc, this.blendDst);
} else {
gl.disable(gl.BLEND);
}
if (this.twoSided) {
gl.disable(gl.CULL_FACE);
} else {
gl.enable(gl.CULL_FACE);
}
if (this.noDepthTest) {
gl.disable(gl.DEPTH_TEST);
} else {
gl.enable(gl.DEPTH_TEST);
}
if (this.noDepthSet) {
gl.depthMask(0);
} else {
gl.depthMask(this.depthMaskValue);
}
}
/**
* @param {Float32Array} out
* @param {ModelInstance} instance
* @return {number}
*/
getAlpha(out, instance) {
return this.getFloatValue(out, 'KMTA', instance, this.alpha);
}
/**
* @param {Uint32Array} out
* @param {ModelInstance} instance
* @return {number}
*/
getTextureId(out, instance) {
let keyframe = this.getUintValue(out, 'KMTF', instance, this.textureId);
// If this layer is using a texture atlas, remap the texture ID to that of the texture atlas.
if (this.texutreAtlasMapping) {
out[0] = this.texutreAtlasMapping[out[0]];
}
return keyframe;
}
/**
* @param {vec3} out
* @param {ModelInstance} instance
* @return {number}
*/
getTranslation(out, instance) {
if (this.textureAnimation) {
return this.textureAnimation.getTranslation(out, instance);
}
vec3.copy(out, VEC3_ZERO);
return -1;
}
/**
* @param {quat} out
* @param {ModelInstance} instance
* @return {number}
*/
getRotation(out, instance) {
if (this.textureAnimation) {
return this.textureAnimation.getRotation(out, instance);
}
quat.copy(out, QUAT_DEFAULT);
return -1;
}
/**
* @param {vec3} out
* @param {ModelInstance} instance
* @return {number}
*/
getScale(out, instance) {
if (this.textureAnimation) {
return this.textureAnimation.getScale(out, instance);
}
vec3.copy(out, VEC3_ONE);
return -1;
}
/**
* @param {number} sequence
* @return {boolean}
*/
isAlphaVariant(sequence) {
return this.isVariant('KMTA', sequence);
}
/**
* @param {number} sequence
* @return {boolean}
*/
isTextureIdVariant(sequence) {
return this.isVariant('KMTF', sequence);
}
/**
* @param {number} sequence
* @return {boolean}
*/
isTranslationVariant(sequence) {
return this.textureAnimation && this.textureAnimation.isTranslationVariant(sequence);
}
/**
* @param {number} sequence
* @return {boolean}
*/
isRotationVariant(sequence) {
return this.textureAnimation && this.textureAnimation.isRotationVariant(sequence);
}
/**
* @param {number} sequence
* @return {boolean}
*/
isScaleVariant(sequence) {
return this.textureAnimation && this.textureAnimation.isScaleVariant(sequence);
}
}