Hi there,
so i try to make an 3D Viewer for Models.
I used this nice tutorial for my project:
I changed and added some things but its almost still the same code. The only difference is, than the script is more dynamic and gets the values from a database.
So in the “normal” version you can select parts and use the swatches to change the color of a part. This works fine. But some models have animations so i tried to add THREE.AnimationMixer to the code.
Here is the code:
var alphaBetString = "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z a b c d e f g h i j k l m n o p q r s t u v w x y z";var alphaBetTable = alphaBetString.split(" ");var rot13String = "N O P Q R S T U V W X Y Z A B C D E F G H I J K L M n o p q r s t u v w x y z a b c d e f g h i j k l m";var rot13Table = rot13String.split(" ");var numberString = "1 2 3 4 5 6 7 8 9 0";var numberTable = numberString.split(" ");var rot5String = "6 7 8 9 0 1 2 3 4 5";var rot5Table = rot5String.split(" ");var symbolString = "~ ` ! @ # $ % ^ & * ( ) _ + - = { } [ ] \\ | ; \' : \" < > ? , . /";var symbolTable = symbolString.split(" ");
const LOADER = document.getElementById('js-loader');
const TRAY = document.getElementById('js-tray-slide');
const DRAG_NOTICE = document.getElementById('js-drag-notice');
var theModel;
var userip = document.getElementById('userip').value;
var username = document.getElementById('username').value;
var path = document.getElementById('path').value;
var size = document.getElementById('size').value;
var parts = document.getElementById('parts').value;
var colores = document.getElementById('colores').value;
var posX = document.getElementById('posX').value;
var posY = document.getElementById('posY').value;
var posZ = document.getElementById('posZ').value;
var rotaX = document.getElementById('rotaX').value;
var rotaY = document.getElementById('rotaY').value;
var rotaZ = document.getElementById('rotaZ').value;
var cameradistance = document.getElementById('cameradistance').value;
//var posX = document.getElementById('TESTANIMATION').value;
const clock = new THREE.Clock();
COLOR_ARRAY = colores.split(",");
var cnumber = document.getElementById('cnumber').value;
path = path.replace(userip,'');
path = path.replace(username,'');
var modelselect = document.getElementById('modelselect').value;
modelselect = modelselect.replace(userip,'');
modelselect = modelselect.replace(username,'');
const MODEL_PATH = "../" + path + "/" + rot13rot5Encode(modelselect) + ".glb";
var activeOption = 'part-001';
var loaded = false;
const BACKGROUND_COLOR = 0xf1f1f1;
// Init the scene
const scene = new THREE.Scene();
// Set background
scene.background = new THREE.Color(BACKGROUND_COLOR);
scene.fog = new THREE.Fog(BACKGROUND_COLOR, 20, 100);
const canvas = document.querySelector('#c'+ cnumber);
// Init the renderer
const renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
renderer.shadowMap.enabled = true;
renderer.setPixelRatio(window.devicePixelRatio);
var cameraFar = 5; // 5 Default
if(cameradistance)
{
cameraFar = cameradistance;
}
document.body.appendChild(renderer.domElement);
// Add a camerra
var camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = cameraFar;
camera.position.x = 0;
camera.position.y = 0;
const INITIAL_MAP = []; for (let i=0; i < parts;i++)
INITIAL_MAP.push({
childID: `part-${String(i+1).padStart(3, '0')}`, // padding the number
mtl: new THREE.MeshPhongMaterial({ color: parseInt(COLOR_ARRAY[i]), shininess: 10 })
})
// Init the object loader
var loader = new THREE.GLTFLoader();
loader.load(MODEL_PATH, function (gltf) {
theModel = gltf.scene;
theModel.traverse(o => {
if (o.isMesh) {
o.castShadow = true;
o.receiveShadow = true;
}
});
// Set the models initial scale
theModel.scale.set(size, size, size);
//Use if model is wrong way turned
if(rotaX != 0)
{
theModel.rotation.x = rotaX;
}
if(rotaY != 0)
{
theModel.rotation.y = rotaY;
}
if(rotaZ != 0)
{
theModel.rotation.z = rotaZ;
}
// Offset the position a bit
if(posX != 0)
{
theModel.position.x = posX;
}
if(posY != 0)
{
theModel.position.y = posY;
}
else
{
theModel.position.y = -1;
}
if(posZ != 0)
{
theModel.position.z = posZ;
}
// Set initial textures
for (let object of INITIAL_MAP) {
initColor(theModel, object.childID, object.mtl);
}
// Add the model to the scene
scene.add(theModel);
// Remove the loader
LOADER.remove();
mixer = new THREE.AnimationMixer( theModel );
mixer.clipAction( gltf.animations[ 0 ] ).play();
}, undefined, function (error) {
console.error(error);
});
// Function - Add the textures to the models
function initColor(parent, type, mtl) {
parent.traverse(o => {
if (o.isMesh) {
if (o.name.includes(type)) {
o.material = mtl;
o.nameID = type; // Set a new property to identify this object
}
}
});
}
pointLight = new THREE.PointLight( 0xffffff, 0.33 );
pointLight.position.set(0,1,2);
camera.add(pointLight);
scene.add( camera );
// Add lights
var hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.61);
hemiLight.position.set(0, 50, 0);
// Add hemisphere light to scene
scene.add(hemiLight);
var dirLight = new THREE.DirectionalLight(0xffffff, 0.54);
dirLight.position.set(-8, 12, 8);
dirLight.castShadow = true;
dirLight.shadow.mapSize = new THREE.Vector2(1024 * 4, 1024 * 4);
// Add directional Light to scene
scene.add(dirLight);
// Floor
var floorGeometry = new THREE.PlaneGeometry(5000, 5000, 1, 1);
var floorMaterial = new THREE.MeshPhongMaterial({
color: 0xeeeeee,
shininess: 0 });
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -0.5 * Math.PI;
floor.receiveShadow = true;
floor.position.y = -1;
scene.add(floor);
// Add controls
var controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.maxPolarAngle = 360;
controls.minPolarAngle = 0;
controls.enableDamping = true;
controls.enablePan = false;
controls.dampingFactor = 0.1;
controls.autoRotate = false; // Toggle this if you'd like the chair to automatically rotate
controls.autoRotateSpeed = 15.5; // 30
function animate() {
const delta = clock.getDelta();
requestAnimationFrame(animate);
controls.update();
//mixer.update( delta );
renderer.render(scene, camera);
if (resizeRendererToDisplaySize(renderer)) {
const canvas = renderer.domElement;
camera.aspect = canvas.clientWidth / canvas.clientHeight;
camera.updateProjectionMatrix();
}
if (theModel != null && loaded == false) {
initialRotation();
DRAG_NOTICE.classList.add('start');
}
}
animate();
// Function - New resizing method
function resizeRendererToDisplaySize(renderer) {
const canvas = renderer.domElement;
var width = window.innerWidth;
var height = window.innerHeight;
var canvasPixelWidth = canvas.width / window.devicePixelRatio;
var canvasPixelHeight = canvas.height / window.devicePixelRatio;
const needResize = canvasPixelWidth !== width || canvasPixelHeight !== height;
if (needResize) {
renderer.setSize(width, height, false);
}
return needResize;
}
// Function - Build Colors
function buildColors(colors) {
for (let [i, color] of colors.entries()) {
let swatch = document.createElement('div');
swatch.classList.add('tray__swatch');
if (color.texture)
{
swatch.style.backgroundImage = "url(" + color.texture + ")";
} else
{
swatch.style.background = "#" + color.color;
}
swatch.setAttribute('data-key', i);
TRAY.append(swatch);
}
}
buildColors(colors);
// Select Option
const options = document.querySelectorAll(".option");
for (const option of options) {
option.addEventListener('click', selectOption);
}
function selectOption(e) {
let option = e.target;
activeOption = e.target.dataset.option;
for (const otherOption of options) {
otherOption.classList.remove('--is-active');
}
option.classList.add('--is-active');
}
// Swatches
const swatches = document.querySelectorAll(".tray__swatch");
for (const swatch of swatches) {
swatch.addEventListener('click', selectSwatch);
}
function selectSwatch(e) {
let color = colors[parseInt(e.target.dataset.key)];
let new_mtl;
if (color.texture) {
let txt = new THREE.TextureLoader().load(color.texture);
txt.repeat.set(color.size[0], color.size[1], color.size[2]);
txt.wrapS = THREE.RepeatWrapping;
txt.wrapT = THREE.RepeatWrapping;
new_mtl = new THREE.MeshPhongMaterial({
map: txt,
shininess: color.shininess ? color.shininess : 10 });
} else
{
new_mtl = new THREE.MeshPhongMaterial({
color: parseInt('0x' + color.color),
shininess: color.shininess ? color.shininess : 10 });
}
setMaterial(theModel, activeOption, new_mtl);
}
function setMaterial(parent, type, mtl) {
parent.traverse(o => {
if (o.isMesh && o.nameID != null) {
if (o.nameID == type) {
o.material = mtl;
}
}
});
}
// Function - Opening rotate
let initRotate = 0;
function initialRotation() {
initRotate++;
if (initRotate <= 120) {
theModel.rotation.y += Math.PI / 60;
} else {
loaded = true;
}
}
var slider = document.getElementById('js-tray'),sliderItems = document.getElementById('js-tray-slide'),difference;
function slide(wrapper, items) {
var posX1 = 0,
posX2 = 0,
posInitial,
threshold = 20,
posFinal,
slides = items.getElementsByClassName('tray__swatch');
// Mouse events
items.onmousedown = dragStart;
// Touch events
items.addEventListener('touchstart', dragStart);
items.addEventListener('touchend', dragEnd);
items.addEventListener('touchmove', dragAction);
function dragStart(e) {
e = e || window.event;
posInitial = items.offsetLeft;
difference = sliderItems.offsetWidth - slider.offsetWidth;
difference = difference * -1;
if (e.type == 'touchstart') {
posX1 = e.touches[0].clientX;
} else {
posX1 = e.clientX;
document.onmouseup = dragEnd;
document.onmousemove = dragAction;
}
}
function dragAction(e) {
e = e || window.event;
if (e.type == 'touchmove') {
posX2 = posX1 - e.touches[0].clientX;
posX1 = e.touches[0].clientX;
} else {
posX2 = posX1 - e.clientX;
posX1 = e.clientX;
}
if (items.offsetLeft - posX2 <= 0 && items.offsetLeft - posX2 >= difference) {
items.style.left = items.offsetLeft - posX2 + "px";
}
}
function dragEnd(e) {
posFinal = items.offsetLeft;
if (posFinal - posInitial < -threshold) {
} else if (posFinal - posInitial > threshold) {
} else {
items.style.left = posInitial + "px";
}
document.onmouseup = null;
document.onmousemove = null;
}
}
function rot13rot5Encode(input) { var output = ""; for (var i=0; i<input.length; i++) { for (var y=0; y<numberTable.length; y++) { if (input[i]==numberTable[y]) { output+=rot5Table[y]; } } for (var x=0; x<alphaBetTable.length; x++) { if (input[i]==alphaBetTable[x]) { output+=rot13Table[x]; } } for (var w=0; w<symbolTable.length; w++) { if (input[i]==symbolTable[w]) { output+=symbolTable[w]; } } if (input[i]==" ") { output+=" "; } } return output;};
slide(slider, sliderItems);
When i add mixer.update( delta ); at line 209 i can’t use the swatches anymore and the info “Drag to rotate 360°” don’t disappear. But the animation works this way. It seems that mixer.update( delta ); blocks the rest of the code…
Normal:
With animation:
Can someone help me please.