mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
155 lines (128 loc) • 4.84 kB
JavaScript
import {vec3} from 'gl-matrix';
import {BYTES_PER_OBJECT, FLOATS_PER_OBJECT, FLOAT_OFFSET_P0, BYTE_OFFSET_COLOR, BYTE_OFFSET_LEFT_RIGHT_TOP} from './geometryemitter';
// Heap allocations needed for this module.
let belowHeap = vec3.create();
let aboveHeap = vec3.create();
let colorHeap = vec3.create();
let alphaHeap = new Float32Array(1);
let slotHeap = new Float32Array(1);
/**
* A ribbon.
*/
export default class Ribbon {
/**
* @param {RibbonEmitter} emitter
*/
constructor(emitter) {
this.emitter = emitter;
this.emitterView = null;
this.index = 0;
this.health = 0;
this.vertices = new Float32Array(12);
}
/**
* @param {RibbonEmitterView} emitterView
*/
bind(emitterView) {
let emitter = this.emitter;
emitterView.ribbonCount++;
this.emitterView = emitterView;
this.index = emitterView.currentRibbon++;
this.health = emitter.modelObject.lifeSpan;
let lastEmit = emitterView.lastEmit;
// If this isn't the first ribbon, construct a quad.
// Otherwise, the vertices will be filled with zeroes, and the ribbon will not render.
// This allows the emitter to always work with quads, and therefore it can work with many views, because the ribbon chains are implicit.
if (lastEmit && lastEmit.health > 0) {
let node = emitterView.instance.nodes[emitter.modelObject.index];
let [x, y, z] = node.pivot;
let worldMatrix = node.worldMatrix;
emitterView.getHeightBelow(belowHeap);
emitterView.getHeightAbove(aboveHeap);
let heightBelow = belowHeap[0];
let heightAbove = aboveHeap[0];
belowHeap[0] = x;
belowHeap[1] = y - heightBelow;
belowHeap[2] = z;
aboveHeap[0] = x;
aboveHeap[1] = y + heightAbove;
aboveHeap[2] = z;
vec3.transformMat4(belowHeap, belowHeap, worldMatrix);
vec3.transformMat4(aboveHeap, aboveHeap, worldMatrix);
let vertices = this.vertices;
let lastVertices = lastEmit.vertices;
// Left top
vertices[0] = aboveHeap[0];
vertices[1] = aboveHeap[1];
vertices[2] = aboveHeap[2];
// Left bottom
vertices[3] = belowHeap[0];
vertices[4] = belowHeap[1];
vertices[5] = belowHeap[2];
// Right bottom
vertices[6] = lastVertices[3];
vertices[7] = lastVertices[4];
vertices[8] = lastVertices[5];
// Right top
vertices[9] = lastVertices[0];
vertices[10] = lastVertices[1];
vertices[11] = lastVertices[2];
}
}
/**
* @param {number} offset
* @param {number} dt
*/
render(offset, dt) {
let emitterView = this.emitterView;
this.health -= dt;
if (this.health > 0) {
let emitter = this.emitter;
let modelObject = emitter.modelObject;
let byteView = emitter.byteView;
let floatView = emitter.floatView;
let byteOffset = offset * BYTES_PER_OBJECT;
let floatOffset = offset * FLOATS_PER_OBJECT;
let p0Offset = floatOffset + FLOAT_OFFSET_P0;
let colorOffset = byteOffset + BYTE_OFFSET_COLOR;
let leftRightTopOffset = byteOffset + BYTE_OFFSET_LEFT_RIGHT_TOP;
emitterView.getColor(colorHeap);
emitterView.getAlpha(alphaHeap);
emitterView.getTextureSlot(slotHeap);
let animatedSlot = slotHeap[0];
let chainLengthFactor = 1 / emitterView.ribbonCount;
let locationInChain = (emitterView.currentRibbon - this.index - 1);
let columns = modelObject.dimensions[0];
let left = (animatedSlot % columns) + (locationInChain * chainLengthFactor);
let top = (animatedSlot / columns) | 0;
let right = left + chainLengthFactor;
let vertices = this.vertices;
let gravity = modelObject.gravity * dt * dt;
vertices[1] -= gravity;
vertices[4] -= gravity;
vertices[7] -= gravity;
vertices[10] -= gravity;
floatView[p0Offset + 0] = vertices[0];
floatView[p0Offset + 1] = vertices[1];
floatView[p0Offset + 2] = vertices[2];
floatView[p0Offset + 3] = vertices[3];
floatView[p0Offset + 4] = vertices[4];
floatView[p0Offset + 5] = vertices[5];
floatView[p0Offset + 6] = vertices[6];
floatView[p0Offset + 7] = vertices[7];
floatView[p0Offset + 8] = vertices[8];
floatView[p0Offset + 9] = vertices[9];
floatView[p0Offset + 10] = vertices[10];
floatView[p0Offset + 11] = vertices[11];
byteView[colorOffset + 0] = colorHeap[0] * 255;
byteView[colorOffset + 1] = colorHeap[1] * 255;
byteView[colorOffset + 2] = colorHeap[2] * 255;
byteView[colorOffset + 3] = alphaHeap[0] * 255;
byteView[leftRightTopOffset + 0] = left * 255;
byteView[leftRightTopOffset + 1] = right * 255;
byteView[leftRightTopOffset + 2] = top * 255;
} else {
emitterView.ribbonCount--;
}
}
}