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; var dist = 192;
this.targetShadowPos = vec3.add([], kart.pos, [Math.sin(kart.angle)*dist, 0, -Math.cos(kart.angle)*dist]) 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; return thisObj.view;
} }

View File

@ -102,7 +102,7 @@ window.cameraIntro = function() {
thisObj.targetShadowPos = lookAtPos; 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) { var initCam = function(scene, came) {

View File

@ -55,6 +55,7 @@ window.cameraSpectator = function(kart) {
if (zoomLevel > curCam.zoomEnd) zoomLevel = curCam.zoomEnd; if (zoomLevel > curCam.zoomEnd) zoomLevel = curCam.zoomEnd;
thisObj.view = camFunc[curCam.camType](scene, curCam); 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; return thisObj.view;
} }

View File

@ -141,8 +141,10 @@ window.MKDS_COLTYPE = new (function(){
this.PHYS_MAP[this.LOOP] = 11; this.PHYS_MAP[this.LOOP] = 11;
//collision sound handlers //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 = { this.SOUNDMAP = {
0x00: //road 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_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}, {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 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_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}, {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, particle: 28},
{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, particle: 28},
{drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW}, //snow {drift: MKDS_COLSOUNDS.DRIFT_ASPHALT, brake: MKDS_COLSOUNDS.BRAKE, land: MKDS_COLSOUNDS.LAND_SNOW, particle:112}, //snow
{}, {},
{} {}
], ],
0x05: //road 5 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_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}, {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, particle: 28},
{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},
{}, {},
{}, {},
{} {}
@ -216,7 +218,7 @@ window.MKDS_COLTYPE = new (function(){
0x06: //slippery 0x06: //slippery
[ [
{drift: MKDS_COLSOUNDS.DRIFT_ICE, brake: MKDS_COLSOUNDS.BRAKE_ICE, land:MKDS_COLSOUNDS.LAND_ICE}, {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 //advance to one of next possible paths
if (battleMode) { 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; ePoiInd = pathInd;
ePoi = points[ePoiInd]; var pt = points[ePoiInd];
if (pt != null) {
ePoi = pt;
recomputePath(); recomputePath();
}
} else { } else {
var pathInd = ePath.dest[Math.floor(Math.random()*ePath.dest.length)]; var pathInd = ePath.dest[Math.floor(Math.random()*ePath.dest.length)];
ePath = paths[pathInd]; 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.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.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")); this.MainFont = new nftr(r.Main2D.getFile("marioFont.NFTR"));
//debugger; //debugger;
@ -43,7 +44,6 @@ window.IngameRes = function(rom) {
var characters = []; var characters = [];
var karts = []; var karts = [];
var test = new spa(r.MainEffect.getFile("RaceEffect.spa"));
loadItems(); loadItems();
loadTires(); loadTires();

View File

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

View File

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

View File

@ -95,9 +95,18 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
this.lastInput = null; this.lastInput = null;
//race statistics //race statistics
this.lapNumber = 0; this.lapNumber = 1;
this.passedKTP2 = false; this.passedKTP2 = false;
this.checkPointNumber = 0; 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 nkm = scene.nkm;
var startLine = nkm.sections["KTPS"].entries[0]; var startLine = nkm.sections["KTPS"].entries[0];
var passLine = nkm.sections["KTP2"].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]); mat4.scale(wmat, wmat, [scale, scale, scale]);
if (i<2) mat4.rotateY(wmat, wmat, ((k.driveAnimF-14)/14)*Math.PI/6); if (i<2) mat4.rotateY(wmat, wmat, ((k.driveAnimF-14)/14)*Math.PI/6);
mat4.rotateX(wmat, wmat, wheelTurn); 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; var scale = 16;
@ -262,7 +275,11 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
drawChar(view, pMatrix); drawChar(view, pMatrix);
} }
k.lWheelParticle = null;
function update(scene) { function update(scene) {
var lastPos = vec3.clone(k.pos); var lastPos = vec3.clone(k.pos);
updateMat = true; updateMat = true;
@ -314,7 +331,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
sounds.boost = null; 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 (lastCollided != sounds.lastTerrain || lastBE != sounds.lastBE || sounds.drive == null) {
if (sounds.drive != null) nitroAudio.kill(sounds.drive); if (sounds.drive != null) nitroAudio.kill(sounds.drive);
if (lastColSounds.drive != null) { if (lastColSounds.drive != null) {
@ -337,7 +355,10 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
} else { } else {
if (sounds.drift != null) { nitroAudio.kill(sounds.drift); sounds.drift = null; } if (sounds.drift != null) { nitroAudio.kill(sounds.drift); sounds.drift = null; }
if (sounds.drive != null) { nitroAudio.kill(sounds.drive); sounds.drive = 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 //end sound update
@ -346,6 +367,22 @@ 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. } 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 c = scene.nkm.sections["KTPC"].entries[k.cannon];
if (c.id2 != 0) {
var c2 = scene.nkm.sections["KTPC"].entries[c.id2];
c = c2;
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.pos = vec3.clone(c2.pos);
vec3.add(k.pos, k.pos, vec3.transformMat4([], [0,16,16], mat));
k.physicalDir = (180-c2.angle[1])*(Math.PI/180);
k.angle = k.physicalDir;
k.cannon = null;
} else {
var mat = mat4.create(); var mat = mat4.create();
mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180)); mat4.rotateY(mat, mat, c.angle[1]*(Math.PI/180));
mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180)); mat4.rotateX(mat, mat, c.angle[0]*(-Math.PI/180));
@ -354,7 +391,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
var up = [0, 1, 0]; var up = [0, 1, 0];
k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), MAXSPEED); k.vel = vec3.scale([], vec3.transformMat4(forward, forward, mat), MAXSPEED);
k.speed = MAXSPEED; k.speed = Math.min(k.speed+1, MAXSPEED);
vec3.add(k.pos, k.pos, k.vel); vec3.add(k.pos, k.pos, k.vel);
k.physicalDir = (180-c.angle[1])*(Math.PI/180); k.physicalDir = (180-c.angle[1])*(Math.PI/180);
k.angle = k.physicalDir; k.angle = k.physicalDir;
@ -363,6 +400,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
var planeConst = -vec3.dot(c.pos, forward); var planeConst = -vec3.dot(c.pos, forward);
var cannonDist = vec3.dot(k.pos, forward) + planeConst; var cannonDist = vec3.dot(k.pos, forward) + planeConst;
if (cannonDist > 0) k.cannon = null; if (cannonDist > 0) k.cannon = null;
}
} else { //default kart mode } else { //default kart mode
var groundEffect = 0; 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))) { if ((onGround) && !(input.accel && input.drift && (k.speed > 2 || !k.driftLanded))) {
//end drift, execute miniturbo //end drift, execute miniturbo
k.drifting = false; k.drifting = false;
clearWheelParticles();
if (sounds.powerslide != null) { if (sounds.powerslide != null) {
nitroAudio.instaKill(sounds.powerslide); nitroAudio.instaKill(sounds.powerslide);
sounds.powerslide = null; sounds.powerslide = null;
@ -434,11 +473,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if (onGround) { if (onGround) {
if (!k.driftLanded) { if (!k.driftLanded) {
if (k.driftMode == 0) k.drifting = false; if (k.driftMode == 0) {
k.drifting = false;
clearWheelParticles();
}
else { else {
k.driftPSMode = 0; k.driftPSMode = 0;
k.driftPSTick = 0; k.driftPSTick = 0;
k.driftLanded = true; k.driftLanded = true;
if (k.drifting) setWheelParticles(20, 1); //20 = smoke, 1 = drift priority
} }
} }
if (k.drifting) { if (k.drifting) {
@ -467,7 +510,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.driftPSMode++; k.driftPSMode++;
k.driftPSTick = 1; 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); var blue = nitroAudio.playSound(210, {}, 0, k);
blue.gainN.gain.value = 2; blue.gainN.gain.value = 2;
@ -487,6 +531,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.driftPSMode++; k.driftPSMode++;
k.driftPSTick = 1; k.driftPSTick = 1;
//play red sparks sound, full MT! //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 = nitroAudio.playSound(209, {}, 0, k);
sounds.powerslide.gainN.gain.value = 2; sounds.powerslide.gainN.gain.value = 2;
} else k.driftPSTick = 0; } else k.driftPSTick = 0;
@ -642,6 +688,24 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
positionChanged(lastPos, k.pos); 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() { function genFutureChecks() {
//all future points that //all future points that
var chosen = {} var chosen = {}
@ -661,6 +725,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
function positionChanged(oldPos, pos) { function positionChanged(oldPos, pos) {
//crossed into new checkpoint? //crossed into new checkpoint?
if (checkpoints.length == 0) return;
for (var i=0; i<futureChecks.length; i++) { for (var i=0; i<futureChecks.length; i++) {
var check = checkpoints[futureChecks[i]]; var check = checkpoints[futureChecks[i]];
var distOld = vec2.sub([], [check.x1, check.z1], [oldPos[0], oldPos[2]]); 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.lapNumber++;
k.checkPointNumber = 0; k.checkPointNumber = 0;
k.passedKTP2 = 0; k.passedKTP2 = 0;
futureChecks = [1];
scene.lapAdvance(k); scene.lapAdvance(k);
} }
} }
@ -805,6 +871,11 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
return MKDS_COLTYPE.SOUNDMAP[collision][effect] || {}; 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) { function project(u, v) {
return vec3.scale([], u, (vec3.dot(u, v)/vec3.dot(u, u))) 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 colType = (plane.CollisionType>>8)&31;
var colBE = (plane.CollisionType>>5)&7; var colBE = (plane.CollisionType>>5)&7;
var change = (colType != lastCollided);
lastCollided = colType; lastCollided = colType;
lastBE = colBE; lastBE = colBE;
lastColSounds = colSound(lastCollided, colBE); lastColSounds = colSound(lastCollided, colBE);
@ -832,9 +904,14 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if (proj < -1) { if (proj < -1) {
if (lastColSounds.hit != null) nitroAudio.playSound(lastColSounds.hit, {volume:1}, 0, k) 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)); vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), adjN, proj));
//convert back to angle + speed to keep change to kart vel //convert back to angle + speed to keep change to kart vel
var v = k.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; } if (proj < -4 && k.vel[1] < -2) { proj -= 1.5; }
vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), n, proj)); vec3.sub(k.vel, k.vel, vec3.scale(vec3.create(), n, proj));
k.kartTargetNormal = dat.pNormal; k.kartTargetNormal = dat.pNormal;
if (change) {
var particle = colParticle(lastCollided, colBE);
if (particle == null)
clearWheelParticles(0);
else
setWheelParticles(particle, 0);
}
if (!onGround) { if (!onGround) {
console.log("ground: "+colType+", "+colBE); console.log("ground: "+colType+", "+colBE);
groundAnim = 0; groundAnim = 0;

View File

@ -9,6 +9,7 @@
window.spa = function(input) { window.spa = function(input) {
var t = this; var t = this;
this.load = load; this.load = load;
this.getTexture = getTexture;
var colourBuffer; var colourBuffer;
@ -36,42 +37,108 @@ window.spa = function(input) {
offset += 24; offset += 24;
if (version == "12_1") { if (version == "12_1") {
this.particles = []; t.particles = [];
for (let i=0; i<particleCount; i++) { for (let i=0; i<particleCount; i++) {
this.particles[i] = readParticle(view, offset); t.particles[i] = readParticle(view, offset);
offset = this.particles[i].nextOff; t.particles[i].parent = t;
offset = t.particles[i].nextOff;
} }
} }
offset = firstTexOffset; offset = firstTexOffset;
this.particleTextures = []; t.particleTextures = [];
for (let i=0; i<particleTexCount; i++) { for (let i=0; i<particleTexCount; i++) {
this.particleTextures[i] = readParticleTexture(view, offset); t.particleTextures[i] = readParticleTexture(view, offset);
offset = this.particleTextures[i].nextOff; offset = t.particleTextures[i].nextOff;
} }
//window.debugParticle = true;
if (window.debugParticle) { if (window.debugParticle) {
for (let i=0; i<particleCount; i++) { for (let i=0; i<particleCount; i++) {
var text = document.createElement("textarea"); 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); text.value = JSON.stringify(p, true, 4);
p.parent = t;
text.style.width = 500; text.style.width = 500;
text.style.height = 200; text.style.height = 200;
var obj = this.particleTextures[p.textureId]; var obj = t.particleTextures[p.textureId];
if (p.texAnim) obj = this.particleTextures[p.texAnim.textures[0]]; if (p.texAnim) obj = t.particleTextures[p.texAnim.textures[0]];
if (obj == null) { if (obj == null) {
continue; continue;
} }
var test = readTexWithPal(obj.info, obj); 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(test);
document.body.appendChild(text); 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) { function readParticle(view, off) {
var obj = {}; var obj = {};
var ParticleFlags = var ParticleFlags =
@ -106,6 +173,7 @@ window.spa = function(input) {
Bit29: 0x20000000 Bit29: 0x20000000
} }
obj.ParticleFlags = ParticleFlags;
obj.flag = view.getUint32(off, true); 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]; 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 //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) //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.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.color = view.getUint16(off+0x22, true); //15 bit, usually 32767 for white.
obj.randomxz = view.getUint32(off+0x24, true); //random xz velocity intensity obj.randomxz = view.getUint32(off+0x24, true)/4096; //random xz velocity intensity
obj.velocity = view.getUint32(off+0x28, true); //initial velocity related obj.velocity = view.getUint32(off+0x28, true)/4096; //initial velocity related (along predefined vector)
obj.size = view.getUint32(off+0x2C, true)/4096; //size obj.size = view.getUint32(off+0x2C, true)/4096; //size
obj.aspect = view.getUint16(off+0x30, true) / 4096; //aspect 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.rotVelFrom = view.getInt16(off+0x34, true);
obj.rotVelTo = view.getInt16(off+0x36, true); obj.rotVelTo = view.getInt16(off+0x36, true);
obj.unknown13 = view.getUint16(off+0x38, true); //??? (0) obj.scX = view.getInt16(off+0x38, true)/0x8000; //??? (0) //scale center offset?
obj.unknown14 = view.getUint16(off+0x3A, true); //??? (4B) 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.emitterLifetime = view.getUint16(off+0x3C, true); //stop emitting particles after this many frames
obj.duration = view.getUint16(off+0x3E, true); obj.duration = view.getUint16(off+0x3E, true);
@ -146,8 +214,8 @@ window.spa = function(input) {
obj.textureId = view.getUint8(off+0x47, true); obj.textureId = view.getUint8(off+0x47, true);
obj.unknown21 = view.getUint32(off+0x48, true); //negative number makes grass disappear (1 for grass, smoke) 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.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.xScaleDelta = view.getInt16(off+0x50, true)/4096; //x scale delta for some reason. usually 0
obj.yScaleDelta = view.getUint16(off+0x52, true); //y 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 obj.unknown25 = view.getUint32(off+0x54, true); //FFFFFFFF makes run at half framerate. idk? usually 0
off += 0x58; off += 0x58;
@ -165,7 +233,8 @@ window.spa = function(input) {
unkBase: view.getUint16(off, true)/4096, unkBase: view.getUint16(off, true)/4096,
scaleFrom: view.getUint16(off+2, true)/4096, scaleFrom: view.getUint16(off+2, true)/4096,
scaleTo: view.getUint16(off+4, 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), flagParam: view.getUint16(off+8, true),
unk4b: view.getUint16(off+10, true), unk4b: view.getUint16(off+10, true),
}; };
@ -173,7 +242,7 @@ window.spa = function(input) {
} }
if ((obj.flag & ParticleFlags.ColorAnimation) != 0) if ((obj.flag & ParticleFlags.ColorAnimation) != 0)
{ {
obj.ColorAnimation = { obj.colorAnim = {
colorFrom: view.getUint16(off, true), //color from colorFrom: view.getUint16(off, true), //color from
colorTo: view.getUint16(off+2, true), //color to (seems to be same as base color) 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) 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); var flags = view.getUint16(off+4, true);
obj.info = { obj.info = {
pal0trans: (flags>>3)&1, //weirdly different format pal0trans: true,//z(flags>>3)&1, //weirdly different format
format: ((flags)&7), format: ((flags)&7),
height: 8 << ((flags>>8)&0xF), height: 8 << ((flags>>8)&0xF),
width: 8 << ((flags>>4)&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

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

View File

@ -16,9 +16,39 @@ window.nitroShaders = new (function() {
\n\ \n\
uniform sampler2D uSampler;\n\ uniform sampler2D uSampler;\n\
\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\ void main(void) {\n\
gl_FragColor = texture2D(uSampler, vTextureCoord)*color;\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\ this.defaultVert = "attribute vec3 aVertexPosition;\n\
@ -62,20 +92,20 @@ window.nitroShaders = new (function() {
\n\ \n\
uniform sampler2D uSampler;\n\ uniform sampler2D uSampler;\n\
\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\ float depth = texture2D(map, pos).r;\n\
return step(compare, depth);\n\ return smoothstep(compare-so, compare, depth);\n\
}\n\ }\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 texelSize = vec2(1.0)/size;\n\
vec2 f = fract(uv*size+0.5);\n\ vec2 f = fract(uv*size+0.5);\n\
vec2 centroidUV = floor(uv*size+0.5)/size;\n\ vec2 centroidUV = floor(uv*size+0.5)/size;\n\
\n\ \n\
float lb = shadowCompare(depths, centroidUV+texelSize*vec2(0.0, 0.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);\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);\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);\n\ float rt = shadowCompare(depths, centroidUV+texelSize*vec2(1.0, 1.0), compare, so);\n\
float a = mix(lb, lt, f.y);\n\ float a = mix(lb, lt, f.y);\n\
float b = mix(rb, rt, f.y);\n\ float b = mix(rb, rt, f.y);\n\
float c = mix(a, b, f.x);\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\ float dist = max(ldNorm.x, ldNorm.y);\n\
\n\ \n\
if (dist > 0.5) {\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\ } else if (dist > 0.4) {\n\
float lerp1 = shadowLerp(farLightDSampler, vec2(4096.0, 4096.0), fLightDist.xy, fLightDist.z-farShadOff);\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);\n\ float lerp2 = shadowLerp(lightDSampler, vec2(2048.0, 2048.0), lightDist.xy, lightDist.z-shadOff, shadOff*4.0);\n\
\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\ 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\ } 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\
\n\ \n\
if (gl_FragColor.a == 0.0) discard;\n\ if (gl_FragColor.a == 0.0) discard;\n\
@ -118,6 +148,7 @@ window.nitroShaders = new (function() {
\n\ \n\
uniform mat4 shadowMat;\n\ uniform mat4 shadowMat;\n\
uniform mat4 farShadowMat;\n\ uniform mat4 farShadowMat;\n\
uniform float lightIntensity; \n\
\n\ \n\
varying vec2 vTextureCoord;\n\ varying vec2 vTextureCoord;\n\
varying vec4 color;\n\ varying vec4 color;\n\
@ -133,7 +164,7 @@ window.nitroShaders = new (function() {
lightDist = (shadowMat*pos + vec4(1, 1, 1, 0)) / 2.0;\n\ lightDist = (shadowMat*pos + vec4(1, 1, 1, 0)) / 2.0;\n\
fLightDist = (farShadowMat*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\ 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\ \n\
color = aColor*colMult;\n\ color = aColor*colMult;\n\
color = vec4(color.x*diffuse, color.y*diffuse, color.z*diffuse, color.w);\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 = [ var shadUnif = [
["shadowMatUniform", "shadowMat"], ["shadowMatUniform", "shadowMat"],
["farShadowMatUniform", "farShadowMat"], ["farShadowMatUniform", "farShadowMat"],
["lightIntensityUniform", "lightIntensity"],
["shadOffUniform", "shadOff"], ["shadOffUniform", "shadOff"],
["farShadOffUniform", "farShadOff"], ["farShadOffUniform", "farShadOff"],

File diff suppressed because one or more lines are too long