mdx-m3-viewer
Version:
A browser WebGL model viewer. Mainly focused on models of the games Warcraft 3 and Starcraft 2.
268 lines (221 loc) • 6.76 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const shader = `
uniform mat4 u_VP;
uniform int u_emitter;
// Shared
uniform vec4 u_colors[3];
uniform vec3 u_vertices[4];
uniform vec3 u_intervals[4];
uniform float u_lifeSpan;
uniform float u_columns;
uniform float u_rows;
// Particle2
uniform vec3 u_scaling;
uniform vec3 u_cameraZ;
uniform float u_timeMiddle;
uniform bool u_teamColored;
// Splat and Uber.
uniform vec3 u_intervalTimes;
// Vertices
attribute float a_position;
// Instances
attribute vec3 a_p0;
attribute vec3 a_p1;
attribute vec3 a_p2;
attribute vec3 a_p3;
attribute float a_health;
attribute vec4 a_color;
attribute float a_tail;
attribute vec3 a_leftRightTop;
varying vec2 v_texcoord;
varying vec4 v_color;
float getCell(vec3 interval, float factor) {
float start = interval[0];
float end = interval[1];
float repeat = interval[2];
float spriteCount = end - start;
if (spriteCount > 0.0) {
// Repeating speeds up the sprite animation, which makes it effectively run N times in its interval.
// E.g. if repeat is 4, the sprite animation will be seen 4 times, and thus also run 4 times as fast.
// The sprite index is limited to the number of actual sprites.
return min(start + mod(floor(spriteCount * repeat * factor), spriteCount), u_columns * u_rows - 1.0);
}
return start;
}
void particle2() {
float factor = (u_lifeSpan - a_health) / u_lifeSpan;
int index = 0;
if (factor < u_timeMiddle) {
factor = factor / u_timeMiddle;
index = 0;
} else {
factor = (factor - u_timeMiddle) / (1.0 - u_timeMiddle);
index = 1;
}
factor = min(factor, 1.0);
float scale = mix(u_scaling[index], u_scaling[index + 1], factor);
vec4 color = mix(u_colors[index], u_colors[index + 1], factor);
float cell = 0.0;
if (u_teamColored) {
cell = a_leftRightTop[0];
} else {
vec3 interval;
if (a_tail == HEAD) {
interval = u_intervals[index];
} else {
interval = u_intervals[index + 2];
}
cell = getCell(interval, factor);
}
float left = floor(mod(cell, u_columns));
float top = floor(cell / u_columns);
float right = left + 1.0;
float bottom = top + 1.0;
left /= u_columns;
right /= u_columns;
top /= u_rows;
bottom /= u_rows;
if (a_position == 0.0) {
v_texcoord = vec2(right, top);
} else if (a_position == 1.0) {
v_texcoord = vec2(left, top);
} else if (a_position == 2.0) {
v_texcoord = vec2(left, bottom);
} else if (a_position == 3.0) {
v_texcoord = vec2(right, bottom);
}
v_color = color;
if (a_tail == HEAD) {
vec3 v = u_vertices[int(a_position)];
float cs = cos(a_p1.x);
float sn = sin(a_p1.x);
float x = v.x * cs - v.y * sn;
float y = v.x * sn + v.y * cs;
vec3 fv = vec3(
v.x * cs - v.y * sn,
v.x * sn + v.y * cs,
v.z);
gl_Position = u_VP * vec4(a_p0 + fv * scale, 1.0);
} else {
// Get the normal to the tail in camera space.
// This allows to build a 2D rectangle around the 3D tail.
vec3 normal = cross(u_cameraZ, normalize(a_p1 - a_p0));
vec3 boundary = normal * scale * a_p2[0];
vec3 position;
if (a_position == 0.0) {
position = a_p0 - boundary;
} else if (a_position == 1.0) {
position = a_p1 - boundary;
} else if (a_position == 2.0) {
position = a_p1 + boundary;
} else if (a_position == 3.0) {
position = a_p0 + boundary;
}
gl_Position = u_VP * vec4(position, 1.0);
}
}
void ribbon() {
vec3 position;
float left = a_leftRightTop[0] / 255.0;
float right = a_leftRightTop[1] / 255.0;
float top = a_leftRightTop[2] / 255.0;
float bottom = top + 1.0;
if (a_position == 0.0) {
v_texcoord = vec2(right, top);
position = a_p0;
} else if (a_position == 1.0) {
v_texcoord = vec2(right, bottom);
position = a_p1;
} else if (a_position == 2.0) {
v_texcoord = vec2(left, bottom);
position = a_p2;
} else if (a_position == 3.0) {
v_texcoord = vec2(left, top);
position = a_p3;
}
v_texcoord[0] /= u_columns;
v_texcoord[1] /= u_rows;
v_color = a_color;
gl_Position = u_VP * vec4(position, 1.0);
}
void splat() {
float factor = u_lifeSpan - a_health;
int index;
if (factor < u_intervalTimes[0]) {
factor = factor / u_intervalTimes[0];
index = 0;
} else {
factor = (factor - u_intervalTimes[0]) / u_intervalTimes[1];
index = 1;
}
float cell = getCell(u_intervals[index], factor);
float left = floor(mod(cell, u_columns));
float top = floor(cell / u_columns);
float right = left + 1.0;
float bottom = top + 1.0;
vec3 position;
if (a_position == 0.0) {
v_texcoord = vec2(left, top);
position = a_p0;
} else if (a_position == 1.0) {
v_texcoord = vec2(left, bottom);
position = a_p1;
} else if (a_position == 2.0) {
v_texcoord = vec2(right, bottom);
position = a_p2;
} else if (a_position == 3.0) {
v_texcoord = vec2(right, top);
position = a_p3;
}
v_texcoord[0] /= u_columns;
v_texcoord[1] /= u_rows;
v_color = mix(u_colors[index], u_colors[index + 1], factor) / 255.0;
gl_Position = u_VP * vec4(position, 1.0);
}
void ubersplat() {
float factor = u_lifeSpan - a_health;
vec4 color;
if (factor < u_intervalTimes[0]) {
color = mix(u_colors[0], u_colors[1], factor / u_intervalTimes[0]);
} else if (factor < u_intervalTimes[0] + u_intervalTimes[1]) {
color = u_colors[1];
} else {
color = mix(u_colors[1], u_colors[2], (factor - u_intervalTimes[0] - u_intervalTimes[1]) / u_intervalTimes[2]);
}
vec3 position;
if (a_position == 0.0) {
v_texcoord = vec2(0.0, 0.0);
position = a_p0;
} else if (a_position == 1.0) {
v_texcoord = vec2(0.0, 1.0);
position = a_p1;
} else if (a_position == 2.0) {
v_texcoord = vec2(1.0, 1.0);
position = a_p2;
} else if (a_position == 3.0) {
v_texcoord = vec2(1.0, 0.0);
position = a_p3;
}
v_color = color / 255.0;
gl_Position = u_VP * vec4(position, 1.0);
}
void main() {
if (u_emitter == EMITTER_PARTICLE2) {
particle2();
} else if (u_emitter == EMITTER_RIBBON) {
ribbon();
} else if (u_emitter == EMITTER_SPLAT) {
splat();
} else if (u_emitter == EMITTER_UBERSPLAT) {
ubersplat();
}
}
`;
exports.default = shader;
//# sourceMappingURL=particles.vert.js.map