This commit is contained in:
soywiz
2019-03-17 05:44:35 +01:00
parent 67c674f24e
commit d6eba1b2a8
60 changed files with 3100 additions and 3237 deletions

View File

@@ -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
View File

@@ -0,0 +1,6 @@
apply plugin: "korge"
korge {
id = "com.soywiz.samples.korge3d"
supportExperimental3d()
}

View 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}")
}
}
}
}

View 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
}
}
}

Binary file not shown.

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

View File

@@ -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()
}

View File

@@ -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()
}

View File

@@ -2,5 +2,5 @@ apply plugin: "korge"
korge {
id = "com.soywiz.samples.lipsync"
dependencyMulti("com.soywiz:korau-mp3:$korauVersion")
supportMp3()
}

View File

@@ -1,6 +0,0 @@
apply plugin: "korge"
korge {
id = "com.soywiz.samples.raw3d"
dependencyMulti("com.soywiz:korag-opengl:1.6.7")
}

View File

@@ -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
}
}
}

View File

@@ -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

View File

@@ -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*/
}

View File

@@ -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
)
}
}
}
}

View File

@@ -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 }

View File

@@ -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
)
}

View File

@@ -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
) {
}

View File

@@ -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]!!
}
}

View File

@@ -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)
)

View File

@@ -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)

View File

@@ -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) }

View File

@@ -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())

View File

@@ -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' }

View File

@@ -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

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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()
}