Dies ist das Repository meines kleinen Portfolios.
Im Hintergrund läuft eine Planetensimulation, geschrieben in JavaScript und Three.js.
Die zu sehenden Texturen stammen von:
https://www.solarsystemscope.com/textures/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
5.1 KiB
276 lines
5.1 KiB
import { |
|
AnimationMixer, |
|
Box3, |
|
Mesh, |
|
MeshLambertMaterial, |
|
Object3D, |
|
TextureLoader, |
|
UVMapping, |
|
sRGBEncoding |
|
} 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: 0xffaa00, wireframe: true } ); |
|
const materialTexture = new MeshLambertMaterial( { color: 0xffffff, wireframe: false, map: skinMap } ); |
|
|
|
// |
|
|
|
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 ]; |
|
textures[ i ].encoding = sRGBEncoding; |
|
|
|
} |
|
|
|
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 ]; |
|
// SKINS |
|
|
|
this.skinsBody = loadTextures( config.baseUrl + 'skins/', config.skins ); |
|
this.skinsWeapon = loadTextures( config.baseUrl + 'skins/', weaponsTextures ); |
|
|
|
// BODY |
|
|
|
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(); |
|
|
|
} ); |
|
|
|
// WEAPONS |
|
|
|
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 };
|
|
|