import * as THREE from "three" ;
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js" ;
import { GUI } from "three/examples/jsm/libs/lil-gui.module.min.js" ;
import { RGBELoader } from "three/examples/jsm/loaders/RGBELoader.js" ;
import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js" ;
import { DRACOLoader } from "three/examples/jsm/loaders/DRACOLoader.js" ;
import { TransformControls } from "three/addons/controls/TransformControls.js" ;
const scene = new THREE. Scene ( ) ;
const camera = new THREE. PerspectiveCamera (
45 ,
window. innerWidth / window. innerHeight,
0.1 ,
1000
) ;
const renderer = new THREE. WebGLRenderer ( {
antialias : true ,
} ) ;
renderer. shadowMap. enabled = true ;
renderer. toneMapping = THREE . ReinhardToneMapping;
renderer. toneMappingExposure = 1 ;
renderer. setSize ( window. innerWidth, window. innerHeight) ;
document. body. appendChild ( renderer. domElement) ;
camera. position. z = 8 ;
camera. position. y = 2.5 ;
camera. position. x = 3 ;
camera. lookAt ( 0 , 1.2 , 0 ) ;
const axesHelper = new THREE. AxesHelper ( 5 ) ;
scene. add ( axesHelper) ;
const gridHelper = new THREE. GridHelper ( 50 , 50 ) ;
gridHelper. material. opacity = 0.3 ;
gridHelper. material. transparent = true ;
scene. add ( gridHelper) ;
const controls = new OrbitControls ( camera, renderer. domElement) ;
controls. enableDamping = true ;
function animate ( ) {
controls. update ( ) ;
requestAnimationFrame ( animate) ;
renderer. render ( scene, camera) ;
}
animate ( ) ;
window. addEventListener ( "resize" , ( ) => {
renderer. setSize ( window. innerWidth, window. innerHeight) ;
camera. aspect = window. innerWidth / window. innerHeight;
camera. updateProjectionMatrix ( ) ;
} ) ;
let rgbeLoader = new RGBELoader ( ) ;
rgbeLoader. load ( "./texture/Alex_Hart-Nature_Lab_Bones_2k.hdr" , ( envMap ) => {
envMap. mapping = THREE . EquirectangularRefractionMapping;
scene. background = new THREE. Color ( 0xcccccc ) ;
scene. environment = envMap;
} ) ;
const gltfLoader = new GLTFLoader ( ) ;
const dracoLoader = new DRACOLoader ( ) ;
dracoLoader. setDecoderPath ( "./draco/" ) ;
gltfLoader. setDRACOLoader ( dracoLoader) ;
gltfLoader. load (
"./model/house/house-scene-min.glb" ,
( gltf ) => {
basicScene = gltf. scene;
}
) ;
let tControls = new TransformControls ( camera, renderer. domElement) ;
tControls. addEventListener ( "change" , animate) ;
tControls. addEventListener ( "dragging-changed" , function ( event ) {
controls. enabled = ! event. value;
} ) ;
tControls. addEventListener ( "change" , ( ) => {
if ( eventObj. isClampGroup) {
tControls. object. position. y = 0 ;
}
} ) ;
scene. add ( tControls) ;
let basicScene;
let eventObj = {
Fullscreen : function ( ) {
document. body. requestFullscreen ( ) ;
console. log ( "全屏" ) ;
} ,
ExitFullscreen : function ( ) {
document. exitFullscreen ( ) ;
console. log ( "退出全屏" ) ;
} ,
addScene : function ( ) {
scene. add ( basicScene) ;
} ,
setTranslate : function ( ) {
tControls. setMode ( "translate" ) ;
} ,
setRotate : function ( ) {
tControls. setMode ( "rotate" ) ;
} ,
setScale : function ( ) {
tControls. setMode ( "scale" ) ;
} ,
toggleSpace : function ( ) {
tControls. setSpace ( tControls. space === "local" ? "world" : "local" ) ;
} ,
cancelMesh : function ( ) {
tControls. detach ( ) ;
} ,
translateSnapNum : null ,
rotateSnapNum : 0 ,
scaleSnapNum : 0 ,
isClampGroup : false ,
isLight : true ,
} ;
const gui = new GUI ( ) ;
gui. add ( eventObj, "addScene" ) . name ( "添加户型基础模型" ) ;
gui. add ( eventObj, "setTranslate" ) . name ( "位移模式" ) ;
gui. add ( eventObj, "setRotate" ) . name ( "旋转模式" ) ;
gui. add ( eventObj, "setScale" ) . name ( "缩放模式" ) ;
gui. add ( eventObj, "toggleSpace" ) . name ( "切换空间模式" ) ;
gui. add ( eventObj, "cancelMesh" ) . name ( "取消选择" ) ;
gui
. add ( eventObj, "isLight" )
. name ( "是否开启灯光" )
. onChange ( ( value ) => {
if ( value) {
renderer. toneMappingExposure = 1 ;
} else {
renderer. toneMappingExposure = 0.1 ;
}
} ) ;
window. addEventListener ( "keydown" , ( event ) => {
if ( event. key === "t" ) {
eventObj. setTranslate ( ) ;
}
if ( event. key === "r" ) {
eventObj. setRotate ( ) ;
}
if ( event. key === "s" ) {
eventObj. setScale ( ) ;
}
} ) ;
let meshList = [
{
name : "盆栽" ,
path : "./model/house/plants-min.glb" ,
} ,
{
name : "单人沙发" ,
path : "./model/house/sofa_chair_min.glb" ,
} ,
] ;
let folderAddMehs = gui. addFolder ( "添加物体" ) ;
let sceneMeshes = [ ] ;
let meshesNum = { } ;
meshList. forEach ( ( item ) => {
item. addMesh = function ( ) {
gltfLoader. load ( item. path, ( gltf ) => {
sceneMeshes. push ( {
... item,
object3d : gltf. scene,
} ) ;
let object3d = gltf. scene;
scene. add ( object3d) ;
tControlSelect ( object3d) ;
let meshOpt = {
toggleMesh : function ( ) {
tControlSelect ( object3d) ;
} ,
} ;
meshesNum[ item. name] = meshesNum[ item. name]
? meshesNum[ item. name] + 1
: 1 ;
meshesFolder
. add ( meshOpt, "toggleMesh" )
. name ( item. name + meshesNum[ item. name] ) ;
} ) ;
} ;
folderAddMehs. add ( item, "addMesh" ) . name ( item. name) ;
} ) ;
function tControlSelect ( mesh ) {
tControls. attach ( mesh) ;
}
let meshesFolder = gui. addFolder ( "家居列表" ) ;
let snapFolder = gui. addFolder ( "固定设置" ) ;
snapFolder
. add ( eventObj, "translateSnapNum" , {
不固定 : null ,
1 : 1 ,
0.1 : 0.1 ,
10 : 10 ,
} )
. name ( "固定位移设置" )
. onChange ( ( ) => {
tControls. setTranslationSnap ( eventObj. translateSnapNum) ;
} ) ;
snapFolder
. add ( eventObj, "rotateSnapNum" , 0 , 1 )
. step ( 0.01 )
. name ( "旋转" )
. onChange ( ( ) => {
tControls. setRotationSnap ( eventObj. rotateSnapNum * Math. PI * 2 ) ;
} ) ;
snapFolder
. add ( eventObj, "scaleSnapNum" , 0 , 2 )
. step ( 0.1 )
. name ( "缩放" )
. onChange ( ( ) => {
tControls. setScaleSnap ( eventObj. scaleSnapNum) ;
} ) ;
snapFolder. add ( eventObj, "isClampGroup" ) . name ( "是否吸附到地面" ) ;