169 lines
5.4 KiB
JavaScript
169 lines
5.4 KiB
JavaScript
import { Object3D, Box3, AnimationMixer, MeshLambertMaterial, Mesh, TextureLoader, UVMapping } from "three";
|
|
import { MD2Loader } from "../loaders/MD2Loader.js";
|
|
class MD2Character {
|
|
constructor() {
|
|
this.scale = 1;
|
|
this.animationFPS = 6;
|
|
this.root = new Object3D();
|
|
this.meshBody = null;
|
|
this.meshWeapon = null;
|
|
this.skinsBody = [];
|
|
this.skinsWeapon = [];
|
|
this.weapons = [];
|
|
this.activeAnimation = null;
|
|
this.mixer = null;
|
|
this.onLoadComplete = function() {
|
|
};
|
|
this.loadCounter = 0;
|
|
}
|
|
loadParts(config) {
|
|
const scope = this;
|
|
function createPart(geometry, skinMap) {
|
|
const materialWireframe = new MeshLambertMaterial({
|
|
color: 16755200,
|
|
wireframe: true,
|
|
morphTargets: true,
|
|
morphNormals: true
|
|
});
|
|
const materialTexture = new MeshLambertMaterial({
|
|
color: 16777215,
|
|
wireframe: false,
|
|
map: skinMap,
|
|
morphTargets: true,
|
|
morphNormals: true
|
|
});
|
|
const mesh = new Mesh(geometry, materialTexture);
|
|
mesh.rotation.y = -Math.PI / 2;
|
|
mesh.castShadow = true;
|
|
mesh.receiveShadow = true;
|
|
mesh.materialTexture = materialTexture;
|
|
mesh.materialWireframe = materialWireframe;
|
|
return mesh;
|
|
}
|
|
function loadTextures(baseUrl, textureUrls) {
|
|
const textureLoader = new TextureLoader();
|
|
const textures = [];
|
|
for (let i = 0; i < textureUrls.length; i++) {
|
|
textures[i] = textureLoader.load(baseUrl + textureUrls[i], checkLoadingComplete);
|
|
textures[i].mapping = UVMapping;
|
|
textures[i].name = textureUrls[i];
|
|
if ("colorSpace" in textures[i])
|
|
textures[i].colorSpace = "srgb";
|
|
else
|
|
textures[i].encoding = 3001;
|
|
}
|
|
return textures;
|
|
}
|
|
function checkLoadingComplete() {
|
|
scope.loadCounter -= 1;
|
|
if (scope.loadCounter === 0)
|
|
scope.onLoadComplete();
|
|
}
|
|
this.loadCounter = config.weapons.length * 2 + config.skins.length + 1;
|
|
const weaponsTextures = [];
|
|
for (let i = 0; i < config.weapons.length; i++)
|
|
weaponsTextures[i] = config.weapons[i][1];
|
|
this.skinsBody = loadTextures(config.baseUrl + "skins/", config.skins);
|
|
this.skinsWeapon = loadTextures(config.baseUrl + "skins/", weaponsTextures);
|
|
const loader = new MD2Loader();
|
|
loader.load(config.baseUrl + config.body, function(geo) {
|
|
const boundingBox = new Box3();
|
|
boundingBox.setFromBufferAttribute(geo.attributes.position);
|
|
scope.root.position.y = -scope.scale * boundingBox.min.y;
|
|
const mesh = createPart(geo, scope.skinsBody[0]);
|
|
mesh.scale.set(scope.scale, scope.scale, scope.scale);
|
|
scope.root.add(mesh);
|
|
scope.meshBody = mesh;
|
|
scope.meshBody.clipOffset = 0;
|
|
scope.activeAnimationClipName = mesh.geometry.animations[0].name;
|
|
scope.mixer = new AnimationMixer(mesh);
|
|
checkLoadingComplete();
|
|
});
|
|
const generateCallback = function(index, name) {
|
|
return function(geo) {
|
|
const mesh = createPart(geo, scope.skinsWeapon[index]);
|
|
mesh.scale.set(scope.scale, scope.scale, scope.scale);
|
|
mesh.visible = false;
|
|
mesh.name = name;
|
|
scope.root.add(mesh);
|
|
scope.weapons[index] = mesh;
|
|
scope.meshWeapon = mesh;
|
|
checkLoadingComplete();
|
|
};
|
|
};
|
|
for (let i = 0; i < config.weapons.length; i++) {
|
|
loader.load(config.baseUrl + config.weapons[i][0], generateCallback(i, config.weapons[i][0]));
|
|
}
|
|
}
|
|
setPlaybackRate(rate) {
|
|
if (rate !== 0) {
|
|
this.mixer.timeScale = 1 / rate;
|
|
} else {
|
|
this.mixer.timeScale = 0;
|
|
}
|
|
}
|
|
setWireframe(wireframeEnabled) {
|
|
if (wireframeEnabled) {
|
|
if (this.meshBody)
|
|
this.meshBody.material = this.meshBody.materialWireframe;
|
|
if (this.meshWeapon)
|
|
this.meshWeapon.material = this.meshWeapon.materialWireframe;
|
|
} else {
|
|
if (this.meshBody)
|
|
this.meshBody.material = this.meshBody.materialTexture;
|
|
if (this.meshWeapon)
|
|
this.meshWeapon.material = this.meshWeapon.materialTexture;
|
|
}
|
|
}
|
|
setSkin(index) {
|
|
if (this.meshBody && this.meshBody.material.wireframe === false) {
|
|
this.meshBody.material.map = this.skinsBody[index];
|
|
}
|
|
}
|
|
setWeapon(index) {
|
|
for (let i = 0; i < this.weapons.length; i++)
|
|
this.weapons[i].visible = false;
|
|
const activeWeapon = this.weapons[index];
|
|
if (activeWeapon) {
|
|
activeWeapon.visible = true;
|
|
this.meshWeapon = activeWeapon;
|
|
this.syncWeaponAnimation();
|
|
}
|
|
}
|
|
setAnimation(clipName) {
|
|
if (this.meshBody) {
|
|
if (this.meshBody.activeAction) {
|
|
this.meshBody.activeAction.stop();
|
|
this.meshBody.activeAction = null;
|
|
}
|
|
const action = this.mixer.clipAction(clipName, this.meshBody);
|
|
if (action) {
|
|
this.meshBody.activeAction = action.play();
|
|
}
|
|
}
|
|
this.activeClipName = clipName;
|
|
this.syncWeaponAnimation();
|
|
}
|
|
syncWeaponAnimation() {
|
|
const clipName = this.activeClipName;
|
|
if (this.meshWeapon) {
|
|
if (this.meshWeapon.activeAction) {
|
|
this.meshWeapon.activeAction.stop();
|
|
this.meshWeapon.activeAction = null;
|
|
}
|
|
const action = this.mixer.clipAction(clipName, this.meshWeapon);
|
|
if (action) {
|
|
this.meshWeapon.activeAction = action.syncWith(this.meshBody.activeAction).play();
|
|
}
|
|
}
|
|
}
|
|
update(delta) {
|
|
if (this.mixer)
|
|
this.mixer.update(delta);
|
|
}
|
|
}
|
|
export {
|
|
MD2Character
|
|
};
|
|
//# sourceMappingURL=MD2Character.js.map
|