[Three.js 代码组织] 资源加载器
2023-08-02 本文已影响0人
alue
如果每个文件都写一遍加载流程, 代码会随着文件数量线性增加. 这里用一个工具类来完成这个工作.
首先将资源列表, 整理成一个规范的三元组列表文件,分别由资源名称、类型、路径构成:
export default [
{
name: 'environmentMapTexture',
type: 'cubeTexture',
path:
[
'textures/environmentMap/px.jpg',
'textures/environmentMap/nx.jpg',
'textures/environmentMap/py.jpg',
'textures/environmentMap/ny.jpg',
'textures/environmentMap/pz.jpg',
'textures/environmentMap/nz.jpg'
] },
{
name: 'grassColorTexture',
type: 'texture',
path: 'textures/dirt/color.jpg'
},
{
name: 'grassNormalTexture',
type: 'texture',
path: 'textures/dirt/normal.jpg'
},
{
name: 'foxModel',
type: 'gltfModel',
path: 'models/Fox/glTF/Fox.gltf'
}
]
然后编写资源加载类如下:
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OBJLoader } from 'three/addons/loaders/OBJLoader.js';
import EventEmitter from './EventEmitter.js'
export default class Resources extends EventEmitter
{
constructor(sources)
{
super()
this.sources = sources
this.items = {}
this.toLoad = this.sources.length
this.loaded = 0
this.setLoaders()
this.startLoading()
}
setLoaders()
{
this.loaders = {}
this.loaders.gltfLoader = new GLTFLoader()
this.loaders.textureLoader = new THREE.TextureLoader()
this.loaders.cubeTextureLoader = new THREE.CubeTextureLoader()
this.loaders.objLoader = new OBJLoader()
}
startLoading()
{
// Load each source
for(const source of this.sources)
{
if(source.type === 'gltfModel')
{
this.loaders.gltfLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
}
)
}
else if(source.type === 'texture')
{
this.loaders.textureLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
} )
} else if(source.type === 'cubeTexture')
{ this.loaders.cubeTextureLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
}
) }
else if(source.type==='obj')
{
this.loaders.objLoader.load(
source.path,
(file) =>
{
this.sourceLoaded(source, file)
}
)
}
}
}
sourceLoaded(source, file)
{
this.items[source.name] = file
this.loaded++
if(this.loaded === this.toLoad)
{
this.trigger('ready')
}
}
}
将需要用到的所有loader都集中在 setLoaders
方法内。通过 startLoading
来加载资源,资源加载完毕后,会向外抛出 ready
事件。
调用资源对象时,可以监听 ready
事件,来完成资源对象类对象的新建。
import Floor from './Floor.js'
import Fox from './Fox.js'
// Wait for resources
this.resources.on('ready', () =>
{
// Setup
this.floor = new Floor()
this.fox = new Fox()
})
资源对象类,可以直接调用 resources 中的材质对象
// Floor.js
import * as THREE from 'three'
import Experience from '../Experience.js'
export default class Floor
{
constructor()
{
this.experience = new Experience()
this.scene = this.experience.scene
this.resources = this.experience.resources
this.setGeometry()
this.setTextures()
this.setMaterial()
this.setMesh()
}
setGeometry()
{
this.geometry = new THREE.CircleGeometry(5, 64)
}
setTextures()
{
this.textures = {}
this.textures.color = this.resources.items.grassColorTexture
this.textures.color.encoding = THREE.sRGBEncoding
this.textures.color.repeat.set(1.5, 1.5)
this.textures.color.wrapS = THREE.RepeatWrapping
this.textures.color.wrapT = THREE.RepeatWrapping
this.textures.normal = this.resources.items.grassNormalTexture
this.textures.normal.repeat.set(1.5, 1.5)
this.textures.normal.wrapS = THREE.RepeatWrapping
this.textures.normal.wrapT = THREE.RepeatWrapping
}
setMaterial()
{
this.material = new THREE.MeshStandardMaterial({
map: this.textures.color,
normalMap: this.textures.normal
})
}
setMesh()
{
this.mesh = new THREE.Mesh(this.geometry, this.material)
this.mesh.rotation.x = - Math.PI * 0.5
this.mesh.receiveShadow = true
this.scene.add(this.mesh)
}
}
直接调用模型资源
// Fox.js
import * as THREE from 'three'
import Experience from '../Experience.js'
export default class Fox
{
constructor()
{
this.experience = new Experience()
this.scene = this.experience.scene
this.resources = this.experience.resources
this.time = this.experience.time
// Resource
this.resource = this.resources.items.foxModel
this.setModel()
this.setAnimation()
}
setModel()
{
this.model = this.resource.scene
this.model.scale.set(0.02, 0.02, 0.02)
this.scene.add(this.model)
this.model.traverse((child) =>
{ if(child instanceof THREE.Mesh)
{
child.castShadow = true
}
})
}
setAnimation()
{
this.animation = {}
// Mixer
this.animation.mixer = new THREE.AnimationMixer(this.model)
// Actions
this.animation.actions = {}
this.animation.actions.idle = this.animation.mixer.clipAction(this.resource.animations[0])
this.animation.actions.walking = this.animation.mixer.clipAction(this.resource.animations[1])
this.animation.actions.running = this.animation.mixer.clipAction(this.resource.animations[2])
this.animation.actions.current = this.animation.actions.idle
this.animation.actions.current.play()
// Play the action
this.animation.play = (name) =>
{ const newAction = this.animation.actions[name]
const oldAction = this.animation.actions.current
newAction.reset()
newAction.play()
newAction.crossFadeFrom(oldAction, 1)
this.animation.actions.current = newAction
}
}
update()
{
this.animation.mixer.update(this.time.delta * 0.001)
}
}