Merge pull request #5 from riperiperi/gh-pages

Particle Effects, Loop/Sticky collision types
master
riperiperi 2018-03-26 10:28:13 +01:00 committed by GitHub
commit 42b11dc1a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 6575 additions and 3152 deletions

View File

@ -42,7 +42,14 @@ window.cameraIngame = function(kart) {
camNormal[2] += (kart.kartNormal[2]-camNormal[2])*0.075; camNormal[2] += (kart.kartNormal[2]-camNormal[2])*0.075;
vec3.normalize(camNormal, camNormal); vec3.normalize(camNormal, camNormal);
if (kart.physBasis != null) {
var kartA = kart.physicalDir+kart.driftOff/2;
var forward = [Math.sin(kartA), 0, -Math.cos(kartA)];
vec3.transformMat4(forward, forward, kart.physBasis.mat);
camAngle += dirDiff(Math.atan2(forward[0], -forward[2]), camAngle)*0.075;
} else {
camAngle += dirDiff(kart.physicalDir+kart.driftOff/2, camAngle)*0.075; camAngle += dirDiff(kart.physicalDir+kart.driftOff/2, camAngle)*0.075;
}
camAngle = fixDir(camAngle); camAngle = fixDir(camAngle);
boostOff += (((kart.boostNorm+kart.boostMT > 0)?5:0) - boostOff)*0.075 boostOff += (((kart.boostNorm+kart.boostMT > 0)?5:0) - boostOff)*0.075
@ -52,14 +59,23 @@ 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;
} }
function buildBasis() { function buildBasis() {
//order y, x, z //order y, x, z
var basis = gramShmidt(camNormal, [Math.cos(camAngle), 0, Math.sin(camAngle)], [Math.sin(camAngle), 0, -Math.cos(camAngle)]); var kart = thisObj.kart;
var forward = [Math.sin(camAngle), 0, -Math.cos(camAngle)];
var side = [Math.cos(camAngle), 0, Math.sin(camAngle)];
/*
if (kart.physBasis != null) {
vec3.transformMat4(forward, forward, kart.physBasis.mat);
vec3.transformMat4(side, side, kart.physBasis.mat);
}
*/
var basis = gramShmidt(camNormal, side, forward);
var temp = basis[0]; var temp = basis[0];
basis[0] = basis[1]; basis[0] = basis[1];
basis[1] = temp; //todo: cleanup basis[1] = temp; //todo: cleanup

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

@ -19,6 +19,7 @@ window.controlRaceCPU = function(nkm) {
} }
this.fetchInput = fetchInput; this.fetchInput = fetchInput;
this.setRouteID = setRouteID;
var battleMode = (nkm.sections["EPAT"] == null); var battleMode = (nkm.sections["EPAT"] == null);
@ -53,11 +54,30 @@ window.controlRaceCPU = function(nkm) {
var dist = vec3.dot(destNorm, kart.pos) + destConst; var dist = vec3.dot(destNorm, kart.pos) + destConst;
if (dist < ePoi.pointSize) advancePoint(); if (dist < ePoi.pointSize) advancePoint();
if (ePath.loop) debugger;
destPoint = vec3.add([], ePoi.pos, vec3.scale([], vec3.lerp([], posOffset, destOff, offTrans), ePoi.pointSize)); destPoint = vec3.add([], ePoi.pos, vec3.scale([], vec3.lerp([], posOffset, destOff, offTrans), ePoi.pointSize));
var dirToPt = Math.atan2(destPoint[0]-kart.pos[0], kart.pos[2]-destPoint[2]); var dirToPt = Math.atan2(destPoint[0]-kart.pos[0], kart.pos[2]-destPoint[2]);
var diff = dirDiff(dirToPt, kart.physicalDir); var physDir = kart.physicalDir;
if (kart.physBasis) {
if (kart.physBasis.loop) {
return {
accel: true, //x
decel: false, //z
drift: false, //s
item: false, //a
//-1 to 1, intensity.
turn: 0,
airTurn: 0 //air excitebike turn, doesn't really have much function
};
}
var forward = [Math.sin(physDir), 0, -Math.cos(physDir)];
vec3.transformMat4(forward, forward, kart.physBasis.mat);
var physDir = Math.atan2(forward[0], -forward[2]);
}
var diff = dirDiff(dirToPt, physDir);
var turn = Math.min(Math.max(-1, (diff*3)), 1); var turn = Math.min(Math.max(-1, (diff*3)), 1);
offTrans += 1/240; offTrans += 1/240;
@ -94,6 +114,11 @@ window.controlRaceCPU = function(nkm) {
} }
function setRouteID(routeID) {
ePoiInd = routeID-1
advancePoint();
}
function advancePoint() { function advancePoint() {
if (++ePoiInd < ePath.startInd+ePath.pathLen) { if (++ePoiInd < ePath.startInd+ePath.pathLen) {
//next within this path //next within this path
@ -102,10 +127,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;
@ -30,20 +31,25 @@ window.IngameRes = function(rom) {
"koura_w" /*blue shell item rep*/, "f_box", "killer" /*bullet bill*/ "koura_w" /*blue shell item rep*/, "f_box", "killer" /*bullet bill*/
] ]
//order
//donkey, toad, bowser?, luigi, mario, peach, wario, yoshi, daisy, waluigi, dry bones (karon), robo, heyho
var toSoundOff = [
4, 0, 1, 2, 5, 6, 7, 3, 10, 8, 9, 11, 12
];
var charNames = [ var charNames = [
"mario", "donkey", "kinopio", "koopa", "peach", "wario", "yoshi", "luigi", "karon", "daisy", "waluigi", "robo", "heyho" "mario", "donkey", "kinopio", "koopa", "peach", "wario", "yoshi", "luigi", "karon", "daisy", "waluigi", "robo", "heyho"
] ];
var charAbbrv = [ var charAbbrv = [
"MR", "DK", "KO", "KP", "PC", "WR", "YS", "LG", "KA", "DS", "WL", "RB", "HH" "MR", "DK", "KO", "KP", "PC", "WR", "YS", "LG", "KA", "DS", "WL", "RB", "HH"
] ];
var tireName = ["kart_tire_L", "kart_tire_M", "kart_tire_S"]; var tireName = ["kart_tire_L", "kart_tire_M", "kart_tire_S"];
var characters = []; var characters = [];
var karts = []; var karts = [];
var test = new spa(r.MainEffect.getFile("RaceEffect.spa"));
loadItems(); loadItems();
loadTires(); loadTires();
@ -75,6 +81,7 @@ window.IngameRes = function(rom) {
loseA: new nsbca(r.KartModelSub.getFile(base+"_lose.nsbca")), loseA: new nsbca(r.KartModelSub.getFile(base+"_lose.nsbca")),
spinA: new nsbca(r.KartModelSub.getFile(base+"_spin.nsbca")), spinA: new nsbca(r.KartModelSub.getFile(base+"_spin.nsbca")),
winA: new nsbca(r.KartModelSub.getFile(base+"_win.nsbca")), winA: new nsbca(r.KartModelSub.getFile(base+"_win.nsbca")),
sndOff: toSoundOff[ind]*14,
} }
characters[ind] = obj; characters[ind] = obj;
return characters[ind]; return characters[ind];

View File

@ -165,6 +165,11 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
vec3.scale(targ, targ, 1/1024); vec3.scale(targ, targ, 1/1024);
mat4.mul(scn.shadMat, mat4.ortho(mat4.create(), targ[0]-shadres, targ[0]+shadres, targ[1]-shadres, targ[1]+shadres, -targ[2]-2.5, -targ[2]+2.5), scn.lightMat); mat4.mul(scn.shadMat, mat4.ortho(mat4.create(), targ[0]-shadres, targ[0]+shadres, targ[1]-shadres, targ[1]+shadres, -targ[2]-2.5, -targ[2]+2.5), scn.lightMat);
var places = [];
for (var i=0; i<scn.karts.length; i++) { places.push(scn.karts[i]); }
places.sort(function(a, b) {return b.getPosition() - a.getPosition()});
for (var i=0; i<places.length; i++) { places[i].placement = i+1; };
for (var i=0; i<scn.karts.length; i++) { for (var i=0; i<scn.karts.length; i++) {
var ent = scn.karts[i]; var ent = scn.karts[i];
if (ent.active) ent.update(scn); if (ent.active) ent.update(scn);
@ -291,9 +296,9 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
// the thresholds for different win sounds and music // the thresholds for different win sounds and music
// thresh, goalsound, goalmusic, goalpostmusic // thresh, goalsound, goalmusic, goalpostmusic
var finishPercents = [ var finishPercents = [
[0, 66, 46, 58], [0, 66, 46, 58, 9],
[0.5, 66, 47, 56], [0.5, 66, 47, 56, 10],
[1.1, 67, 48, 57] [1.1, 67, 48, 57, 11]
] ]
function lapAdvance(kart) { function lapAdvance(kart) {
@ -309,20 +314,21 @@ window.courseScene = function(mainNarc, texNarc, music, chars, options, gameRes)
else if (kart.lapNumber == 4) { else if (kart.lapNumber == 4) {
var finishTuple = []; var finishTuple = [];
for (var i=0; i<finishPercents.length; i++) { for (var i=0; i<finishPercents.length; i++) {
if (finishPercents[i][0] > winPercent) continue;
finishTuple = finishPercents[i]; finishTuple = finishPercents[i];
if (finishPercents[i][0] >= winPercent) break;
} }
kart.controller = new controlRaceCPU(scn.nkm, {}); kart.controller = new controlRaceCPU(scn.nkm, {});
kart.controller.setKart(kart); kart.controller.setKart(kart);
kart.anim.setAnim(winPercent>0.5?kart.charRes.LoseA:kart.charRes.winA); kart.anim.setAnim(winPercent>0.5?kart.charRes.loseA:kart.charRes.winA);
kart.animMode = "raceEnd"; kart.animMode = "raceEnd";
scn.camera = (new cameraSpectator(kart, scn)); scn.camera = (new cameraSpectator(kart, scn));
nitroAudio.playSound(finishTuple[1], {volume:2}, 0); nitroAudio.playSound(finishTuple[1], {volume:2}, 0);
nitroAudio.playSound(finishTuple[2], {volume:2}, null); nitroAudio.playSound(finishTuple[2], {volume:2}, null);
nitroAudio.instaKill(scn.musicPlayer); nitroAudio.instaKill(scn.musicPlayer);
kart.playCharacterSound(finishTuple[4], 2);
musicRestartTimer = 0; musicRestartTimer = 0;
musicRestart = 7.5*60; musicRestart = 7.5*60;
musicRestartType = 1; musicRestartType = 1;

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

@ -21,6 +21,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
var params = scene.gameRes.kartPhys.karts[kartN]; var params = scene.gameRes.kartPhys.karts[kartN];
var offsets = scene.gameRes.kartOff.karts[kartN]; var offsets = scene.gameRes.kartOff.karts[kartN];
this.wheelClass = (offsets.name[10] == "L")?2:((offsets.name[10] == "M")?1:0);
this.local = controller.local; this.local = controller.local;
this.active = true; this.active = true;
@ -66,12 +67,20 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
this.drawWheels = drawWheels; this.drawWheels = drawWheels;
this.drawChar = drawChar; this.drawChar = drawChar;
this.getPosition = getPosition;
this.playCharacterSound = playCharacterSound;
this.trackAttach = null; //a normal for the kart to attach to (loop) this.trackAttach = null; //a normal for the kart to attach to (loop)
this.boostMT = 0; this.boostMT = 0;
this.boostNorm = 0; this.boostNorm = 0;
this.kartColVel = vec3.create(); this.kartColVel = vec3.create();
this.kartColTimer = 0; this.kartColTimer = 0;
this.kartWallTimer = 0;
this.charSoundTimer = 0;
this.placement = 0;
this.lastPlacement = 0;
var charRes = scene.gameRes.getChar(charN); var charRes = scene.gameRes.getChar(charN);
var kartRes = scene.gameRes.getKart(kartN); var kartRes = scene.gameRes.getKart(kartN);
@ -95,14 +104,24 @@ 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.OOB = 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];
var checkpoints = nkm.sections["CPOI"].entries; var checkpoints = nkm.sections["CPOI"].entries;
var respawns = nkm.sections["CPOI"].entries; var respawns = nkm.sections["KTPJ"].entries;
var futureChecks = [1]; var futureChecks = [1];
var hitGroundAnim = [ //length 13, on y axis var hitGroundAnim = [ //length 13, on y axis
@ -193,6 +212,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], [k.wheelClass*(i-2.5)*-2, (-params.colRadius)-k.wheelClass*2, 0]), 1/16);
}
} }
var scale = 16; var scale = 16;
@ -262,7 +285,20 @@ 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) {
if (k.placement != k.lastPlacement) {
if (k.placement < k.lastPlacement) {
if (k.charSoundTimer == 0) {
playCharacterSound(5);
k.charSoundTimer = 60;
}
}
k.lastPlacement = k.placement;
}
var lastPos = vec3.clone(k.pos); var lastPos = vec3.clone(k.pos);
updateMat = true; updateMat = true;
@ -314,7 +350,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 +374,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 +386,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 +410,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,8 +419,22 @@ 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
if (k.OOB > 0) {
playCharacterSound(0);
var current = checkpoints[k.checkPointNumber];
var respawn = respawns[current.respawn];
k.physicalDir = (180-respawn.angle[1])*(Math.PI/180);
k.angle = k.physicalDir;
k.speed = 0;
k.vel = vec3.create();
k.pos = vec3.clone(respawn.pos);
vec3.add(k.pos, k.pos, [0,16,0]);
if (k.controller.setRouteID != null) k.controller.setRouteID(respawn.id1);
k.OOB = 0;
}
var groundEffect = 0; var groundEffect = 0;
if (lastCollided != -1) { if (lastCollided != -1) {
groundEffect = MKDS_COLTYPE.PHYS_MAP[lastCollided]; groundEffect = MKDS_COLTYPE.PHYS_MAP[lastCollided];
@ -402,6 +472,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 +505,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 +542,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 +563,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;
@ -562,6 +640,7 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
if (!onGround) { if (!onGround) {
this.kartTargetNormal = [0, 1, 0]; this.kartTargetNormal = [0, 1, 0];
if (k.physBasis != null) vec3.transformMat4(this.kartTargetNormal,this.kartTargetNormal,k.physBasis.mat);
vec3.add(k.vel, k.vel, k.gravity) vec3.add(k.vel, k.vel, k.gravity)
if (k.ylock >= 0) { if (k.ylock >= 0) {
ylvel += k.gravity[1]; ylvel += k.gravity[1];
@ -584,6 +663,8 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
} }
if (k.kartColTimer > 0) k.kartColTimer--; if (k.kartColTimer > 0) k.kartColTimer--;
if (k.kartWallTimer > 0) k.kartWallTimer--;
if (k.charSoundTimer > 0) k.charSoundTimer--;
wheelTurn += k.speed/16; wheelTurn += k.speed/16;
wheelTurn = fixDir(wheelTurn); wheelTurn = fixDir(wheelTurn);
@ -602,7 +683,15 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
var steps = 0; var steps = 0;
var remainingT = 1; var remainingT = 1;
var velSeg = vec3.clone(k.vel); var baseVel = k.vel;
if (k.physBasis != null) {
if (k.physBasis.time-- < 0) exitBasis();
else {
baseVel = vec3.transformMat4([], baseVel, k.physBasis.mat);
k.vel[1] = -1;
}
}
var velSeg = vec3.clone(baseVel);
var posSeg = vec3.clone(k.pos); var posSeg = vec3.clone(k.pos);
var ignoreList = []; var ignoreList = [];
while (steps++ < 10 && remainingT > 0.01) { while (steps++ < 10 && remainingT > 0.01) {
@ -611,7 +700,10 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
colResponse(posSeg, velSeg, result, ignoreList) colResponse(posSeg, velSeg, result, ignoreList)
remainingT -= result.t; remainingT -= result.t;
if (remainingT > 0.01) { if (remainingT > 0.01) {
velSeg = vec3.scale(vec3.create(), k.vel, remainingT); if (k.physBasis != null) {
baseVel = vec3.transformMat4([], k.vel, k.physBasis.mat);
}
velSeg = vec3.scale(vec3.create(), baseVel, remainingT);
} }
} else { } else {
vec3.add(posSeg, posSeg, velSeg); vec3.add(posSeg, posSeg, velSeg);
@ -642,6 +734,43 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
positionChanged(lastPos, k.pos); positionChanged(lastPos, k.pos);
} }
function playCharacterSound(sound, volume) {
//0 - hit
//1 - hit spin
//2 - hit ?? hit grnd
//3 - hit banana? race start?
//4 - hit spin?
//5 - good pass?
//6 - good OK!
//7 - use item
//8 - hit someone?
//9 = win
//10 = alright
//11 = bad
//12 = good record
//13 = bad record
if (volume == null) volume = 1;
nitroAudio.playSound(sound + charRes.sndOff, {volume: 2*volume}, 2, k);
}
function clearWheelParticles(prio) {
for (var 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 (var 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 +790,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,11 +815,21 @@ 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);
} }
} }
} }
function getPosition() {
if (futureChecks.length == 0) return 0;
var check = checkpoints[futureChecks[0]];
var dist = vec2.sub([], [check.x1, check.z1], [k.pos[0], k.pos[2]]);
var dot = vec2.dot(dist, [check.sinus, check.cosinus]);
return k.checkPointNumber + (1-(Math.abs(dot)/(0xFFFF))) + k.lapNumber*checkpoints.length;
}
function forwardCrossedKTP(ktp, oldPos, pos) { function forwardCrossedKTP(ktp, oldPos, pos) {
var distOld = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [oldPos[0], oldPos[2]]); var distOld = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [oldPos[0], oldPos[2]]);
var dist = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [pos[0], pos[2]]); var dist = vec2.sub([], [ktp.pos[0], ktp.pos[2]], [pos[0], pos[2]]);
@ -768,7 +908,13 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
function buildBasis() { function buildBasis() {
//order y, x, z //order y, x, z
var dir = k.physicalDir+k.driftOff+(Math.sin((COLBOUNCE_TIME-k.kartColTimer)/3)*(Math.PI/6)*(k.kartColTimer/COLBOUNCE_TIME)); var dir = k.physicalDir+k.driftOff+(Math.sin((COLBOUNCE_TIME-k.kartColTimer)/3)*(Math.PI/6)*(k.kartColTimer/COLBOUNCE_TIME));
var basis = gramShmidt(k.kartNormal, [Math.cos(dir), 0, Math.sin(dir)], [Math.sin(dir), 0, -Math.cos(dir)]); var forward = [Math.sin(dir), 0, -Math.cos(dir)];
var side = [Math.cos(dir), 0, Math.sin(dir)];
if (k.physBasis != null) {
vec3.transformMat4(forward, forward, k.physBasis.mat);
vec3.transformMat4(side, side, k.physBasis.mat);
}
var basis = gramShmidt(k.kartNormal, side, forward);
var temp = basis[0]; var temp = basis[0];
basis[0] = basis[1]; basis[0] = basis[1];
basis[1] = temp; //todo: cleanup basis[1] = temp; //todo: cleanup
@ -781,6 +927,56 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
} }
function enterBasis(normal) {
//establish a new basis for the kart velocity based on this normal.
//used by looping and sticky track surface types.
//first let's get the forward direction in our current basis
var dir = k.angle;
var forward, side;
if (k.physBasis != null) {
forward = vec3.transformMat4([], [- Math.sin(dir), 0, Math.cos(dir)], k.physBasis.mat);
side = vec3.transformMat4([], [Math.cos(dir), 0, Math.sin(dir)], k.physBasis.mat);
} else {
forward = [-Math.sin(dir), 0, Math.cos(dir)];
side = [Math.cos(dir), 0, Math.sin(dir)];
}
var basis = gramShmidt(normal, side, forward);
var temp = basis[0];
basis[0] = basis[1];
basis[1] = temp; //todo: cleanup
var m4 = [
basis[0][0], basis[0][1], basis[0][2], 0,
basis[1][0], basis[1][1], basis[1][2], 0,
basis[2][0], basis[2][1], basis[2][2], 0,
0, 0, 0, 1
];
k.physicalDir = dirDiff(k.physicalDir, k.angle);
k.angle = 0; //our front direction is now aligned with z.
k.vel = [Math.sin(k.angle)*k.speed, k.vel[1], -Math.cos(k.angle)*k.speed];
k.physBasis = {
mat: m4,
inv: mat4.invert([], m4),
time: 15,
loop: false
};
}
function exitBasis() {
//return to a normal y up, z forward basis.
var v = vec3.transformMat4([], k.vel, k.physBasis.mat);
k.physicalDir = dirDiff(k.physicalDir, k.angle);
k.angle = Math.atan2(v[0], -v[2]);
k.physicalDir += k.angle;
k.vel = v;
k.physBasis = null;
}
function sndUpdate(view) { function sndUpdate(view) {
k.soundProps.pos = vec3.transformMat4([], k.pos, view); k.soundProps.pos = vec3.transformMat4([], k.pos, view);
if (k.soundProps.lastPos != null) k.soundProps.vel = vec3.sub([], k.soundProps.pos, k.soundProps.lastPos); if (k.soundProps.lastPos != null) k.soundProps.vel = vec3.sub([], k.soundProps.pos, k.soundProps.lastPos);
@ -805,6 +1001,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,26 +1016,42 @@ 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);
var n = vec3.normalize([], dat.normal); var n = vec3.normalize([], dat.normal);
var an = n;
if (k.physBasis != null) {
an = vec3.transformMat4([], n, k.physBasis.inv);
}
var gravS = Math.sqrt(vec3.dot(k.gravity, k.gravity)); var gravS = Math.sqrt(vec3.dot(k.gravity, k.gravity));
var angle = Math.acos(vec3.dot(vec3.scale(vec3.create(), k.gravity, -1/gravS), n)); var angle = Math.acos(vec3.dot(vec3.scale(vec3.create(), k.gravity, -1/gravS), n));
var adjustPos = true; var adjustPos = true;
if (colType == MKDS_COLTYPE.OOB || colType == MKDS_COLTYPE.FALL) {
k.OOB = 1;
}
if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall if (MKDS_COLTYPE.GROUP_WALL.indexOf(colType) != -1) { //wall
//sliding plane, except normal is transformed to be entirely on the xz plane (cannot ride on top of wall, treated as vertical) //sliding plane, except normal is transformed to be entirely on the xz plane (cannot ride on top of wall, treated as vertical)
var xz = Math.sqrt(n[0]*n[0]+n[2]*n[2]) var xz = Math.sqrt(an[0]*an[0]+an[2]*an[2])
var adjN = [n[0]/xz, 0, n[2]/xz] var adjN = [an[0]/xz, 0, an[2]/xz]
var proj = vec3.dot(k.vel, adjN); var proj = vec3.dot(k.vel, adjN);
if (proj < -1) { if (proj < -1) {
if (k.kartWallTimer == 0) {
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));
}
k.kartWallTimer = 15;
} }
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;
@ -846,13 +1063,31 @@ window.Kart = function(pos, angle, speed, kartN, charN, controller, scene) {
k.boostNorm = BOOSTTIME; k.boostNorm = BOOSTTIME;
} }
var stick = (colType == MKDS_COLTYPE.STICKY || colType == MKDS_COLTYPE.LOOP);
if (k.vel[1] > 0) k.vel[1] = 0; if (k.vel[1] > 0) k.vel[1] = 0;
var proj = vec3.dot(k.vel, n); var proj = vec3.dot(k.vel, an);
if (proj < -4 && k.vel[1] < -2) { proj -= 1.5; } if (!stick && 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(), an, proj));
if (stick) {
enterBasis(dat.pNormal);
k.physBasis.loop = colType == MKDS_COLTYPE.LOOP;
} else {
if (k.physBasis != null)
exitBasis();
}
k.kartTargetNormal = dat.pNormal; k.kartTargetNormal = dat.pNormal;
if (!onGround) {
console.log("ground: "+colType+", "+colBE); if (change) {
var particle = colParticle(lastCollided, colBE);
if (particle == null)
clearWheelParticles(0);
else
setWheelParticles(particle, 0);
}
if (!onGround && !stick) {
groundAnim = 0; groundAnim = 0;
if (lastColSounds.land != null) nitroAudio.playSound(lastColSounds.land, {volume:1}, 0, k) if (lastColSounds.land != null) nitroAudio.playSound(lastColSounds.land, {volume:1}, 0, k)
} }

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 (var 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 (var 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 (var 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)
@ -203,7 +272,7 @@ window.spa = function(input) {
if ((obj.flag & ParticleFlags.TextureAnimation) != 0) if ((obj.flag & ParticleFlags.TextureAnimation) != 0)
{ {
var textures = []; var textures = [];
for (let i=0; i<8; i++) textures[i] = view.getUint8(off+i); for (var i=0; i<8; i++) textures[i] = view.getUint8(off+i);
obj.texAnim = { obj.texAnim = {
textures: textures, textures: textures,
frames: view.getUint8(off+8), frames: view.getUint8(off+8),
@ -215,7 +284,7 @@ window.spa = function(input) {
if ((obj.flag & ParticleFlags.Bit16) != 0) if ((obj.flag & ParticleFlags.Bit16) != 0)
{ {
obj.Bit16 = []; obj.Bit16 = [];
for (let i=0; i<20; i++) obj.Bit16[i] = view.getUint8(off+i); for (var i=0; i<20; i++) obj.Bit16[i] = view.getUint8(off+i);
off += 20; off += 20;
} }
if ((obj.flag & ParticleFlags.Gravity) != 0) if ((obj.flag & ParticleFlags.Gravity) != 0)
@ -239,31 +308,31 @@ window.spa = function(input) {
{ {
//seems to be 4 int 16s typically in some kind of pattern. //seems to be 4 int 16s typically in some kind of pattern.
obj.Bit25 = []; obj.Bit25 = [];
for (let i=0; i<8; i++) obj.Bit25[i] = view.getUint8(off+i); for (var i=0; i<8; i++) obj.Bit25[i] = view.getUint8(off+i);
off += 8; off += 8;
} }
if ((obj.flag & ParticleFlags.Bit26) != 0) if ((obj.flag & ParticleFlags.Bit26) != 0)
{ {
obj.Bit26 = []; obj.Bit26 = [];
for (let i=0; i<16; i++) obj.Bit26[i] = view.getUint8(off+i); for (var i=0; i<16; i++) obj.Bit26[i] = view.getUint8(off+i);
off += 16; off += 16;
} }
if ((obj.flag & ParticleFlags.Bit27) != 0) if ((obj.flag & ParticleFlags.Bit27) != 0)
{ {
obj.Bit27 = []; obj.Bit27 = [];
for (let i=0; i<4; i++) obj.Bit27[i] = view.getUint8(off+i); for (var i=0; i<4; i++) obj.Bit27[i] = view.getUint8(off+i);
off += 4; off += 4;
} }
if ((obj.flag & ParticleFlags.Bit28) != 0) if ((obj.flag & ParticleFlags.Bit28) != 0)
{ {
obj.Bit28 = []; obj.Bit28 = [];
for (let i=0; i<8; i++) obj.Bit28[i] = view.getUint8(off+i); for (var i=0; i<8; i++) obj.Bit28[i] = view.getUint8(off+i);
off += 8; off += 8;
} }
if ((obj.flag & ParticleFlags.Bit29) != 0) if ((obj.flag & ParticleFlags.Bit29) != 0)
{ {
obj.Bit29 = []; obj.Bit29 = [];
for (let i=0; i<16; i++) obj.Bit29[i] = view.getUint8(off+i); for (var i=0; i<16; i++) obj.Bit29[i] = view.getUint8(off+i);
off += 16; off += 16;
} }
@ -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;
var pos = t.pos;
var 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