Initial implementation of Nitro Particles

- Drifting particles and mini turbo (sans extra spark particles)
- Slowdown particles (grass/sand/water)
- Itembox collect particle
- Wall collide particle (this is really extreme and there should
probably be others)
- Shader improvements, such as dithered shadows for alpha and reduction
of artifacts. The best way to remove all artifacts would be generating
normals for courses and using the diffuse direction as a more reliable
indication of shadowed/not.
- Fixed checkpoints not resetting on new laps.
- Fixed me not being able to count to 3. (now 3 laps instead of 4)
- Fixed battle mode courses crashing on load and during due to AI being
funny (above 31, eg 36 for block fort)
pull/5/head
RHY3756547 2018-03-24 23:50:10 +00:00
parent 870bef6689
commit 4af42625a0
18 changed files with 6338 additions and 3119 deletions

View File

@ -52,7 +52,7 @@ window.cameraIngame = function(kart) {
var dist = 192;
this.targetShadowPos = vec3.add([], kart.pos, [Math.sin(kart.angle)*dist, 0, -Math.cos(kart.angle)*dist])
thisObj.view = {p:p, mv:mat};
thisObj.view = {p:p, mv:mat, pos: vec3.scale([], vec3.transformMat4([], [0,0,0], mat4.invert([], mat)), 1024)};
return thisObj.view;
}

View File

@ -102,7 +102,7 @@ window.cameraIntro = function() {
thisObj.targetShadowPos = lookAtPos;
return {p:p, mv:mat}
return {p:p, mv:mat, pos: vec3.scale([], vec3.transformMat4([], [0,0,0], mat4.invert([], mat)), 1024)}
}
var initCam = function(scene, came) {

View File

@ -55,6 +55,7 @@ window.cameraSpectator = function(kart) {
if (zoomLevel > curCam.zoomEnd) zoomLevel = curCam.zoomEnd;
thisObj.view = camFunc[curCam.camType](scene, curCam);
thisObj.view.pos = vec3.scale([], vec3.transformMat4([], [0,0,0], mat4.invert([], thisObj.view.mv)), 1024)
return thisObj.view;
}

View File

@ -141,8 +141,10 @@ window.MKDS_COLTYPE = new (function(){
this.PHYS_MAP[this.LOOP] = 11;
//collision sound handlers
//26 is blue water, 30 is white
//28 and 15 might be sand/dirt
var waterRoad = {drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATER, drive: MKDS_COLSOUNDS.DRIVE_WATER};
var waterRoad = {drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATER, drive: MKDS_COLSOUNDS.DRIVE_WATER, particle: 30};
this.SOUNDMAP = {
0x00: //road
@ -164,7 +166,7 @@ window.MKDS_COLTYPE = new (function(){
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
{drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATERDEEP, drive: MKDS_COLSOUNDS.DRIVE_WATER},
{drift: MKDS_COLSOUNDS.DRIFT_WATER, brake: MKDS_COLSOUNDS.BRAKE_WATER, land: MKDS_COLSOUNDS.LAND_WATERDEEP, drive: MKDS_COLSOUNDS.DRIVE_WATER, particle: 30},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_ASPHALT},
{},
{},
@ -187,27 +189,27 @@ window.MKDS_COLTYPE = new (function(){
0x03: //road 4
[
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND , land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
{drift: MKDS_COLSOUNDS.DRIFT_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT},
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND , land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
{drift: MKDS_COLSOUNDS.DRIFT_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT, particle: 15},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS, particle: 32},
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW}, //snow
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW, particle:112}, //snow
{},
{}
],
0x05: //road 5
[
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND , land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
{drift: MKDS_COLSOUNDS.DRIFT_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT},
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND , land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
{drift: MKDS_COLSOUNDS.DRIFT_DIRT, brake: MKDS_COLSOUNDS.BRAKE_DIRT, land: MKDS_COLSOUNDS.LAND_DIRT, drive: MKDS_COLSOUNDS.DRIVE_DIRT, particle: 15},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS, particle: 32},
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS},
{drift: MKDS_COLSOUNDS.DRIFT_SAND, brake: MKDS_COLSOUNDS.BRAKE_SAND, land: MKDS_COLSOUNDS.LAND_SAND, drive: MKDS_COLSOUNDS.DRIVE_SAND, particle: 28},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_GRASS, drive: MKDS_COLSOUNDS.DRIVE_GRASS, particle: 32},
{},
{},
{}
@ -216,7 +218,7 @@ window.MKDS_COLTYPE = new (function(){
0x06: //slippery
[
{drift: MKDS_COLSOUNDS.DRIFT_ICE, brake: MKDS_COLSOUNDS.BRAKE_ICE, land:MKDS_COLSOUNDS.LAND_ICE},
{drift: MKDS_COLSOUNDS.DRIFT_MARSH, brake: MKDS_COLSOUNDS.BRAKE_MARSH, land:MKDS_COLSOUNDS.LAND_MARSH, drive: MKDS_COLSOUNDS.DRIVE_MARSH},
{drift: MKDS_COLSOUNDS.DRIFT_MARSH, brake: MKDS_COLSOUNDS.BRAKE_MARSH, land:MKDS_COLSOUNDS.LAND_MARSH, drive: MKDS_COLSOUNDS.DRIVE_MARSH, particle: 24},
{},
{},
{},

View File

@ -102,10 +102,14 @@ window.controlRaceCPU = function(nkm) {
//advance to one of next possible paths
if (battleMode) {
var pathInd = ((Math.random()>0.5 && ePath.source.length>0)?ePath.source:ePath.dest)[Math.floor(Math.random()*ePath.dest.length)];
var loc = (Math.random()>0.5 && ePath.source.length>0)?ePath.source:ePath.dest;
var pathInd = loc[Math.floor(Math.random()*loc.length)];
ePoiInd = pathInd;
ePoi = points[ePoiInd];
recomputePath();
var pt = points[ePoiInd];
if (pt != null) {
ePoi = pt;
recomputePath();
}
} else {
var pathInd = ePath.dest[Math.floor(Math.random()*ePath.dest.length)];
ePath = paths[pathInd];

View File

@ -18,6 +18,7 @@ window.IngameRes = function(rom) {
this.Race = new narc(lz77.decompress(rom.getFile("/data/Scene/Race.carc"))); //contains lakitu, count, various graphics
this.RaceLoc = new narc(lz77.decompress(rom.getFile("/data/Scene/Race_us.carc"))); //contains lakitu lap signs, START, YOU WIN etc. some of these will be replaced by hi res graphics by default.
this.RaceEffect = new spa(r.MainEffect.getFile("RaceEffect.spa"));
this.MainFont = new nftr(r.Main2D.getFile("marioFont.NFTR"));
//debugger;
@ -43,7 +44,6 @@ window.IngameRes = function(rom) {
var characters = [];
var karts = [];
var test = new spa(r.MainEffect.getFile("RaceEffect.spa"));
loadItems();
loadTires();

View File

@ -179,7 +179,7 @@ window.ObjDecor = function(obji, scene) {
forceBill = false;
return {mdl:[{nsbmd:"choropu.nsbmd"}], other:[null, null, "choropu.nsbtp"]}; //has nsbtp
case 0x019B: //cheep cheep (bouncing)
return {mdl:[{nsbmd:"pukupuku.nsbmd"}], other:[null, null, "pukupuku.nsbtp"]}; //has nsbtp
return {mdl:[{nsbmd:"pukupuku.nsbmd"}]}; //has nsbtp //, other:[null, null, "pukupuku.nsbtp"]
case 0x019D: //snowman
return {mdl:[{nsbmd:"sman_top.nsbmd"}, {nsbmd:"sman_bottom.nsbmd"}]};
case 0x019E: //trunk with bats

View File

@ -45,6 +45,7 @@ window.ItemBox = function(obji, scene) {
for (var j=0; j<10; j++) {
scene.particles.push(new ItemShard(scene, ok, res.mdl[2]));
}
scene.particles.push(new NitroEmitter(scene, ok, 47));
t.mode = 1;
t.time = 0;
break;

View File

@ -95,9 +95,18 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
this.lastInput = null;
//race statistics
this.lapNumber = 0;
this.lapNumber = 1;
this.passedKTP2 = false;
this.checkPointNumber = 0;
this.wheelParticles = [
new NitroEmitter(scene, k, -1, [1, 1.5, -1]),
new NitroEmitter(scene, k, -1, [-1, 1.5, -1])
];
scene.particles.push(this.wheelParticles[0]);
scene.particles.push(this.wheelParticles[1]);
var nkm = scene.nkm;
var startLine = nkm.sections["KTPS"].entries[0];
var passLine = nkm.sections["KTP2"].entries[0];
@ -193,6 +202,10 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
mat4.scale(wmat, wmat, [scale, scale, scale]);
if (i<2) mat4.rotateY(wmat, wmat, ((k.driveAnimF-14)/14)*Math.PI/6);
mat4.rotateX(wmat, wmat, wheelTurn);
if (i>1) {
k.wheelParticles[i-2].offset = vec3.scale(k.wheelParticles[i-2].offset, vec3.add(k.wheelParticles[i-2].offset, offsets.wheels[i], [0, -params.colRadius, 0]), 1/16);
}
}
var scale = 16;
@ -262,7 +275,11 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
drawChar(view, pMatrix);
}
k.lWheelParticle = null;
function update(scene) {
var lastPos = vec3.clone(k.pos);
updateMat = true;
@ -314,7 +331,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
sounds.boost = null;
}
if (onGround && k.speed > 0.5) {
var isMoving = onGround && k.speed > 0.5;
if (isMoving) {
if (lastCollided != sounds.lastTerrain || lastBE != sounds.lastBE || sounds.drive == null) {
if (sounds.drive != null) nitroAudio.kill(sounds.drive);
if (lastColSounds.drive != null) {
@ -337,7 +355,10 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
} else {
if (sounds.drift != null) { nitroAudio.kill(sounds.drift); sounds.drift = null; }
if (sounds.drive != null) { nitroAudio.kill(sounds.drive); sounds.drive = null; }
}
k.wheelParticles[0].pause = !isMoving;
k.wheelParticles[1].pause = !isMoving;
//end sound update
@ -346,23 +367,40 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
} else if (k.cannon != null) { //when cannon is active, we fly forward at max move speed until we get to the cannon point.
var c = scene.nkm.sections["KTPC"].entries[k.cannon];
var mat = mat4.create();
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
if (c.id2 != 0) {
var c2 = scene.nkm.sections["KTPC"].entries[c.id2];
c = c2;
var forward = [0, 0, 1];
var up = [0, 1, 0];
var mat = mat4.create();
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), MAXSPEED);
k.speed = MAXSPEED;
vec3.add(k.pos, k.pos, k.vel);
k.physicalDir = (180-c.angle[1])*(Math.PI/180);
k.angle = k.physicalDir;
k.kartTargetNormal = vec3.transformMat4(up, up, mat);
k.pos = vec3.clone(c2.pos);
vec3.add(k.pos, k.pos, vec3.transformMat4([], [0,16,16], mat));
var planeConst = -vec3.dot(c.pos, forward);
var cannonDist = vec3.dot(k.pos, forward) + planeConst;
if (cannonDist > 0) k.cannon = null;
k.physicalDir = (180-c2.angle[1])*(Math.PI/180);
k.angle = k.physicalDir;
k.cannon = null;
} else {
var mat = mat4.create();
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
var forward = [0, 0, 1];
var up = [0, 1, 0];
k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), MAXSPEED);
k.speed = Math.min(k.speed+1, MAXSPEED);
vec3.add(k.pos, k.pos, k.vel);
k.physicalDir = (180-c.angle[1])*(Math.PI/180);
k.angle = k.physicalDir;
k.kartTargetNormal = vec3.transformMat4(up, up, mat);
var planeConst = -vec3.dot(c.pos, forward);
var cannonDist = vec3.dot(k.pos, forward) + planeConst;
if (cannonDist > 0) k.cannon = null;
}
} else { //default kart mode
var groundEffect = 0;
@ -402,6 +440,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if ((onGround) && !(input.accel && input.drift && (k.speed > 2 || !k.driftLanded))) {
//end drift, execute miniturbo
k.drifting = false;
clearWheelParticles();
if (sounds.powerslide != null) {
nitroAudio.instaKill(sounds.powerslide);
sounds.powerslide = null;
@ -434,11 +473,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if (onGround) {
if (!k.driftLanded) {
if (k.driftMode == 0) k.drifting = false;
if (k.driftMode == 0) {
k.drifting = false;
clearWheelParticles();
}
else {
k.driftPSMode = 0;
k.driftPSTick = 0;
k.driftLanded = true;
if (k.drifting) setWheelParticles(20, 1); //20 = smoke, 1 = drift priority
}
}
if (k.drifting) {
@ -467,7 +510,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.driftPSMode++;
k.driftPSTick = 1;
//play blue spark sound
//play blue spark sound, flare
setWheelParticles(126, 2); //126 = blue flare, 2 = flare priority
var blue = nitroAudio.playSound(210, {}, 0, k);
blue.gainN.gain.value = 2;
@ -487,6 +531,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.driftPSMode++;
k.driftPSTick = 1;
//play red sparks sound, full MT!
setWheelParticles(22, 2); //22 = red flare, 2 = flare priority
setWheelParticles(17, 1); //17 = red mt, 1 = drift priority
sounds.powerslide = nitroAudio.playSound(209, {}, 0, k);
sounds.powerslide.gainN.gain.value = 2;
} else k.driftPSTick = 0;
@ -642,6 +688,24 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
positionChanged(lastPos, k.pos);
}
function clearWheelParticles(prio) {
for (let i=0; i<2; i++) {
if (prio == null) {
//clear all specials
k.wheelParticles[i].clearEmitter(1); //drift mode
//k.wheelParticles[i].clearEmitter(2); //drift flare (blue mt, red big flash)
} else {
k.wheelParticles[i].clearEmitter(0);
}
}
}
function setWheelParticles(id, prio) {
for (let i=0; i<2; i++) {
k.wheelParticles[i].setEmitter(id, prio);
}
}
function genFutureChecks() {
//all future points that
var chosen = {}
@ -661,6 +725,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
function positionChanged(oldPos, pos) {
//crossed into new checkpoint?
if (checkpoints.length == 0) return;
for (var i=0; i<futureChecks.length; i++) {
var check = checkpoints[futureChecks[i]];
var distOld = vec2.sub([], [check.x1, check.z1], [oldPos[0], oldPos[2]]);
@ -685,6 +750,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.lapNumber++;
k.checkPointNumber = 0;
k.passedKTP2 = 0;
futureChecks = [1];
scene.lapAdvance(k);
}
}
@ -805,6 +871,11 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
return MKDS_COLTYPE.SOUNDMAP[collision][effect] || {};
}
function colParticle(collision, effect) {
if (MKDS_COLTYPE.SOUNDMAP[collision] == null) return null
return MKDS_COLTYPE.SOUNDMAP[collision][effect].particle || null;
}
function project(u, v) {
return vec3.scale([], u, (vec3.dot(u, v)/vec3.dot(u, u)))
}
@ -815,6 +886,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
var colType = (plane.CollisionType>>8)&31;
var colBE = (plane.CollisionType>>5)&7;
var change = (colType != lastCollided);
lastCollided = colType;
lastBE = colBE;
lastColSounds = colSound(lastCollided, colBE);
@ -832,9 +904,14 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if (proj < -1) {
if (lastColSounds.hit != null) nitroAudio.playSound(lastColSounds.hit, {volume:1}, 0, k)
var colObj = {pos:pos, vel:[0,0,0], mat: mat4.fromTranslation([], pos)};
scene.particles.push(new NitroEmitter(scene, colObj, 13));
scene.particles.push(new NitroEmitter(scene, colObj, 14));
}
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), adjN, proj));
//convert back to angle + speed to keep change to kart vel
var v = k.vel;
@ -851,6 +928,14 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if (proj < -4 && k.vel[1] < -2) { proj -= 1.5; }
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), n, proj));
k.kartTargetNormal = dat.pNormal;
if (change) {
var particle = colParticle(lastCollided, colBE);
if (particle == null)
clearWheelParticles(0);
else
setWheelParticles(particle, 0);
}
if (!onGround) {
console.log("ground: "+colType+", "+colBE);
groundAnim = 0;

View File

@ -9,6 +9,7 @@
window.spa = function(input) {
var t = this;
this.load = load;
this.getTexture = getTexture;
var colourBuffer;
@ -36,42 +37,108 @@ window.spa = function(input) {
offset += 24;
if (version == "12_1") {
this.particles = [];
t.particles = [];
for (let i=0; i<particleCount; i++) {
this.particles[i] = readParticle(view, offset);
offset = this.particles[i].nextOff;
t.particles[i] = readParticle(view, offset);
t.particles[i].parent = t;
offset = t.particles[i].nextOff;
}
}
offset = firstTexOffset;
this.particleTextures = [];
t.particleTextures = [];
for (let i=0; i<particleTexCount; i++) {
this.particleTextures[i] = readParticleTexture(view, offset);
offset = this.particleTextures[i].nextOff;
t.particleTextures[i] = readParticleTexture(view, offset);
offset = t.particleTextures[i].nextOff;
}
//window.debugParticle = true;
if (window.debugParticle) {
for (let i=0; i<particleCount; i++) {
var text = document.createElement("textarea");
var p = this.particles[i];
var p = t.particles[i];
p.parent = null;
text.value = JSON.stringify(p, true, 4);
p.parent = t;
text.style.width = 500;
text.style.height = 200;
var obj = this.particleTextures[p.textureId];
if (p.texAnim) obj = this.particleTextures[p.texAnim.textures[0]];
var obj = t.particleTextures[p.textureId];
if (p.texAnim) obj = t.particleTextures[p.texAnim.textures[0]];
if (obj == null) {
continue;
}
var test = readTexWithPal(obj.info, obj);
document.body.appendChild(document.createElement("br"));
document.body.appendChild(document.createTextNode(i+":"));
document.body.appendChild(test);
document.body.appendChild(text);
document.body.appendChild(document.createElement("br"));
}
}
}
function getTexture(id, gl) {
var t = this;
var obj = t.particleTextures[id];
if (obj == null) {
return null;
}
if (obj.glTex == null) {
var canvas = readTexWithPal(obj.info, obj);
var m = obj.info;
if (m.flipX || m.flipY) {
var fC = document.createElement("canvas");
var ctx = fC.getContext("2d");
fC.width = (m.flipX)?canvas.width*2:canvas.width;
fC.height = (m.flipY)?canvas.height*2:canvas.height;
ctx.drawImage(canvas, 0, 0);
ctx.save();
if (m.flipX) {
ctx.translate(2*canvas.width, 0);
ctx.scale(-1, 1);
ctx.drawImage(canvas, 0, 0);
ctx.restore();
ctx.save();
}
if (m.flipY) {
ctx.translate(0, 2*canvas.height);
ctx.scale(1, -1);
ctx.drawImage(fC, 0, 0);
ctx.restore();
}
var t = loadTex(fC, gl, !m.repeatX, !m.repeatY);
t.realWidth = canvas.width;
t.realHeight = canvas.height;
obj.glTex = t;
} else {
var t = loadTex(canvas, gl, !m.repeatX, !m.repeatY);
t.realWidth = canvas.width;
t.realHeight = canvas.height;
obj.glTex = t;
}
}
return obj.glTex;
}
function loadTex(img, gl, clampx, clampy) { //general purpose function for loading an image into a texture.
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
if (clampx) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
if (clampy) gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
texture.width = img.width;
texture.height = img.height;
gl.bindTexture(gl.TEXTURE_2D, null);
return texture;
}
function readParticle(view, off) {
var obj = {};
var ParticleFlags =
@ -106,6 +173,7 @@ window.spa = function(input) {
Bit29: 0x20000000
}
obj.ParticleFlags = ParticleFlags;
obj.flag = view.getUint32(off, true);
obj.position = [view.getInt32(off+0x4, true)/4096, view.getInt32(off+0x8, true)/4096, view.getInt32(off+0xC, true)/4096];
//this is just hilarious at this point
@ -118,8 +186,8 @@ window.spa = function(input) {
//not sure what this vector is for. grass it's (0, 1, 0), smoke it's (-0.706787109375, 0, -0.707275390625) billboard alignment vector? (it's a bit crazy for powerslide)
obj.vector = [view.getInt16(off+0x1C, true)/4096, view.getInt16(off+0x1E, true)/4096, view.getInt16(off+0x20, true)/4096];
obj.color = view.getUint16(off+0x22, true); //15 bit, usually 32767 for white.
obj.randomxz = view.getUint32(off+0x24, true); //random xz velocity intensity
obj.velocity = view.getUint32(off+0x28, true); //initial velocity related
obj.randomxz = view.getUint32(off+0x24, true)/4096; //random xz velocity intensity
obj.velocity = view.getUint32(off+0x28, true)/4096; //initial velocity related (along predefined vector)
obj.size = view.getUint32(off+0x2C, true)/4096; //size
obj.aspect = view.getUint16(off+0x30, true) / 4096; //aspect
@ -130,8 +198,8 @@ window.spa = function(input) {
obj.rotVelFrom = view.getInt16(off+0x34, true);
obj.rotVelTo = view.getInt16(off+0x36, true);
obj.unknown13 = view.getUint16(off+0x38, true); //??? (0)
obj.unknown14 = view.getUint16(off+0x3A, true); //??? (4B)
obj.scX = view.getInt16(off+0x38, true)/0x8000; //??? (0) //scale center offset?
obj.scY = view.getInt16(off+0x3A, true)/0x8000; //??? (4B) //scale center offset?
obj.emitterLifetime = view.getUint16(off+0x3C, true); //stop emitting particles after this many frames
obj.duration = view.getUint16(off+0x3E, true);
@ -146,8 +214,8 @@ window.spa = function(input) {
obj.textureId = view.getUint8(off+0x47, true);
obj.unknown21 = view.getUint32(off+0x48, true); //negative number makes grass disappear (1 for grass, smoke)
obj.unknown22 = view.getUint32(off+0x4C, true); //some numbers make grass disappear (0x458d00 for grass, 0x74725f60 for smoke)
obj.xScaleDelta = view.getUint16(off+0x50, true); //x scale delta for some reason. usually 0
obj.yScaleDelta = view.getUint16(off+0x52, true); //y scale delta for some reason. usually 0
obj.xScaleDelta = view.getInt16(off+0x50, true)/4096; //x scale delta for some reason. usually 0
obj.yScaleDelta = view.getInt16(off+0x52, true)/4096; //y scale delta for some reason. usually 0
obj.unknown25 = view.getUint32(off+0x54, true); //FFFFFFFF makes run at half framerate. idk? usually 0
off += 0x58;
@ -165,7 +233,8 @@ window.spa = function(input) {
unkBase: view.getUint16(off, true)/4096,
scaleFrom: view.getUint16(off+2, true)/4096,
scaleTo: view.getUint16(off+4, true)/4096,
param: view.getUint16(off+6, true),
fromZeroTime: view.getUint8(off+6, true)/0xFF, //time to dedicate to an animation from zero size
holdTime: view.getUint8(off+7, true)/0xFF, //time to dedicate to holding state at the end.
flagParam: view.getUint16(off+8, true),
unk4b: view.getUint16(off+10, true),
};
@ -173,7 +242,7 @@ window.spa = function(input) {
}
if ((obj.flag & ParticleFlags.ColorAnimation) != 0)
{
obj.ColorAnimation = {
obj.colorAnim = {
colorFrom: view.getUint16(off, true), //color from
colorTo: view.getUint16(off+2, true), //color to (seems to be same as base color)
framePct: view.getUint16(off+4, true), //frame pct to become color to (FFFF means always from, 8000 is about the middle)
@ -278,7 +347,7 @@ window.spa = function(input) {
var flags = view.getUint16(off+4, true);
obj.info = {
pal0trans: (flags>>3)&1, //weirdly different format
pal0trans: true,//z(flags>>3)&1, //weirdly different format
format: ((flags)&7),
height: 8 << ((flags>>8)&0xF),
width: 8 << ((flags>>4)&0xF),

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -5,35 +5,35 @@
//
window.ItemShard = function(scene, targ, model) {
var t = this;
t.update = update;
t.draw = draw;
var t = this;
t.update = update;
t.draw = draw;
t.time = 0;
t.pos = vec3.clone(targ.pos);
t.vel = vec3.add([], targ.vel, [(Math.random()-0.5)*5, Math.random()*7, (Math.random()-0.5)*5]);
t.dirVel = [(Math.random()-0.5), (Math.random()-0.5), (Math.random()-0.5)];
t.dir = [Math.random()*2*Math.PI, Math.random()*2*Math.PI, Math.random()*2*Math.PI];
t.scale = Math.random()+0.5;
t.scale = [t.scale, t.scale, t.scale];
t.time = 0;
t.pos = vec3.clone(targ.pos);
t.vel = vec3.add([], targ.vel, [(Math.random()-0.5)*5, Math.random()*7, (Math.random()-0.5)*5]);
t.dirVel = [(Math.random()-0.5), (Math.random()-0.5), (Math.random()-0.5)];
t.dir = [Math.random()*2*Math.PI, Math.random()*2*Math.PI, Math.random()*2*Math.PI];
t.scale = Math.random()+0.5;
t.scale = [t.scale, t.scale, t.scale];
function update(scene) {
vec3.add(t.pos, t.pos, t.vel);
vec3.add(t.vel, t.vel, [0, -0.17, 0]);
vec3.add(t.dir, t.dir, t.dirVel);
function update(scene) {
vec3.add(t.pos, t.pos, t.vel);
vec3.add(t.vel, t.vel, [0, -0.17, 0]);
vec3.add(t.dir, t.dir, t.dirVel);
if (t.time++ > 30) scene.removeParticle(t);
}
if (t.time++ > 30) scene.removeParticle(t);
}
function draw(view, pMatrix, gl) {
var mat = mat4.translate(mat4.create(), view, t.pos);
function draw(view, pMatrix, gl) {
var mat = mat4.translate(mat4.create(), view, t.pos);
mat4.rotateZ(mat, mat, t.dir[2]);
mat4.rotateY(mat, mat, t.dir[1]);
mat4.rotateX(mat, mat, t.dir[0]);
mat4.rotateZ(mat, mat, t.dir[2]);
mat4.rotateY(mat, mat, t.dir[1]);
mat4.rotateX(mat, mat, t.dir[0]);
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
model.draw(mat, pMatrix);
}
mat4.scale(mat, mat, vec3.scale([], t.scale, 16));
model.draw(mat, pMatrix);
}
}

View File

@ -0,0 +1,173 @@
//
// nitroEmitter.js
//--------------------
// Implemtents the generic nitro particle emitter.
// by riperiperi
//
window.NitroEmitter = function(scene, targ, emitterID, vector, offset) {
var t = this;
t.update = update;
t.draw = draw;
var pRes = scene.gameRes.RaceEffect;
var emitter = (emitterID == -1)?null:pRes.particles[emitterID];
t.attached = targ; //an entity with pos and vel.
if (vector == null) vector = [0, 1, 0];
if (offset == null) offset = [0,0,0];
t.offset = offset;
t.vector = vector;
t.pctParticle = 0;
t.time = 0;
t.dead = false;
t.curPrio = 0;
t.doNotDelete = (emitter == null);
t.pause = false;
t.setEmitter = setEmitter;
t.clearEmitter = clearEmitter;
var particleList = [emitterID, -1, -1, -1];
function setEmitter(emitterID, prio) {
particleList[prio] = emitterID;
if (t.curPrio <= prio) {
//activate this emitter immediately.
t.curPrio = prio;
t.dead = false;
emitter = pRes.particles[emitterID];
t.time = 0;
}
}
function clearEmitter(prio) {
//if (t.curPrio > prio) return; //this emitter cannot be unset
if (prio == t.curPrio) {
findNextEmitter();
} else {
particleList[prio] = -1;
}
}
function findNextEmitter() {
particleList[t.curPrio] = -1;
var em = t.curPrio-1;
while (em >= 0) {
if (particleList[em] != -1) {
t.dead = false;
emitter = pRes.particles[particleList[em]];
t.time = 0;
t.curPrio = em;
return;
}
em--;
}
if (em == -1) {
t.curPrio = 0;
emitter = null;
dead = true;
}
t.curPrio = em;
}
function update(scene) {
if (emitter == null || t.dead || t.pause) return;
if ((t.time % (emitter.frequency)) == 0 && t.time >= emitter.delay) {
//should we create new particles? fractional logic for doing this
t.pctParticle += emitter.particleChance;
while (t.pctParticle >= 1) {
var attach = (emitter.flag & 0x8000) > 0;
t.pctParticle -= 1;
//create a new particle
//TODO: make these transform with the target's world matrix
var pos = vec3.create();
//add offset
vec3.add(pos, pos, t.offset);
//add emitter properties
vec3.add(pos, pos, emitter.position);
var spread = [Math.random()*2-1, Math.random()*2-1, Math.random()*2-1];
var spreadMode = (emitter.flag & 0xF);
if (spreadMode == 2) {
spread[1] = 0; //spread is only in xz direction
}
vec3.normalize(spread, spread);
if (spreadMode == 0) {
spread = [0,0,0];
}
vec3.scale(spread, spread, Math.random()*emitter.areaSpread*2);
vec3.add(pos, pos, spread);
vec3.scale(pos, pos, 16);
if (!attach) {
if (targ.mat != null) {
vec3.transformMat4(pos, pos, targ.mat);
} else {
vec3.add(pos, pos, targ.pos);
}
}
//inherit velocity
var vel = (attach)?vec3.create():vec3.clone(targ.vel);
vec3.scale(vel, vel, 1/32);
var vector = vec3.clone(t.vector);
if (!attach) {
if (targ.mat != null) {
//tranform our vector by the target matrix
var mat = targ.mat;
var org = [];
mat4.getTranslation(org, mat);
mat[12] = 0;
mat[13] = 0;
mat[14] = 0;
vec3.transformMat4(vector, vector, mat);
mat[12] = org[0];
mat[13] = org[1];
mat[14] = org[2];
}
}
vec3.normalize(vector, vector);
vec3.add(vel, vel, vec3.scale([], vector, emitter.velocity));
var xz = [Math.random()*2-1, 0, Math.random()*2-1];
vec3.normalize(xz, xz);
vec3.scale(xz, xz, Math.random()*emitter.randomxz);
vec3.add(vel, vel, xz);
var rotVel = ((emitter.rotVelFrom + ((Math.random()*emitter.rotVelTo-emitter.rotVelFrom) | 0))/65535) * Math.PI*2;
var dir = ((emitter.flag & 0x2000) > 0)?(Math.random()*Math.PI*2):0;
var duration = emitter.duration + emitter.duration * emitter.varDuration/0xFF * (Math.random() * 2 - 1);
var scaleMod = (emitter.varScale/0xFF * (Math.random() * 2 - 1)) + 1;
var scale = [scaleMod * emitter.size, scaleMod * emitter.size * emitter.aspect];
var particle = new NitroParticle(scene, emitter, pos, vel, dir, rotVel, duration, scale, (attach?t.attached:null));
particle.ovel = (attach)?vec3.create():vec3.scale([], targ.vel, 1/32);
scene.particles.push(particle);
}
var pos = vec3.clone(targ.pos);
}
t.time++;
if (t.time == emitter.emitterLifetime) {
t.dead = true;
if (!t.doNotDelete) scene.removeParticle(t);
else {
findNextEmitter();
}
}
}
function draw(view, pMatrix, gl) {
}
}

View File

@ -0,0 +1,251 @@
//
// nitroParticle.js
//--------------------
// Implements a generic nitro particle. Currently positioned and updated in software for debug
// simplicity - but should be moved to vertex shader in future.
// by riperiperi
//
window.NitroParticle = function(scene, emitter, pos, vel, dir, dirVel, duration, scale, attached) {
var t = this;
t.update = update;
t.draw = draw;
t.time = 0;
t.duration = duration | 0;
t.pos = vec3.add(pos, pos, [0, 0, 0]);
t.vel = vel;
t.dirVel = dirVel; //float
t.dir = dir; //float
t.attached = attached;
t.scale = scale; //vec2
t.emitter = emitter;
t.aScale = 1;
//decode 16 bit color into float
t.baseColor = convertCol(emitter.color);
t.baseColor[3] = emitter.opacity / 0x1F;
t.aColor = vec4.clone(t.baseColor);
t.frame = emitter.textureId;
if (t.emitter.texAnim)
t.frame = t.emitter.texAnim.textures[0];
function convertCol(col) {
return [
((col&31)/31),
(((col>>5)&31)/31),
(((col>>10)&31)/31),
1 //Math.round((col>>15)*255);
];
}
function update(scene) {
var particlePct = t.time / t.duration;
t.pos[0] += t.vel[0] * 16;
t.pos[1] += t.vel[1] * (t.emitter.yOffIntensity/128) * 16;
t.pos[2] += t.vel[2] * 16;
if (t.emitter.gravity) {
vec3.add(t.vel, t.vel, t.emitter.gravity);
}
if (t.emitter.colorAnim) {
var ca = t.emitter.colorAnim;
var from = convertCol(ca.colorFrom);
var to = convertCol(ca.colorTo);
var pctFloat = (ca.framePct/0xFFFF);
vec4.lerp(t.aColor, from, to, Math.max(0, (particlePct - pctFloat)/(1 - pctFloat)));
t.aColor[3] = t.emitter.opacity/0x1F;
} else {
t.aColor = vec4.clone(t.baseColor);
}
if (t.emitter.opacityAnim) {
var oa = t.emitter.opacityAnim;
var pctFade = oa.startFade / 0xFFFF;
var opaMul = 1-Math.max(0, (particlePct - pctFade)/(1 - pctFade));
t.aColor[3] = opaMul;// * oa.intensity/0x0FFF;
//vec4.scale(t.aColor, t.aColor, opaMul);
}
if (t.emitter.texAnim) {
var ta = t.emitter.texAnim;
var frame = 0;
if ((ta.unknown1 & 128) > 0) {
//select frame based on particle duration
var frame = ta.textures[Math.min((particlePct * ta.frames) | 0, ta.frames-1)];
} else {
//repeating anim with framerate
//not sure what framerate is, but its likely in the unknowns.
var frame = ta.textures[(t.time % ta.frames)];
}
t.frame = frame;
}
t.dir += t.dirVel;
if (t.time++ >= t.duration) scene.removeParticle(t);
}
function draw(view, pMatrix, gl) {
var particlePct = t.time / t.duration;
let pos = t.pos;
let vel = t.vel;
if (t.attached != null) {
pos = vec3.transformMat4([], pos, t.attached.mat);
//tranform our vector by the target matrix
var mat = t.attached.mat;
var org = [];
mat4.getTranslation(org, mat);
mat[12] = 0;
mat[13] = 0;
mat[14] = 0;
vel = vec3.transformMat4([], vel, mat);
mat[12] = org[0];
mat[13] = org[1];
mat[14] = org[2];
}
var mat = mat4.translate(mat4.create(), view, pos);
var bbMode = t.emitter.flag & 0xF0;
if (bbMode == 0x10) { //spark, billboards towards camera
var camPos = scene.camera.view.pos;
camPos = vec3.sub([], camPos, pos);
vec3.normalize(camPos, camPos);
var n = vec3.sub([], vel, t.ovel);
vec3.normalize(n,n);
mat4.multiply(mat, mat, mat4.invert([], mat4.lookAt([], [0,0,0], camPos, n)));
} else if (bbMode == 0x20) { //no billboard
mat4.rotateY(mat, mat, t.dir);
} else if (bbMode == 0x30) { //spark, no billboard
var camPos = scene.camera.view.pos;
camPos = vec3.sub([], camPos, pos);
vec3.normalize(camPos, camPos);
var n = vec3.sub([], vel, t.ovel);
vec3.normalize(n,n);
mat4.multiply(mat, mat, mat4.invert([], mat4.lookAt([], [0,0,0], camPos, n)));
mat4.rotateY(mat, mat, t.dir);
} else { //billboard
mat4.multiply(mat, mat, nitroRender.billboardMat);
mat4.rotateZ(mat, mat, t.dir);
}
var finalScale = 1;
if (t.emitter.scaleAnim) {
var sa = t.emitter.scaleAnim;
if (particlePct < sa.fromZeroTime) {
var fzPct = particlePct / sa.fromZeroTime;
finalScale = sa.scaleFrom * fzPct;
} else {
var rescaledPct = Math.min(1, (particlePct - sa.fromZeroTime) / (1-(sa.fromZeroTime + sa.holdTime*(1-sa.fromZeroTime))));
finalScale = sa.scaleFrom * (1-rescaledPct) + sa.scaleTo * rescaledPct;
}
}
mat4.scale(mat, mat, vec3.scale([], [t.scale[0], t.scale[1], 1], 12*finalScale));
mat4.translate(mat, mat, [t.emitter.xScaleDelta, t.emitter.yScaleDelta, 0]);
drawGeneric(mat, pMatrix, gl);
}
var MAT3I = mat3.create();
var MAT4I = mat4.create();
function drawGeneric(mv, project, gl) {
var shader = nitroRender.nitroShader;
if (!nitroRender.flagShadow) {
gl.uniform1f(shader.shadOffUniform, 0.001);
gl.uniform1f(shader.lightIntensityUniform, 0);
}
if (window.VTX_PARTICLE == null) genGlobalVtx(gl);
var obj = window.VTX_PARTICLE;
nitroRender.setColMult(t.aColor);
gl.uniformMatrix4fv(shader.mvMatrixUniform, false, mv);
gl.uniformMatrix4fv(shader.pMatrixUniform, false, project);
//matrix stack unused, just put an identity in slot 0
gl.uniformMatrix4fv(shader.matStackUniform, false, MAT4I);
var frame = t.emitter.parent.getTexture(t.frame, gl);
gl.bindTexture(gl.TEXTURE_2D, frame);
//texture matrix not used
gl.uniformMatrix3fv(shader.texMatrixUniform, false, MAT3I);
if (obj != nitroRender.last.obj) {
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vPos);
gl.vertexAttribPointer(shader.vertexPositionAttribute, 3, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vTx);
gl.vertexAttribPointer(shader.textureCoordAttribute, 2, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vCol);
gl.vertexAttribPointer(shader.colorAttribute, 4, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vMat);
gl.vertexAttribPointer(shader.matAttribute, 1, gl.FLOAT, false, 0, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, obj.vNorm);
gl.vertexAttribPointer(shader.normAttribute, 3, gl.FLOAT, false, 0, 0);
nitroRender.last.obj = obj;
}
gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4);
nitroRender.setColMult([1, 1, 1, 1]);
if (!nitroRender.flagShadow) {
nitroRender.resetShadOff();
gl.uniform1f(shader.lightIntensityUniform, 0.3);
}
}
function genGlobalVtx(gl) {
var vecPos = [-1,-1,0, 1,-1,0, -1,1,0, 1,1,0];
var vecTx = [1,1, 0,1, 1,0, 0,0];
var vecCol = [1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1];
var vecMat = [0,0,0,0];
var vecNorm = [0,1,0, 0,1,0, 0,1,0, 0,1,0];
var pos = gl.createBuffer();
var col = gl.createBuffer();
var tx = gl.createBuffer();
var mat = gl.createBuffer();
var norm = gl.createBuffer();
var posArray = new Float32Array(vecPos);
gl.bindBuffer(gl.ARRAY_BUFFER, pos);
gl.bufferData(gl.ARRAY_BUFFER, posArray, gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, tx);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecTx), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, col);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecCol), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, mat);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecMat), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, norm);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vecNorm), gl.STATIC_DRAW);
window.VTX_PARTICLE = {
posArray: posArray,
vPos: pos,
vTx: tx,
vCol: col,
vMat: mat,
vNorm: norm,
verts: vecPos.length/3,
};
}
}

View File

@ -246,10 +246,9 @@ window.nitroRender = new function() {
gl.uniformMatrix4fv(shader.shadowMatUniform, false, sMat);
gl.uniformMatrix4fv(shader.farShadowMatUniform, false, fsMat);
gl.uniform1f(shader.lightIntensityUniform, 0.3);
gl.uniform1f(shader.shadOffUniform, 0.00005+((mobile)?0.0005:0));
gl.uniform1f(shader.farShadOffUniform, 0.0005);
this.resetShadOff();
gl.activeTexture(gl.TEXTURE1);
gl.bindTexture(gl.TEXTURE_2D, sTex);
gl.uniform1i(shader.lightSamplerUniform, 1);
@ -262,6 +261,12 @@ window.nitroRender = new function() {
this.prepareShader();
}
this.resetShadOff = function() {
var shader = shaders[1];
gl.uniform1f(shader.shadOffUniform, 0.00005+((mobile)?0.0005:0));
gl.uniform1f(shader.farShadOffUniform, 0.0005);
}
this.unsetShadowMode = function() {
this.nitroShader = shaders[0];
gl.useProgram(this.nitroShader);
@ -384,7 +389,7 @@ function nitroModel(bmd, btx, remap) {
}
if (remap != null) {
setTextureRemap(remap)
setTextureRemap(remap);
}
if (btx != null) {

View File

@ -16,9 +16,39 @@ window.nitroShaders = new (function() {
\n\
uniform sampler2D uSampler;\n\
\n\
float indexValue() {\n\
int x = int(mod(gl_FragCoord.x, 4.0));\n\
int y = int(mod(gl_FragCoord.y, 4.0));\n\
int i = (x + y * 4);\n\
if (i == 0) return 0.0;\n\
else if (i == 1) return 8.0;\n\
else if (i == 2) return 2.0;\n\
else if (i == 3) return 10.0;\n\
else if (i == 4) return 12.0;\n\
else if (i == 5) return 4.0;\n\
else if (i == 6) return 14.0;\n\
else if (i == 7) return 6.0;\n\
else if (i == 8) return 3.0;\n\
else if (i == 9) return 11.0;\n\
else if (i == 10) return 1.0;\n\
else if (i == 11) return 9.0;\n\
else if (i == 12) return 15.0;\n\
else if (i == 13) return 7.0;\n\
else if (i == 14) return 13.0;\n\
else if (i == 15) return 5.0;\n\
}\n\
\n\
float dither(float color) {\n\
float closestColor = (color < 0.5) ? 0.0 : 1.0;\n\
float secondClosestColor = 1.0 - closestColor;\n\
float d = indexValue();\n\
float distance = abs(closestColor - color);\n\
return (distance < d) ? closestColor : secondClosestColor;\n\
}\n\
\n\
void main(void) {\n\
gl_FragColor = texture2D(uSampler, vTextureCoord)*color;\n\
if (gl_FragColor.a == 0.0) discard;\n\
if (gl_FragColor.a < 1.0 && (gl_FragColor.a == 0.0 || dither(gl_FragColor.a) == 0.0)) discard;\n\
}"
this.defaultVert = "attribute vec3 aVertexPosition;\n\
@ -62,20 +92,20 @@ window.nitroShaders = new (function() {
\n\
uniform sampler2D uSampler;\n\
\n\
float shadowCompare(sampler2D map, vec2 pos, float compare) {\n\
float shadowCompare(sampler2D map, vec2 pos, float compare, float so) {\n\
float depth = texture2D(map, pos).r;\n\
return step(compare, depth);\n\
return smoothstep(compare-so, compare, depth);\n\
}\n\
\n\
float shadowLerp(sampler2D depths, vec2 size, vec2 uv, float compare){\n\
float shadowLerp(sampler2D depths, vec2 size, vec2 uv, float compare, float so){\n\
vec2 texelSize = vec2(1.0)/size;\n\
vec2 f = fract(uv*size+0.5);\n\
vec2 centroidUV = floor(uv*size+0.5)/size;\n\
\n\
float lb = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 0.0), compare);\n\
float lt = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 1.0), compare);\n\
float rb = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 0.0), compare);\n\
float rt = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 1.0), compare);\n\
float lb = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 0.0), compare, so);\n\
float lt = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 1.0), compare, so);\n\
float rb = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 0.0), compare, so);\n\
float rt = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 1.0), compare, so);\n\
float a = mix(lb, lt, f.y);\n\
float b = mix(rb, rt, f.y);\n\
float c = mix(a, b, f.x);\n\
@ -89,14 +119,14 @@ window.nitroShaders = new (function() {
float dist = max(ldNorm.x, ldNorm.y);\n\
\n\
if (dist > 0.5) {\n\
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff));\n\
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff, farShadOff*2.0));\n\
} else if (dist > 0.4) {\n\
float lerp1 = shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff);\n\
float lerp2 = shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff);\n\
float lerp1 = shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff, farShadOff*2.0);\n\
float lerp2 = shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff, shadOff*4.0);\n\
\n\
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), mix(lerp2, lerp1, (dist-0.4)*10.0));\n\
} else {\n\
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff));\n\
gl_FragColor = col*mix(vec4(0.5, 0.5, 0.7, 1.0), vec4(1.0, 1.0, 1.0, 1.0), shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff, shadOff*4.0));\n\
}\n\
\n\
if (gl_FragColor.a == 0.0) discard;\n\
@ -118,6 +148,7 @@ window.nitroShaders = new (function() {
\n\
uniform mat4 shadowMat;\n\
uniform mat4 farShadowMat;\n\
uniform float lightIntensity; \n\
\n\
varying vec2 vTextureCoord;\n\
varying vec4 color;\n\
@ -133,7 +164,7 @@ window.nitroShaders = new (function() {
lightDist = (shadowMat*pos + vec4(1, 1, 1, 0)) / 2.0;\n\
fLightDist = (farShadowMat*pos + vec4(1, 1, 1, 0)) / 2.0;\n\
vec3 adjNorm = normalize(vec3(uMVMatrix * matStack[int(matrixID)] * vec4(aNormal, 0.0)));\n\
float diffuse = 0.7-dot(adjNorm, vec3(0.0, -1.0, 0.0))*0.3;\n\
float diffuse = (1.0-lightIntensity)-dot(adjNorm, vec3(0.0, -1.0, 0.0))*lightIntensity;\n\
\n\
color = aColor*colMult;\n\
color = vec4(color.x*diffuse, color.y*diffuse, color.z*diffuse, color.w);\n\
@ -270,6 +301,7 @@ window.nitroShaders = new (function() {
var shadUnif = [
["shadowMatUniform", "shadowMat"],
["farShadowMatUniform", "farShadowMat"],
["lightIntensityUniform", "lightIntensity"],
["shadOffUniform", "shadOff"],
["farShadOffUniform", "farShadOff"],

File diff suppressed because one or more lines are too long