mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
188 lines (165 loc) • 4.65 kB
JavaScript
import {testCell} from '../common/gl-matrix-addon';
/**
* A grid cell.
*/
class Cell {
/**
* @param {number} left
* @param {number} right
* @param {number} bottom
* @param {number} top
*/
constructor(left, right, bottom, top) {
/** @member {number} */
this.left = left;
/** @member {number} */
this.right = right;
/** @member {number} */
this.bottom = bottom;
/** @member {number} */
this.top = top;
/** @member {boolean} */
this.plane = -1;
/** @member {Array<ModelInstance>} */
this.instances = [];
/** @member {boolean} */
this.visible = false;
}
/**
* @param {ModelInstance} instance
*/
add(instance) {
this.instances.push(instance);
}
/**
* @param {ModelInstance} instance
*/
remove(instance) {
let index = this.instances.indexOf(instance);
this.instances.splice(index, 1);
}
/**
* Remove all of the instances from this cell.
*/
clear() {
this.instances.length = 0;
}
/**
* @param {Camera} camera
* @return {boolean}
*/
isVisible(camera) {
this.plane = testCell(camera.planes, this.left, this.right, this.bottom, this.top, this.plane);
return this.plane === -1;
}
}
/**
* A grid.
*/
export default class Grid {
/**
* @param {Array<number>} location
* @param {Array<number>} size
* @param {Array<number>} cellSize
*/
constructor(location, size, cellSize) {
let columns = size[0] / cellSize[0];
let rows = size[1] / cellSize[1];
this.location = location;
this.size = size;
this.columns = columns;
this.rows = rows;
this.cellSize = cellSize;
this.cells = [];
for (let y = 0; y < rows; y++) {
for (let x = 0; x < columns; x++) {
let left = location[0] + x * cellSize[0];
let right = left + cellSize[0];
let bottom = location[1] + y * cellSize[1];
let top = bottom + cellSize[1];
this.cells[y * columns + x] = new Cell(left, right, bottom, top);
}
}
}
/**
* @param {ModelInstance} instance
*/
add(instance) {
let cells = this.cells;
let columns = this.columns;
let left = instance.left;
let right = instance.right + 1;
let bottom = instance.bottom;
let top = instance.top + 1;
if (left !== -1) {
for (let y = bottom; y < top; y++) {
for (let x = left; x < right; x++) {
cells[y * columns + x].add(instance);
}
}
}
}
/**
* @param {ModelInstance} instance
*/
remove(instance) {
let cells = this.cells;
let columns = this.columns;
let left = instance.left;
let right = instance.right + 1;
let bottom = instance.bottom;
let top = instance.top + 1;
if (left !== -1) {
instance.left = -1;
for (let y = bottom; y < top; y++) {
for (let x = left; x < right; x++) {
cells[y * columns + x].remove(instance);
}
}
}
}
/**
* @param {ModelInstance} instance
*/
moved(instance) {
let cellSize = this.cellSize;
let bounds = instance.model.bounds;
let x = instance.worldLocation[0] + bounds.x - this.location[0];
let y = instance.worldLocation[1] + bounds.y - this.location[1];
let r = bounds.r;
let s = instance.worldScale;
let left = Math.floor((x - r * s[0]) / cellSize[0]);
let right = Math.floor((x + r * s[0]) / cellSize[0]);
let bottom = Math.floor((y - r * s[1]) / cellSize[1]);
let top = Math.floor((y + r * s[1]) / cellSize[1]);
if (right < 0 || left > this.columns - 1 || top < 0 || bottom > this.rows - 1) {
// The instance is outside of the grid, so remove it.
this.remove(instance);
} else {
// Clamp the values so they are in the grid.
left = Math.max(left, 0);
right = Math.min(right, this.columns - 1);
bottom = Math.max(bottom, 0);
top = Math.min(top, this.rows - 1);
// If the values actually changed, update the cells.
if (left !== instance.left || right !== instance.right || bottom !== instance.bottom || top !== instance.top) {
/// TODO: This can be optimized by checking if there are shared cells.
/// That can be done in precisely the same way as done a few lines above, i.e. simple rectangle intersection.
this.remove(instance);
instance.left = left;
instance.right = right;
instance.bottom = bottom;
instance.top = top;
this.add(instance);
}
}
}
/**
* Removes all of the instances from this grid.
*/
clear() {
for (let cell of this.cells) {
cell.clear();
}
}
}