mirror of
https://github.com/jlengrand/korge-samples.git
synced 2026-03-10 08:31:18 +00:00
1.2.0
This commit is contained in:
@@ -6,6 +6,6 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath("com.soywiz:korge-gradle-plugin:1.1.3")
|
||||
classpath("com.soywiz:korge-gradle-plugin:1.2.0")
|
||||
}
|
||||
}
|
||||
|
||||
6
sample-3d/build.gradle
Normal file
6
sample-3d/build.gradle
Normal file
@@ -0,0 +1,6 @@
|
||||
apply plugin: "korge"
|
||||
|
||||
korge {
|
||||
id = "com.soywiz.samples.korge3d"
|
||||
supportExperimental3d()
|
||||
}
|
||||
174
sample-3d/src/commonMain/kotlin/main.kt
Normal file
174
sample-3d/src/commonMain/kotlin/main.kt
Normal file
@@ -0,0 +1,174 @@
|
||||
import com.soywiz.kds.*
|
||||
import com.soywiz.klock.*
|
||||
import com.soywiz.korge.*
|
||||
import com.soywiz.korge.input.*
|
||||
import com.soywiz.korge.render.*
|
||||
import com.soywiz.korge.scene.*
|
||||
import com.soywiz.korge.tween.*
|
||||
import com.soywiz.korge.view.*
|
||||
import com.soywiz.korge3d.experimental.*
|
||||
import com.soywiz.korge3d.experimental.animation.*
|
||||
import com.soywiz.korge3d.experimental.format.*
|
||||
import com.soywiz.korim.color.*
|
||||
import com.soywiz.korim.format.*
|
||||
import com.soywiz.korinject.*
|
||||
import com.soywiz.korio.async.*
|
||||
import com.soywiz.korio.file.std.*
|
||||
import com.soywiz.korma.geom.*
|
||||
import com.soywiz.korma.geom.vector.*
|
||||
import com.soywiz.korma.interpolation.*
|
||||
|
||||
//suspend fun main() = Demo3.main(args)
|
||||
suspend fun main() = Korge(Korge.Config(module = Korge3DSampleModule()))
|
||||
|
||||
@Korge3DExperimental
|
||||
class Korge3DSampleModule : KorgeModule(RootScene::class) {
|
||||
override val size: SizeInt = SizeInt(1280, 720)
|
||||
override val title: String = "KorGE 3D"
|
||||
override val bgcolor: RGBA = RGBA.float(.25f, .25f, .25f, 1f)
|
||||
|
||||
override suspend fun AsyncInjector.configure() {
|
||||
mapPrototype { RootScene() }
|
||||
mapPrototype { CratesScene() }
|
||||
mapPrototype { MonkeyScene() }
|
||||
mapPrototype { SkinningScene() }
|
||||
}
|
||||
}
|
||||
|
||||
class RootScene : Scene() {
|
||||
lateinit var contentSceneContainer: SceneContainer
|
||||
|
||||
override suspend fun Container.sceneInit() {
|
||||
contentSceneContainer = sceneContainer(views)
|
||||
|
||||
sceneButton<CratesScene>("Crates", 0)
|
||||
sceneButton<MonkeyScene>("Monkey", 1)
|
||||
sceneButton<SkinningScene>("Skinning", 2)
|
||||
|
||||
//mySceneContainer.changeToDisablingButtons<CratesScene>(this)
|
||||
contentSceneContainer.changeToDisablingButtons<SkinningScene>(this)
|
||||
}
|
||||
|
||||
inline fun <reified T : Scene> Container.sceneButton(title: String, x: Int) {
|
||||
this += Button(title) { contentSceneContainer.changeToDisablingButtons<T>(this) }
|
||||
.position(8 + x * 200, views.virtualHeight - 48)
|
||||
}
|
||||
}
|
||||
|
||||
@Korge3DExperimental
|
||||
class CratesScene : Scene() {
|
||||
override suspend fun Container.sceneInit() {
|
||||
val korgeTex = resourcesVfs["korge.png"].readNativeImage().mipmaps(false)
|
||||
val crateTex = resourcesVfs["crate.jpg"].readNativeImage().mipmaps(true)
|
||||
val crateMaterial = Material3D(diffuse = Material3D.LightTexture(crateTex))
|
||||
|
||||
image(korgeTex).alpha(0.5)
|
||||
|
||||
scene3D {
|
||||
//camera.set(fov = 60.degrees, near = 0.3, far = 1000.0)
|
||||
|
||||
light().position(0, 0, -3)
|
||||
|
||||
val cube1 = box().material(crateMaterial)
|
||||
val cube2 = box().position(0, 2, 0).scale(1, 2, 1).rotation(0.degrees, 0.degrees, 45.degrees).material(crateMaterial)
|
||||
val cube3 = box().position(-5, 0, 0).material(crateMaterial)
|
||||
val cube4 = box().position(+5, 0, 0).material(crateMaterial)
|
||||
val cube5 = box().position(0, -5, 0).material(crateMaterial)
|
||||
val cube6 = box().position(0, +5, 0).material(crateMaterial)
|
||||
val cube7 = box().position(0, 0, -5).material(crateMaterial)
|
||||
val cube8 = box().position(0, 0, +5).material(crateMaterial)
|
||||
|
||||
var tick = 0
|
||||
addUpdatable {
|
||||
val angle = (tick / 4.0).degrees
|
||||
camera.positionLookingAt(
|
||||
cos(angle * 2) * 4, cos(angle * 3) * 4, -sin(angle) * 4, // Orbiting camera
|
||||
0, 1, 0
|
||||
)
|
||||
tick++
|
||||
}
|
||||
|
||||
launchImmediately {
|
||||
while (true) {
|
||||
tween(time = 16.seconds) {
|
||||
cube1.modelMat.identity().rotate((it * 360).degrees, 0.degrees, 0.degrees)
|
||||
cube2.modelMat.identity().rotate(0.degrees, (it * 360).degrees, 0.degrees)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image(korgeTex).position(views.virtualWidth, 0).anchor(1, 0).alpha(0.5)
|
||||
}
|
||||
}
|
||||
|
||||
@Korge3DExperimental
|
||||
class MonkeyScene : Scene() {
|
||||
override suspend fun Container.sceneInit() {
|
||||
//delay(10.seconds)
|
||||
//println("delay")
|
||||
scene3D {
|
||||
val light1 = light().position(0, 10, +10).setTo(Colors.RED)
|
||||
val light2 = light().position(10, 0, +10).setTo(Colors.BLUE)
|
||||
|
||||
launchImmediately {
|
||||
while (true) {
|
||||
tween(light1::y[-20], light2::x[-20], time = 1.seconds, easing = Easing.SMOOTH)
|
||||
tween(light1::y[+20], light2::x[+20], time = 1.seconds, easing = Easing.SMOOTH)
|
||||
}
|
||||
}
|
||||
|
||||
val library = resourcesVfs["monkey-smooth.dae"].readColladaLibrary()
|
||||
val model = library.geometryDefs.values.first()
|
||||
val view = mesh(model.mesh).rotation(-90.degrees, 0.degrees, 0.degrees)
|
||||
|
||||
var tick = 0
|
||||
addUpdatable {
|
||||
val angle = (tick / 1.0).degrees
|
||||
camera.positionLookingAt(
|
||||
cos(angle * 1) * 4, 0.0, -sin(angle * 1) * 4, // Orbiting camera
|
||||
0, 0, 0
|
||||
)
|
||||
tick++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Korge3DExperimental
|
||||
class SkinningScene : Scene() {
|
||||
override suspend fun Container.sceneInit() {
|
||||
scene3D {
|
||||
//val library = resourcesVfs["model.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["ball.dae"].readColladaLibrary()
|
||||
val library = resourcesVfs["skinning.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["model_skinned_animated.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["Fallera.dae"].readColladaLibrary()
|
||||
|
||||
val mainSceneView = library.mainScene.instantiate()
|
||||
val cameras = mainSceneView.findByType<Camera3D>()
|
||||
|
||||
val animator = Animator3D(library.animationDefs.values, mainSceneView)
|
||||
addUpdatable { animator.update(it) }
|
||||
val model = mainSceneView.findByType<ViewWithMesh3D>().first()
|
||||
//.rotation(-90.degrees, 90.degrees, 0.degrees)
|
||||
|
||||
val camera1 = cameras.firstOrNull() ?: camera
|
||||
val camera2 = cameras.lastOrNull() ?: camera
|
||||
|
||||
camera = camera1.clone()
|
||||
|
||||
this += mainSceneView
|
||||
addUpdatable {
|
||||
//val mainSceneView = mainSceneView
|
||||
//println(mainSceneView)
|
||||
|
||||
//println("Camera: ${camera.transform}")
|
||||
//println("Model: ${model.transform}")
|
||||
//println("Skeleton: ${model.skeleton}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
84
sample-3d/src/commonMain/kotlin/utils.kt
Normal file
84
sample-3d/src/commonMain/kotlin/utils.kt
Normal file
@@ -0,0 +1,84 @@
|
||||
import com.soywiz.klock.*
|
||||
import com.soywiz.korge.input.*
|
||||
import com.soywiz.korge.scene.*
|
||||
import com.soywiz.korge.tween.*
|
||||
import com.soywiz.korge.view.*
|
||||
import com.soywiz.korge3d.experimental.*
|
||||
import com.soywiz.korim.color.*
|
||||
import com.soywiz.korio.async.*
|
||||
import com.soywiz.korma.geom.*
|
||||
import com.soywiz.korma.geom.vector.*
|
||||
|
||||
@Korge3DExperimental
|
||||
private suspend fun Stage3D.orbit(v: View3D, distance: Double, time: TimeSpan) {
|
||||
view.tween(time = time) { ratio ->
|
||||
val angle = 360.degrees * ratio
|
||||
camera.positionLookingAt(
|
||||
cos(angle) * distance, 0.0, sin(angle) * distance, // Orbiting camera
|
||||
v.transform.translation.x, v.transform.translation.y, v.transform.translation.z
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class Button(text: String, handler: suspend () -> Unit) : Container() {
|
||||
val textField = Text(text, textSize = 32.0).apply { filtering = false }
|
||||
private val bounds = textField.textBounds
|
||||
val g = Graphics().apply {
|
||||
fill(Colors.DARKGREY, 0.7) {
|
||||
roundRect(bounds.x, bounds.y, bounds.width + 16, bounds.height + 16, 8.0, 8.0)
|
||||
}
|
||||
}
|
||||
var enabledButton = true
|
||||
set(value) {
|
||||
field = value
|
||||
updateState()
|
||||
}
|
||||
private var overButton = false
|
||||
set(value) {
|
||||
field = value
|
||||
updateState()
|
||||
}
|
||||
|
||||
fun updateState() {
|
||||
when {
|
||||
!enabledButton -> alpha = 0.3
|
||||
overButton -> alpha = 1.0
|
||||
else -> alpha = 0.8
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
//this += this.solidRect(bounds.width, bounds.height, Colors.TRANSPARENT_BLACK)
|
||||
this += g.apply {
|
||||
mouseEnabled = true
|
||||
}
|
||||
this += textField.position(8, 8)
|
||||
|
||||
mouse {
|
||||
over { overButton = true }
|
||||
out { overButton = false }
|
||||
}
|
||||
onClick {
|
||||
stage?.views?.launchImmediately {
|
||||
if (enabledButton) handler()
|
||||
}
|
||||
}
|
||||
updateState()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend inline fun <reified T : Scene> SceneContainer.changeToDisablingButtons(buttonContainer: Container) {
|
||||
for (child in buttonContainer.children.filterIsInstance<Button>()) {
|
||||
//println("DISABLE BUTTON: $child")
|
||||
child.enabledButton = false
|
||||
}
|
||||
try {
|
||||
changeTo<T>()
|
||||
} finally {
|
||||
for (child in buttonContainer.children.filterIsInstance<Button>()) {
|
||||
//println("ENABLE BUTTON: $child")
|
||||
child.enabledButton = true
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
sample-3d/src/commonMain/resources/ball.blend
Normal file
BIN
sample-3d/src/commonMain/resources/ball.blend
Normal file
Binary file not shown.
BIN
sample-3d/src/commonMain/resources/ball.blend1
Normal file
BIN
sample-3d/src/commonMain/resources/ball.blend1
Normal file
Binary file not shown.
1189
sample-3d/src/commonMain/resources/ball.dae
Normal file
1189
sample-3d/src/commonMain/resources/ball.dae
Normal file
File diff suppressed because one or more lines are too long
BIN
sample-3d/src/commonMain/resources/crate.jpg
Executable file
BIN
sample-3d/src/commonMain/resources/crate.jpg
Executable file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
BIN
sample-3d/src/commonMain/resources/model_skinned_animated.blend
Normal file
BIN
sample-3d/src/commonMain/resources/model_skinned_animated.blend
Normal file
Binary file not shown.
Binary file not shown.
1452
sample-3d/src/commonMain/resources/model_skinned_animated.dae
Normal file
1452
sample-3d/src/commonMain/resources/model_skinned_animated.dae
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
BIN
sample-3d/src/commonMain/resources/skinning.blend
Normal file
BIN
sample-3d/src/commonMain/resources/skinning.blend
Normal file
Binary file not shown.
BIN
sample-3d/src/commonMain/resources/skinning.blend1
Normal file
BIN
sample-3d/src/commonMain/resources/skinning.blend1
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -11,8 +11,8 @@ korge {
|
||||
|
||||
admob("ca-app-pub-xxxxxxxx~yyyyyy")
|
||||
|
||||
dependencyMulti("com.soywiz:korma-shape-ops:$kormaVersion")
|
||||
dependencyMulti("com.soywiz:korma-triangulate-pathfind:$kormaVersion")
|
||||
dependencyMulti("com.soywiz:korge-dragonbones:$korgeVersion")
|
||||
dependencyMulti("com.soywiz:korge-box2d:$korgeVersion")
|
||||
supportShapeOps()
|
||||
supportTriangulation()
|
||||
supportDragonbones()
|
||||
supportBox2d()
|
||||
}
|
||||
|
||||
@@ -4,6 +4,5 @@ apply(plugin = "korge")
|
||||
|
||||
korge {
|
||||
id = "com.soywiz.samples.dragonbones"
|
||||
dependencyMulti("com.soywiz:korge-dragonbones:$korgeVersion")
|
||||
dependencyMulti("com.soywiz:korag-opengl:1.6.7")
|
||||
supportDragonbones()
|
||||
}
|
||||
|
||||
@@ -2,5 +2,5 @@ apply plugin: "korge"
|
||||
|
||||
korge {
|
||||
id = "com.soywiz.samples.lipsync"
|
||||
dependencyMulti("com.soywiz:korau-mp3:$korauVersion")
|
||||
supportMp3()
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
apply plugin: "korge"
|
||||
|
||||
korge {
|
||||
id = "com.soywiz.samples.raw3d"
|
||||
dependencyMulti("com.soywiz:korag-opengl:1.6.7")
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d
|
||||
|
||||
import com.soywiz.kds.*
|
||||
import com.soywiz.kds.iterators.*
|
||||
import com.soywiz.klock.*
|
||||
import com.soywiz.korge.experimental.s3d.model.*
|
||||
import com.soywiz.korma.geom.*
|
||||
import com.soywiz.korma.interpolation.*
|
||||
|
||||
class Animator3D(val animations: List<Animation3D>, val rootView: View3D) {
|
||||
var currentTime = 0.milliseconds
|
||||
fun update(ms: Int) {
|
||||
//currentTime += ms.milliseconds * 0.1
|
||||
currentTime += ms.milliseconds
|
||||
animations.fastForEach { animation ->
|
||||
val keyFrames = animation.keyFrames
|
||||
val fseconds = keyFrames.seconds
|
||||
val ftransforms = keyFrames.transforms
|
||||
val ffloats = keyFrames.floats
|
||||
val aproperty = animation.property
|
||||
val elapsedTimeInAnimation = (currentTime % animation.totalTime)
|
||||
//genericBinarySearch(0, animation.keys.size) { animation.keys[it] }
|
||||
|
||||
val n = keyFrames.findIndex(elapsedTimeInAnimation)
|
||||
if (n < 0) return@fastForEach
|
||||
|
||||
val startTime = fseconds[n].seconds
|
||||
val endTime = fseconds.getOrNull(n + 1)?.seconds ?: startTime
|
||||
val ratio = (elapsedTimeInAnimation - startTime) / (endTime - startTime)
|
||||
val aview = rootView[animation.target]
|
||||
if (aview != null) {
|
||||
when (aproperty) {
|
||||
"transform" -> {
|
||||
if (ftransforms != null) {
|
||||
if (n >= ftransforms.size) {
|
||||
error("Unexpected")
|
||||
}
|
||||
aview.transform.setToInterpolated(ftransforms[n], ftransforms.getCyclic(n + 1), ratio.toDouble())
|
||||
}
|
||||
}
|
||||
"location.X", "location.Y", "location.Z", "scale.X", "scale.Y", "scale.Z", "rotationX.ANGLE", "rotationY.ANGLE", "rotationZ.ANGLE" -> {
|
||||
if (ffloats != null) {
|
||||
val value = ratio.interpolate(ffloats[n], ffloats[n % ffloats.size]).toDouble()
|
||||
when (aproperty) {
|
||||
"location.X" -> aview.x = value
|
||||
"location.Y" -> aview.y = value
|
||||
"location.Z" -> aview.z = value
|
||||
"scale.X" -> aview.scaleX = value
|
||||
"scale.Y" -> aview.scaleY = value
|
||||
"scale.Z" -> aview.scaleZ = value
|
||||
"rotationX.ANGLE" -> aview.rotationX = value.degrees
|
||||
"rotationY.ANGLE" -> aview.rotationY = value.degrees
|
||||
"rotationZ.ANGLE" -> aview.rotationZ = value.degrees
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
println("WARNING: animation.property=${animation.property} not implemented")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//animation.keyFrames.binarySearch { it.time.millisecondsInt }
|
||||
//println(animation)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Animation3D(val id: String, val target: String, val property: String, val keyFrames: Frames) : Library3D.Def() {
|
||||
val totalTime = keyFrames.totalTime
|
||||
|
||||
class Frames(
|
||||
var seconds: FloatArray = floatArrayOf(),
|
||||
var interpolations: Array<String> = arrayOf(),
|
||||
var floats: FloatArray? = null,
|
||||
var matrices: Array<Matrix3D>? = null
|
||||
) {
|
||||
val transforms = matrices?.map { Transform3D().setMatrix(it) }?.toTypedArray()
|
||||
val totalFrames = seconds.size
|
||||
val totalTime = seconds.max()?.seconds ?: 0.seconds
|
||||
|
||||
// @TODO: Binary Search
|
||||
fun findIndex(time: TimeSpan): Int {
|
||||
val elapsedSeconds = time.seconds
|
||||
for (n in 0 until totalFrames - 1) {
|
||||
if (elapsedSeconds >= seconds[n] && elapsedSeconds < seconds[n + 1]) {
|
||||
return n
|
||||
}
|
||||
}
|
||||
return totalFrames - 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d
|
||||
|
||||
import com.soywiz.korge.experimental.s3d.model.internal.*
|
||||
import com.soywiz.korma.geom.*
|
||||
|
||||
abstract class Camera3D : View3D() {
|
||||
private var projMat = Matrix3D()
|
||||
private var width: Double = 0.0
|
||||
private var height: Double = 0.0
|
||||
protected var dirty = true
|
||||
|
||||
protected inline fun dirty(cond: () -> Boolean = { true }, callback: () -> Unit) {
|
||||
if (cond()) {
|
||||
this.dirty = true
|
||||
callback()
|
||||
}
|
||||
}
|
||||
|
||||
fun getProjMatrix(width: Double, height: Double): Matrix3D {
|
||||
if (this.width != width || this.height != height) {
|
||||
this.dirty = true
|
||||
this.width = width
|
||||
this.height = height
|
||||
}
|
||||
if (dirty) {
|
||||
dirty = false
|
||||
updateMatrix(projMat, this.width, this.height)
|
||||
}
|
||||
return projMat
|
||||
}
|
||||
|
||||
protected abstract fun updateMatrix(mat: Matrix3D, width: Double, height: Double)
|
||||
|
||||
override fun render(ctx: RenderContext3D) {
|
||||
// Do nothing except when debugging
|
||||
}
|
||||
|
||||
abstract fun clone(): Camera3D
|
||||
|
||||
class Perspective(
|
||||
fov: Angle = 60.degrees,
|
||||
near: Double = 0.3,
|
||||
far: Double = 1000.0
|
||||
) : Camera3D() {
|
||||
var fov: Angle = fov; set(value) = dirty({ field != value }) { field = value }
|
||||
var near: Double = near; set(value) = dirty({ field != value }) { field = value }
|
||||
var far: Double = far; set(value) = dirty({ field != value }) { field = value }
|
||||
|
||||
fun set(fov: Angle = this.fov, near: Double = this.near, far: Double = this.far) = this.apply {
|
||||
this.fov = fov
|
||||
this.near = near
|
||||
this.far = far
|
||||
}
|
||||
|
||||
override fun updateMatrix(mat: Matrix3D, width: Double, height: Double) {
|
||||
mat.setToPerspective(fov, if (height != 0.0) width / height else 1.0, near, far)
|
||||
}
|
||||
|
||||
override fun clone(): Perspective = Perspective(fov, near, far).apply {
|
||||
this.transform.copyFrom(this@Perspective.transform)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// @TODO: Move to KorMA
|
||||
private val tempMatrix3D = Matrix3D()
|
||||
class Transform3D {
|
||||
@PublishedApi
|
||||
internal var matrixDirty = false
|
||||
@PublishedApi
|
||||
internal var transformDirty = false
|
||||
|
||||
companion object {
|
||||
private val identityMat = Matrix3D()
|
||||
}
|
||||
|
||||
val globalMatrixUncached: Matrix3D = Matrix3D()
|
||||
get() = run {
|
||||
val parent = parent?.globalMatrixUncached ?: identityMat
|
||||
field.multiply(parent, matrix)
|
||||
field
|
||||
}
|
||||
|
||||
val globalMatrix: Matrix3D
|
||||
get() = run {
|
||||
// @TODO: Cache!
|
||||
globalMatrixUncached
|
||||
}
|
||||
|
||||
val matrix: Matrix3D = Matrix3D()
|
||||
get() = run {
|
||||
if (matrixDirty) {
|
||||
matrixDirty = false
|
||||
field.setTRS(translation, rotation, scale)
|
||||
}
|
||||
field
|
||||
}
|
||||
|
||||
var children: ArrayList<Transform3D> = arrayListOf()
|
||||
|
||||
var parent: Transform3D? = null
|
||||
set(value) {
|
||||
field?.children?.remove(this)
|
||||
field = value
|
||||
field?.children?.add(this)
|
||||
}
|
||||
|
||||
private val _translation = Position3D(0, 0, 0)
|
||||
private val _rotation = Quaternion()
|
||||
private val _scale = Scale3D(1, 1, 1)
|
||||
@PublishedApi internal var _eulerRotationDirty: Boolean = true
|
||||
private fun updateTRS() {
|
||||
transformDirty = false
|
||||
matrix.getTRS(_translation, rotation, _scale)
|
||||
_eulerRotationDirty = true
|
||||
transformDirty = false
|
||||
}
|
||||
|
||||
val translation: Position3D get() {
|
||||
if (transformDirty) updateTRS()
|
||||
return _translation
|
||||
}
|
||||
val rotation: Quaternion get() {
|
||||
if (transformDirty) updateTRS()
|
||||
return _rotation
|
||||
}
|
||||
var rotationEuler: EulerRotation = EulerRotation()
|
||||
private set
|
||||
get() {
|
||||
if (_eulerRotationDirty) {
|
||||
_eulerRotationDirty = false
|
||||
field.setQuaternion(rotation)
|
||||
}
|
||||
return field
|
||||
}
|
||||
val scale: Scale3D get() {
|
||||
if (transformDirty) updateTRS()
|
||||
return _scale
|
||||
}
|
||||
|
||||
fun setMatrix(mat: Matrix3D) = this.apply {
|
||||
transformDirty = true
|
||||
this.matrix.copyFrom(mat)
|
||||
}
|
||||
|
||||
@PublishedApi
|
||||
internal val UP = Vector3D(0f, 1f, 0f)
|
||||
|
||||
@PublishedApi internal val tempMat1 = Matrix3D()
|
||||
@PublishedApi internal val tempMat2 = Matrix3D()
|
||||
@PublishedApi internal val tempVec1 = Vector3D()
|
||||
@PublishedApi internal val tempVec2 = Vector3D()
|
||||
|
||||
inline fun lookAt(
|
||||
tx: Number, ty: Number, tz: Number,
|
||||
up: Vector3D = UP
|
||||
) = this.apply {
|
||||
tempMat1.setToLookAt(translation, tempVec1.setTo(tx, ty, tz, 1f), up)
|
||||
rotation.setFromRotationMatrix(tempMat1)
|
||||
}
|
||||
|
||||
inline fun setTranslationAndLookAt(
|
||||
px: Number, py: Number, pz: Number,
|
||||
tx: Number, ty: Number, tz: Number,
|
||||
up: Vector3D = UP
|
||||
) = this.apply {
|
||||
//setTranslation(px, py, pz)
|
||||
//lookUp(tx, ty, tz, up)
|
||||
setMatrix(matrix.multiply(
|
||||
tempMat1.setToTranslation(px, py, pz),
|
||||
tempMat2.setToLookAt(tempVec1.setTo(px, py, pz), tempVec2.setTo(tx, ty, tz), up)
|
||||
))
|
||||
}
|
||||
|
||||
inline fun setTranslation(x: Number, y: Number, z: Number, w: Number = 1f) = this.apply {
|
||||
matrixDirty = true
|
||||
translation.setTo(x, y, z, w)
|
||||
}
|
||||
|
||||
fun setRotation(quat: Quaternion) = this.apply {
|
||||
matrixDirty = true
|
||||
_eulerRotationDirty = true
|
||||
rotation.setTo(quat)
|
||||
}
|
||||
|
||||
inline fun setRotation(x: Number, y: Number, z: Number, w: Number) = this.apply {
|
||||
matrixDirty = true
|
||||
_eulerRotationDirty = true
|
||||
rotation.setTo(x, y, z, w)
|
||||
}
|
||||
|
||||
fun setRotation(euler: EulerRotation) = this.apply {
|
||||
matrixDirty = true
|
||||
_eulerRotationDirty = true
|
||||
rotation.setEuler(euler)
|
||||
}
|
||||
|
||||
fun setRotation(x: Angle, y: Angle, z: Angle) = this.apply {
|
||||
matrixDirty = true
|
||||
_eulerRotationDirty = true
|
||||
rotation.setEuler(x, y, z)
|
||||
}
|
||||
|
||||
private val tempEuler = EulerRotation()
|
||||
fun rotate(x: Angle, y: Angle, z: Angle) = this.apply {
|
||||
tempEuler.setQuaternion(this.rotation)
|
||||
setRotation(tempEuler.x + x, tempEuler.y + y, tempEuler.z + z)
|
||||
}
|
||||
|
||||
inline fun setScale(x: Number = 1f, y: Number = 1f, z: Number = 1f, w: Number = 1f) = this.apply {
|
||||
matrixDirty = true
|
||||
scale.setTo(x, y, z, w)
|
||||
}
|
||||
|
||||
fun copyFrom(localTransform: Transform3D) {
|
||||
this.setMatrix(localTransform.matrix)
|
||||
}
|
||||
|
||||
fun setToInterpolated(a: Transform3D, b: Transform3D, t: Double): Transform3D {
|
||||
_translation.setToInterpolated(a.translation, b.translation, t)
|
||||
_rotation.setToInterpolated(a.rotation, b.rotation, t)
|
||||
_scale.setToInterpolated(a.scale, b.scale, t)
|
||||
matrixDirty = true
|
||||
return this
|
||||
}
|
||||
|
||||
override fun toString(): String = "Transform3D(translation=$translation,rotation=$rotation,scale=$scale)"
|
||||
fun clone(): Transform3D = Transform3D().setMatrix(this.matrix)
|
||||
}
|
||||
|
||||
typealias PerspectiveCamera3D = Camera3D.Perspective
|
||||
@@ -1,214 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d
|
||||
|
||||
import com.soywiz.korag.shader.*
|
||||
import com.soywiz.korag.shader.gl.*
|
||||
import kotlin.native.concurrent.*
|
||||
|
||||
object Shaders3D {
|
||||
fun transpose(a: Operand) = Program.Func("transpose", a)
|
||||
fun inverse(a: Operand) = Program.Func("inverse", a)
|
||||
fun int(a: Operand) = Program.Func("int", a)
|
||||
operator fun Operand.get(index: Operand) = Program.ArrayAccess(this, index)
|
||||
|
||||
val u_Shiness = Uniform("u_shiness", VarType.Float1)
|
||||
val u_IndexOfRefraction = Uniform("u_indexOfRefraction", VarType.Float1)
|
||||
val u_AmbientColor = Uniform("u_ambientColor", VarType.Float4)
|
||||
val u_ProjMat = Uniform("u_ProjMat", VarType.Mat4)
|
||||
val u_ViewMat = Uniform("u_ViewMat", VarType.Mat4)
|
||||
val u_BindShapeMatrix = Uniform("u_BindMat", VarType.Mat4)
|
||||
val u_InvBindShapeMatrix = Uniform("u_InvBindMat", VarType.Mat4)
|
||||
val u_ModMat = Uniform("u_ModMat", VarType.Mat4)
|
||||
val u_NormMat = Uniform("u_NormMat", VarType.Mat4)
|
||||
//val MAX_BONE_MATS = 16
|
||||
val MAX_BONE_MATS = 64
|
||||
val u_BoneMats = Uniform("u_BoneMats", VarType.Mat4, arrayCount = MAX_BONE_MATS)
|
||||
val u_TexUnit = Uniform("u_TexUnit", VarType.TextureUnit)
|
||||
val a_pos = Attribute("a_Pos", VarType.Float3, normalized = false)
|
||||
val a_norm = Attribute("a_Norm", VarType.Float3, normalized = false)
|
||||
val a_tex = Attribute("a_TexCoords", VarType.Float2, normalized = false)
|
||||
val a_boneIndex = Array(4) { Attribute("a_BoneIndex$it", VarType.Float4, normalized = false) }
|
||||
val a_weight = Array(4) { Attribute("a_Weight$it", VarType.Float4, normalized = false) }
|
||||
val a_col = Attribute("a_Col", VarType.Float3, normalized = true)
|
||||
val v_col = Varying("v_Col", VarType.Float3)
|
||||
|
||||
val v_Pos = Varying("v_Pos", VarType.Float3)
|
||||
val v_Norm = Varying("v_Norm", VarType.Float3)
|
||||
val v_TexCoords = Varying("v_TexCoords", VarType.Float2)
|
||||
|
||||
val v_Temp1 = Varying("v_Temp1", VarType.Float4)
|
||||
|
||||
val programColor3D = Program(
|
||||
vertex = VertexShader {
|
||||
SET(v_col, a_col)
|
||||
SET(out, u_ProjMat * u_ModMat * u_ViewMat * vec4(a_pos, 1f.lit))
|
||||
},
|
||||
fragment = FragmentShader {
|
||||
SET(out, vec4(v_col, 1f.lit))
|
||||
//SET(out, vec4(1f.lit, 1f.lit, 1f.lit, 1f.lit))
|
||||
},
|
||||
name = "programColor3D"
|
||||
)
|
||||
|
||||
class LightAttributes(val id: Int) {
|
||||
val u_sourcePos = Uniform("light${id}_pos", VarType.Float3)
|
||||
val u_color = Uniform("light${id}_color", VarType.Float4)
|
||||
val u_attenuation = Uniform("light${id}_attenuation", VarType.Float3)
|
||||
}
|
||||
|
||||
val lights = (0 until 4).map { LightAttributes(it) }
|
||||
|
||||
class MaterialLightUniform(val kind: String) {
|
||||
//val mat = Material3D
|
||||
val u_color = Uniform("u_${kind}_color", VarType.Float4)
|
||||
val u_texUnit = Uniform("u_${kind}_texUnit", VarType.TextureUnit)
|
||||
}
|
||||
|
||||
val emission = MaterialLightUniform("emission")
|
||||
val ambient = MaterialLightUniform("ambient")
|
||||
val diffuse = MaterialLightUniform("diffuse")
|
||||
val specular = MaterialLightUniform("specular")
|
||||
|
||||
fun Program.Builder.computeMaterialLightColor(out: Operand, uniform: MaterialLightUniform, light: MaterialLight) {
|
||||
when (light) {
|
||||
is MaterialLightColor -> {
|
||||
SET(out, uniform.u_color)
|
||||
}
|
||||
is MaterialLightTexture -> {
|
||||
SET(out, vec4(texture2D(uniform.u_texUnit, v_TexCoords["xy"])["rgb"], 1f.lit))
|
||||
}
|
||||
else -> error("Unsupported MateriaList: $light")
|
||||
}
|
||||
}
|
||||
|
||||
fun Program.Builder.addLight(light: LightAttributes, out: Operand) {
|
||||
val v = v_Pos
|
||||
val N = v_Norm
|
||||
|
||||
val L = createTemp(VarType.Float3)
|
||||
val E = createTemp(VarType.Float3)
|
||||
val R = createTemp(VarType.Float3)
|
||||
|
||||
val attenuation = createTemp(VarType.Float1)
|
||||
val dist = createTemp(VarType.Float1)
|
||||
val NdotL = createTemp(VarType.Float1)
|
||||
val lightDir = createTemp(VarType.Float3)
|
||||
|
||||
SET(L, normalize(light.u_sourcePos["xyz"] - v))
|
||||
SET(E, normalize(-v)) // we are in Eye Coordinates, so EyePos is (0,0,0)
|
||||
SET(R, normalize(-reflect(L, N)))
|
||||
|
||||
val constantAttenuation = light.u_attenuation.x
|
||||
val linearAttenuation = light.u_attenuation.y
|
||||
val quadraticAttenuation = light.u_attenuation.z
|
||||
SET(lightDir, light.u_sourcePos["xyz"] - v_Pos)
|
||||
SET(dist, length(lightDir))
|
||||
//SET(dist, length(vec3(4f.lit, 1f.lit, 6f.lit) - vec3(0f.lit, 0f.lit, 0f.lit)))
|
||||
|
||||
SET(attenuation, 1f.lit / (constantAttenuation + linearAttenuation * dist + quadraticAttenuation * dist * dist))
|
||||
//SET(attenuation, 1f.lit / (1f.lit + 0f.lit * dist + 0.00111109f.lit * dist * dist))
|
||||
//SET(attenuation, 0.9.lit)
|
||||
SET(NdotL, max(dot(normalize(N), normalize(lightDir)), 0f.lit))
|
||||
|
||||
IF(NdotL ge 0f.lit) {
|
||||
SET(out["rgb"], out["rgb"] + (light.u_color["rgb"] * NdotL + u_AmbientColor["rgb"]) * attenuation * u_Shiness)
|
||||
}
|
||||
//SET(out["rgb"], out["rgb"] * attenuation)
|
||||
//SET(out["rgb"], out["rgb"] + clamp(light.diffuse * max(dot(N, L), 0f.lit), 0f.lit, 1f.lit)["rgb"])
|
||||
//SET(out["rgb"], out["rgb"] + clamp(light.specular * pow(max(dot(R, E), 0f.lit), 0.3f.lit * u_Shiness), 0f.lit, 1f.lit)["rgb"])
|
||||
}
|
||||
|
||||
@ThreadLocal
|
||||
val programCache = LinkedHashMap<String, Program>()
|
||||
|
||||
private fun Program.Builder.getBoneIndex(index: Int) = int(a_boneIndex[index / 4][index % 4])
|
||||
private fun Program.Builder.getWeight(index: Int) = a_weight[index / 4][index % 4]
|
||||
private fun Program.Builder.getBone(index: Int) = u_BoneMats[getBoneIndex(index)]
|
||||
|
||||
fun Program.Builder.mat4Identity() = Program.Func("mat4",
|
||||
1f.lit, 0f.lit, 0f.lit, 0f.lit,
|
||||
0f.lit, 1f.lit, 0f.lit, 0f.lit,
|
||||
0f.lit, 0f.lit, 1f.lit, 0f.lit,
|
||||
0f.lit, 0f.lit, 0f.lit, 1f.lit
|
||||
)
|
||||
|
||||
@Suppress("RemoveCurlyBracesFromTemplate")
|
||||
fun getProgram3D(nlights: Int, nweights: Int, meshMaterial: Material3D?, hasTexture: Boolean): Program {
|
||||
return programCache.getOrPut("program_L${nlights}_W${nweights}_M${meshMaterial?.kind}_T${hasTexture}") {
|
||||
Program(
|
||||
vertex = VertexShader {
|
||||
val modelViewMat = createTemp(VarType.Mat4)
|
||||
val normalMat = createTemp(VarType.Mat4)
|
||||
|
||||
val skinMatrix = createTemp(VarType.Mat4)
|
||||
|
||||
val localPos = createTemp(VarType.Float4)
|
||||
val localNorm = createTemp(VarType.Float4)
|
||||
val skinPos = createTemp(VarType.Float4)
|
||||
|
||||
SET(localPos, vec4(1f.lit))
|
||||
SET(localNorm, vec4(0f.lit))
|
||||
|
||||
if (nweights == 0) {
|
||||
SET(skinMatrix, mat4Identity())
|
||||
SET(localPos, vec4(a_pos, 1f.lit))
|
||||
SET(localNorm, vec4(a_norm, 0f.lit))
|
||||
} else {
|
||||
SET(skinPos, u_BindShapeMatrix * vec4(a_pos["xyz"], 1f.lit))
|
||||
for (wIndex in 0 until nweights) {
|
||||
IF(getBoneIndex(wIndex) ge 0.lit) {
|
||||
SET(skinMatrix, getBone(wIndex))
|
||||
SET(localPos, localPos + skinMatrix * vec4(skinPos["xyz"], 1f.lit) * getWeight(wIndex))
|
||||
SET(localNorm, localNorm + skinMatrix * vec4(a_norm, 0f.lit) * getWeight(wIndex))
|
||||
//SET(localPos, localPos + vec4(a_pos, 1f.lit) * getWeight(wIndex))
|
||||
//SET(localNorm, localNorm + vec4(a_norm, 0f.lit) * getWeight(wIndex))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SET(modelViewMat, u_ModMat * u_ViewMat)
|
||||
SET(normalMat, u_NormMat)
|
||||
SET(v_Pos, vec3(modelViewMat * (u_InvBindShapeMatrix * vec4(localPos["xyz"], 1f.lit))))
|
||||
SET(v_Norm, vec3(normalMat * vec4(localNorm["xyz"], 1f.lit)))
|
||||
if (hasTexture) {
|
||||
SET(v_TexCoords, a_tex["xy"])
|
||||
}
|
||||
|
||||
SET(out, u_ProjMat * vec4(v_Pos, 1f.lit))
|
||||
|
||||
//SET(v_Temp1.x, u_BoneMats[int(a_weight0.x)][0][0])
|
||||
|
||||
//SET(v_Temp1, a_weight0)
|
||||
//SET(v_Temp1, a_boneIndex0 / 4f.lit)
|
||||
},
|
||||
fragment = FragmentShader {
|
||||
//SET(out, vec4(1f.lit, 1f.lit, 1f.lit, 1f.lit))
|
||||
//SET(out, vec4(0f.lit, 0f.lit, 0f.lit, 1f.lit))
|
||||
//if (hasTexture) {
|
||||
// SET(out, vec4(texture2D(u_TexUnit, v_TexCoords["xy"])["rgb"], 1f.lit))
|
||||
//} else {
|
||||
// SET(out, vec4(0f.lit, 0f.lit, 0f.lit, 1f.lit))
|
||||
//}
|
||||
|
||||
if (meshMaterial != null) {
|
||||
computeMaterialLightColor(out, diffuse, meshMaterial.diffuse)
|
||||
} else {
|
||||
SET(out, vec4(0f.lit, 0f.lit, 0f.lit, 1f.lit))
|
||||
}
|
||||
|
||||
for (n in 0 until nlights) {
|
||||
addLight(lights[n], out)
|
||||
}
|
||||
//SET(out, vec4(v_Temp1.x, v_Temp1.y, v_Temp1.z, 1f.lit))
|
||||
},
|
||||
name = "programColor3D"
|
||||
).apply {
|
||||
println(GlslGenerator(kind = ShaderType.VERTEX).generate(this.vertex.stm))
|
||||
println(GlslGenerator(kind = ShaderType.FRAGMENT).generate(this.fragment.stm))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val layoutPosCol = VertexLayout(a_pos, a_col)
|
||||
|
||||
private val FLOATS_PER_VERTEX = layoutPosCol.totalSize / Int.SIZE_BYTES /*Float.SIZE_BYTES is not defined*/
|
||||
}
|
||||
@@ -1,569 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d
|
||||
|
||||
import com.soywiz.kds.*
|
||||
import com.soywiz.kds.iterators.*
|
||||
import com.soywiz.kmem.*
|
||||
import com.soywiz.korag.*
|
||||
import com.soywiz.korag.shader.*
|
||||
import com.soywiz.korge.experimental.s3d.model.internal.*
|
||||
import com.soywiz.korge.render.*
|
||||
import com.soywiz.korge.view.*
|
||||
import com.soywiz.korim.bitmap.*
|
||||
import com.soywiz.korim.color.*
|
||||
import com.soywiz.korma.geom.*
|
||||
|
||||
inline fun Container.scene3D(views: Views3D = Views3D(), callback: Stage3D.() -> Unit = {}): Stage3DView =
|
||||
Stage3DView(Stage3D(views).apply(callback)).addTo(this)
|
||||
|
||||
class Views3D {
|
||||
}
|
||||
|
||||
fun Container3D.light(callback: Light3D.() -> Unit = {}) = Light3D().apply(callback).addTo(this)
|
||||
|
||||
open class Light3D(
|
||||
var color: RGBA = Colors.WHITE,
|
||||
var constantAttenuation: Double = 1.0,
|
||||
var linearAttenuation: Double = 0.0,
|
||||
var quadraticAttenuation: Double = 0.00111109
|
||||
) : View3D() {
|
||||
internal val colorVec = Vector3D()
|
||||
internal val attenuationVec = Vector3D()
|
||||
|
||||
fun setTo(
|
||||
color: RGBA = Colors.WHITE,
|
||||
constantAttenuation: Double = 1.0,
|
||||
linearAttenuation: Double = 0.0,
|
||||
quadraticAttenuation: Double = 0.00111109
|
||||
) = this.apply {
|
||||
this.color = color
|
||||
this.constantAttenuation = constantAttenuation
|
||||
this.linearAttenuation = linearAttenuation
|
||||
this.quadraticAttenuation = quadraticAttenuation
|
||||
}
|
||||
|
||||
override fun render(ctx: RenderContext3D) {
|
||||
}
|
||||
}
|
||||
|
||||
class Stage3D(val views: Views3D) : Container3D() {
|
||||
lateinit var view: Stage3DView
|
||||
//var ambientColor: RGBA = Colors.WHITE
|
||||
var ambientColor: RGBA = Colors.BLACK // No ambient light
|
||||
var ambientPower: Double = 0.3
|
||||
var camera: Camera3D = Camera3D.Perspective().apply {
|
||||
positionLookingAt(0, 1, -10, 0, 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
class Stage3DView(val stage3D: Stage3D) : View() {
|
||||
init {
|
||||
stage3D.view = this
|
||||
}
|
||||
|
||||
private val ctx3D = RenderContext3D()
|
||||
override fun renderInternal(ctx: RenderContext) {
|
||||
ctx.flush()
|
||||
ctx.ag.clear(depth = 1f, clearColor = false)
|
||||
//ctx.ag.clear(color = Colors.RED)
|
||||
ctx3D.ag = ctx.ag
|
||||
ctx3D.rctx = ctx
|
||||
ctx3D.projMat.copyFrom(stage3D.camera.getProjMatrix(ctx.ag.backWidth.toDouble(), ctx.ag.backHeight.toDouble()))
|
||||
ctx3D.cameraMat.copyFrom(stage3D.camera.transform.matrix)
|
||||
ctx3D.ambientColor.setToColorPremultiplied(stage3D.ambientColor).scale(stage3D.ambientPower)
|
||||
ctx3D.cameraMatInv.invert(stage3D.camera.transform.matrix)
|
||||
ctx3D.projCameraMat.multiply(ctx3D.projMat, ctx3D.cameraMatInv)
|
||||
ctx3D.lights.clear()
|
||||
stage3D.foreachDescendant {
|
||||
if (it is Light3D) {
|
||||
if (it.active) ctx3D.lights.add(it)
|
||||
}
|
||||
}
|
||||
stage3D.render(ctx3D)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun View3D?.foreachDescendant(handler: (View3D) -> Unit) {
|
||||
if (this != null) {
|
||||
handler(this)
|
||||
if (this is Container3D) {
|
||||
this.children.fastForEach { child ->
|
||||
child.foreachDescendant(handler)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RenderContext3D() {
|
||||
lateinit var ag: AG
|
||||
lateinit var rctx: RenderContext
|
||||
val textureUnit = AG.TextureUnit()
|
||||
val bindMat4 = Matrix3D()
|
||||
val bones = Array(128) { Matrix3D() }
|
||||
val tmepMat = Matrix3D()
|
||||
val projMat: Matrix3D = Matrix3D()
|
||||
val lights = arrayListOf<Light3D>()
|
||||
val projCameraMat: Matrix3D = Matrix3D()
|
||||
val cameraMat: Matrix3D = Matrix3D()
|
||||
val cameraMatInv: Matrix3D = Matrix3D()
|
||||
val dynamicVertexBufferPool = Pool { ag.createVertexBuffer() }
|
||||
val ambientColor: Vector3D = Vector3D()
|
||||
}
|
||||
|
||||
abstract class View3D {
|
||||
var active = true
|
||||
var id: String? = null
|
||||
var name: String? = null
|
||||
val transform = Transform3D()
|
||||
|
||||
///////
|
||||
|
||||
var x: Double
|
||||
set(localX) = run { transform.setTranslation(localX, y, z, localW) }
|
||||
get() = transform.translation.x.toDouble()
|
||||
|
||||
var y: Double
|
||||
set(localY) = run { transform.setTranslation(x, localY, z, localW) }
|
||||
get() = transform.translation.y.toDouble()
|
||||
|
||||
var z: Double
|
||||
set(localZ) = run { transform.setTranslation(x, y, localZ, localW) }
|
||||
get() = transform.translation.z.toDouble()
|
||||
|
||||
var localW: Double
|
||||
set(localW) = run { transform.setTranslation(x, y, z, localW) }
|
||||
get() = transform.translation.w.toDouble()
|
||||
|
||||
///////
|
||||
|
||||
var scaleX: Double
|
||||
set(scaleX) = run { transform.setScale(scaleX, scaleY, scaleZ, localScaleW) }
|
||||
get() = transform.scale.x.toDouble()
|
||||
|
||||
var scaleY: Double
|
||||
set(scaleY) = run { transform.setScale(scaleX, scaleY, scaleZ, localScaleW) }
|
||||
get() = transform.scale.y.toDouble()
|
||||
|
||||
var scaleZ: Double
|
||||
set(scaleZ) = run { transform.setScale(scaleX, scaleY, scaleZ, localScaleW) }
|
||||
get() = transform.scale.z.toDouble()
|
||||
|
||||
var localScaleW: Double
|
||||
set(scaleW) = run { transform.setScale(scaleX, scaleY, scaleZ, scaleW) }
|
||||
get() = transform.scale.w.toDouble()
|
||||
|
||||
///////
|
||||
|
||||
var rotationX: Angle
|
||||
set(rotationX) = run { transform.setRotation(rotationX, rotationY, rotationZ) }
|
||||
get() = transform.rotationEuler.x
|
||||
|
||||
var rotationY: Angle
|
||||
set(rotationY) = run { transform.setRotation(rotationX, rotationY, rotationZ) }
|
||||
get() = transform.rotationEuler.y
|
||||
|
||||
var rotationZ: Angle
|
||||
set(rotationZ) = run { transform.setRotation(rotationX, rotationY, rotationZ) }
|
||||
get() = transform.rotationEuler.z
|
||||
|
||||
///////
|
||||
|
||||
var rotationQuatX: Double
|
||||
set(rotationQuatX) = run { transform.setRotation(rotationQuatX, rotationQuatY, rotationQuatZ, rotationQuatW) }
|
||||
get() = transform.rotation.x
|
||||
|
||||
var rotationQuatY: Double
|
||||
set(rotationQuatY) = run { transform.setRotation(rotationQuatX, rotationQuatY, rotationQuatZ, rotationQuatW) }
|
||||
get() = transform.rotation.y
|
||||
|
||||
var rotationQuatZ: Double
|
||||
set(rotationQuatZ) = run { transform.setRotation(rotationQuatX, rotationQuatY, rotationQuatZ, rotationQuatW) }
|
||||
get() = transform.rotation.z
|
||||
|
||||
var rotationQuatW: Double
|
||||
set(rotationQuatW) = run { transform.setRotation(rotationQuatX, rotationQuatY, rotationQuatZ, rotationQuatW) }
|
||||
get() = transform.rotation.w
|
||||
|
||||
///////
|
||||
|
||||
internal var _parent: Container3D? = null
|
||||
|
||||
var parent: Container3D?
|
||||
set(value) {
|
||||
_parent = value
|
||||
_parent?.addChild(this)
|
||||
}
|
||||
get() = _parent
|
||||
|
||||
val modelMat = Matrix3D()
|
||||
//val position = Vector3D()
|
||||
|
||||
abstract fun render(ctx: RenderContext3D)
|
||||
}
|
||||
|
||||
class Skeleton3D(val skin: Skin3D, val headJoint: Joint3D) : View3D() {
|
||||
val allJoints = headJoint.descendantsAndThis
|
||||
val jointsByName = allJoints.associateBy { it.name }
|
||||
val matrices = Array(allJoints.size) { Matrix3D() }
|
||||
|
||||
//init {
|
||||
// println(jointsByName)
|
||||
// println(jointsByName.values.map { it.name })
|
||||
// println(jointsByName.values.map { it.name })
|
||||
//}
|
||||
|
||||
override fun render(ctx: RenderContext3D) {
|
||||
}
|
||||
}
|
||||
|
||||
open class Joint3D constructor(val skin: Skin3D, val bone: Bone3D, val jointParent: Joint3D? = null, initialMatrix: Matrix3D) : Container3D() {
|
||||
//open class Joint3D constructor(val jointParent: Joint3D? = null, initialMatrix: Matrix3D) : View3D() {
|
||||
val index = bone.index
|
||||
init {
|
||||
this.transform.setMatrix(initialMatrix)
|
||||
this.name = bone.name
|
||||
if (jointParent != null) {
|
||||
this.parent = jointParent
|
||||
}
|
||||
|
||||
}
|
||||
val poseMatrix = this.transform.globalMatrix.clone()
|
||||
val poseMatrixInv = poseMatrix.clone().invert()
|
||||
//val poseMatrixInv = bone.invBindMatrix
|
||||
//val poseMatrixInv = bone.invBindMatrix * skin.bindShapeMatrix
|
||||
|
||||
val childJoints = arrayListOf<Joint3D>()
|
||||
val descendants: List<Joint3D> get() = childJoints.flatMap { it.descendantsAndThis }
|
||||
val descendantsAndThis: List<Joint3D> get() = listOf(this) + descendants
|
||||
|
||||
//val jointTransform = Transform3D()
|
||||
|
||||
override fun render(ctx: RenderContext3D) {
|
||||
}
|
||||
|
||||
override fun toString(): String = "Joint3D($index, $name)"
|
||||
}
|
||||
|
||||
open class Container3D : View3D() {
|
||||
val children = arrayListOf<View3D>()
|
||||
|
||||
fun removeChild(child: View3D) {
|
||||
children.remove(child)
|
||||
}
|
||||
|
||||
fun addChild(child: View3D) {
|
||||
child.removeFromParent()
|
||||
children += child
|
||||
child._parent = this
|
||||
child.transform.parent = this.transform
|
||||
}
|
||||
|
||||
operator fun plusAssign(child: View3D) = addChild(child)
|
||||
|
||||
override fun render(ctx: RenderContext3D) {
|
||||
children.fastForEach {
|
||||
it.render(ctx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun View3D.removeFromParent() {
|
||||
parent?.removeChild(this)
|
||||
parent = null
|
||||
}
|
||||
|
||||
inline fun <reified T : View3D> View3D?.findByType() = sequence<T> {
|
||||
for (it in descendants()) {
|
||||
if (it is T) yield(it)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <reified T : View3D> View3D?.findByTypeWithName(name: String) = sequence<T> {
|
||||
for (it in descendants()) {
|
||||
if (it is T && it.name == name) yield(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun View3D?.descendants(): Sequence<View3D> = sequence<View3D> {
|
||||
val view = this@descendants ?: return@sequence
|
||||
yield(view)
|
||||
if (view is Container3D) {
|
||||
view.children.fastForEach {
|
||||
yieldAll(it.descendants())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
operator fun View3D?.get(name: String): View3D? {
|
||||
if (this?.id == name) return this
|
||||
if (this?.name == name) return this
|
||||
if (this is Container3D) {
|
||||
this.children.fastForEach {
|
||||
val result = it[name]
|
||||
if (result != null) return result
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun <T : View3D> T.name(name: String) = this.apply { this.name = name }
|
||||
|
||||
inline fun <T : View3D> T.position(x: Number, y: Number, z: Number, w: Number = 1f): T = this.apply {
|
||||
transform.setTranslation(x, y, z, w)
|
||||
}
|
||||
|
||||
inline fun <T : View3D> T.rotation(x: Angle = 0.degrees, y: Angle = 0.degrees, z: Angle = 0.degrees): T = this.apply {
|
||||
transform.setRotation(x, y, z)
|
||||
}
|
||||
|
||||
inline fun <T : View3D> T.scale(x: Number = 1, y: Number = 1, z: Number = 1, w: Number = 1): T = this.apply {
|
||||
transform.setScale(x, y, z, w)
|
||||
}
|
||||
|
||||
inline fun <T : View3D> T.lookAt(x: Number, y: Number, z: Number): T = this.apply {
|
||||
transform.lookAt(x, y, z)
|
||||
}
|
||||
|
||||
inline fun <T : View3D> T.positionLookingAt(px: Number, py: Number, pz: Number, tx: Number, ty: Number, tz: Number): T = this.apply {
|
||||
transform.setTranslationAndLookAt(px, py, pz, tx, ty, tz)
|
||||
}
|
||||
|
||||
fun <T : View3D> T.addTo(container: Container3D) = this.apply {
|
||||
container.addChild(this)
|
||||
}
|
||||
|
||||
data class Bone3D constructor(
|
||||
val index: Int,
|
||||
val name: String,
|
||||
val invBindMatrix: Matrix3D
|
||||
) {
|
||||
}
|
||||
|
||||
data class Skin3D(val invBindShapeMatrix: Matrix3D, val bones: List<Bone3D>) {
|
||||
val bindShapeMatrix = invBindShapeMatrix.clone().invert()
|
||||
//val matrices = Array(bones.size) { Matrix3D() }
|
||||
}
|
||||
|
||||
open class MaterialLight(val kind: String)
|
||||
data class MaterialLightColor(val color: RGBA) : MaterialLight("color") {
|
||||
val colorVec = Vector3D().setToColor(color)
|
||||
}
|
||||
data class MaterialLightTexture(val bitmap: Bitmap?) : MaterialLight("texture") {
|
||||
val textureUnit = AG.TextureUnit()
|
||||
}
|
||||
|
||||
data class Material3D(
|
||||
val emission: MaterialLight,
|
||||
val ambient: MaterialLight,
|
||||
val diffuse: MaterialLight,
|
||||
val specular: MaterialLight,
|
||||
val shiness: Float,
|
||||
val indexOfRefraction: Float
|
||||
) {
|
||||
val kind: String = "${emission.kind}_${ambient.kind}_${diffuse.kind}_${specular.kind}"
|
||||
}
|
||||
|
||||
class Mesh3D constructor(
|
||||
val data: FloatArray,
|
||||
val layout: VertexLayout,
|
||||
val program: Program?,
|
||||
val drawType: AG.DrawType,
|
||||
val hasTexture: Boolean = false,
|
||||
val maxWeights: Int = 0
|
||||
) {
|
||||
var skin: Skin3D? = null
|
||||
|
||||
val fbuffer by lazy {
|
||||
FBuffer.alloc(data.size * 4).apply {
|
||||
setAlignedArrayFloat32(0, this@Mesh3D.data, 0, this@Mesh3D.data.size)
|
||||
}
|
||||
//FBuffer.wrap(MemBufferAlloc(data.size * 4)).apply {
|
||||
// arraycopy(this@Mesh3D.data, 0, this@apply.mem, 0, this@Mesh3D.data.size) // Bug in kmem-js?
|
||||
//}
|
||||
}
|
||||
|
||||
var material: Material3D? = null
|
||||
|
||||
//val modelMat = Matrix3D()
|
||||
val vertexSizeInBytes = layout.totalSize
|
||||
val vertexSizeInFloats = vertexSizeInBytes / 4
|
||||
val vertexCount = data.size / vertexSizeInFloats
|
||||
|
||||
init {
|
||||
println("vertexCount: $vertexCount, vertexSizeInFloats: $vertexSizeInFloats, data.size: ${data.size}")
|
||||
}
|
||||
}
|
||||
|
||||
inline fun Container3D.box(width: Number = 1, height: Number = width, depth: Number = height, callback: Cube.() -> Unit = {}): Cube {
|
||||
return Cube(width.toDouble(), height.toDouble(), depth.toDouble()).apply(callback).addTo(this)
|
||||
}
|
||||
|
||||
class Cube(var width: Double, var height: Double, var depth: Double) : ViewWithMesh3D(Cube.mesh) {
|
||||
override fun prepareExtraModelMatrix(mat: Matrix3D) {
|
||||
mat.identity().scale(width, height, depth)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val cubeSize = .5f
|
||||
|
||||
private val vertices = floatArrayOf(
|
||||
-cubeSize, -cubeSize, -cubeSize, 1f, 0f, 0f, //p1
|
||||
-cubeSize, -cubeSize, +cubeSize, 1f, 0f, 0f, //p2
|
||||
-cubeSize, +cubeSize, +cubeSize, 1f, 0f, 0f, //p3
|
||||
-cubeSize, -cubeSize, -cubeSize, 1f, 0f, 0f, //p1
|
||||
-cubeSize, +cubeSize, +cubeSize, 1f, 0f, 0f, //p3
|
||||
-cubeSize, +cubeSize, -cubeSize, 1f, 0f, 0f, //p4
|
||||
|
||||
+cubeSize, +cubeSize, -cubeSize, 0f, 1f, 0f, //p5
|
||||
-cubeSize, -cubeSize, -cubeSize, 0f, 1f, 0f, //p1
|
||||
-cubeSize, +cubeSize, -cubeSize, 0f, 1f, 0f, //p4
|
||||
+cubeSize, +cubeSize, -cubeSize, 0f, 1f, 0f, //p5
|
||||
+cubeSize, -cubeSize, -cubeSize, 0f, 1f, 0f, //p7
|
||||
-cubeSize, -cubeSize, -cubeSize, 0f, 1f, 0f, //p1
|
||||
|
||||
+cubeSize, -cubeSize, +cubeSize, 0f, 0f, 1f, //p6
|
||||
-cubeSize, -cubeSize, -cubeSize, 0f, 0f, 1f, //p1
|
||||
+cubeSize, -cubeSize, -cubeSize, 0f, 0f, 1f, //p7
|
||||
+cubeSize, -cubeSize, +cubeSize, 0f, 0f, 1f, //p6
|
||||
-cubeSize, -cubeSize, +cubeSize, 0f, 0f, 1f, //p2
|
||||
-cubeSize, -cubeSize, -cubeSize, 0f, 0f, 1f, //p1
|
||||
|
||||
+cubeSize, +cubeSize, +cubeSize, 0f, 1f, 1f, //p8
|
||||
+cubeSize, +cubeSize, -cubeSize, 0f, 1f, 1f, //p5
|
||||
-cubeSize, +cubeSize, -cubeSize, 0f, 1f, 1f, //p4
|
||||
+cubeSize, +cubeSize, +cubeSize, 0f, 1f, 1f, //p8
|
||||
-cubeSize, +cubeSize, -cubeSize, 0f, 1f, 1f, //p4
|
||||
-cubeSize, +cubeSize, +cubeSize, 0f, 1f, 1f, //p3
|
||||
|
||||
+cubeSize, +cubeSize, +cubeSize, 1f, 1f, 0f, //p8
|
||||
-cubeSize, +cubeSize, +cubeSize, 1f, 1f, 0f, //p3
|
||||
+cubeSize, -cubeSize, +cubeSize, 1f, 1f, 0f, //p6
|
||||
-cubeSize, +cubeSize, +cubeSize, 1f, 1f, 0f, //p3
|
||||
-cubeSize, -cubeSize, +cubeSize, 1f, 1f, 0f, //p2
|
||||
+cubeSize, -cubeSize, +cubeSize, 1f, 1f, 0f, //p6
|
||||
|
||||
+cubeSize, +cubeSize, +cubeSize, 1f, 0f, 1f, //p8
|
||||
+cubeSize, -cubeSize, -cubeSize, 1f, 0f, 1f, //p7
|
||||
+cubeSize, +cubeSize, -cubeSize, 1f, 0f, 1f, //p5
|
||||
+cubeSize, -cubeSize, -cubeSize, 1f, 0f, 1f, //p7
|
||||
+cubeSize, +cubeSize, +cubeSize, 1f, 0f, 1f, //p8
|
||||
+cubeSize, -cubeSize, +cubeSize, 1f, 0f, 1f //p6
|
||||
)
|
||||
|
||||
val mesh = Mesh3D(vertices, Shaders3D.layoutPosCol, Shaders3D.programColor3D, AG.DrawType.TRIANGLES, hasTexture = false)
|
||||
}
|
||||
}
|
||||
|
||||
inline fun Container3D.mesh(mesh: Mesh3D, callback: ViewWithMesh3D.() -> Unit = {}): ViewWithMesh3D {
|
||||
return ViewWithMesh3D(mesh).apply(callback).addTo(this)
|
||||
}
|
||||
|
||||
open class ViewWithMesh3D(
|
||||
var mesh: Mesh3D,
|
||||
var skeleton: Skeleton3D? = null
|
||||
) : View3D() {
|
||||
|
||||
private val uniformValues = AG.UniformValues()
|
||||
private val rs = AG.RenderState(depthFunc = AG.CompareMode.LESS_EQUAL)
|
||||
//private val rs = AG.RenderState(depthFunc = AG.CompareMode.ALWAYS)
|
||||
|
||||
private val tempMat1 = Matrix3D()
|
||||
private val tempMat2 = Matrix3D()
|
||||
private val tempMat3 = Matrix3D()
|
||||
|
||||
protected open fun prepareExtraModelMatrix(mat: Matrix3D) {
|
||||
mat.identity()
|
||||
}
|
||||
|
||||
fun AG.UniformValues.setMaterialLight(ctx: RenderContext3D, uniform: Shaders3D.MaterialLightUniform, actual: MaterialLight) {
|
||||
when (actual) {
|
||||
is MaterialLightColor -> {
|
||||
this[uniform.u_color] = actual.colorVec
|
||||
}
|
||||
is MaterialLightTexture -> {
|
||||
actual.textureUnit.texture = actual.bitmap?.let { ctx.rctx.agBitmapTextureManager.getTextureBase(it).base }
|
||||
actual.textureUnit.linear = true
|
||||
this[uniform.u_texUnit] = actual.textureUnit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val identity = Matrix3D()
|
||||
private val identityInv = identity.clone().invert()
|
||||
|
||||
override fun render(ctx: RenderContext3D) {
|
||||
val ag = ctx.ag
|
||||
|
||||
ctx.dynamicVertexBufferPool.alloc { vertexBuffer ->
|
||||
//vertexBuffer.upload(mesh.data)
|
||||
vertexBuffer.upload(mesh.fbuffer)
|
||||
|
||||
//tempMat2.invert()
|
||||
//tempMat3.multiply(ctx.cameraMatInv, this.localTransform.matrix)
|
||||
//tempMat3.multiply(ctx.cameraMatInv, Matrix3D().invert(this.localTransform.matrix))
|
||||
//tempMat3.multiply(this.localTransform.matrix, ctx.cameraMat)
|
||||
|
||||
Shaders3D.apply {
|
||||
val meshMaterial = mesh.material
|
||||
ag.draw(
|
||||
vertexBuffer,
|
||||
type = mesh.drawType,
|
||||
program = mesh.program ?: getProgram3D(ctx.lights.size.clamp(0, 4), mesh.maxWeights, meshMaterial, mesh.hasTexture),
|
||||
vertexLayout = mesh.layout,
|
||||
vertexCount = mesh.vertexCount,
|
||||
blending = AG.Blending.NONE,
|
||||
//vertexCount = 6 * 6,
|
||||
uniforms = uniformValues.apply {
|
||||
this[u_ProjMat] = ctx.projCameraMat
|
||||
this[u_ViewMat] = transform.globalMatrix
|
||||
this[u_ModMat] = tempMat2.multiply(tempMat1.apply { prepareExtraModelMatrix(this) }, modelMat)
|
||||
//this[u_NormMat] = tempMat3.multiply(tempMat2, localTransform.matrix).invert().transpose()
|
||||
this[u_NormMat] = tempMat3.multiply(tempMat2, transform.globalMatrix).invert()
|
||||
|
||||
this[u_Shiness] = meshMaterial?.shiness ?: 0.5f
|
||||
this[u_IndexOfRefraction] = meshMaterial?.indexOfRefraction ?: 1f
|
||||
|
||||
if (meshMaterial != null) {
|
||||
setMaterialLight(ctx, ambient, meshMaterial.ambient)
|
||||
setMaterialLight(ctx, diffuse, meshMaterial.diffuse)
|
||||
setMaterialLight(ctx, emission, meshMaterial.emission)
|
||||
setMaterialLight(ctx, specular, meshMaterial.specular)
|
||||
}
|
||||
|
||||
val skeleton = this@ViewWithMesh3D.skeleton
|
||||
this[u_BindShapeMatrix] = identity
|
||||
this[u_InvBindShapeMatrix] = identityInv
|
||||
if (skeleton != null) {
|
||||
//this[u_BindShapeMatrix] = ctx.bindMat4.copyFrom(skeleton.skin.bindShapeMatrix)
|
||||
//skeleton.allJoints[1].transform.rotate(10.degrees, 0.degrees, 0.degrees)
|
||||
skeleton.allJoints.fastForEach {
|
||||
//skeleton.matrices[it.index].copyFrom(it.inverseBindTransform)
|
||||
//skeleton.matrices[it.index].copyFrom(it.transform.globalMatrix)
|
||||
if (it.index in skeleton.matrices.indices) {
|
||||
skeleton.matrices[it.index].multiply(it.transform.globalMatrix, it.poseMatrixInv)
|
||||
}
|
||||
//skeleton.matrices[it.index].multiply(it.poseMatrixInv, (it.transform.globalMatrix * it.jointTransform.matrix))
|
||||
//skeleton.matrices[it.index].copyFrom(((it.poseMatrixInv * it.transform.globalMatrix) * it.jointTransform.matrix))
|
||||
//if (it.name == "Upper_Leg.L") println("${it.name}: ${skeleton.matrices[it.index]}")
|
||||
}
|
||||
//this[u_BindShapeMatrix] = this@ViewWithMesh3D.mesh.skin!!.bindShapeMatrix
|
||||
//this[u_InvBindShapeMatrix] = this@ViewWithMesh3D.mesh.skin!!.invBindShapeMatrix
|
||||
this[u_BoneMats] = skeleton.matrices
|
||||
}
|
||||
|
||||
this[u_AmbientColor] = ctx.ambientColor
|
||||
|
||||
ctx.lights.fastForEachWithIndex { index, light: Light3D ->
|
||||
val lightColor = light.color
|
||||
this[lights[index].u_sourcePos] = light.transform.translation
|
||||
this[lights[index].u_color] = light.colorVec.setTo(lightColor.rf, lightColor.gf, lightColor.bf, 1f)
|
||||
this[lights[index].u_attenuation] = light.attenuationVec.setTo(light.constantAttenuation, light.linearAttenuation, light.quadraticAttenuation)
|
||||
}
|
||||
},
|
||||
renderState = rs
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,804 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model
|
||||
|
||||
import com.soywiz.kds.*
|
||||
import com.soywiz.kds.iterators.*
|
||||
import com.soywiz.korag.*
|
||||
import com.soywiz.korag.shader.*
|
||||
import com.soywiz.korge.experimental.s3d.*
|
||||
import com.soywiz.korge.experimental.s3d.model.internal.*
|
||||
import com.soywiz.korim.color.*
|
||||
import com.soywiz.korio.file.*
|
||||
import com.soywiz.korio.serialization.xml.*
|
||||
import com.soywiz.korio.util.*
|
||||
import com.soywiz.korma.geom.*
|
||||
import kotlin.math.*
|
||||
|
||||
suspend fun VfsFile.readColladaLibrary(loadTextures: Boolean = true): Library3D {
|
||||
return ColladaParser.parse(readXml()).also { if (loadTextures) it.loadTextures() }.also { it.instantiateMaterials() }
|
||||
}
|
||||
|
||||
class ColladaParser {
|
||||
interface SourceParam {
|
||||
val name: String
|
||||
}
|
||||
data class MatrixSourceParam(override val name: String, val matrices: Array<Matrix3D>) : SourceParam
|
||||
data class FloatSourceParam(override val name: String, val floats: FloatArrayList) : SourceParam
|
||||
data class NamesSourceParam(override val name: String, val names: ArrayList<String>) : SourceParam
|
||||
data class Source(val id: String, val params: FastStringMap<SourceParam>)
|
||||
data class Input(val semantic: String, val offset: Int, val source: Source, val indices: IntArrayList)
|
||||
data class Geometry(val id: String, val name: String, val inputs: FastStringMap<Input> = FastStringMap(), var materialId: String? = null)
|
||||
data class Skin(
|
||||
val controllerId: String,
|
||||
val controllerName: String,
|
||||
val inputs: FastStringMap<Input>,
|
||||
val vcounts: IntArrayList,
|
||||
val bindShapeMatrix: Matrix3D,
|
||||
val jointInputs: FastStringMap<Input>,
|
||||
val skinSource: String
|
||||
) {
|
||||
val maxVcount = vcounts.map { it }.max() ?: 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun parse(xml: Xml): Library3D = ColladaParser().parse(xml)
|
||||
}
|
||||
|
||||
fun parse(xml: Xml): Library3D = Library3D().apply {
|
||||
parseCameras(xml)
|
||||
parseLights(xml)
|
||||
parseImages(xml)
|
||||
parseEffects(xml)
|
||||
parseMaterials(xml)
|
||||
val geometries = parseGeometries(xml)
|
||||
parseAnimations(xml)
|
||||
val skins = parseControllers(xml)
|
||||
for (skin in skins) {
|
||||
library.skinDefs[skin.controllerId] = skin
|
||||
}
|
||||
generateGeometries(geometries, skins)
|
||||
parseVisualScenes(xml)
|
||||
parseScene(xml)
|
||||
}
|
||||
|
||||
fun Library3D.generateGeometries(geometries: List<Geometry>, skins: List<Skin>) {
|
||||
val geomIdToSkin = FastStringMap<Skin>()
|
||||
|
||||
for (skin in skins) {
|
||||
geomIdToSkin[skin.skinSource] = skin
|
||||
}
|
||||
|
||||
for (geom in geometries) {
|
||||
val px = FloatArrayList()
|
||||
val py = FloatArrayList()
|
||||
val pz = FloatArrayList()
|
||||
|
||||
val nx = FloatArrayList()
|
||||
val ny = FloatArrayList()
|
||||
val nz = FloatArrayList()
|
||||
|
||||
val u0 = FloatArrayList()
|
||||
val v0 = FloatArrayList()
|
||||
|
||||
val weightIndices = Array(16) { FloatArrayList() }
|
||||
val weightWeights = Array(16) { FloatArrayList() }
|
||||
|
||||
val VERTEX = geom.inputs["VERTEX"] ?: error("Do not have vertices!")
|
||||
val VERTEX_indices = VERTEX.indices
|
||||
|
||||
for (pname in listOf("X", "Y", "Z")) {
|
||||
val p = (VERTEX.source.params[pname] as? FloatSourceParam)?.floats
|
||||
val array = when (pname) {
|
||||
"X" -> px
|
||||
"Y" -> py
|
||||
"Z" -> pz
|
||||
else -> TODO()
|
||||
}
|
||||
if (p != null) {
|
||||
//println(VERTEX.indices)
|
||||
VERTEX.indices.fastForEach { index ->
|
||||
array.add(p[index])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val NORMAL = geom.inputs["NORMAL"]
|
||||
if (NORMAL != null) {
|
||||
for (pname in listOf("X", "Y", "Z")) {
|
||||
val p = (NORMAL.source.params[pname] as? FloatSourceParam)?.floats
|
||||
val array = when (pname) {
|
||||
"X" -> nx
|
||||
"Y" -> ny
|
||||
"Z" -> nz
|
||||
else -> TODO()
|
||||
}
|
||||
if (p != null) {
|
||||
NORMAL.indices.fastForEach { index -> array.add(p[index]) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val TEXCOORD = geom.inputs["TEXCOORD"]
|
||||
if (TEXCOORD != null) {
|
||||
for (pname in listOf("S", "T")) {
|
||||
val p = (TEXCOORD.source.params[pname] as? FloatSourceParam)?.floats
|
||||
val array = when (pname) {
|
||||
"S" -> u0
|
||||
"T" -> v0
|
||||
else -> TODO()
|
||||
}
|
||||
if (p != null) {
|
||||
TEXCOORD.indices.fastForEach { index -> array.add(p[index]) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val skin = geomIdToSkin[geom.id]
|
||||
|
||||
val maxWeights: Int
|
||||
|
||||
if (skin != null) {
|
||||
val joint = skin.inputs["JOINT"] ?: error("Can't find JOINT")
|
||||
val weight = skin.inputs["WEIGHT"] ?: error("Can't find WEIGHT")
|
||||
maxWeights = skin.maxVcount.nextMultipleOf(4)
|
||||
var pos = 0
|
||||
|
||||
//if (maxWeights > 4) error("Too much weights for the current implementation $maxWeights > 4")
|
||||
|
||||
val jointSrcParam = joint.source.params["JOINT"] as NamesSourceParam
|
||||
val weightSrcParam = weight.source.params["WEIGHT"] as FloatSourceParam
|
||||
|
||||
val jointsToIndex = FastStringMap<Int>()
|
||||
jointSrcParam.names.fastForEachWithIndex { index, value ->
|
||||
jointsToIndex[value] = index
|
||||
}
|
||||
|
||||
for (vcount in skin.vcounts) {
|
||||
//println("-- vcount=$vcount")
|
||||
for (n in 0 until vcount) {
|
||||
//joint.source = joint.indices[pos]
|
||||
val jointIndex = joint.indices[pos]
|
||||
//val jointtName = jointSrcParam.names[jointIndex]
|
||||
val w = weightSrcParam.floats[weight.indices[pos]]
|
||||
//println("$jointName[$joinIndex]: $weight")
|
||||
weightIndices[n].add(jointIndex.toFloat())
|
||||
weightWeights[n].add(w)
|
||||
pos++
|
||||
}
|
||||
for (n in vcount until maxWeights) {
|
||||
weightIndices[n].add(-1f)
|
||||
weightWeights[n].add(0f)
|
||||
}
|
||||
}
|
||||
//println("jointSrcParam: $jointSrcParam")
|
||||
//println("weightSrcParam: $weightSrcParam")
|
||||
//println("joint: $joint")
|
||||
//println("weight: $weight")
|
||||
//println("---")
|
||||
} else {
|
||||
maxWeights = 0
|
||||
}
|
||||
|
||||
val skinDef = if (skin != null) {
|
||||
val JOINT = skin.jointInputs["JOINT"] ?: error("Can't find JOINT")
|
||||
val INV_BIND_MATRIX = skin.jointInputs["INV_BIND_MATRIX"] ?: error("Can't find INV_BIND_MATRIX")
|
||||
val JOINT_NAMES = (JOINT.source.params["JOINT"] as? NamesSourceParam)?.names ?: error("Can't find JOINT.JOINT")
|
||||
val TRANSFORM = (INV_BIND_MATRIX.source.params["TRANSFORM"] as? MatrixSourceParam)?.matrices ?: error("Can't find INV_BIND_MATRIX.TRANSFORM")
|
||||
val skin = Library3D.SkinDef(skin.bindShapeMatrix, JOINT_NAMES.zip(TRANSFORM).withIndex().map { Library3D.BoneDef(it.index, it.value.first, it.value.second) })
|
||||
skin.bones.fastForEach { it.skin = skin }
|
||||
skin
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// @TODO: We should use separate components
|
||||
val combinedData = floatArrayListOf()
|
||||
val hasNormals = (nx.size >= px.size)
|
||||
val hasTexture = TEXCOORD != null
|
||||
for (n in 0 until px.size) {
|
||||
combinedData.add(px[n])
|
||||
combinedData.add(py[n])
|
||||
combinedData.add(pz[n])
|
||||
if (hasNormals) {
|
||||
combinedData.add(nx[n])
|
||||
combinedData.add(ny[n])
|
||||
combinedData.add(nz[n])
|
||||
}
|
||||
if (hasTexture) {
|
||||
combinedData.add(u0[n])
|
||||
combinedData.add(1f - v0[n])
|
||||
}
|
||||
if (maxWeights > 0) {
|
||||
for (m in 0 until maxWeights) {
|
||||
combinedData.add(weightIndices[m][VERTEX_indices[n]])
|
||||
}
|
||||
}
|
||||
if (maxWeights > 0) {
|
||||
for (m in 0 until maxWeights) {
|
||||
combinedData.add(weightWeights[m][VERTEX_indices[n]])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//println(combinedData.toString())
|
||||
|
||||
val materialDef = geom.materialId?.let { materialDefs[it] }
|
||||
|
||||
geometryDefs[geom.id] = Library3D.GeometryDef(
|
||||
Mesh3D(
|
||||
//combinedData.toFloatArray().toFBuffer(),
|
||||
combinedData.toFloatArray(),
|
||||
VertexLayout(buildList {
|
||||
add(Shaders3D.a_pos)
|
||||
if (hasNormals) add(Shaders3D.a_norm)
|
||||
if (hasTexture) add(Shaders3D.a_tex)
|
||||
for (n in 0 until 4) if (maxWeights > n * 4) add(Shaders3D.a_boneIndex[n])
|
||||
for (n in 0 until 4) if (maxWeights > n * 4) add(Shaders3D.a_weight[n])
|
||||
}),
|
||||
null,
|
||||
AG.DrawType.TRIANGLES,
|
||||
hasTexture = hasTexture,
|
||||
maxWeights = maxWeights
|
||||
).apply {
|
||||
if (skinDef != null) {
|
||||
this.skin = skinDef.toSkin()
|
||||
}
|
||||
},
|
||||
skin = skinDef,
|
||||
material = materialDef
|
||||
)
|
||||
log { "px: $px" }
|
||||
log { "py: $py" }
|
||||
log { "pz: $pz" }
|
||||
log { "nx: $nx" }
|
||||
log { "ny: $ny" }
|
||||
log { "nz: $nz" }
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.parseScene(xml: Xml) {
|
||||
val scene = xml["scene"]
|
||||
for (instance_visual_scene in scene["instance_visual_scene"]) {
|
||||
val id = instance_visual_scene.str("url").trim('#')
|
||||
val scene = scenes[id]
|
||||
if (scene != null) {
|
||||
mainScene.children += scene
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseControllers(xml: Xml): List<Skin> {
|
||||
val skins = arrayListOf<Skin>()
|
||||
for (controller in xml["library_controllers"]["controller"]) {
|
||||
val controllerId = controller.str("id")
|
||||
val controllerName = controller.str("name")
|
||||
val skin = controller["skin"].firstOrNull()
|
||||
if (skin != null) {
|
||||
val skinSource = skin.str("source")
|
||||
val bindShapeMatrix = skin["bind_shape_matrix"].firstOrNull()?.text?.reader()?.readMatrix3D() ?: Matrix3D()
|
||||
for (source in parseSources(skin, this@ColladaParser.sourceArrayParams)) {
|
||||
sources[source.id] = source
|
||||
}
|
||||
val jointsXml = skin["joints"].firstOrNull()
|
||||
val jointInputs = FastStringMap<Input>()
|
||||
if (jointsXml != null) {
|
||||
for (input in jointsXml["input"]) {
|
||||
val semantic = input.str("semantic")
|
||||
val sourceId = input.str("source").trim('#')
|
||||
val offset = input.int("offset")
|
||||
val source = sources[sourceId] ?: continue
|
||||
jointInputs[semantic] = Input(semantic, offset, source, intArrayListOf())
|
||||
}
|
||||
}
|
||||
val vertexWeightsXml = skin["vertex_weights"].firstOrNull()
|
||||
val inputs = arrayListOf<Input>()
|
||||
if (vertexWeightsXml != null) {
|
||||
val count = vertexWeightsXml.int("count")
|
||||
val vcount = (vertexWeightsXml["vcount"].firstOrNull()?.text ?: "").reader().readInts()
|
||||
val v = (vertexWeightsXml["v"].firstOrNull()?.text ?: "").reader().readInts()
|
||||
for (input in vertexWeightsXml["input"]) {
|
||||
val semantic = input.str("semantic")
|
||||
val sourceId = input.str("source").trim('#')
|
||||
val offset = input.int("offset")
|
||||
val source = sources[sourceId] ?: continue
|
||||
inputs += Input(semantic, offset, source, intArrayListOf())
|
||||
}
|
||||
val stride = (inputs.map { it.offset }.max() ?: 0) + 1
|
||||
|
||||
for (i in inputs) {
|
||||
for (n in 0 until v.size / stride) {
|
||||
i.indices += v[n * stride + i.offset]
|
||||
}
|
||||
}
|
||||
|
||||
skins += Skin(controllerId, controllerName, inputs.associate { it.semantic to it }.toMap().toFast(), vcount, bindShapeMatrix, jointInputs, skinSource.trim('#'))
|
||||
}
|
||||
}
|
||||
}
|
||||
return skins
|
||||
}
|
||||
|
||||
fun Library3D.parseMaterials(xml: Xml) {
|
||||
for (materialXml in xml["library_materials"]["material"]) {
|
||||
val materialId = materialXml.str("id")
|
||||
val materialName = materialXml.str("name")
|
||||
val effects = materialXml["instance_effect"].map { instance_effect ->
|
||||
effectDefs[instance_effect.str("url").trim('#')]
|
||||
}.filterNotNull()
|
||||
val material = Library3D.MaterialDef(materialId, materialName, effects)
|
||||
materialDefs[materialId] = material
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.parseLightKindType(xml: Xml?, lightKind: String, params: FastStringMap<Any>): Library3D.LightKindDef? {
|
||||
if (xml == null) return null
|
||||
val nodeName = xml.nameLC
|
||||
val colorXml = xml["color"].firstOrNull()
|
||||
if (colorXml != null) {
|
||||
val sid = colorXml.str("sid")
|
||||
val f = colorXml.text.reader().readFloats()
|
||||
return Library3D.LightColorDef(sid, RGBA.float(f[0], f[1], f[2], f[3]), lightKind)
|
||||
}
|
||||
val textureXml = xml["texture"].firstOrNull()
|
||||
if (textureXml != null) {
|
||||
return Library3D.LightTexDef(nodeName, params[textureXml.str("texture")] as? Library3D.EffectParamSampler2D?, lightKind)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun Library3D.parseEffects(xml: Xml) {
|
||||
for (effectXml in xml["library_effects"]["effect"]) {
|
||||
val effectId = effectXml.str("id")
|
||||
val effectName = effectXml.str("name")
|
||||
val profile_COMMONXml = effectXml["profile_COMMON"].firstOrNull()
|
||||
if (profile_COMMONXml != null) {
|
||||
var emission: Library3D.LightKindDef? = null
|
||||
var ambient: Library3D.LightKindDef? = null
|
||||
var diffuse: Library3D.LightKindDef? = null
|
||||
var specular: Library3D.LightKindDef? = null
|
||||
var shiness: Float? = null
|
||||
var index_of_refraction: Float? = null
|
||||
val params = FastStringMap<Any>()
|
||||
|
||||
for (nodeXml in profile_COMMONXml.allNodeChildren) {
|
||||
when (nodeXml.nameLC) {
|
||||
"newparam" -> {
|
||||
val sid = nodeXml.str("sid")
|
||||
val surfaceXml = nodeXml["surface"].firstOrNull()
|
||||
if (surfaceXml != null) {
|
||||
val surfaceType = surfaceXml.str("type")
|
||||
val initFrom = surfaceXml["init_from"].text
|
||||
val image = imageDefs[initFrom]
|
||||
params[sid] = Library3D.EffectParamSurface(surfaceType, image)
|
||||
}
|
||||
val sampler2DSource = nodeXml["sampler2D"]["source"].firstText
|
||||
if (sampler2DSource != null) {
|
||||
params[sid] = Library3D.EffectParamSampler2D(params[sampler2DSource] as? Library3D.EffectParamSurface?)
|
||||
}
|
||||
}
|
||||
"technique" -> {
|
||||
val sid = nodeXml.str("sid")
|
||||
for (tech in nodeXml.allNodeChildren) {
|
||||
when (tech.nameLC) {
|
||||
"gouraud", "phong" -> { // Smooth lighting
|
||||
emission = parseLightKindType(tech["emission"].firstOrNull(), tech.nameLC, params) ?: emission
|
||||
ambient = parseLightKindType(tech["ambient"].firstOrNull(), tech.nameLC, params) ?: ambient
|
||||
diffuse = parseLightKindType(tech["diffuse"].firstOrNull(), tech.nameLC, params) ?: diffuse
|
||||
specular = parseLightKindType(tech["specular"].firstOrNull(), tech.nameLC, params) ?: specular
|
||||
shiness = tech["shiness"]["float"].firstText?.toFloatOrNull() ?: shiness
|
||||
index_of_refraction = tech["index_of_refraction"]["float"].firstText?.toFloatOrNull() ?: index_of_refraction
|
||||
}
|
||||
"extra" -> Unit
|
||||
else -> println("WARNING: Unsupported library_effects.effect.profile_COMMON.technique.${tech.nameLC}")
|
||||
}
|
||||
}
|
||||
}
|
||||
"extra" -> Unit
|
||||
else -> {
|
||||
println("WARNING: Unsupported library_effects.effect.profile_COMMON.${nodeXml.nameLC}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
effectDefs[effectId] = Library3D.StandardEffectDef(effectId, effectName, emission, ambient, diffuse, specular, shiness, index_of_refraction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.parseImages(xml: Xml) {
|
||||
for (image in xml["library_images"]["image"]) {
|
||||
val imageId = image.str("id")
|
||||
val imageName = image.str("name")
|
||||
val initFrom = image["init_from"].text.trim()
|
||||
imageDefs[imageId] = Library3D.ImageDef(imageId, imageName, initFrom)
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.parseAnimations(xml: Xml) {
|
||||
val sources = FastStringMap<SourceParam>()
|
||||
for (animationXml in xml["library_animations"]["animation"]) {
|
||||
val srcs = parseSources(animationXml, sources)
|
||||
val animationId = animationXml.str("id")
|
||||
val sourcesById = srcs.associateBy { it.id }
|
||||
val samplerXml = animationXml["sampler"].firstOrNull()
|
||||
val inputParams = FastStringMap<Source?>()
|
||||
if (samplerXml != null) {
|
||||
val samplerId = samplerXml.str("id")
|
||||
for (inputXml in samplerXml["input"]) {
|
||||
val inputSemantic = inputXml.str("semantic")
|
||||
val inputSourceId = inputXml.str("source").trim('#')
|
||||
inputParams[inputSemantic] = sourcesById[inputSourceId]
|
||||
}
|
||||
println("$samplerId -> $inputParams")
|
||||
println("$samplerId -> $inputParams")
|
||||
}
|
||||
val channelXml = animationXml["channel"].firstOrNull()
|
||||
if (channelXml != null) {
|
||||
val channelSource = channelXml.str("source").trim('#')
|
||||
val channelTargetInfo = channelXml.str("target").split('/', limit = 2)
|
||||
val channelTarget = channelTargetInfo.getOrElse(0) { "" }
|
||||
val channelProp = channelTargetInfo.getOrElse(1) { "" }
|
||||
|
||||
val times = inputParams.getFloats("INPUT", "TIME") ?: error("Can't find INPUT.TIME for animationId=$animationId")
|
||||
val interpolations = inputParams.getStrings("INTERPOLATION", "INTERPOLATION") ?: error("Can't find INTERPOLATION.INTERPOLATION for animationId=$animationId")
|
||||
//val transforms = inputParams.getMatrices("OUTPUT", "TRANSFORM")
|
||||
val outputSourceParam = inputParams["OUTPUT"]?.params?.values?.first()
|
||||
val matrices = (outputSourceParam as? MatrixSourceParam?)?.matrices
|
||||
val floats = (outputSourceParam as? FloatSourceParam?)?.floats?.toFloatArray()
|
||||
|
||||
//println("$channelSource -> $channelTarget")
|
||||
val frames = Animation3D.Frames(
|
||||
seconds = times,
|
||||
interpolations = interpolations,
|
||||
matrices = matrices,
|
||||
floats = floats
|
||||
)
|
||||
animationDefs[animationId] = Animation3D(
|
||||
animationId,
|
||||
channelTarget, channelProp,
|
||||
frames
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun FastStringMap<Source?>.getMatrices(a: String, b: String): Array<Matrix3D>? =
|
||||
(this[a]?.params?.get(b) as? MatrixSourceParam?)?.matrices
|
||||
|
||||
fun FastStringMap<Source?>.getStrings(a: String, b: String): Array<String>? =
|
||||
(this[a]?.params?.get(b) as? NamesSourceParam?)?.names?.toTypedArray()
|
||||
|
||||
fun FastStringMap<Source?>.getFloats(a: String, b: String): FloatArray? =
|
||||
(this[a]?.params?.get(b) as? FloatSourceParam?)?.floats?.toFloatArray()
|
||||
|
||||
fun Library3D.parseLights(xml: Xml) {
|
||||
for (light in xml["library_lights"]["light"]) {
|
||||
var lightDef: Library3D.LightDef? = null
|
||||
val id = light.str("id")
|
||||
val name = light.str("name")
|
||||
log { "Light id=$id, name=$name" }
|
||||
for (technique in light["technique_common"].allNodeChildren) {
|
||||
when (technique.nameLC) {
|
||||
"point" -> {
|
||||
val color = technique["color"].firstOrNull()?.text?.reader()?.readVector3D() ?: Vector3D(1, 1, 1)
|
||||
val constant_attenuation = technique["constant_attenuation"].firstOrNull()?.text?.toDoubleOrNull() ?: 1.0
|
||||
val linear_attenuation = technique["linear_attenuation"].firstOrNull()?.text?.toDoubleOrNull() ?: 0.0
|
||||
val quadratic_attenuation = technique["quadratic_attenuation"].firstOrNull()?.text?.toDoubleOrNull() ?: 0.00111109
|
||||
lightDef = Library3D.PointLightDef(RGBA.float(color.x, color.y, color.z, 1f), constant_attenuation, linear_attenuation, quadratic_attenuation)
|
||||
}
|
||||
else -> {
|
||||
println("WARNING: Unsupported light.technique_common.${technique.nameLC}")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lightDef != null) {
|
||||
lightDefs[id] = lightDef
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.parseCameras(xml: Xml) {
|
||||
for (camera in xml["library_cameras"]["camera"]) {
|
||||
val id = camera.getString("id") ?: "Unknown"
|
||||
val name = camera.getString("name") ?: "Unknown"
|
||||
var persp: Library3D.PerspectiveCameraDef? = null
|
||||
for (v in camera["optics"]["technique_common"].allChildren) {
|
||||
when (v.nameLC) {
|
||||
"_text_" -> Unit
|
||||
"perspective" -> {
|
||||
val xfov = v["xfov"].firstOrNull()?.text?.toDoubleOrNull() ?: 45.0
|
||||
val znear = v["znear"].firstOrNull()?.text?.toDoubleOrNull() ?: 0.01
|
||||
val zfar = v["zfar"].firstOrNull()?.text?.toDoubleOrNull() ?: 100.0
|
||||
persp = Library3D.PerspectiveCameraDef(xfov.degrees, znear, zfar)
|
||||
}
|
||||
else -> {
|
||||
log { "Unsupported camera technique ${v.nameLC}" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cameraDefs[id] = persp ?: Library3D.CameraDef()
|
||||
log { "Camera id=$id, name=$name, persp=$persp" }
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.parseGeometries(xml: Xml): List<Geometry> {
|
||||
val geometries = arrayListOf<Geometry>()
|
||||
|
||||
for (geometry in xml["library_geometries"]["geometry"]) {
|
||||
val id = geometry.getString("id") ?: "unknown"
|
||||
val name = geometry.getString("name") ?: "unknown"
|
||||
val geom = Geometry(id, name)
|
||||
geometries += geom
|
||||
log { "Geometry id=$id, name=$name" }
|
||||
for (mesh in geometry["mesh"]) {
|
||||
for (source in parseSources(mesh, this@ColladaParser.sourceArrayParams)) {
|
||||
sources[source.id] = source
|
||||
}
|
||||
|
||||
for (vertices in mesh["vertices"]) {
|
||||
val verticesId = vertices.getString("id") ?: vertices.getString("name") ?: "unknown"
|
||||
log { "vertices: $vertices" }
|
||||
for (input in vertices["input"]) {
|
||||
val semantic = input.str("semantic", "UNKNOWN")
|
||||
val source = input.getString("source")?.trim('#') ?: "unknown"
|
||||
val rsource = sources[source]
|
||||
if (rsource != null) {
|
||||
sources[verticesId] = rsource
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
log { "SOURCES.KEYS: " + sources.keys }
|
||||
log { "SOURCES: ${sources.keys.map { it to sources[it] }.toMap()}" }
|
||||
|
||||
val triangles = mesh["triangles"]?.firstOrNull() ?: mesh["polylist"]?.firstOrNull()
|
||||
|
||||
if (triangles != null) {
|
||||
if (triangles.nameLC != "triangles") {
|
||||
println("WARNING: polylist instead of triangles not fully implemented!")
|
||||
}
|
||||
val trianglesCount = triangles.getInt("count") ?: 0
|
||||
geom.materialId = triangles.getString("material")
|
||||
|
||||
log { "triangles: $triangles" }
|
||||
var stride = 1
|
||||
val inputs = arrayListOf<Input>()
|
||||
for (input in triangles["input"]) {
|
||||
val offset = input.getInt("offset") ?: 0
|
||||
stride = max(stride, offset + 1)
|
||||
|
||||
val semantic = input.getString("semantic") ?: "unknown"
|
||||
val source = input.getString("source")?.trim('#') ?: "unknown"
|
||||
val rsource = sources[source] ?: continue
|
||||
inputs += Input(semantic, offset, rsource, intArrayListOf())
|
||||
log { "INPUT: semantic=$semantic, source=$source, offset=$offset, source=$rsource" }
|
||||
}
|
||||
val pdata = (triangles["p"].firstOrNull()?.text ?: "").reader().readInts()
|
||||
//println("P: " + pdata.toList())
|
||||
for (input in inputs) {
|
||||
log { "INPUT: semantic=${input.semantic}, trianglesCount=$trianglesCount, stride=$stride, offset=${input.offset}" }
|
||||
for (n in 0 until trianglesCount * 3) {
|
||||
input.indices.add(pdata[input.offset + n * stride])
|
||||
}
|
||||
log { " - ${input.indices}" }
|
||||
}
|
||||
for (input in inputs) {
|
||||
geom.inputs[input.semantic] = input
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return geometries
|
||||
}
|
||||
|
||||
fun Library3D.parseVisualScenes(xml: Xml) {
|
||||
val instancesById = FastStringMap<Library3D.Instance3D>()
|
||||
for (vscene in xml["library_visual_scenes"]["visual_scene"]) {
|
||||
val scene = Library3D.Scene3D(this)
|
||||
scene.id = vscene.str("id")
|
||||
scene.name = vscene.str("name")
|
||||
for (node in vscene["node"]) {
|
||||
val instance = parseVisualSceneNode(node, instancesById)
|
||||
scene.children += instance
|
||||
}
|
||||
scenes[scene.id] = scene
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.parseVisualSceneNode(node: Xml, instancesById: FastStringMap<Library3D.Instance3D>): Library3D.Instance3D {
|
||||
val instance = Library3D.Instance3D(this)
|
||||
var location: Vector3D? = null
|
||||
var scale: Vector3D? = null
|
||||
var rotationX: Vector3D? = null
|
||||
var rotationY: Vector3D? = null
|
||||
var rotationZ: Vector3D? = null
|
||||
|
||||
instance.id = node.str("id")
|
||||
instance.sid = node.getString("sid")
|
||||
instance.name = node.str("name")
|
||||
instance.type = node.str("type")
|
||||
|
||||
instancesById[instance.id] = instance
|
||||
|
||||
for (child in node.allNodeChildren) {
|
||||
when (child.nameLC) {
|
||||
"matrix" -> {
|
||||
val sid = child.str("sid")
|
||||
when (sid) {
|
||||
"transform" -> instance.transform.copyFrom(child.text.reader().readMatrix3D())
|
||||
else -> println("WARNING: Unsupported node.matrix.sid=$sid")
|
||||
}
|
||||
}
|
||||
"translate" -> {
|
||||
val sid = child.str("sid")
|
||||
when (sid) {
|
||||
"location" -> location = child.text.reader().readVector3D()
|
||||
else -> println("WARNING: Unsupported node.translate.sid=$sid")
|
||||
}
|
||||
}
|
||||
"rotate" -> {
|
||||
val sid = child.str("sid")
|
||||
when (sid) {
|
||||
"rotationX" -> rotationX = child.text.reader().readVector3D()
|
||||
"rotationY" -> rotationY = child.text.reader().readVector3D()
|
||||
"rotationZ" -> rotationZ = child.text.reader().readVector3D()
|
||||
else -> println("WARNING: Unsupported node.rotate.sid=$sid")
|
||||
}
|
||||
}
|
||||
"scale" -> {
|
||||
val sid = child.str("sid")
|
||||
when (sid) {
|
||||
"scale" -> scale = child.text.reader().readVector3D()
|
||||
else -> println("WARNING: Unsupported node.scale.sid=$sid")
|
||||
}
|
||||
}
|
||||
"instance_camera" -> {
|
||||
val cameraId = child.str("url").trim('#')
|
||||
instance.def = cameraDefs[cameraId]
|
||||
}
|
||||
"instance_light" -> {
|
||||
val lightId = child.str("url").trim('#')
|
||||
instance.def = lightDefs[lightId]
|
||||
}
|
||||
"instance_geometry" -> {
|
||||
val geometryId = child.str("url").trim('#')
|
||||
val geometryName = child.str("name")
|
||||
instance.def = geometryDefs[geometryId]
|
||||
}
|
||||
"node" -> {
|
||||
val childInstance = parseVisualSceneNode(child, instancesById)
|
||||
instance.children.add(childInstance)
|
||||
}
|
||||
"instance_controller" -> {
|
||||
val skinId = child.str("url").trim('#')
|
||||
val skeletonId = child["skeleton"].firstText?.trim('#') ?: ""
|
||||
val skin = skinDefs[skinId]
|
||||
val skeleton = instancesById[skeletonId]
|
||||
instance.def = geometryDefs[skin?.skinSource ?: ""]
|
||||
instance.skin = skin
|
||||
instance.skeleton = skeleton
|
||||
instance.skeletonId = skeletonId
|
||||
}
|
||||
"extra" -> {
|
||||
}
|
||||
else -> {
|
||||
println("WARNING: Unsupported node.${child.nameLC}")
|
||||
}
|
||||
}
|
||||
}
|
||||
if (location != null || scale != null || rotationX != null || rotationY != null || rotationZ != null) {
|
||||
val trns = location ?: Vector3D(0, 0, 0, 1)
|
||||
val scl = scale ?: Vector3D(1, 1, 1)
|
||||
val rotX = rotationX ?: Vector3D(1, 0, 0, 0)
|
||||
val rotY = rotationY ?: Vector3D(0, 1, 0, 0)
|
||||
val rotZ = rotationZ ?: Vector3D(0, 0, 1, 0)
|
||||
rotationVectorToEulerRotation(rotX)
|
||||
|
||||
instance.transform.setTRS(
|
||||
trns,
|
||||
Quaternion().setTo(combine(rotationVectorToEulerRotation(rotX), rotationVectorToEulerRotation(rotY), rotationVectorToEulerRotation(rotZ))),
|
||||
scl
|
||||
)
|
||||
}
|
||||
return instance
|
||||
}
|
||||
|
||||
private fun combine(a: EulerRotation, b: EulerRotation, c: EulerRotation, out: EulerRotation = EulerRotation()): EulerRotation {
|
||||
return out.setTo(a.x + b.x + c.x, a.y + b.y + c.y, a.z + b.z + c.z)
|
||||
}
|
||||
|
||||
private fun rotationVectorToEulerRotation(vec: Vector3D, out: EulerRotation = EulerRotation()): EulerRotation {
|
||||
val degrees = vec.w.degrees
|
||||
return out.setTo(degrees * vec.x, degrees * vec.y, degrees * vec.z)
|
||||
}
|
||||
|
||||
val sourceArrayParams = FastStringMap<SourceParam>()
|
||||
val sources = FastStringMap<Source>()
|
||||
|
||||
fun parseSources(xml: Xml, arraySourceParams: FastStringMap<SourceParam>): List<Source> {
|
||||
val sources = arrayListOf<Source>()
|
||||
for (source in xml["source"]) {
|
||||
val sourceId = source.str("id")
|
||||
val sourceParams = FastStringMap<SourceParam>()
|
||||
for (item in source.allNodeChildren) {
|
||||
when (item.nameLC) {
|
||||
"float_array", "name_array" -> {
|
||||
val arrayId = item.str("id")
|
||||
val arrayCount = item.int("count")
|
||||
val arrayDataStr = item.text
|
||||
val arrayDataReader = arrayDataStr.reader()
|
||||
val arraySourceParam = when (item.nameLC) {
|
||||
"float_array" -> FloatSourceParam(arrayId, arrayDataReader.readFloats(FloatArrayList(arrayCount)))
|
||||
"name_array" -> NamesSourceParam(arrayId, arrayDataReader.readIds(ArrayList(arrayCount)))
|
||||
else -> TODO()
|
||||
}
|
||||
arraySourceParams[arraySourceParam.name] = arraySourceParam
|
||||
}
|
||||
"technique_common" -> {
|
||||
for (accessor in item["accessor"]) {
|
||||
val accessorArrayId = accessor.str("source").trim('#')
|
||||
val offset = accessor.int("offset")
|
||||
val count = accessor.int("count")
|
||||
val stride = accessor.int("stride")
|
||||
val data = arraySourceParams[accessorArrayId]
|
||||
if (data != null) {
|
||||
for ((index, param) in accessor["param"].withIndex()) {
|
||||
val paramName = param.str("name")
|
||||
val paramType = param.str("type")
|
||||
val paramOffset = param.int("offset", index)
|
||||
val totalOffset = offset + paramOffset
|
||||
|
||||
when (paramType) {
|
||||
"float" -> {
|
||||
val floats = (data as FloatSourceParam).floats.data
|
||||
val out = FloatArray(count)
|
||||
for (n in 0 until count) out[n] = floats[(n * stride) + totalOffset]
|
||||
sourceParams[paramName] = FloatSourceParam(paramName, FloatArrayList(*out))
|
||||
}
|
||||
"float4x4" -> {
|
||||
val floats = (data as FloatSourceParam).floats.data
|
||||
val out = Array(count) { Matrix3D() }
|
||||
for (n in 0 until count) {
|
||||
out[n].setFromColladaData(floats, (n * stride) + totalOffset)
|
||||
//mat.transpose()
|
||||
}
|
||||
sourceParams[paramName] = MatrixSourceParam(paramName, out)
|
||||
}
|
||||
"name" -> {
|
||||
val data = (data as NamesSourceParam).names
|
||||
val out = ArrayList<String>(count)
|
||||
for (n in 0 until count) out.add(data[(n * stride) + totalOffset])
|
||||
sourceParams[paramName] = NamesSourceParam(paramName, out)
|
||||
}
|
||||
else -> error("Unsupported paramType=$paramType")
|
||||
}
|
||||
|
||||
//params[paramName] =
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"extra" -> {
|
||||
}
|
||||
else -> {
|
||||
error("Unsupported tag <${item.nameLC}> in <source>")
|
||||
}
|
||||
}
|
||||
}
|
||||
sources += Source(sourceId, sourceParams)
|
||||
}
|
||||
return sources
|
||||
}
|
||||
|
||||
inline fun log(str: () -> String) {
|
||||
// DO NOTHING
|
||||
}
|
||||
|
||||
@Deprecated("", ReplaceWith("log { str }", "com.soywiz.korge.experimental.s3d.model.ColladaParser.log"))
|
||||
inline fun log(str: String) = log { str }
|
||||
}
|
||||
|
||||
private val Iterable<Xml>.allNodeChildren: Iterable<Xml> get() = this.flatMap(Xml::allNodeChildren)
|
||||
private val Iterable<Xml>.firstText: String? get() = this.firstOrNull()?.text
|
||||
private val Iterable<Xml>.text: String get() = this.joinToString("") { it.text }
|
||||
@@ -1,202 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model
|
||||
|
||||
import com.soywiz.kds.*
|
||||
import com.soywiz.korge.experimental.s3d.*
|
||||
import com.soywiz.korim.bitmap.*
|
||||
import com.soywiz.korim.color.*
|
||||
import com.soywiz.korim.format.*
|
||||
import com.soywiz.korio.file.std.*
|
||||
import com.soywiz.korma.geom.*
|
||||
|
||||
data class Library3D(
|
||||
val cameraDefs: FastStringMap<CameraDef> = FastStringMap(),
|
||||
val lightDefs: FastStringMap<LightDef> = FastStringMap(),
|
||||
val materialDefs: FastStringMap<MaterialDef> = FastStringMap(),
|
||||
val effectDefs: FastStringMap<EffectDef> = FastStringMap(),
|
||||
val imageDefs: FastStringMap<ImageDef> = FastStringMap(),
|
||||
val geometryDefs: FastStringMap<GeometryDef> = FastStringMap(),
|
||||
val skinDefs: FastStringMap<ColladaParser.Skin> = FastStringMap(),
|
||||
val animationDefs: FastStringMap<Animation3D> = FastStringMap()
|
||||
) {
|
||||
suspend fun loadTextures() {
|
||||
imageDefs.fastValueForEach { image ->
|
||||
image.texure = resourcesVfs[image.initFrom].readBitmap()
|
||||
}
|
||||
}
|
||||
|
||||
val boneDefs = FastStringMap<BoneDef>()
|
||||
|
||||
fun instantiateMaterials() {
|
||||
geometryDefs.fastValueForEach { geom ->
|
||||
for (bone in geom.skin?.bones ?: listOf()) {
|
||||
boneDefs[bone.name] = bone
|
||||
}
|
||||
geom.mesh.material = geom.material?.instantiate()
|
||||
}
|
||||
}
|
||||
|
||||
val library = this
|
||||
|
||||
val mainScene = Scene3D(this).apply {
|
||||
id = "MainScene"
|
||||
name = "MainScene"
|
||||
}
|
||||
var scenes = FastStringMap<Scene3D>()
|
||||
|
||||
open class Instance3D(val library: Library3D) {
|
||||
val transform = Matrix3D()
|
||||
var def: Def? = null
|
||||
val children = arrayListOf<Instance3D>()
|
||||
var id: String = ""
|
||||
var sid: String? = null
|
||||
var name: String = ""
|
||||
var type: String = ""
|
||||
var skin: ColladaParser.Skin? = null
|
||||
var skeleton: Instance3D? = null
|
||||
var skeletonId: String? = null
|
||||
}
|
||||
|
||||
open class Scene3D(library: Library3D) : Instance3D(library) {
|
||||
}
|
||||
|
||||
open class Def
|
||||
|
||||
open class Pong
|
||||
|
||||
open class ImageDef(val id: String, val name: String, val initFrom: String, var texure: Bitmap? = null) : Def() {
|
||||
|
||||
}
|
||||
|
||||
open class ObjectDef : Def()
|
||||
|
||||
open class MaterialDef(val id: String, val name: String, val effects: List<EffectDef>) : Def()
|
||||
|
||||
open class LightDef : ObjectDef()
|
||||
|
||||
open class CameraDef : ObjectDef()
|
||||
data class PerspectiveCameraDef(val xfov: Angle, val zmin: Double, val zmax: Double) : CameraDef()
|
||||
|
||||
interface LightKindDef {
|
||||
val sid: String
|
||||
}
|
||||
open class LightTexDef(override val sid: String, val texture: EffectParamSampler2D?, val lightKind: String) : LightKindDef
|
||||
open class LightColorDef(override val sid: String, val color: RGBA, val lightKind: String) : LightKindDef
|
||||
|
||||
data class EffectParamSurface(val surfaceType: String, val initFrom: Library3D.ImageDef?)
|
||||
data class EffectParamSampler2D(val surface: EffectParamSurface?)
|
||||
|
||||
open class EffectDef() : Def()
|
||||
|
||||
data class StandardEffectDef(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val emission: LightKindDef?,
|
||||
val ambient: LightKindDef?,
|
||||
val diffuse: LightKindDef?,
|
||||
val specular: LightKindDef?,
|
||||
val shiness: Float?,
|
||||
val index_of_refraction: Float?
|
||||
) : EffectDef()
|
||||
|
||||
data class GeometryDef(
|
||||
val mesh: Mesh3D,
|
||||
val skin: SkinDef? = null,
|
||||
val material: MaterialDef? = null
|
||||
) : ObjectDef()
|
||||
|
||||
data class BoneDef constructor(val index: Int, val name: String, val invBindMatrix: Matrix3D) : Def() {
|
||||
lateinit var skin: SkinDef
|
||||
fun toBone() = Bone3D(index, name, invBindMatrix.clone())
|
||||
}
|
||||
|
||||
data class SkinDef(
|
||||
val bindShapeMatrix: Matrix3D,
|
||||
val bones: List<BoneDef>
|
||||
) : Def() {
|
||||
fun toSkin() = Skin3D(bindShapeMatrix, bones.map { it.toBone() })
|
||||
}
|
||||
|
||||
|
||||
class PointLightDef(
|
||||
val color: RGBA,
|
||||
val constantAttenuation: Double,
|
||||
val linearAttenuation: Double,
|
||||
val quadraticAttenuation: Double
|
||||
) : LightDef()
|
||||
}
|
||||
|
||||
class LibraryInstantiateContext {
|
||||
val viewsById = FastStringMap<View3D>()
|
||||
}
|
||||
|
||||
fun Library3D.Instance3D.instantiate(jointParent: Joint3D? = null, ctx: LibraryInstantiateContext = LibraryInstantiateContext()): View3D {
|
||||
val def = this.def
|
||||
val view: View3D = when (def) {
|
||||
null -> {
|
||||
if (type.equals("JOINT", ignoreCase = true)) {
|
||||
//val boneDef = library.boneDefs[sid ?: ""] ?: error("Can't find bone '$sid'")
|
||||
val boneDef = library.boneDefs[sid ?: ""] ?: Library3D.BoneDef(-1, "UNKNOWN_$sid", Matrix3D()).also { it.skin = Library3D.SkinDef(Matrix3D(), listOf()) }
|
||||
Joint3D(boneDef.skin.toSkin(), boneDef.toBone(), jointParent, this.transform).also {
|
||||
//Joint3D(jointParent, this.transform).also {
|
||||
jointParent?.childJoints?.add(it)
|
||||
jointParent?.children?.add(it)
|
||||
}
|
||||
} else {
|
||||
Container3D()
|
||||
}
|
||||
}
|
||||
is Library3D.GeometryDef -> {
|
||||
val skeletonInstance = if (skeletonId != null) ctx.viewsById[skeletonId!!] as? Joint3D? else null
|
||||
ViewWithMesh3D(def.mesh, skeletonInstance?.let { Skeleton3D(def.mesh.skin!!, it) })
|
||||
}
|
||||
is Library3D.PerspectiveCameraDef -> {
|
||||
Camera3D.Perspective(def.xfov, def.zmin, def.zmax)
|
||||
}
|
||||
is Library3D.PointLightDef -> {
|
||||
Light3D(def.color, def.constantAttenuation, def.linearAttenuation, def.quadraticAttenuation)
|
||||
}
|
||||
else -> TODO("def=$def")
|
||||
}
|
||||
view.id = this.id
|
||||
ctx.viewsById[this.id] = view
|
||||
view.name = this.name
|
||||
//view.localTransform.setMatrix(this.transform.clone().transpose())
|
||||
view.transform.setMatrix(this.transform)
|
||||
if (def is Library3D.PointLightDef) {
|
||||
println(view.transform.matrix)
|
||||
println(view.transform.translation)
|
||||
println(view.transform.rotation)
|
||||
println(view.transform.scale)
|
||||
println("def: $def")
|
||||
}
|
||||
if (view is Joint3D) {
|
||||
for (child in children) {
|
||||
child.instantiate(view, ctx)
|
||||
}
|
||||
} else if (view is Container3D) {
|
||||
for (child in children) {
|
||||
view.addChild(child.instantiate(null, ctx))
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
fun Library3D.LightKindDef.instantiate(): MaterialLight {
|
||||
return when (this) {
|
||||
is Library3D.LightTexDef -> MaterialLightTexture(this.texture?.surface?.initFrom?.texure)
|
||||
is Library3D.LightColorDef -> MaterialLightColor(this.color)
|
||||
else -> error("Unsupported $this")
|
||||
}
|
||||
}
|
||||
|
||||
fun Library3D.MaterialDef.instantiate(): Material3D {
|
||||
val effect = this.effects.firstOrNull() as? Library3D.StandardEffectDef?
|
||||
return Material3D(
|
||||
emission = effect?.emission?.instantiate() ?: MaterialLightColor(Colors.BLACK),
|
||||
ambient = effect?.ambient?.instantiate() ?: MaterialLightColor(Colors.BLACK),
|
||||
diffuse = effect?.diffuse?.instantiate() ?: MaterialLightColor(Colors.BLACK),
|
||||
specular = effect?.specular?.instantiate() ?: MaterialLightColor(Colors.BLACK),
|
||||
shiness = effect?.shiness ?: 0.5f,
|
||||
indexOfRefraction = effect?.index_of_refraction ?: 1f
|
||||
)
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model
|
||||
|
||||
import com.soywiz.kds.*
|
||||
import com.soywiz.korge.experimental.s3d.model.internal.*
|
||||
import com.soywiz.korio.file.*
|
||||
import com.soywiz.korio.util.*
|
||||
|
||||
// https://en.wikipedia.org/wiki/Wavefront_.obj_file
|
||||
|
||||
suspend fun VfsFile.readOBJScene(): WavefrontScene = OBJ.read(this.readString())
|
||||
|
||||
class OBJ {
|
||||
companion object {
|
||||
fun read(str: String): WavefrontScene = OBJ().read(str)
|
||||
}
|
||||
|
||||
private val vData = doubleArrayListOf()
|
||||
private val vtData = doubleArrayListOf()
|
||||
private val vnData = doubleArrayListOf()
|
||||
|
||||
fun read(str: String) = str.reader().readObj()
|
||||
|
||||
fun StrReader.readObj(): WavefrontScene {
|
||||
vData.clear()
|
||||
vtData.clear()
|
||||
vnData.clear()
|
||||
|
||||
val vertexData = FloatArrayList()
|
||||
val indices = IntArrayList()
|
||||
|
||||
var vertexCount = 0
|
||||
var currentObjectName: String? = null
|
||||
var materialName: String? = null
|
||||
|
||||
val objects = linkedHashMapOf<String, WavefrontMesh>()
|
||||
|
||||
fun flushObj() {
|
||||
if (currentObjectName != null) {
|
||||
objects[currentObjectName!!] = WavefrontMesh(vertexData.toFloatArray(), indices.toIntArray())
|
||||
}
|
||||
|
||||
vertexData.clear()
|
||||
indices.clear()
|
||||
}
|
||||
|
||||
while (!eof) {
|
||||
skipSpaces()
|
||||
when {
|
||||
tryExpect("v ") -> {
|
||||
val x = skipSpaces().tryReadNumber()
|
||||
val y = skipSpaces().tryReadNumber()
|
||||
val z = skipSpaces().tryReadNumber()
|
||||
val w = skipSpaces().tryReadNumber(1.0)
|
||||
vData.apply { add(x); add(y); add(z); add(w) }
|
||||
skipUntilIncluded('\n')
|
||||
//println("v: $x, $y, $z, $w")
|
||||
}
|
||||
tryExpect("vt ") -> {
|
||||
val u = skipSpaces().tryReadNumber()
|
||||
val v = skipSpaces().tryReadNumber()
|
||||
val w = skipSpaces().tryReadNumber()
|
||||
vtData.apply { add(u); add(v); add(w) }
|
||||
skipUntilIncluded('\n')
|
||||
//println("vt: $u, $v, $w")
|
||||
}
|
||||
tryExpect("vn ") -> {
|
||||
val x = skipSpaces().tryReadNumber()
|
||||
val y = skipSpaces().tryReadNumber()
|
||||
val z = skipSpaces().tryReadNumber()
|
||||
vnData.apply { add(x); add(y); add(z) }
|
||||
skipUntilIncluded('\n')
|
||||
//println("vn: $x, $y, $z")
|
||||
}
|
||||
tryExpect("f ") -> {
|
||||
//println("faces:")
|
||||
while (peekChar() != '\n') {
|
||||
skipSpaces()
|
||||
val vi = tryReadInt(-1)
|
||||
val vti = if (tryExpect("/")) tryReadInt(-1) else -1
|
||||
val vni = if (tryExpect("/")) tryReadInt(-1) else -1
|
||||
//for (n in 0 until 4)
|
||||
//println(" - $vi, $vti, $vni")
|
||||
}
|
||||
expect('\n')
|
||||
}
|
||||
tryExpect("s ") -> {
|
||||
val shading = readUntilIncluded('\n')?.trim() ?: ""
|
||||
//println("s: $shading")
|
||||
}
|
||||
tryExpect("o ") -> {
|
||||
val name = readUntilIncluded('\n')?.trim() ?: ""
|
||||
flushObj()
|
||||
currentObjectName = name
|
||||
//println("o: $name")
|
||||
}
|
||||
tryExpect("#") -> {
|
||||
skipUntilIncluded('\n')
|
||||
}
|
||||
tryExpect("mtllib ") -> {
|
||||
val file = readUntilIncluded('\n')?.trim() ?: ""
|
||||
//println("mtllib: $file")
|
||||
}
|
||||
tryExpect("usemtl ") -> {
|
||||
val material = readUntilIncluded('\n')?.trim() ?: ""
|
||||
//println("usemtl: $material")
|
||||
materialName = material
|
||||
}
|
||||
else -> {
|
||||
val line = readUntilIncluded('\n')
|
||||
error("Invalid or unsupported command at line '$line'")
|
||||
}
|
||||
}
|
||||
}
|
||||
flushObj()
|
||||
|
||||
return WavefrontScene(objects)
|
||||
}
|
||||
}
|
||||
|
||||
class WavefrontScene(
|
||||
val objects: Map<String, WavefrontMesh>
|
||||
)
|
||||
|
||||
class WavefrontMesh(
|
||||
// (x, y, z, w), (u, v, w), (nx, ny, nz)
|
||||
val vertexData: FloatArray,
|
||||
val indices: IntArray
|
||||
) {
|
||||
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model.internal
|
||||
|
||||
import com.soywiz.kds.*
|
||||
|
||||
fun <T> Map<String, T>.toFast() = FastStringMap<T>().apply {
|
||||
for (k in this@toFast.keys) {
|
||||
this[k] = this@toFast[k]!!
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model.internal
|
||||
|
||||
import com.soywiz.korma.geom.*
|
||||
import com.soywiz.korma.geom.interpolate
|
||||
import com.soywiz.korma.interpolation.*
|
||||
|
||||
fun Matrix3D.setToInterpolated(a: Matrix3D, b: Matrix3D, ratio: Double) = setColumns(
|
||||
ratio.interpolate(a.v00, b.v00), ratio.interpolate(a.v10, b.v10), ratio.interpolate(a.v20, b.v20), ratio.interpolate(a.v30, b.v30),
|
||||
ratio.interpolate(a.v01, b.v01), ratio.interpolate(a.v11, b.v11), ratio.interpolate(a.v21, b.v21), ratio.interpolate(a.v31, b.v31),
|
||||
ratio.interpolate(a.v02, b.v02), ratio.interpolate(a.v12, b.v12), ratio.interpolate(a.v22, b.v22), ratio.interpolate(a.v32, b.v32),
|
||||
ratio.interpolate(a.v03, b.v03), ratio.interpolate(a.v13, b.v13), ratio.interpolate(a.v23, b.v23), ratio.interpolate(a.v33, b.v33)
|
||||
)
|
||||
@@ -1,10 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model.internal
|
||||
|
||||
import com.soywiz.kmem.*
|
||||
|
||||
fun Int.nextMultipleOf(v: Int) = this.nextAlignedTo(v)
|
||||
fun Long.nextMultipleOf(v: Long) = this.nextAlignedTo(v)
|
||||
|
||||
fun Int.prevMultipleOf(v: Int) = this.prevAlignedTo(v)
|
||||
fun Long.prevMultipleOf(v: Long) = this.prevAlignedTo(v)
|
||||
|
||||
@@ -1,71 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model.internal
|
||||
|
||||
import com.soywiz.korma.geom.*
|
||||
import com.soywiz.korma.geom.interpolate
|
||||
import com.soywiz.korma.interpolation.*
|
||||
import kotlin.math.*
|
||||
|
||||
fun Vector3D.Companion.lengthSq(x: Double, y: Double, z: Double, w: Double) = x * x + y * y + z * z + w * w
|
||||
fun Vector3D.Companion.length(x: Double, y: Double, z: Double, w: Double) = sqrt(lengthSq(x, y, z, w))
|
||||
|
||||
fun Quaternion.normalize(v: Quaternion = this): Quaternion {
|
||||
val length = 1.0 / Vector3D.length(v.x, v.y, v.z, v.w)
|
||||
return this.setTo(v.x / length, v.y / length, v.z / length, v.w / length)
|
||||
}
|
||||
|
||||
// @TODO: Make Quaternions interpolable
|
||||
|
||||
object Quaternion_Companion
|
||||
|
||||
fun dotProduct(l: Quaternion, r: Quaternion): Double = l.x * r.x + l.y * r.y + l.z * r.z + l.w * r.w
|
||||
|
||||
operator fun Quaternion.unaryMinus(): Quaternion = Quaternion(-x, -y, -z, -w)
|
||||
operator fun Quaternion.plus(other: Quaternion): Quaternion = Quaternion(x + other.x, y + other.y, z + other.z, w + other.w)
|
||||
operator fun Quaternion.minus(other: Quaternion): Quaternion = Quaternion(x - other.x, y - other.y, z - other.z, w - other.w)
|
||||
operator fun Quaternion.times(scale: Double): Quaternion = Quaternion(x * scale, y * scale, z * scale, w * scale)
|
||||
operator fun Double.times(scale: Quaternion): Quaternion = scale.times(this)
|
||||
|
||||
fun Quaternion.negate() = this.setTo(-x, -y, -z, -w)
|
||||
|
||||
inline fun Quaternion.setToFunc(l: Quaternion, r: Quaternion, func: (l: Double, r: Double) -> Double) = setTo(
|
||||
func(l.x, r.x),
|
||||
func(l.y, r.y),
|
||||
func(l.z, r.z),
|
||||
func(l.w, r.w)
|
||||
)
|
||||
|
||||
inline fun Vector3D.setToFunc(l: Vector3D, r: Vector3D, func: (l: Float, r: Float) -> Float) = setTo(
|
||||
func(l.x, r.x),
|
||||
func(l.y, r.y),
|
||||
func(l.z, r.z),
|
||||
func(l.w, r.w)
|
||||
)
|
||||
|
||||
// @TODO: Allocations and temps!
|
||||
private val tleft: Quaternion = Quaternion()
|
||||
private val tright: Quaternion = Quaternion()
|
||||
fun Quaternion.setToSlerp(left: Quaternion, right: Quaternion, t: Double): Quaternion {
|
||||
val tleft = tleft.copyFrom(left).normalize()
|
||||
val tright = tright.copyFrom(right).normalize()
|
||||
|
||||
var dot = dotProduct(tleft, right)
|
||||
|
||||
if (dot < 0.0f) {
|
||||
tright.negate()
|
||||
dot = -dot
|
||||
}
|
||||
|
||||
if (dot > 0.99995f) return setToFunc(tleft, tright) { l, r -> l + t * (r - l) }
|
||||
|
||||
val angle0 = acos(dot)
|
||||
val angle1 = angle0 * t
|
||||
|
||||
val s1 = sin(angle1) / sin(angle0)
|
||||
val s0 = cos(angle1) - dot * s1
|
||||
|
||||
return setToFunc(tleft, tright) { l, r -> (s0 * l) + (s1 * r) }
|
||||
}
|
||||
|
||||
fun Quaternion.setToInterpolated(left: Quaternion, right: Quaternion, t: Double): Quaternion = setToSlerp(left, right, t)
|
||||
|
||||
fun Vector3D.setToInterpolated(left: Vector3D, right: Vector3D, t: Double): Vector3D = setToFunc(left, right) { l, r -> t.interpolate(l, r) }
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model.internal
|
||||
|
||||
import com.soywiz.korim.color.*
|
||||
import com.soywiz.korma.geom.*
|
||||
|
||||
internal fun Vector3D.setToColorPremultiplied(col: RGBA): Vector3D = this.apply { col.toPremultipliedVector3D(this) }
|
||||
internal fun Vector3D.setToColor(col: RGBA): Vector3D = this.apply { col.toPremultipliedVector3D(this) }
|
||||
|
||||
internal fun RGBA.toPremultipliedVector3D(out: Vector3D = Vector3D()): Vector3D = out.setTo(
|
||||
rf * af, gf * af, bf * af, 1f
|
||||
)
|
||||
|
||||
internal fun RGBA.toVector3D(out: Vector3D = Vector3D()): Vector3D = out.setTo(
|
||||
rf, gf, bf, af
|
||||
)
|
||||
|
||||
fun Vector3D.scale(scale: Float) = this.setTo(this.x * scale, this.y * scale, this.z * scale, this.w * scale)
|
||||
inline fun Vector3D.scale(scale: Number) = scale(scale.toFloat())
|
||||
@@ -1,126 +0,0 @@
|
||||
package com.soywiz.korge.experimental.s3d.model.internal
|
||||
|
||||
import com.soywiz.korio.util.*
|
||||
|
||||
internal fun StrReader.readFloats(list: com.soywiz.kds.FloatArrayList = com.soywiz.kds.FloatArrayList(7)): com.soywiz.kds.FloatArrayList {
|
||||
while (!eof) {
|
||||
val pos0 = pos
|
||||
val float = skipSpaces().tryReadNumber().toFloat()
|
||||
skipSpaces()
|
||||
val pos1 = pos
|
||||
if (pos1 == pos0) error("Invalid number at $pos0 in '$str'")
|
||||
list.add(float)
|
||||
//println("float: $float, ${reader.pos}/${reader.length}")
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
internal fun StrReader.readIds(list: ArrayList<String> = ArrayList(7)): ArrayList<String> {
|
||||
while (!eof) {
|
||||
val pos0 = pos
|
||||
val id = skipSpaces().tryReadId() ?: ""
|
||||
skipSpaces()
|
||||
val pos1 = pos
|
||||
if (pos1 == pos0) error("Invalid identifier at $pos0 in '$str'")
|
||||
list.add(id)
|
||||
//println("float: $float, ${reader.pos}/${reader.length}")
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
internal fun StrReader.readInts(list: com.soywiz.kds.IntArrayList = com.soywiz.kds.IntArrayList(7)): com.soywiz.kds.IntArrayList {
|
||||
while (!eof) {
|
||||
val pos0 = pos
|
||||
val v = skipSpaces().tryReadInt(0)
|
||||
skipSpaces()
|
||||
val pos1 = pos
|
||||
if (pos1 == pos0) error("Invalid int at $pos0 in '$str'")
|
||||
list.add(v)
|
||||
//println("float: $float, ${reader.pos}/${reader.length}")
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
internal fun StrReader.readVector3D(): com.soywiz.korma.geom.Vector3D {
|
||||
val f = readFloats(com.soywiz.kds.FloatArrayList())
|
||||
return when {
|
||||
f.size == 4 -> com.soywiz.korma.geom.Vector3D(f[0], f[1], f[2], f[3])
|
||||
else -> com.soywiz.korma.geom.Vector3D(f[0], f[1], f[2])
|
||||
}
|
||||
}
|
||||
|
||||
//fun com.soywiz.korma.geom.Matrix3D.setFromColladaData(f: FloatArray, o: Int) = setColumns(
|
||||
fun com.soywiz.korma.geom.Matrix3D.setFromColladaData(f: FloatArray, o: Int) = setRows(
|
||||
f[o + 0], f[o + 1], f[o + 2], f[o + 3],
|
||||
f[o + 4], f[o + 5], f[o + 6], f[o + 7],
|
||||
f[o + 8], f[o + 9], f[o + 10], f[o + 11],
|
||||
f[o + 12], f[o + 13], f[o + 14], f[o + 15]
|
||||
)
|
||||
|
||||
internal fun StrReader.readMatrix3D(): com.soywiz.korma.geom.Matrix3D {
|
||||
val f = readFloats(com.soywiz.kds.FloatArrayList())
|
||||
if (f.size == 16) {
|
||||
//return com.soywiz.korma.geom.Matrix3D().setRows(
|
||||
return com.soywiz.korma.geom.Matrix3D().setFromColladaData(f.data, 0)
|
||||
} else {
|
||||
error("Invalid matrix size ${f.size} : str='$str'")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun StrReader.tryReadInt(default: Int): Int {
|
||||
var digitCount = 0
|
||||
var integral = 0
|
||||
var mult = 1
|
||||
loop@ while (!eof) {
|
||||
when (val c = peek()) {
|
||||
'-' -> {
|
||||
skip(1)
|
||||
mult *= -1
|
||||
}
|
||||
in '0'..'9' -> {
|
||||
val digit = c - '0'
|
||||
skip(1)
|
||||
digitCount++
|
||||
integral *= 10
|
||||
integral += digit
|
||||
}
|
||||
else -> {
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
}
|
||||
return if (digitCount == 0) default else integral
|
||||
}
|
||||
|
||||
internal fun StrReader.tryReadNumber(default: Double = Double.NaN): Double {
|
||||
val start = pos
|
||||
skipWhile {
|
||||
@Suppress("ConvertTwoComparisonsToRangeCheck")
|
||||
(it >= '0' && it <= '9') || (it == '+') || (it == '-') || (it == 'e') || (it == 'E') || (it == '.')
|
||||
}
|
||||
val end = pos
|
||||
if (end == start) return default
|
||||
return NumberParser.parseDouble(this.str, start, end)
|
||||
}
|
||||
|
||||
internal fun StrReader.tryReadId(): String? {
|
||||
val start = pos
|
||||
skipWhile {
|
||||
@Suppress("ConvertTwoComparisonsToRangeCheck")
|
||||
(it >= '0' && it <= '9') || (it >= 'a' && it <= 'z') || (it >= 'A' && it <= 'Z') || (it == '_') || (it == '.')
|
||||
}
|
||||
val end = pos
|
||||
if (end == start) return null
|
||||
return this.str.substring(start, end)
|
||||
}
|
||||
|
||||
// Allocation-free matching
|
||||
internal fun StrReader.tryExpect(str: String): Boolean {
|
||||
for (n in 0 until str.length) {
|
||||
if (this.peekOffset(n) != str[n]) return false
|
||||
}
|
||||
skip(str.length)
|
||||
return true
|
||||
}
|
||||
|
||||
internal fun StrReader.peekOffset(offset: Int = 0): Char = this.str.getOrElse(pos + offset) { '\u0000' }
|
||||
@@ -1,234 +0,0 @@
|
||||
import com.soywiz.kds.*
|
||||
import com.soywiz.klock.*
|
||||
import com.soywiz.korge.*
|
||||
import com.soywiz.korge.experimental.s3d.*
|
||||
import com.soywiz.korge.experimental.s3d.model.*
|
||||
import com.soywiz.korge.tween.*
|
||||
import com.soywiz.korge.view.*
|
||||
import com.soywiz.korim.color.*
|
||||
import com.soywiz.korim.format.*
|
||||
import com.soywiz.korio.*
|
||||
import com.soywiz.korio.async.*
|
||||
import com.soywiz.korio.file.std.*
|
||||
import com.soywiz.korma.geom.*
|
||||
import com.soywiz.korma.interpolation.*
|
||||
import kotlin.jvm.*
|
||||
|
||||
//suspend fun main(args: Array<String>) = Demo3.main(args)
|
||||
suspend fun main(args: Array<String>) = Demo3.main()
|
||||
//suspend fun main(args: Array<String>) = Demo1.main()
|
||||
|
||||
object Demo1 {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) = Korio { main() }
|
||||
|
||||
suspend fun main() = Korge(title = "KorGE 3D") {
|
||||
image(resourcesVfs["korge.png"].readNativeImage()).alpha(0.5)
|
||||
|
||||
scene3D {
|
||||
//camera.set(fov = 60.degrees, near = 0.3, far = 1000.0)
|
||||
|
||||
val cube1 = box()
|
||||
val cube2 = box().position(0, 2, 0).scale(1, 2, 1).rotation(0.degrees, 0.degrees, 45.degrees)
|
||||
val cube3 = box().position(-5, 0, 0)
|
||||
val cube4 = box().position(+5, 0, 0)
|
||||
val cube5 = box().position(0, -5, 0)
|
||||
val cube6 = box().position(0, +5, 0)
|
||||
val cube7 = box().position(0, 0, -5)
|
||||
val cube8 = box().position(0, 0, +5)
|
||||
|
||||
var tick = 0
|
||||
addUpdatable {
|
||||
val angle = (tick / 4.0).degrees
|
||||
camera.positionLookingAt(
|
||||
cos(angle * 2) * 4, cos(angle * 3) * 4, -sin(angle) * 4, // Orbiting camera
|
||||
0, 1, 0
|
||||
)
|
||||
tick++
|
||||
}
|
||||
|
||||
launchImmediately {
|
||||
while (true) {
|
||||
tween(time = 16.seconds) {
|
||||
cube1.modelMat.identity().rotate((it * 360).degrees, 0.degrees, 0.degrees)
|
||||
cube2.modelMat.identity().rotate(0.degrees, (it * 360).degrees, 0.degrees)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
image(resourcesVfs["korge.png"].readNativeImage()).position(views.virtualWidth, 0).anchor(1, 0).alpha(0.5)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
object Demo2 {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) = Korio { main() }
|
||||
|
||||
suspend fun main() = Korge(title = "KorGE 3D", bgcolor = Colors.DARKGREY) {
|
||||
//delay(10.seconds)
|
||||
//println("delay")
|
||||
scene3D {
|
||||
val light1 = light().position(0, 10, +10).setTo(Colors.RED)
|
||||
val light2 = light().position(10, 0, +10).setTo(Colors.BLUE)
|
||||
|
||||
launchImmediately {
|
||||
while (true) {
|
||||
tween(light1::y[-20], light2::x[-20], time = 1.seconds, easing = Easing.SMOOTH)
|
||||
tween(light1::y[+20], light2::x[+20], time = 1.seconds, easing = Easing.SMOOTH)
|
||||
}
|
||||
}
|
||||
|
||||
//val library = resourcesVfs["scene.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["cilinder.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["monkey.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["monkey-smooth.dae"].readColladaLibrary()
|
||||
val library = resourcesVfs["monkey-smooth.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["plane.dae"].readColladaLibrary()
|
||||
//val cubeGeom = library.geometryDefs["Cube-mesh"]!! as Library3D.RawGeometryDef
|
||||
val cubeGeom = library.geometryDefs.values.first() as Library3D.GeometryDef
|
||||
val cube = mesh(cubeGeom.mesh).rotation(-90.degrees, 0.degrees, 0.degrees)
|
||||
println(library)
|
||||
/*
|
||||
launchImmediately {
|
||||
orbit(cube, 4.0, time = 10.seconds)
|
||||
}
|
||||
*/
|
||||
|
||||
var tick = 0
|
||||
addUpdatable {
|
||||
val angle = (tick / 1.0).degrees
|
||||
camera.positionLookingAt(
|
||||
cos(angle * 1) * 4, 0.0, -sin(angle * 1) * 4, // Orbiting camera
|
||||
0, 0, 0
|
||||
)
|
||||
tick++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
suspend fun Stage3D.orbit(v: View3D, distance: Double, time: TimeSpan) {
|
||||
view.tween(time = time) { ratio ->
|
||||
val angle = 360.degrees * ratio
|
||||
camera.positionLookingAt(
|
||||
cos(angle) * distance, 0.0, sin(angle) * distance, // Orbiting camera
|
||||
v.transform.translation.x, v.transform.translation.y, v.transform.translation.z
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object Demo3 {
|
||||
@JvmStatic
|
||||
fun main(args: Array<String>) = Korio { main() }
|
||||
|
||||
suspend fun main() = Korge(title = "KorGE 3D", bgcolor = Colors.DARKGREY) {
|
||||
//delay(10.seconds)
|
||||
//println("delay")
|
||||
scene3D {
|
||||
//val light1 = light().position(0, 1, 1).colors(Colors.WHITE, Colors.WHITE, Colors.WHITE).power(1.0)
|
||||
/*
|
||||
val light1 = light().position(0, 10, +10).colors(Colors.RED, Colors.RED, Colors.RED)
|
||||
val light2 = light().position(10, 0, +10).colors(Colors.BLUE, Colors.BLUE, Colors.BLUE)
|
||||
|
||||
for (light in findByType<Light3D>()) println("LIGHT: $light")
|
||||
|
||||
launchImmediately {
|
||||
while (true) {
|
||||
tween(light1::localY[-20], light2::localX[-20], time = 1.seconds, easing = Easing.SMOOTH)
|
||||
tween(light1::localY[+20], light2::localX[+20], time = 1.seconds, easing = Easing.SMOOTH)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//val library = resourcesVfs["scene.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["cilinder.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["box_textured.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["monkey.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["monkey-smooth.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["monkey_smooth_two_camera.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["shape2.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["skinning.dae"].readColladaLibrary()
|
||||
val library = resourcesVfs["model.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["Fallera.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["model.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["skinning_sample.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["box_textured.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["shape1.dae"].readColladaLibrary()
|
||||
//val library = resourcesVfs["plane.dae"].readColladaLibrary()
|
||||
//val cubeGeom = library.geometryDefs["Cube-mesh"]!! as Library3D.RawGeometryDef
|
||||
//val cubeGeom = library.geometryDefs.values.first() as Library3D.GeometryDef
|
||||
//val tex = resourcesVfs["korge.png"].readBitmapOptimized()
|
||||
//val tex = resourcesVfs["Fallera.jpg"].readBitmapOptimized()
|
||||
//val tex = resourcesVfs["diffuse.png"].readBitmapOptimized()
|
||||
//val cube = mesh(cubeGeom.mesh)
|
||||
//val cube = mesh(cubeGeom.mesh).scale(0.02, 0.02, 0.02).rotation(z = 90.degrees) // @TODO: Kotlin.JS BUG
|
||||
//val cube = mesh(cubeGeom.mesh).scale(0.02, 0.02, 0.02).rotation(0.degrees, 0.degrees, 90.degrees)
|
||||
//val cube = mesh(cubeGeom.mesh).rotation(y = 90.degrees) // @TODO: Kotlin.JS BUG
|
||||
//val cube = mesh(cubeGeom.mesh)
|
||||
//cube.mesh.texture = tex
|
||||
|
||||
val mainSceneView = library.mainScene.instantiate()
|
||||
val cameras = mainSceneView.findByType<Camera3D>()
|
||||
//mainSceneView.rotation(90.degrees, 0.degrees, 0.degrees)
|
||||
|
||||
|
||||
|
||||
val animator = Animator3D(library.animationDefs.values, mainSceneView)
|
||||
addUpdatable {
|
||||
animator.update(it)
|
||||
}
|
||||
|
||||
val camera1 = cameras.firstOrNull() ?: camera
|
||||
val camera2 = cameras.lastOrNull() ?: camera
|
||||
|
||||
//ambientColor = Colors.RED
|
||||
//ambientColor = Colors.WHITE
|
||||
//ambientColor = Colors.WHITE
|
||||
camera = camera1.clone()
|
||||
this += mainSceneView
|
||||
//this += box()
|
||||
|
||||
//val cube = mesh(cubeGeom.mesh).scale(0.02, 0.02, 0.02)
|
||||
//val cube = mesh(cubeGeom.mesh).rotation(-90.degrees, 0.degrees, 0.degrees)
|
||||
println(library)
|
||||
/*
|
||||
launchImmediately {
|
||||
orbit(cube, 4.0, time = 10.seconds)
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
if (camera1 == camera2) {
|
||||
var tick = 0
|
||||
val camera1Len = camera.transform.translation.length3
|
||||
addUpdatable {
|
||||
val angle = (tick / 1.0).degrees
|
||||
camera.positionLookingAt(
|
||||
cos(angle * 1) * camera1Len, 2.0, -sin(angle * 1) * camera1Len, // Orbiting camera
|
||||
//1, 0, 4,
|
||||
0, 0, 0
|
||||
)
|
||||
tick++
|
||||
}
|
||||
} else {
|
||||
launchImmediately {
|
||||
while (true) {
|
||||
tween(time = 2.seconds, easing = Easing.SMOOTH) {
|
||||
camera.transform.setToInterpolated(camera1.transform, camera2.transform, it)
|
||||
}
|
||||
tween(time = 2.seconds, easing = Easing.SMOOTH) {
|
||||
camera.transform.setToInterpolated(camera2.transform, camera1.transform, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
//println(camera1.localTransform)
|
||||
//println(camera2.localTransform)
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,116 +0,0 @@
|
||||
import com.soywiz.korge.experimental.s3d.*
|
||||
import com.soywiz.korma.geom.*
|
||||
import kotlin.math.*
|
||||
import kotlin.test.*
|
||||
|
||||
class Matrix3DTest {
|
||||
val transMat = Matrix3D.fromRows(
|
||||
1f, 0f, 0f, 1f,
|
||||
0f, 1f, 0f, 2f,
|
||||
0f, 0f, 1f, 3f,
|
||||
0f, 0f, 0f, 1f
|
||||
)
|
||||
|
||||
@Test
|
||||
fun testSetTRS() {
|
||||
assertEquals(
|
||||
Matrix3D(),
|
||||
Matrix3D().setTRS(
|
||||
Position3D(0, 0, 0),
|
||||
Quaternion(),
|
||||
Scale3D(1, 1, 1)
|
||||
)
|
||||
)
|
||||
assertEquals(
|
||||
transMat,
|
||||
Matrix3D().setTRS(
|
||||
Position3D(1, 2, 3),
|
||||
Quaternion(),
|
||||
Scale3D(1, 1, 1)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGetTRS() {
|
||||
val pos = Position3D()
|
||||
val quat = Quaternion()
|
||||
val scale = Scale3D()
|
||||
transMat.getTRS(pos, quat, scale)
|
||||
|
||||
assertEquals(Position3D(1, 2, 3), pos)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testSetGetTRS() {
|
||||
val mat = Matrix3D()
|
||||
val opos = Position3D(1, 2, 3)
|
||||
val oquat = Quaternion().setEuler(15.degrees, 30.degrees, 60.degrees)
|
||||
val oscale = Scale3D(1, 2, 3)
|
||||
|
||||
val pos = Position3D().copyFrom(opos)
|
||||
val quat = Quaternion().copyFrom(oquat)
|
||||
val scale = Scale3D().copyFrom(oscale)
|
||||
|
||||
mat.setTRS(pos, quat, scale)
|
||||
mat.getTRS(pos, quat, scale)
|
||||
|
||||
assertEquals(opos, pos)
|
||||
assertEquals(oquat, quat)
|
||||
assertEquals(oscale, scale.round())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testQuat() {
|
||||
assertEquals(Quaternion(0.7, 0.0, 0.0, 0.7).round(1), Quaternion().setEuler(90.degrees, 0.degrees, 0.degrees).round(1))
|
||||
assertEquals(Quaternion(0.0, 0.7, 0.0, 0.7).round(1), Quaternion().setEuler(0.degrees, 90.degrees, 0.degrees).round(1))
|
||||
assertEquals(Quaternion(0.0, 0.0, 0.7, 0.7).round(1), Quaternion().setEuler(0.degrees, 0.degrees, 90.degrees).round(1))
|
||||
|
||||
assertEquals(EulerRotation(90.degrees, 0.degrees, 0.degrees), EulerRotation().setQuaternion(Quaternion().setEuler(90.degrees, 0.degrees, 0.degrees)), 0.1)
|
||||
assertEquals(EulerRotation(0.degrees, 90.degrees, 0.degrees), EulerRotation().setQuaternion(Quaternion().setEuler(0.degrees, 90.degrees, 0.degrees)), 0.1)
|
||||
assertEquals(EulerRotation(0.degrees, 0.degrees, 90.degrees), EulerRotation().setQuaternion(Quaternion().setEuler(0.degrees, 0.degrees, 90.degrees)), 0.1)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testInvert() {
|
||||
val mat = Matrix3D().setTRS(Position3D(1, 2, 3), Quaternion().setEuler(15.degrees, 30.degrees, 60.degrees), Scale3D(1, 2, 3))
|
||||
val inv = Matrix3D().invert(mat)
|
||||
assertEquals(
|
||||
Matrix3D().round(2).toString(),
|
||||
(mat * inv).round(2).toString()
|
||||
)
|
||||
}
|
||||
|
||||
fun assertEquals(a: EulerRotation, b: EulerRotation, delta: Double = 0.01) {
|
||||
assertTrue("$a\n$b\na!=b // delta=$delta") {
|
||||
abs(a.x.degrees - b.x.degrees) <= delta &&
|
||||
abs(a.y.degrees - b.y.degrees) <= delta &&
|
||||
abs(a.z.degrees - b.z.degrees) <= delta
|
||||
}
|
||||
}
|
||||
|
||||
fun assertEquals(a: Quaternion, b: Quaternion, delta: Double = 0.01) {
|
||||
assertTrue("$a\n$b\na!=b // delta=$delta") {
|
||||
abs(a.x - b.x) <= delta &&
|
||||
abs(a.y - b.y) <= delta &&
|
||||
abs(a.z - b.z) <= delta &&
|
||||
abs(a.w - b.w) <= delta
|
||||
}
|
||||
}
|
||||
|
||||
fun assertEquals(a: Double, b: Double, delta: Double) {
|
||||
assertTrue("$a != $b // delta=$delta") { abs(a - b) <= delta }
|
||||
}
|
||||
|
||||
fun assertEquals(a: Float, b: Float, delta: Float) {
|
||||
assertTrue("$a != $b // delta=$delta") { abs(a - b) <= delta }
|
||||
}
|
||||
|
||||
fun Vector3D.round(digits: Int = 0) = setTo(round(x, digits), round(y, digits), round(z, digits), round(w, digits))
|
||||
fun Quaternion.round(digits: Int = 0) = setTo(round(x, digits), round(y, digits), round(z, digits), round(w, digits))
|
||||
fun Matrix3D.round(digits: Int = 0) = setToMap { round(it, digits) }
|
||||
|
||||
fun round(x: Float, digits: Int) = (round(x * 10.0.pow(digits)) / 10.0.pow(digits)).toFloat()
|
||||
fun round(x: Double, digits: Int) = round(x * 10.0.pow(digits)) / 10.0.pow(digits)
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import com.soywiz.korge.experimental.s3d.model.*
|
||||
import com.soywiz.korio.async.*
|
||||
import com.soywiz.korio.file.std.*
|
||||
import kotlin.test.*
|
||||
|
||||
class Library3DTest {
|
||||
@Test
|
||||
fun test() = suspendTest {
|
||||
val library = resourcesVfs["scene.dae"].readColladaLibrary()
|
||||
println(library)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
@@ -1,198 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<asset>
|
||||
<contributor>
|
||||
<author>Blender User</author>
|
||||
<authoring_tool>Blender 2.79.0 commit date:2018-03-22, commit time:14:10, hash:f4dc9f9</authoring_tool>
|
||||
</contributor>
|
||||
<created>2019-03-03T16:23:44</created>
|
||||
<modified>2019-03-03T16:23:44</modified>
|
||||
<unit name="meter" meter="1"/>
|
||||
<up_axis>Z_UP</up_axis>
|
||||
</asset>
|
||||
<library_cameras>
|
||||
<camera id="Camera-camera" name="Camera">
|
||||
<optics>
|
||||
<technique_common>
|
||||
<perspective>
|
||||
<xfov sid="xfov">49.13434</xfov>
|
||||
<aspect_ratio>1.777778</aspect_ratio>
|
||||
<znear sid="znear">0.1</znear>
|
||||
<zfar sid="zfar">100</zfar>
|
||||
</perspective>
|
||||
</technique_common>
|
||||
</optics>
|
||||
<extra>
|
||||
<technique profile="blender">
|
||||
<shiftx sid="shiftx" type="float">0</shiftx>
|
||||
<shifty sid="shifty" type="float">0</shifty>
|
||||
<YF_dofdist sid="YF_dofdist" type="float">0</YF_dofdist>
|
||||
</technique>
|
||||
</extra>
|
||||
</camera>
|
||||
</library_cameras>
|
||||
<library_lights>
|
||||
<light id="Lamp-light" name="Lamp">
|
||||
<technique_common>
|
||||
<point>
|
||||
<color sid="color">1 1 1</color>
|
||||
<constant_attenuation>1</constant_attenuation>
|
||||
<linear_attenuation>0</linear_attenuation>
|
||||
<quadratic_attenuation>0.00111109</quadratic_attenuation>
|
||||
</point>
|
||||
</technique_common>
|
||||
<extra>
|
||||
<technique profile="blender">
|
||||
<type sid="type" type="int">0</type>
|
||||
<flag sid="flag" type="int">0</flag>
|
||||
<mode sid="mode" type="int">8192</mode>
|
||||
<gamma sid="blender_gamma" type="float">1</gamma>
|
||||
<red sid="red" type="float">1</red>
|
||||
<green sid="green" type="float">1</green>
|
||||
<blue sid="blue" type="float">1</blue>
|
||||
<shadow_r sid="blender_shadow_r" type="float">0</shadow_r>
|
||||
<shadow_g sid="blender_shadow_g" type="float">0</shadow_g>
|
||||
<shadow_b sid="blender_shadow_b" type="float">0</shadow_b>
|
||||
<energy sid="blender_energy" type="float">1</energy>
|
||||
<dist sid="blender_dist" type="float">29.99998</dist>
|
||||
<spotsize sid="spotsize" type="float">75</spotsize>
|
||||
<spotblend sid="spotblend" type="float">0.15</spotblend>
|
||||
<halo_intensity sid="blnder_halo_intensity" type="float">1</halo_intensity>
|
||||
<att1 sid="att1" type="float">0</att1>
|
||||
<att2 sid="att2" type="float">1</att2>
|
||||
<falloff_type sid="falloff_type" type="int">2</falloff_type>
|
||||
<clipsta sid="clipsta" type="float">1.000799</clipsta>
|
||||
<clipend sid="clipend" type="float">30.002</clipend>
|
||||
<bias sid="bias" type="float">1</bias>
|
||||
<soft sid="soft" type="float">3</soft>
|
||||
<compressthresh sid="compressthresh" type="float">0.04999995</compressthresh>
|
||||
<bufsize sid="bufsize" type="int">2880</bufsize>
|
||||
<samp sid="samp" type="int">3</samp>
|
||||
<buffers sid="buffers" type="int">1</buffers>
|
||||
<filtertype sid="filtertype" type="int">0</filtertype>
|
||||
<bufflag sid="bufflag" type="int">0</bufflag>
|
||||
<buftype sid="buftype" type="int">2</buftype>
|
||||
<ray_samp sid="ray_samp" type="int">1</ray_samp>
|
||||
<ray_sampy sid="ray_sampy" type="int">1</ray_sampy>
|
||||
<ray_sampz sid="ray_sampz" type="int">1</ray_sampz>
|
||||
<ray_samp_type sid="ray_samp_type" type="int">0</ray_samp_type>
|
||||
<area_shape sid="area_shape" type="int">1</area_shape>
|
||||
<area_size sid="area_size" type="float">0.1</area_size>
|
||||
<area_sizey sid="area_sizey" type="float">0.1</area_sizey>
|
||||
<area_sizez sid="area_sizez" type="float">1</area_sizez>
|
||||
<adapt_thresh sid="adapt_thresh" type="float">0.000999987</adapt_thresh>
|
||||
<ray_samp_method sid="ray_samp_method" type="int">1</ray_samp_method>
|
||||
<shadhalostep sid="shadhalostep" type="int">0</shadhalostep>
|
||||
<sun_effect_type sid="sun_effect_type" type="int">0</sun_effect_type>
|
||||
<skyblendtype sid="skyblendtype" type="int">1</skyblendtype>
|
||||
<horizon_brightness sid="horizon_brightness" type="float">1</horizon_brightness>
|
||||
<spread sid="spread" type="float">1</spread>
|
||||
<sun_brightness sid="sun_brightness" type="float">1</sun_brightness>
|
||||
<sun_size sid="sun_size" type="float">1</sun_size>
|
||||
<backscattered_light sid="backscattered_light" type="float">1</backscattered_light>
|
||||
<sun_intensity sid="sun_intensity" type="float">1</sun_intensity>
|
||||
<atm_turbidity sid="atm_turbidity" type="float">2</atm_turbidity>
|
||||
<atm_extinction_factor sid="atm_extinction_factor" type="float">1</atm_extinction_factor>
|
||||
<atm_distance_factor sid="atm_distance_factor" type="float">1</atm_distance_factor>
|
||||
<skyblendfac sid="skyblendfac" type="float">1</skyblendfac>
|
||||
<sky_exposure sid="sky_exposure" type="float">1</sky_exposure>
|
||||
<sky_colorspace sid="sky_colorspace" type="int">0</sky_colorspace>
|
||||
</technique>
|
||||
</extra>
|
||||
</light>
|
||||
</library_lights>
|
||||
<library_images/>
|
||||
<library_effects>
|
||||
<effect id="Material-effect">
|
||||
<profile_COMMON>
|
||||
<technique sid="common">
|
||||
<phong>
|
||||
<emission>
|
||||
<color sid="emission">0 0 0 1</color>
|
||||
</emission>
|
||||
<ambient>
|
||||
<color sid="ambient">0 0 0 1</color>
|
||||
</ambient>
|
||||
<diffuse>
|
||||
<color sid="diffuse">0.64 0.64 0.64 1</color>
|
||||
</diffuse>
|
||||
<specular>
|
||||
<color sid="specular">0.5 0.5 0.5 1</color>
|
||||
</specular>
|
||||
<shininess>
|
||||
<float sid="shininess">50</float>
|
||||
</shininess>
|
||||
<index_of_refraction>
|
||||
<float sid="index_of_refraction">1</float>
|
||||
</index_of_refraction>
|
||||
</phong>
|
||||
</technique>
|
||||
</profile_COMMON>
|
||||
</effect>
|
||||
</library_effects>
|
||||
<library_materials>
|
||||
<material id="Material-material" name="Material">
|
||||
<instance_effect url="#Material-effect"/>
|
||||
</material>
|
||||
</library_materials>
|
||||
<library_geometries>
|
||||
<geometry id="Cube-mesh" name="Cube">
|
||||
<mesh>
|
||||
<source id="Cube-mesh-positions">
|
||||
<float_array id="Cube-mesh-positions-array" count="24">1 1 -1 1 -1 -1 -1 -0.9999998 -1 -0.9999997 1 -1 1 0.9999995 1 0.9999994 -1.000001 1 -1 -0.9999997 1 -1 1 1</float_array>
|
||||
<technique_common>
|
||||
<accessor source="#Cube-mesh-positions-array" count="8" stride="3">
|
||||
<param name="X" type="float"/>
|
||||
<param name="Y" type="float"/>
|
||||
<param name="Z" type="float"/>
|
||||
</accessor>
|
||||
</technique_common>
|
||||
</source>
|
||||
<source id="Cube-mesh-normals">
|
||||
<float_array id="Cube-mesh-normals-array" count="36">0 0 -1 0 0 1 1 0 -2.38419e-7 0 -1 -4.76837e-7 -1 2.38419e-7 -1.49012e-7 2.68221e-7 1 2.38419e-7 0 0 -1 0 0 1 1 -5.96046e-7 3.27825e-7 -4.76837e-7 -1 0 -1 2.38419e-7 -1.19209e-7 2.08616e-7 1 0</float_array>
|
||||
<technique_common>
|
||||
<accessor source="#Cube-mesh-normals-array" count="12" stride="3">
|
||||
<param name="X" type="float"/>
|
||||
<param name="Y" type="float"/>
|
||||
<param name="Z" type="float"/>
|
||||
</accessor>
|
||||
</technique_common>
|
||||
</source>
|
||||
<vertices id="Cube-mesh-vertices">
|
||||
<input semantic="POSITION" source="#Cube-mesh-positions"/>
|
||||
</vertices>
|
||||
<triangles material="Material-material" count="12">
|
||||
<input semantic="VERTEX" source="#Cube-mesh-vertices" offset="0"/>
|
||||
<input semantic="NORMAL" source="#Cube-mesh-normals" offset="1"/>
|
||||
<p>0 0 2 0 3 0 7 1 5 1 4 1 4 2 1 2 0 2 5 3 2 3 1 3 2 4 7 4 3 4 0 5 7 5 4 5 0 6 1 6 2 6 7 7 6 7 5 7 4 8 5 8 1 8 5 9 6 9 2 9 2 10 6 10 7 10 0 11 3 11 7 11</p>
|
||||
</triangles>
|
||||
</mesh>
|
||||
</geometry>
|
||||
</library_geometries>
|
||||
<library_controllers/>
|
||||
<library_visual_scenes>
|
||||
<visual_scene id="Scene" name="Scene">
|
||||
<node id="Camera" name="Camera" type="NODE">
|
||||
<matrix sid="transform">0.6859207 -0.3240135 0.6515582 7.481132 0.7276763 0.3054208 -0.6141704 -6.50764 0 0.8953956 0.4452714 5.343665 0 0 0 1</matrix>
|
||||
<instance_camera url="#Camera-camera"/>
|
||||
</node>
|
||||
<node id="Lamp" name="Lamp" type="NODE">
|
||||
<matrix sid="transform">-0.2908646 -0.7711008 0.5663932 4.076245 0.9551712 -0.1998834 0.2183912 1.005454 -0.05518906 0.6045247 0.7946723 5.903862 0 0 0 1</matrix>
|
||||
<instance_light url="#Lamp-light"/>
|
||||
</node>
|
||||
<node id="Cube" name="Cube" type="NODE">
|
||||
<matrix sid="transform">1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
|
||||
<instance_geometry url="#Cube-mesh" name="Cube">
|
||||
<bind_material>
|
||||
<technique_common>
|
||||
<instance_material symbol="Material-material" target="#Material-material"/>
|
||||
</technique_common>
|
||||
</bind_material>
|
||||
</instance_geometry>
|
||||
</node>
|
||||
</visual_scene>
|
||||
</library_visual_scenes>
|
||||
<scene>
|
||||
<instance_visual_scene url="#Scene"/>
|
||||
</scene>
|
||||
</COLLADA>
|
||||
@@ -8,5 +8,6 @@ korge {
|
||||
name = "tic-tac-toe"
|
||||
icon = projectDir["src/commonMain/resources/icon.png"]
|
||||
|
||||
dependencyMulti("com.soywiz:korge-swf:$korgeVersion")
|
||||
//dependencyMulti("com.soywiz:korge-swf:$korgeVersion")
|
||||
supportSwf()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user