mirror of
https://github.com/tengge1/ShadowEditor.git
synced 2026-01-25 15:08:11 +00:00
425 lines
14 KiB
JavaScript
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; |