425 lines
14 KiB
JavaScript

import { dispatch } from '../third_party';
import DataView from './DataView2';
import Vertex from './Vertex';
import Texture from './Texture';
import Bone from './Bone';
import HiddenBones from './HiddenBones';
import Animation from './Animation';
import BaseAnimations from './BaseAnimations';
import AnimationBone from './AnimationBone';
import vertShader from './shader/lol_vertex.glsl';
import fragShader from './shader/lol_fragment.glsl';
/**
* @author lolking / http://www.lolking.net/models
* @author tengge / https://github.com/tengge1
*/
function Model(options) {
var self = this;
self.champion = options.champion || "1";
self.skin = options.skin || 0;
self.loaded = false;
self.animsLoaded = false;
self.meshes = null;
self.vertices = null;
self.indices = null;
self.transforms = null;
self.bones = null;
self.boneLookup = {};
self.animIndex = -1;
self.animName = null;
self.baseAnim = null;
self.newAnimation = false;
self.animTime = 0;
self.tmpMat = mat4.create();
self.tmpVec = vec4.create();
self.ANIMATED = true;
self.dispatch = dispatch('load');
self.hiddenBones = null;
var hiddenBones = HiddenBones;
if (hiddenBones[self.champion] !== undefined) {
if (hiddenBones[self.champion][self.skin] !== undefined) {
self.hiddenBones = hiddenBones[self.champion][self.skin]
}
}
self.ambientColor = [.35, .35, .35, 1];
self.primaryColor = [1, 1, 1, 1];
self.secondaryColor = [.35, .35, .35, 1];
self.lightDir1 = vec3.create();
self.lightDir2 = vec3.create();
self.lightDir3 = vec3.create();
vec3.normalize(self.lightDir1, [5, 5, -5]);
vec3.normalize(self.lightDir2, [5, 5, 5]);
vec3.normalize(self.lightDir3, [-5, -5, -5]);
self.texture = null;
self.geometry = new THREE.BufferGeometry();
self.material = new THREE.RawShaderMaterial({
uniforms: {
uAmbientColor: {
value: new THREE.Vector4().fromArray(self.ambientColor)
},
uPrimaryColor: {
value: new THREE.Vector4().fromArray(self.primaryColor)
},
uSecondaryColor: {
value: new THREE.Vector4().fromArray(self.secondaryColor)
},
uLightDir1: {
value: new THREE.Vector3().fromArray(self.lightDir1)
},
uLightDir2: {
value: new THREE.Vector3().fromArray(self.lightDir2)
},
uLightDir3: {
value: new THREE.Vector3().fromArray(self.lightDir3)
},
uHasTexture: {
value: 0
},
uTexture: {
value: null
}
},
vertexShader: vertShader,
fragmentShader: fragShader,
});
};
Model.prototype.getAnimations = function () {
if (!this.animations) {
return null;
}
var names = [];
this.animations.forEach(function (n) {
names.push(n.name);
});
return names;
};
Model.prototype.getAnimation = function (name) {
var self = this,
i, animIndex = -1;
if (!self.animations) {
return animIndex
};
name = name.toLowerCase();
if (name == "idle" || name == "attack") {
var anims = [],
re = new RegExp(name + "[0-9]*");
for (i = 0; i < self.animations.length; ++i) {
if (self.animations[i].name.search(re) == 0) anims.push(i)
}
if (anims.length > 0) {
animIndex = anims[0];
}
} else {
for (i = 0; i < self.animations.length; ++i) {
if (self.animations[i].name == name) {
animIndex = i;
break
}
}
}
return animIndex
};
Model.prototype.setAnimation = function (name) {
var self = this;
self.animName = name;
self.newAnimation = true;
};
Model.prototype.update = function (time) {
var self = this,
i, j;
if (self.animTime == 0) {
self.animTime = time;
}
if (!self.loaded || !self.vertices || !self.animations || self.animations.length == 0) {
return;
}
self.animIndex = self.getAnimation(self.animName);
if (self.animIndex == -1) {
self.animIndex = 0;
self.animName = "idle";
}
var baseAnims = BaseAnimations;
if (baseAnims[self.champion] !== undefined) {
if (baseAnims[self.champion][self.skin] !== undefined) {
var baseAnim = baseAnims[self.champion][self.skin],
baseIndex = -1;
if (baseAnim[self.animations[self.animIndex].name]) {
baseIndex = self.getAnimation(baseAnim[self.animations[self.animIndex].name]);
} else if (baseAnim["all"]) {
baseIndex = self.getAnimation(baseAnim["all"]);
}
if (baseIndex > -1) {
self.baseAnim = self.animations[baseIndex];
} else {
self.baseAnim = null;
}
}
}
var deltaTime = time - self.animTime;
var anim = self.animations[self.animIndex];
if (deltaTime >= anim.duration) {
self.animTime = time;
deltaTime = 0
}
if (self.ANIMATED) {
var timePerFrame = 1e3 / anim.fps;
var frame = Math.floor(deltaTime / timePerFrame);
var r = deltaTime % timePerFrame / timePerFrame;
var hiddenBones = {};
if (self.hiddenBones) {
if (self.hiddenBones[anim.name]) {
hiddenBones = self.hiddenBones[anim.name];
} else if (self.hiddenBones["all"]) {
hiddenBones = self.hiddenBones["all"];
}
}
var b;
if (self.version >= 1) {
for (i = 0; i < self.bones.length; ++i) {
b = self.bones[i];
if (hiddenBones[b.name]) {
mat4.identity(self.tmpMat);
mat4.scale(self.tmpMat, self.tmpMat, vec3.set(self.tmpVec, 0, 0, 0));
mat4.copy(self.transforms[i], self.tmpMat)
} else if (anim.lookup[b.name] !== undefined) {
anim.bones[anim.lookup[b.name]].update(i, frame, r)
} else if (self.baseAnim && self.baseAnim.lookup[b.name] !== undefined) {
self.baseAnim.bones[self.baseAnim.lookup[b.name]].update(i, frame, r)
} else {
if (b.parent != -1) {
AnimationBone.prototype.mulSlimDX(self.transforms[i], b.incrMatrix, self.transforms[b.parent])
} else {
mat4.copy(self.transforms[i], b.incrMatrix)
}
}
}
} else {
for (i = 0; i < anim.bones.length; ++i) {
b = anim.bones[i];
if (self.boneLookup[b.bone] !== undefined) {
b.update(self.boneLookup[b.bone], frame, r)
} else {
var parentBone = anim.bones[i - 1];
if (!parentBone) continue;
if (parentBone.index + 1 < self.transforms.length) {
mat4.copy(self.transforms[parentBone.index + 1], self.transforms[parentBone.index])
}
b.index = parentBone.index + 1
}
}
}
var numBones = Math.min(self.transforms.length, self.bones.length);
for (i = 0; i < numBones; ++i) {
AnimationBone.prototype.mulSlimDX(self.transforms[i], self.bones[i].baseMatrix, self.transforms[i])
}
mat4.identity(self.tmpMat);
var numVerts = self.vertices.length,
vec = self.tmpVec,
position = self.geometry.attributes.position.array,
normal = self.geometry.attributes.normal.array,
v, w, m, idx;
for (i = 0; i < numVerts; ++i) {
v = self.vertices[i];
idx = i * 3;
position[idx] = position[idx + 1] = position[idx + 2] = 0;
normal[idx] = normal[idx + 1] = normal[idx + 2] = 0;
for (j = 0; j < 4; ++j) {
if (v.weights[j] > 0) {
w = v.weights[j];
m = anim.fps == 1 ? self.tmpMat : self.transforms[v.bones[j]];
vec3.transformMat4(vec, v.position, m);
position[idx] += vec[0] * w;
position[idx + 1] += vec[1] * w;
position[idx + 2] += vec[2] * w;
vec4.transformMat4(vec, v.normal, m);
normal[idx] += vec[0] * w;
normal[idx + 1] += vec[1] * w;
normal[idx + 2] += vec[2] * w
}
}
}
self.geometry.attributes.position.needsUpdate = true;
self.geometry.attributes.normal.needsUpdate = true;
}
if (self.newAnimation) {
self.newAnimation = false
}
};
Model.prototype.load = function () {
var self = this;
var url = 'assets/models/lol/models/' + self.champion + '_' + self.skin + '.lmesh';
var loader = new THREE.FileLoader();
loader.setResponseType('arraybuffer');
loader.load(url, function (buffer) {
self.loadMesh(buffer);
});
};
Model.prototype.loadMesh = function (buffer) {
if (!buffer) {
console.error("Bad buffer for DataView");
return
}
var self = this,
r = new DataView(buffer),
i,
v,
idx;
try {
var magic = r.getUint32();
if (magic != 604210091) {
console.log("Bad magic value");
return
}
} catch (err) {
alert("Model currently isn't loading! We're sorry and hope to have this fixed soon.");
console.log(err);
return
}
self.version = r.getUint32();
var animFile = r.getString();
var textureFile = r.getString();
if (animFile && animFile.length > 0) {
var url = "assets/models/lol/models/" + animFile + ".lanim";
var loader = new THREE.FileLoader();
loader.setResponseType('arraybuffer');
loader.load(url, function (buffer) {
self.loadAnim(buffer)
});
}
if (textureFile && textureFile.length > 0) {
self.texture = new Texture(self, "assets/models/lol/textures/" + self.champion + "/" + textureFile + ".png")
}
var numMeshes = r.getUint32();
if (numMeshes > 0) {
self.meshes = new Array(numMeshes);
for (i = 0; i < numMeshes; ++i) {
var name = r.getString().toLowerCase();
var vStart = r.getUint32();
var vCount = r.getUint32();
var iStart = r.getUint32();
var iCount = r.getUint32();
self.meshes[i] = {
name: name,
vStart: vStart,
vCount: vCount,
iStart: iStart,
iCount: iCount
}
}
}
var numVerts = r.getUint32();
if (numVerts > 0) {
self.vertices = new Array(numVerts);
self.vbData = new Float32Array(numVerts * 8);
var position = [];
var normal = [];
var uv = [];
for (i = 0; i < numVerts; ++i) {
idx = i * 8;
self.vertices[i] = v = new Vertex(r);
self.vbData[idx] = v.position[0];
self.vbData[idx + 1] = v.position[1];
self.vbData[idx + 2] = v.position[2];
self.vbData[idx + 3] = v.normal[0];
self.vbData[idx + 4] = v.normal[1];
self.vbData[idx + 5] = v.normal[2];
self.vbData[idx + 6] = v.u;
self.vbData[idx + 7] = v.v
position.push(v.position[0], v.position[1], v.position[2]);
normal.push(v.normal[0], v.normal[1], v.normal[2]);
uv.push(v.u, v.v);
}
self.geometry.addAttribute('position',
new THREE.BufferAttribute(new Float32Array(position), 3));
self.geometry.addAttribute('normal',
new THREE.BufferAttribute(new Float32Array(normal), 3));
self.geometry.addAttribute('uv',
new THREE.BufferAttribute(new Float32Array(uv), 2));
}
var numIndices = r.getUint32();
if (numIndices > 0) {
self.indices = new Array(numIndices);
for (i = 0; i < numIndices; ++i) {
self.indices[i] = r.getUint16()
}
self.geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(self.indices), 1));
}
var numBones = r.getUint32();
if (numBones > 0) {
self.transforms = new Array(numBones);
self.bones = new Array(numBones);
for (i = 0; i < numBones; ++i) {
self.bones[i] = new Bone(self, i, r);
if (self.boneLookup[self.bones[i].name] !== undefined) {
self.bones[i].name = self.bones[i].name + "2"
}
self.boneLookup[self.bones[i].name] = i;
self.transforms[i] = new mat4.create
}
}
self.loaded = true;
self.dispatch.call('load');
};
Model.prototype.loadAnim = function (buffer) {
if (!buffer) {
console.error("Bad buffer for DataView");
return
}
var self = this,
r = new DataView(buffer),
i;
var magic = r.getUint32();
if (magic != 604210092) {
console.log("Bad magic value");
return
}
var version = r.getUint32();
if (version >= 2) {
var compressedData = new Uint8Array(buffer, r.position);
var data = null;
try {
data = pako.inflate(compressedData)
} catch (err) {
console.log("Decompression error: " + err);
return
}
r = new DataView(data.buffer)
}
var numAnims = r.getUint32();
if (numAnims > 0) {
self.animations = new Array(numAnims);
for (i = 0; i < numAnims; ++i) {
self.animations[i] = new Animation(self, r, version)
}
}
self.animsLoaded = true
};
Model.prototype.on = function (eventName, callback) {
this.dispatch.on(eventName, callback);
};
export default Model;