solar system

This commit is contained in:
Raffaele Mignone 2019-01-12 16:39:42 +01:00
parent ceff79a18f
commit c8a2cfa483
Signed by: norangebit
GPG Key ID: 4B9DF72AB9508845
4 changed files with 213 additions and 14 deletions

View File

@ -25,15 +25,19 @@ import android.support.v7.app.AppCompatActivity
import android.view.MotionEvent
import com.google.ar.core.HitResult
import com.google.ar.core.Plane
import com.google.ar.sceneform.Node
import com.google.ar.sceneform.math.Vector3
import com.google.ar.sceneform.rendering.ModelRenderable
import com.google.ar.sceneform.ux.ArFragment
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
private val TAG = MainActivity::class.java.canonicalName
private val AU_TO_METERS = 1.1f
private lateinit var arFragment: ArFragment
private var isModelAdded = false
private lateinit var planets: Map<Planet, ModelRenderable>
private lateinit var renderablePlanets: Map<Planet, ModelRenderable>
private lateinit var loadPlanetsJob: Job
override fun onCreate(savedInstanceState: Bundle?) {
@ -45,17 +49,65 @@ class MainActivity : AppCompatActivity() {
setContentView(R.layout.activity_main)
arFragment = supportFragmentManager.findFragmentById(R.id.ar_fragment) as ArFragment
loadPlanetsJob = GlobalScope.launch (Dispatchers.Main) {
planets = loadPlanets(this@MainActivity)
loadPlanetsJob = GlobalScope.launch(Dispatchers.Main) {
renderablePlanets = loadPlanets(this@MainActivity)
}
arFragment.setOnTapArPlaneListener(this::placeSolarSystem)
}
private fun placeSolarSystem(hitResult: HitResult, plane: Plane, motionEvent: MotionEvent) {
if (isModelAdded)
return
GlobalScope.launch(Dispatchers.Main) {
loadPlanetsJob.join()
addTransformableNodeToScene(arFragment, hitResult.createAnchor(), planets[Planet.EARTH]!!)
val solarSystem = createSolarSystem(renderablePlanets)
addNodeToScene(arFragment, hitResult.createAnchor(), solarSystem)
isModelAdded = true
}
}
private fun createSolarSystem(renderablePlanets: Map<Planet, ModelRenderable>): Node {
val base = Node()
val sun = Node()
sun.setParent(base)
sun.localPosition = Vector3(0.0f, 0.5f, 0.0f)
val sunVisual = Node()
sunVisual.setParent(sun)
sunVisual.renderable = renderablePlanets[Planet.SUN]
sunVisual.localScale = Vector3(0.5f, 0.5f, 0.5f)
createPlanetNode(Planet.MERCURY, sun, 0.4f, 47f, renderablePlanets)
createPlanetNode(Planet.VENUS, sun, 0.7f, 35f, renderablePlanets)
createPlanetNode(Planet.EARTH, sun, 1.0f, 29f, renderablePlanets)
createPlanetNode(Planet.MARS, sun, 1.5f, 24f, renderablePlanets)
createPlanetNode(Planet.JUPITER, sun, 2.2f, 13f, renderablePlanets)
createPlanetNode(Planet.SATURN, sun, 3.5f, 9f, renderablePlanets)
createPlanetNode(Planet.URANUS, sun, 5.2f, 7f, renderablePlanets)
createPlanetNode(Planet.NEPTUNE, sun, 6.1f, 5f, renderablePlanets)
return base
}
private fun createPlanetNode(
planet: Planet,
parent: Node,
auFromParent: Float,
orbitDegreesPerSecond: Float,
renderablePlanets: Map<Planet, ModelRenderable>
) {
val orbit = RotationNode()
orbit.degreesPerSecond = orbitDegreesPerSecond
orbit.setParent(parent)
val renderable = renderablePlanets[planet] ?: return
val planetNode = PlanetNode(renderable)
planetNode.setParent(orbit)
planetNode.localPosition = Vector3(AU_TO_METERS * auFromParent, 0.0f, 0.0f)
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (c) 2019, norangebit
*
* This file is part of solar-system.
*
* solar-system is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* solar-system is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with solar-system. If not, see <http://www.gnu.org/licenses/>
*
*/
package it.norangeb.solarsystem
import com.google.ar.sceneform.FrameTime
import com.google.ar.sceneform.Node
import com.google.ar.sceneform.math.Vector3
import com.google.ar.sceneform.rendering.ModelRenderable
class PlanetNode(
private val planetRenderable: ModelRenderable
) : Node() {
private val planetScale = 0.6f
private var planetVisual: RotationNode? = null
override fun onActivate() {
if (scene == null)
throw IllegalStateException("Scene is null!")
if (planetVisual == null)
initRotationNode()
}
override fun onUpdate(frameTime: FrameTime?) {
if (scene == null)
return
}
fun initRotationNode() {
planetVisual = RotationNode(false)
planetVisual?.setParent(this)
planetVisual?.renderable = planetRenderable
planetVisual?.localScale = Vector3(planetScale, planetScale, planetScale)
}
}

View File

@ -0,0 +1,98 @@
/*
* Copyright (c) 2019, norangebit
*
* This file is part of solar-system.
*
* solar-system is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* solar-system is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with solar-system. If not, see <http://www.gnu.org/licenses/>
*
*/
package it.norangeb.solarsystem
import android.animation.ObjectAnimator
import android.view.animation.LinearInterpolator
import com.google.ar.sceneform.FrameTime
import com.google.ar.sceneform.Node
import com.google.ar.sceneform.math.Quaternion
import com.google.ar.sceneform.math.QuaternionEvaluator
import com.google.ar.sceneform.math.Vector3
class RotationNode(private val isOrbit: Boolean = true) : Node() {
val SPEED_MULTIPLIER = 1
val ROTATION_MULTIPLIER = 1
private var orbitAnimation: ObjectAnimator? = null
var degreesPerSecond = 90.0f
private val animationDuration: Long
get() = (1000 * 360 / (degreesPerSecond * if (isOrbit)
SPEED_MULTIPLIER
else
ROTATION_MULTIPLIER))
.toLong()
override fun onUpdate(frameTime: FrameTime?) {
super.onUpdate(frameTime)
if (orbitAnimation == null)
return
}
override fun onActivate() {
startAnimation()
}
override fun onDeactivate() {
stopAnimation()
}
private fun startAnimation() {
if (orbitAnimation != null) {
return
}
orbitAnimation = createAnimator()
orbitAnimation!!.target = this
orbitAnimation!!.duration = animationDuration
orbitAnimation!!.start()
}
private fun stopAnimation() {
if (orbitAnimation == null) {
return
}
orbitAnimation!!.cancel()
orbitAnimation = null
}
private fun createAnimator(): ObjectAnimator {
val orientation1 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 0f)
val orientation2 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 120f)
val orientation3 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 240f)
val orientation4 = Quaternion.axisAngle(Vector3(0.0f, 1.0f, 0.0f), 360f)
val orbitAnimation = ObjectAnimator()
orbitAnimation.setObjectValues(orientation1, orientation2, orientation3, orientation4)
orbitAnimation.propertyName = "localRotation"
orbitAnimation.setEvaluator(QuaternionEvaluator())
orbitAnimation.repeatCount = ObjectAnimator.INFINITE
orbitAnimation.repeatMode = ObjectAnimator.RESTART
orbitAnimation.interpolator = LinearInterpolator()
orbitAnimation.setAutoCancel(true)
return orbitAnimation
}
}

View File

@ -34,7 +34,6 @@ import com.google.ar.sceneform.Node
import com.google.ar.sceneform.assets.RenderableSource
import com.google.ar.sceneform.rendering.*
import com.google.ar.sceneform.ux.ArFragment
import com.google.ar.sceneform.ux.TransformableNode
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
@ -98,9 +97,9 @@ fun loadPlanet(context: Context, planet: Planet): Deferred<ModelRenderable> {
)
return GlobalScope.async(Dispatchers.IO) {
threadLog("start load: $planet")
threadLog("start loading: $planet")
val renderable = futureRenderable.get()
threadLog("end load: $planet")
threadLog("end loading: $planet")
renderable
}
}
@ -124,14 +123,10 @@ fun fetchModel(
.setRecenterMode(RenderableSource.RecenterMode.ROOT)
.build()
fun addTransformableNodeToScene(arFragment: ArFragment, anchor: Anchor, renderable: ModelRenderable): Node {
fun addNodeToScene(arFragment: ArFragment, anchor: Anchor, node: Node) {
val anchorNode = AnchorNode(anchor)
val transformableNode = TransformableNode(arFragment.transformationSystem)
transformableNode.renderable = renderable
transformableNode.setParent(anchorNode)
anchorNode.addChild(node)
arFragment.arSceneView.scene.addChild(anchorNode)
transformableNode.select()
return transformableNode
}
enum class Planet(val value: String) {
@ -149,4 +144,4 @@ enum class Planet(val value: String) {
fun threadLog(message: String) = Log.d(
"COROUTINES",
"[${Thread.currentThread().name}]: $message"
)
)