|
var THREE = require('three'); |
|
THREE.OrbitControls = require('three-orbit-controls')(THREE); |
|
THREE.GLTFLoader = require('./GLTFLoader.js')(THREE); |
|
|
|
import {glAvatarSystem} from './GLTFAvatarSystem.js'; |
|
import {mergeGLTFAvatar} from './GLTFAvatarMerge.js'; |
|
import {fileSave} from './lib/makeglb.js'; |
|
|
|
var clock = new THREE.Clock(); |
|
|
|
|
|
|
|
|
|
function Viewer(preserveDrawingBuffer) { |
|
|
|
|
|
this.preserveDrawingBuffer = preserveDrawingBuffer || false; |
|
|
|
|
|
this.canvas = null; |
|
this.fullWindow = true; |
|
|
|
this.skeletonMixer = null; |
|
this.skinMixers = []; |
|
|
|
this.gltf_skeleton = null; |
|
|
|
this.renderer = null; |
|
this.scene = null; |
|
this.camera = null; |
|
this.orbitControls = null; |
|
|
|
this.loader = null; |
|
|
|
|
|
this.skeletonAnimations = []; |
|
this.skeletonClips = {}; |
|
this.skeletonActionStates = {}; |
|
|
|
|
|
|
|
|
|
|
|
this.skeletonUpdateCallback = null; |
|
this.skinUpdateCallback = null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
Viewer.prototype.init = function(canvas) { |
|
|
|
|
|
|
|
if (canvas) { |
|
this.canvas = canvas; |
|
this.fullWindow = false; |
|
this.renderer = new THREE.WebGLRenderer( |
|
{ |
|
canvas: this.canvas, |
|
antialias: true, |
|
preserveDrawingBuffer: this.preserveDrawingBuffer |
|
} |
|
); |
|
} else { |
|
this.renderer = new THREE.WebGLRenderer( { |
|
antialias: true , |
|
preserveDrawingBuffer: this.preserveDrawingBuffer |
|
} ); |
|
this.canvas = this.renderer.domElement; |
|
this.fullWindow = true; |
|
this.canvas.width = window.innerWidth; |
|
this.canvas.height = window.innerHeight; |
|
this.renderer.setSize( window.innerWidth, window.innerHeight ); |
|
document.getElementById('container').appendChild(this.canvas); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
this.camera = new THREE.PerspectiveCamera( 45, this.canvas.width / this.canvas.height, 0.01, 100 ); |
|
|
|
this.renderer.setPixelRatio(window.devicePixelRatio); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.loader = new THREE.GLTFLoader(); |
|
this.orbitControls = new THREE.OrbitControls(this.camera, this.renderer.domElement); |
|
|
|
|
|
this.onWindowResize(); |
|
window.addEventListener( 'resize', this.onWindowResize.bind(this), false ); |
|
|
|
|
|
this.initScene(); |
|
|
|
|
|
|
|
this.selectSkeleton(Object.keys(glAvatarSystem.repo.skeletons)[0]); |
|
|
|
this.animate(); |
|
|
|
}; |
|
|
|
Viewer.prototype.initScene = function () { |
|
this.scene = new THREE.Scene(); |
|
this.scene.background = new THREE.Color( 0x222222 ); |
|
|
|
this.scene.add(this.camera); |
|
|
|
var ambient = new THREE.AmbientLight( 0x222222 ); |
|
this.scene.add( ambient ); |
|
var directionalLight = new THREE.DirectionalLight( 0xdddddd ); |
|
directionalLight.position.set( 1, 1, 1 ).normalize(); |
|
this.scene.add( directionalLight ); |
|
|
|
var spot1 = new THREE.SpotLight( 0xffffff, 1 ); |
|
|
|
spot1.position.set( 10, 20, -30 ); |
|
spot1.angle = 0.25; |
|
spot1.distance = 1024; |
|
spot1.penumbra = 0.75; |
|
|
|
|
|
|
|
|
|
|
|
|
|
this.scene.add( spot1 ); |
|
}; |
|
|
|
Viewer.prototype.cleanup = function() { |
|
if (this.skeletonMixer) { |
|
this.skeletonMixer.stopAllAction(); |
|
this.skeletonMixer = null; |
|
} |
|
|
|
if (this.skinMixers) { |
|
for (var i = 0, len = this.skinMixers.length; i < len; i++) { |
|
this.skinMixers[i].stopAllAction(); |
|
} |
|
this.skinMixers = []; |
|
} |
|
|
|
if (this.scene) { |
|
for (var i = 0, len = this.scene.children.length; i < len; i++) { |
|
this.scene.remove(this.scene.children[i]); |
|
} |
|
} |
|
|
|
this.initScene(); |
|
|
|
}; |
|
|
|
var onWindowResize = Viewer.prototype.onWindowResize = function() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (this.fullWindow) { |
|
this.renderer.setSize( window.innerWidth, window.innerHeight ); |
|
} else { |
|
this.renderer.setSize(this.canvas.width, this.canvas.height); |
|
} |
|
|
|
this.camera.aspect = this.canvas.width / this.canvas.height; |
|
this.camera.updateProjectionMatrix(); |
|
}; |
|
|
|
var animate = Viewer.prototype.animate = function() { |
|
requestAnimationFrame( this.animate.bind(this) ); |
|
|
|
|
|
var delta = clock.getDelta(); |
|
|
|
if (this.skeletonMixer) { |
|
this.skeletonMixer.update(delta); |
|
} |
|
|
|
for (var i = 0, len = this.skinMixers.length; i < len; i++) { |
|
this.skinMixers[i].update(delta); |
|
} |
|
|
|
|
|
|
|
this.orbitControls.update(); |
|
|
|
|
|
this.renderer.render(this.scene, this.camera); |
|
}; |
|
|
|
|
|
|
|
|
|
Viewer.prototype.playAnimation = function(index) { |
|
if (this.skeletonMixer) { |
|
this.skeletonMixer.stopAllAction(); |
|
this.skeletonMixer.clipAction(this.gltf_skeleton.animations[index]).play(); |
|
} |
|
}; |
|
|
|
|
|
Viewer.prototype.playAnimationMixing = function(key, isPlaying) { |
|
if (this.skeletonMixer) { |
|
var action = this.skeletonMixer.clipAction(this.skeletonClips[key]); |
|
action.setEffectiveTimeScale(1); |
|
isPlaying ? action.play() : action.stop(); |
|
} |
|
}; |
|
|
|
Viewer.prototype.updateVisibilityArray = function(v, v1) { |
|
for (var i = 0, len = v1.length; i < len; i++) { |
|
v[i] = v1[i] ? v[i] : 0; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
this.gltf_skeleton.gl_avatar.visibilityLUT.needsUpdate = true; |
|
}; |
|
|
|
Viewer.prototype.updateVisibilityValue = function(id, value) { |
|
this.gltf_skeleton.gl_avatar.visibility[id] = value ? 255 : 0; |
|
|
|
|
|
this.gltf_skeleton.gl_avatar.visibilityLUT.needsUpdate = true; |
|
}; |
|
|
|
Viewer.prototype.getVisibilityArray = function() { |
|
return this.gltf_skeleton.gl_avatar.visibility; |
|
}; |
|
|
|
Viewer.prototype.selectSkin = function(type, key, uri) { |
|
|
|
if (!uri) { |
|
|
|
uri = glAvatarSystem.repo[type][key]; |
|
} |
|
|
|
|
|
|
|
|
|
if (glAvatarSystem.isLoaded(type, key)) { |
|
this.skinOnload(type, key, glAvatarSystem.accessories[type][key].gltf); |
|
} else { |
|
var self = this; |
|
this.loader.setGlAvatarOfLinkingSkeleton(this.gltf_skeleton.gl_avatar); |
|
this.loader.load( uri, function(data, json, bins, imgs) { |
|
|
|
|
|
|
|
|
|
glAvatarSystem.accessories[type][key] = { |
|
gltf: data, |
|
|
|
json: json, |
|
bins: bins, |
|
imgs: imgs |
|
}; |
|
|
|
self.skinOnload(type, key, data); |
|
}, undefined, function ( error ) { |
|
console.error( error ); |
|
} ); |
|
} |
|
|
|
}; |
|
|
|
Viewer.prototype.skinOnload = function(type, key, data) { |
|
var c = glAvatarSystem.curAccessories[type]; |
|
|
|
var skinMixers = this.skinMixers; |
|
|
|
if (key === c.name) { |
|
console.log('same ' + type); |
|
return; |
|
} |
|
|
|
|
|
|
|
if (c.scene) { |
|
|
|
c.scene.parent.remove(c.scene); |
|
if (c.scene.attach_child) { |
|
console.log('has attach child (sub skeleton or rigid bind)'); |
|
c.scene.attach_child.parent.remove(c.scene.attach_child); |
|
} |
|
|
|
if (c.scene.skinMixer) { |
|
|
|
|
|
for ( var i = 0, len = skinMixers.length; i < len; i ++ ) { |
|
if (skinMixers[i] == c.scene.skinMixer) { |
|
|
|
skinMixers.splice(i, 1); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
|
|
this.gltf_skeleton.gl_avatar.visibility.fill(255); |
|
for (var t in glAvatarSystem.curAccessories) { |
|
if (t !== type && glAvatarSystem.curAccessories[t].scene) { |
|
var a = glAvatarSystem.curAccessories[t]; |
|
this.updateVisibilityArray(this.gltf_skeleton.gl_avatar.visibility, glAvatarSystem.accessories[t][a.name].gltf.gl_avatar.visibility); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
if (this.loader.enableGLTFAvatar) { |
|
this.updateVisibilityArray(this.gltf_skeleton.gl_avatar.visibility, data.gl_avatar.visibility); |
|
} |
|
|
|
|
|
var gltf = data; |
|
var object = gltf.scene; |
|
|
|
c.name = key; |
|
c.scene = object; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
object.traverse( function ( node ) { |
|
if ( node.isMesh ) node.castShadow = true; |
|
} ); |
|
|
|
|
|
|
|
if (object.attach_child) { |
|
|
|
object.attach_child.gl_avatar_base_root.add(object.attach_child); |
|
object.attach_child.updateMatrixWorld(); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (!object.skinMixer) { |
|
var animations = gltf.animations; |
|
if ( animations && animations.length ) { |
|
|
|
|
|
var mixer = new THREE.AnimationMixer( object.attach_child || object ); |
|
|
|
for ( var i = 0, len = animations.length; i < len; i ++ ) { |
|
var animation = animations[ i ]; |
|
|
|
mixer.clipAction( animation ).play(); |
|
} |
|
|
|
|
|
skinMixers.push(mixer); |
|
object.skinMixer = mixer; |
|
} |
|
} |
|
else { |
|
var m = object.skinMixer; |
|
|
|
|
|
|
|
skinMixers.push(m); |
|
|
|
} |
|
|
|
|
|
this.scene.add(object); |
|
object.updateMatrixWorld(); |
|
|
|
|
|
|
|
|
|
|
|
if (this.skinUpdateCallback) { |
|
this.skinUpdateCallback(type, key); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Viewer.prototype.selectSkeleton = function(key, uri) { |
|
var info = null; |
|
if (!uri) { |
|
|
|
info = glAvatarSystem.repo.skeletons[key]; |
|
uri = info.url; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
var self = this; |
|
|
|
this.loader.load( uri, function(data, json, bins, imgs) { |
|
|
|
|
|
|
|
|
|
glAvatarSystem.skeletons[key] = { |
|
gltf: data, |
|
|
|
json: json, |
|
bins: bins, |
|
imgs: imgs |
|
}; |
|
|
|
|
|
self.cleanup(); |
|
|
|
|
|
if (info) { |
|
self.camera.position.copy(info.cameraPos); |
|
self.orbitControls.target.copy(info.center); |
|
data.scene.rotation.copy(info.objectRotation); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
self.skeletonOnLoad(key, data); |
|
|
|
if (info) { |
|
for (var key in info.skins) { |
|
var skin = info.skins[key]; |
|
if (skin) { |
|
self.selectSkin(key, info.skins[key]); |
|
} |
|
} |
|
} |
|
|
|
|
|
}, undefined, function ( error ) { |
|
console.error( error ); |
|
} ); |
|
}; |
|
|
|
|
|
Viewer.prototype.skeletonOnLoad = function(key, data) { |
|
var gltf = data; |
|
|
|
this.gltf_skeleton = gltf; |
|
|
|
glAvatarSystem.curSkeleton.name = key; |
|
glAvatarSystem.curSkeleton.scene = gltf.scene; |
|
|
|
|
|
for (var cat in glAvatarSystem.curAccessories) { |
|
glAvatarSystem.curAccessories[cat].name = null; |
|
glAvatarSystem.curAccessories[cat].scene = null; |
|
} |
|
|
|
for (var cat in glAvatarSystem.accessories) { |
|
glAvatarSystem.accessories[cat] = {}; |
|
} |
|
|
|
|
|
var animations = gltf.animations; |
|
if ( animations && animations.length ) { |
|
|
|
|
|
|
|
|
|
this.skeletonAnimations = []; |
|
|
|
this.skeletonClips = {}; |
|
this.skeletonActionStates = {}; |
|
this.skeletonVisibilityId2Name = gltf.gl_avatar.visibilityId2Name || []; |
|
|
|
|
|
this.skeletonMixer = new THREE.AnimationMixer( gltf.scene ); |
|
for ( var i = 0; i < animations.length; i ++ ) { |
|
var animation = animations[ i ]; |
|
|
|
|
|
|
|
this.skeletonAnimations.push(animation.name || i.toFixed()); |
|
|
|
|
|
|
|
var key = animation.name || i; |
|
this.skeletonActionStates[key] = false; |
|
this.skeletonClips[key] = animation; |
|
|
|
if (i === 0) { |
|
this.skeletonActionStates[key] = true; |
|
this.playAnimationMixing(key, true); |
|
} |
|
} |
|
|
|
|
|
} |
|
this.scene.add( gltf.scene ); |
|
|
|
if (this.skeletonUpdateCallback) { |
|
this.skeletonUpdateCallback(key); |
|
} |
|
}; |
|
|
|
|
|
Viewer.prototype.mergeAndExport = function() { |
|
var skinArray = []; |
|
|
|
for (var cat in glAvatarSystem.curAccessories) { |
|
var c = glAvatarSystem.curAccessories[cat]; |
|
if (c.name) { |
|
skinArray.push(glAvatarSystem.accessories[cat][c.name]); |
|
} |
|
} |
|
|
|
var merged = mergeGLTFAvatar( |
|
glAvatarSystem.skeletons[glAvatarSystem.curSkeleton.name], |
|
skinArray |
|
); |
|
|
|
fileSave(merged.json, merged.bins, merged.imgs); |
|
}; |
|
|
|
|
|
var AvatarSystem = glAvatarSystem; |
|
export { Viewer, AvatarSystem }; |