该源码翻译自 three.js 官方示例 https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_mmd_audio.html
主要源码如下
import kotlinx.browser.document
import kotlinx.browser.window
import three.*
import kotlin.js.Promise
import kotlin.js.json
external val Math: dynamic
external fun require(module: String): dynamic
external val Ammo: () -> Promise<Unit>
val OutlineEffect: dynamic = require("./jsm/effects/OutlineEffect.js").OutlineEffect
val MMDLoader: dynamic = require("./jsm/loaders/MMDLoader.js").MMDLoader
val MMDAnimationHelper: dynamic = require("./jsm/animation/MMDAnimationHelper.js").MMDAnimationHelper
val jsNew: (classDef: dynamic) -> dynamic = js("function(Clazz){return new Clazz()}")
val jsNew1: (classDef: dynamic, arg: dynamic) -> dynamic = js("function(Clazz,arg){return new Clazz(arg)}")
fun onProgress(xhr: dynamic) {
if (xhr.lengthComputable) {
val percentComplete = xhr.loaded / xhr.total * 100
console.log(Math.round(percentComplete, 2) + "% downloaded")
}
}
fun init() {
val overlay = document.getElementById("overlay")
overlay?.remove();
val container = document.createElement("div")
document.body?.appendChild(container)
var mesh: Mesh
var ready: Boolean = false
val clock = Clock()
val camera = PerspectiveCamera(45.0, window.innerWidth.toDouble() / window.innerHeight, 1.0, 2000.0)
// Scene
val scene = Scene()
scene.background = Color(0xffffff)
scene.add(PolarGridHelper(30.0, 0.0))
val listener = AudioListener()
camera.add(listener)
scene.add(camera)
val ambient = AmbientLight(0xaaaaaa, 3f)
scene.add(ambient)
val directionalLight = DirectionalLight(0xffffff, 3f)
directionalLight.position.set(-1f, 1f, 1f).normalize()
scene.add(directionalLight)
// renderer
val renderer = WebGLRenderer(json("antialias" to true))
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(window.innerWidth, window.innerHeight)
container.appendChild(renderer.domElement)
val effect = jsNew1(OutlineEffect, renderer)
// model
val modelFile = "models/mmd/miku/miku_v2.pmd";
val vmdFiles = arrayOf("models/mmd/vmds/wavefile_v2.vmd")
val cameraFiles = arrayOf("models/mmd/vmds/wavefile_camera.vmd")
val audioFile = "models/mmd/audios/wavefile_short.mp3"
val audioParams = json("delayTime" to 160f * 1 / 30)
val helper = jsNew(MMDAnimationHelper)
val loader = jsNew(MMDLoader)
loader.loadWithAnimation(modelFile, vmdFiles, { mmd ->
mesh = mmd.mesh;
helper.add(
mesh, json(
"animation" to mmd.animation,
"physics" to true
)
)
loader.loadAnimation(cameraFiles, camera, { cameraAnimation ->
helper.add(camera, json("animation" to cameraAnimation))
AudioLoader().load(audioFile, { buffer ->
val audio = Audio(listener).setBuffer(buffer);
helper.add(audio, audioParams);
scene.add(mesh);
ready = true;
}, { onProgress(it) })
}, { it -> onProgress(it) })
}, { it -> onProgress(it) })
window.addEventListener("resize", { _ ->
camera.aspect = window.innerWidth.toDouble() / window.innerHeight;
camera.updateProjectionMatrix()
effect.setSize(window.innerWidth, window.innerHeight);
})
// drive
renderer.setAnimationLoop {
if (ready) {
helper.update(clock.getDelta());
}
effect.render(scene, camera);
}
}
fun main() {
document.querySelector("#startButton")?.addEventListener("click", {
Ammo().then { init() }
})
}