import * as THREE from 'libs/threejs/three.min'
import * as OIMO from 'libs/threejs/plugins/oimo.min'
import 'libs/threejs/controls/OrbitControls'
let ctx = canvas.getContext('webgl')
const winWidth = window.innerWidth
const winHeight = window.innerHeight
const cameraAspect = winWidth / winHeight
const length = 20
// const bastUrl = 'https://raw.githubusercontent.com/stephenml/wegame-threejs/master/model'
export default class Main {
constructor() {
// 场景
this.scene = new THREE.Scene();
// 添加聚光灯光源
this.scene.add(new THREE.PointLight(0x0000FF))
// let pointLight = new THREE.PointLight(0xFFFFFF)
// 启用光线投影
// pointLight.castShadow=true
// this.scene.add(pointLight)
// 添加环境光
this.scene.add(new THREE.AmbientLight(0xFFFFFF))
// 创建一个透视摄像机,可视角度、宽高比、近端距离、远端距离
this.camera = new THREE.PerspectiveCamera(90, cameraAspect, 0.1, 1000)
// 设置摄像机的位置,并让其指向场景的中心(0,0,0)
this.camera.position.x = -30;
this.camera.position.y = 30;
this.camera.position.z = 30;
// 创建一个渲染器并设置其大小
this.renderer = new THREE.WebGLRenderer({ context: ctx, canvas: canvas })
// 允许阴影映射,如果物体能有阴影,还需要启动光线的投影,设置模型生成投影,设置地面或者其它物体接受阴影
this.renderer.shadowMap.enabled = true
this.renderer.setSize(winWidth, winHeight)
// 设置背景色
this.renderer.setClearColor(0xFFFFFF, 1)
// 抗锯齿-设置设备像素比
this.renderer.setPixelRatio(window.devicePixelRatio)
// 摄像机控制器,在threejs目录下的controls,用来实现对摄像机的控制
this.controls = new THREE.OrbitControls(this.camera)
this.controls.addEventListener('change', () => {
this.renderer.render(this.scene, this.camera)
})
// Oimo物理世界 scale full world
this.world = new OIMO.World({ worldscale: 1 })
// 初始化
this.boxs = []
this.boxBodys = []
this.models = []
this.modelBodys = []
this.start()
}
start() {
// 清除Oimo物理世界 Reset the world and remove all rigid bodies,shapes,joints and any object from the world
this.world.clear()
// 加载材质贴图
//金属地面
let metal_texture = new THREE.TextureLoader().load("images/metal.jpg")
//木头方块
let wood_texture = new THREE.TextureLoader().load("images/wood.jpg")
//布料球材质
let cloth_texture = new THREE.TextureLoader().load("images/cloth.jpg")
// 地面 MeshBasicMaterial是一种非常简单的材质,这种材质的网格会被渲染成简单的多边形或几何体。对光照无感。
let ground_material = new THREE.MeshBasicMaterial({ map: metal_texture })
this.ground = new THREE.Mesh(new THREE.BoxGeometry(30, 1, 30), ground_material)
// 把模型设置为接收投影
this.ground.receiveShadow = true
// 把模型设置为生成投影
this.ground.castShadow = true
this.scene.add(this.ground)
// 物理地面 Oimo的physics object 设置跟地面重合,进而发生碰撞
this.groundBody = this.world.add({
size: [30, 1, 30],
pos: [0, 0, 0],//start position in degree
name: 'groundBody'
})
// 载入json模型
// new THREE.JSONLoader().load(`${bastUrl}/sphere.json`,
new THREE.JSONLoader().load('model/sphere.json',
(geometry, materials) => {
geometry.center()
// 盒子材质
let box_meterial = new THREE.MeshLambertMaterial({ map: wood_texture })
// 模型材质
let model_meterial = new THREE.MeshLambertMaterial({ map: cloth_texture })
// 载入模型后再创建盒子
// 创建盒子和模型
for (let i = 0; i < length; i++) {
// 创建盒子
let box = new THREE.Mesh(new THREE.BoxGeometry(3, 3, 3), box_meterial)
box.position.y = 50 + i * 2
box.receiveShadow = true
box.castShadow = true
this.scene.add(box)
this.boxs.push(box)
// 创建物理盒子
let boxBody = this.world.add({
type: 'box',
move: true,
size: [3, 3, 3],
pos: [0, 50 + i * 2, 0],
name: `boxBody${i}`
})
this.boxBodys.push(boxBody)
// 创建模型
let model = new THREE.Mesh(geometry, model_meterial)
model.scale.set(2, 2, 2)
model.position.y = 50 + i * 2
model.receiveShadow = true
model.castShadow = true
this.scene.add(model)
this.models.push(model)
// 创建物理模型
let modelBody = this.world.add({
type: 'sphere',
move: true,
size: [2],
pos: [0, 50 + i * 2, 0],
name: `modelBody${i}`
})
this.modelBodys.push(modelBody)
}
// 开启主线程
this.loop()
},
// 进度条 小游戏内无效
(xhr) => {
console.log(`${(xhr.loaded / xhr.total * 100)}% 已载入`)
},
// 载入出错
(error) => {
console.log(`载入出错: ${error}`)
}
)
}
loop() {
// 更新物理世界 Oimo引擎 the time between each step default 1/60 is 60 frame second
this.world.step()
// 复制物理世界的位置到Threejs的网格上
// quaternion 四元数 物体的方向,
// copy position and rotation to three mesh
for (let i = 0; i < length; i++) {
this.boxs[i].position.copy(this.boxBodys[i].getPosition())
this.boxs[i].quaternion.copy(this.boxBodys[i].getQuaternion())
this.models[i].position.copy(this.modelBodys[i].getPosition())
this.models[i].quaternion.copy(this.modelBodys[i].getQuaternion())
}
//碰撞检测
if (this.world.checkContact('boxBody', 'modelBody')) {
console.log('contact...')
}
// 渲染
this.renderer.render(this.scene, this.camera)
window.requestAnimationFrame(this.loop.bind(this), canvas)
}
}
