Как сделать переключение моделей без подключения GUI?
Мне нужно чтобы осуществлялось переключение 3D моделей без подключения {GUI} dat.gui.js. Т.е в данный момент модели переключаются в выпадающем списке select в GUI интерфейсе. Нужно чтобы переключение происходило без GUI в моей разметке по клику на button type="radio". Помогите пожалуйста, как это можно сделать? Код прилагается.
import * as THREE from './three.module.js';
import { GUI } from './dat.gui.module.js';
import { OrbitControls } from './OrbitControls.js';
import { GLTFLoader } from './GLTFLoader.js';
import { DDSLoader } from './DDSLoader.js';
import { DRACOLoader } from './DRACOLoader.js';
import { RGBELoader } from './RGBELoader.js';
var orbitControls;
var container, camera, scene, renderer, loader;
var gltf, background, envMap, mixer, gui, extensionControls;
var clock = new THREE.Clock();
var base_path = '/wp-content/themes/store/models/';
// Object info
let pack = {
'293x273x21': {
url: base_path+'pack/293x273x21.gltf', cameraPos: new THREE.Vector3( 0.01, 0.3, 0.45 ), objectRotation: new THREE.Euler( 0, 3.2, 0 ), extensions: [ 'glTF' ], addEnvMap: true, addLights: true },
'300x200x40': {
url: base_path+'pack/300x200x40.gltf',
cameraPos: new THREE.Vector3( 0.01, 0.3, 0.45 ),
objectRotation: new THREE.Euler( 0, 0, 0 ),
extensions: [ 'glTF' ],
addEnvMap: true,
addLights: true
}};
let pencil = {
'71x27x187' :{url: base_path+'pencil/71x27x187.gltf', models: 'cover_box', cameraPos: new THREE.Vector3( 0.01, 0.25, 0.2 ), objectRotation: new THREE.Euler( 0, 3.2, 0 ), extensions: [ 'glTF' ], addEnvMap: true, addLights: true}};
let hidden = {
'85x80x60' : {
url: base_path+'hidden/85x80x60.gltf', models: 'hidden_box', cameraPos: new THREE.Vector3( 0.1, 0.2, 0.3 ), objectRotation: new THREE.Euler( 0, 3.2, 0 ), extensions: [ 'glTF' ], addEnvMap: true, addLights: true},
'90x50x32': {
url: base_path+'/hidden/90x50x32.gltf',
cameraPos: new THREE.Vector3( 0.01, 0.3, 0.2 ),
objectRotation: new THREE.Euler( 0, 0, 0 ),
extensions: [ 'glTF' ],
addEnvMap: true,
addLights: true
}};
let cover = {
'250х40х220' :{id: 'cover', url: base_path+'cover/250x40x220.gltf', models: 'cover_box', cameraPos: new THREE.Vector3( 0.01, 0.25, 0.2 ), objectRotation: new THREE.Euler( 0, 3.2, 0 ), extensions: [ 'glTF' ], addEnvMap: true, addLights: true}};
var scenes = [];
var counter = 0;
// Object URL init
let url1 = window.location.href;
const splittedUrl = url1.split("?");
let id = splittedUrl[splittedUrl.length - 1];
if (id === url1) {
scenes = pack;
}
else if (id === 'pack') {
scenes = pack;
}
else if (id === 'pencil') {
scenes = pencil;
}
else if (id === 'hidden') {
scenes = hidden;
}
else if (id === 'cover') {
scenes = cover;
}
// Size ID
var sizeID;
let box = document.getElementsByName(id);
box.forEach(box => {
box.addEventListener('click', e => {
const data_id = e.target.getAttribute('data-id');
const size_val = e.target.getAttribute('value');
counter = data_id;
sizeID = size_val;
console.log('Size: ' +size_val)
})});
// State
var state = {
scene: Object.keys( scenes )[ 0 ],
extension: scenes[ Object.keys( scenes )[ 0 ] ].extensions[ 0 ],
playAnimation: true,
};
function onload() {
// container = document.getElementsByClassName( 'figure_3d' );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.physicallyCorrectLights = true;
container = document.querySelector('.figure_3d');
container.append( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
// Load background and generate envMap
new RGBELoader()
.setDataType( THREE.UnsignedByteType )
.load(base_path+'venice_sunset_1k.hdr', function ( texture ) {
envMap = pmremGenerator.fromEquirectangular( texture ).texture;
pmremGenerator.dispose();
background = envMap;
//
buildGUI();
initScene( scenes[ state.scene ] );
animate();
} );
var pmremGenerator = new THREE.PMREMGenerator( renderer );
pmremGenerator.compileEquirectangularShader();
}
function initScene( sceneInfo ) {
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xFFFFFF );
camera = new THREE.PerspectiveCamera( 45, container.offsetWidth / container.offsetHeight, 0.001, 1000 );
scene.add( camera );
var spot1;
if ( sceneInfo.addLights ) {
var ambient = new THREE.AmbientLight( 0x222222 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xdddddd, 4 );
directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
spot1 = new THREE.SpotLight( 0xffffff, 1 );
spot1.position.set( 5, 10, 5 );
spot1.angle = 0.50;
spot1.penumbra = 0.75;
spot1.intensity = 100;
spot1.decay = 2;
if ( sceneInfo.shadows ) {
spot1.castShadow = true;
spot1.shadow.bias = 0.0001;
spot1.shadow.mapSize.width = 2048;
spot1.shadow.mapSize.height = 2048;
}
scene.add( spot1 );
}
if ( sceneInfo.shadows ) {
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
}
// TODO: Reuse existing OrbitControls, GLTFLoaders, and so on
orbitControls = new OrbitControls( camera, renderer.domElement );
if ( sceneInfo.addGround ) {
var groundMaterial = new THREE.MeshPhongMaterial( { color: 0xFFFFFF } );
var ground = new THREE.Mesh( new THREE.PlaneBufferGeometry( 512, 512 ), groundMaterial );
ground.receiveShadow = !! sceneInfo.shadows;
if ( sceneInfo.groundPos ) {
ground.position.copy( sceneInfo.groundPos );
} else {
ground.position.z = - 70;
}
ground.rotation.x = - Math.PI / 2;
scene.add( ground );
}
// manager
loader = new GLTFLoader();
var dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( './libs/draco/gltf/' );
loader.setDRACOLoader( dracoLoader );
loader.setDDSLoader( new DDSLoader() );
var url = sceneInfo.url.replace( /%s/g, state.extension );
if ( state.extension === 'glTF-Binary' ) {
url = url.replace( '.gltf', '.glb' );
}
var loadStartTime = performance.now();
loader.load( url, function ( data ) {
gltf = data;
var object = gltf.scene;
var basePath = '/wp-content/themes/store/models/';
// texture
var textures = [];
var textureLoader = new THREE.TextureLoader();
textures.push(
textureLoader.load(basePath+'materials/white.png'),
textureLoader.load(basePath+'materials/grey.png'),
textureLoader.load(basePath+'materials/craft.png')
);
material_1.addEventListener("click", function(){setTexture(0);});
material_2.addEventListener("click", function(){setTexture(1);});
material_3.addEventListener("click", function(){setTexture(2);});
function setTexture (texIdx){
object.traverse(function(object) {
if (object.isMesh) {
object.material.map = textures[texIdx];
}
});
}
// setTexture(counter);
console.info( 'Load time: ' + ( performance.now() - loadStartTime ).toFixed( 2 ) + ' ms.' );
if ( sceneInfo.cameraPos ) {
camera.position.copy( sceneInfo.cameraPos );
}
if ( sceneInfo.center ) {
orbitControls.target.copy( sceneInfo.center );
}
if ( sceneInfo.objectPosition ) {
object.position.copy( sceneInfo.objectPosition );
if ( spot1 ) {
spot1.target.position.copy( sceneInfo.objectPosition );
}
}
if ( sceneInfo.objectRotation ) {
object.rotation.copy( sceneInfo.objectRotation );
}
if ( sceneInfo.objectScale ) {
object.scale.copy( sceneInfo.objectScale );
}
object.traverse( function ( node ) {
if ( node.isMesh || node.isLight ) node.castShadow = true;
} );
var animations = gltf.animations;
if ( animations && animations.length ) {
mixer = new THREE.AnimationMixer( object );
for ( var i = 0; i < animations.length; i ++ ) {
var animation = animations[ i ];
// There's .3333 seconds junk at the tail of the Monster animation that
// keeps it from looping cleanly. Clip it at 3 seconds
if ( sceneInfo.animationTime ) {
animation.duration = sceneInfo.animationTime;
}
var action = mixer.clipAction( animation );
if ( state.playAnimation ) action.play();
}
}
scene.add( object );
onWindowResize();
}, undefined, function ( error ) {
console.error( error );
} );
}
function onWindowResize() {
const maxValue = 550;
var w = window.innerWidth;
var h = window.innerHeight;
w = innerWidth < maxValue ? innerWidth : maxValue;
h = innerWidth < maxValue ? innerWidth : maxValue;
// camera.aspect = container.offsetWidth / container.offsetHeight;
camera.aspect = w / h;
camera.updateProjectionMatrix();
renderer.setSize( w, h );
console.log('Object resized: ' + w + 'x' + h);
}
function animate() {
requestAnimationFrame( animate );
if ( mixer ) mixer.update( clock.getDelta() );
orbitControls.update();
render();
}
function render() {
renderer.render( scene, camera );
}
function buildGUI() {
gui = new GUI( { width: 330 } );
gui.domElement.container;
var sceneCtrl = gui.add( state, 'scene', Object.keys( scenes ) );
sceneCtrl.onChange( reload );
var animCtrl = gui.add( state, 'playAnimation' );
animCtrl.onChange( toggleAnimations );
updateGUI();
}
function updateGUI() {
if ( extensionControls ) extensionControls.remove();
var sceneInfo = scenes[ state.scene ];
if ( sceneInfo.extensions.indexOf( state.extension ) === - 1 ) {
state.extension = sceneInfo.extensions[ 0 ];
}
extensionControls = gui.add( state, 'extension', sceneInfo.extensions );
extensionControls.onChange( reload );
}
function toggleAnimations() {
for ( var i = 0; i < gltf.animations.length; i ++ ) {
var clip = gltf.animations[ i ];
var action = mixer.existingAction( clip );
state.playAnimation ? action.play() : action.stop();
}
}
function reload() {
if ( loader && mixer ) mixer.stopAllAction();
updateGUI();
initScene( scenes[ state.scene ] );
}
onload();
> Здесь в зависимости от выбранной модели генерируются input'ы со
> значениями из базы. Их может быть 1-2, так и может быть 3-5 и более.
> Для этого написал функцию которая получает data-id или value текущей
> кликнутой кнопки и можно перезаписывать переменную для замены url'a
> или имени модели. Сейчас все модели захардкодил, но в идеале получать
> их json'ом.
> В идеале, если бы можно было кликая на кнопку выбора размера
> перезаписывать в переменных путь и имя модели, и не хранить данные о
> всех моделях. И после обновлять её на странице.
<div class="box_size">
<div class="col_size radio_type_1">
<input name="pack" id="size_0_pack" data-id="0" type="radio" value="293x273x21">
<label for="size_0_pack">293x273x21</label>
</div>
<div class="col_size radio_type_1">
<input name="pack" id="size_1_pack" data-id="1" type="radio" value="300x200x40">
<label for="size_1_pack">300x200x40</label>
</div>
</div>