solar system
This commit is contained in:
parent
ceff79a18f
commit
c8a2cfa483
@ -25,15 +25,19 @@ import android.support.v7.app.AppCompatActivity
|
|||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import com.google.ar.core.HitResult
|
import com.google.ar.core.HitResult
|
||||||
import com.google.ar.core.Plane
|
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.rendering.ModelRenderable
|
||||||
import com.google.ar.sceneform.ux.ArFragment
|
import com.google.ar.sceneform.ux.ArFragment
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private val TAG = MainActivity::class.java.canonicalName
|
private val TAG = MainActivity::class.java.canonicalName
|
||||||
|
private val AU_TO_METERS = 1.1f
|
||||||
|
|
||||||
private lateinit var arFragment: ArFragment
|
private lateinit var arFragment: ArFragment
|
||||||
private var isModelAdded = false
|
private var isModelAdded = false
|
||||||
private lateinit var planets: Map<Planet, ModelRenderable>
|
private lateinit var renderablePlanets: Map<Planet, ModelRenderable>
|
||||||
private lateinit var loadPlanetsJob: Job
|
private lateinit var loadPlanetsJob: Job
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -45,17 +49,65 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
arFragment = supportFragmentManager.findFragmentById(R.id.ar_fragment) as ArFragment
|
arFragment = supportFragmentManager.findFragmentById(R.id.ar_fragment) as ArFragment
|
||||||
|
|
||||||
loadPlanetsJob = GlobalScope.launch (Dispatchers.Main) {
|
loadPlanetsJob = GlobalScope.launch(Dispatchers.Main) {
|
||||||
planets = loadPlanets(this@MainActivity)
|
renderablePlanets = loadPlanets(this@MainActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
arFragment.setOnTapArPlaneListener(this::placeSolarSystem)
|
arFragment.setOnTapArPlaneListener(this::placeSolarSystem)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun placeSolarSystem(hitResult: HitResult, plane: Plane, motionEvent: MotionEvent) {
|
private fun placeSolarSystem(hitResult: HitResult, plane: Plane, motionEvent: MotionEvent) {
|
||||||
|
if (isModelAdded)
|
||||||
|
return
|
||||||
|
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
loadPlanetsJob.join()
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -34,7 +34,6 @@ import com.google.ar.sceneform.Node
|
|||||||
import com.google.ar.sceneform.assets.RenderableSource
|
import com.google.ar.sceneform.assets.RenderableSource
|
||||||
import com.google.ar.sceneform.rendering.*
|
import com.google.ar.sceneform.rendering.*
|
||||||
import com.google.ar.sceneform.ux.ArFragment
|
import com.google.ar.sceneform.ux.ArFragment
|
||||||
import com.google.ar.sceneform.ux.TransformableNode
|
|
||||||
import kotlinx.coroutines.Deferred
|
import kotlinx.coroutines.Deferred
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
@ -98,9 +97,9 @@ fun loadPlanet(context: Context, planet: Planet): Deferred<ModelRenderable> {
|
|||||||
)
|
)
|
||||||
|
|
||||||
return GlobalScope.async(Dispatchers.IO) {
|
return GlobalScope.async(Dispatchers.IO) {
|
||||||
threadLog("start load: $planet")
|
threadLog("start loading: $planet")
|
||||||
val renderable = futureRenderable.get()
|
val renderable = futureRenderable.get()
|
||||||
threadLog("end load: $planet")
|
threadLog("end loading: $planet")
|
||||||
renderable
|
renderable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,14 +123,10 @@ fun fetchModel(
|
|||||||
.setRecenterMode(RenderableSource.RecenterMode.ROOT)
|
.setRecenterMode(RenderableSource.RecenterMode.ROOT)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun addTransformableNodeToScene(arFragment: ArFragment, anchor: Anchor, renderable: ModelRenderable): Node {
|
fun addNodeToScene(arFragment: ArFragment, anchor: Anchor, node: Node) {
|
||||||
val anchorNode = AnchorNode(anchor)
|
val anchorNode = AnchorNode(anchor)
|
||||||
val transformableNode = TransformableNode(arFragment.transformationSystem)
|
anchorNode.addChild(node)
|
||||||
transformableNode.renderable = renderable
|
|
||||||
transformableNode.setParent(anchorNode)
|
|
||||||
arFragment.arSceneView.scene.addChild(anchorNode)
|
arFragment.arSceneView.scene.addChild(anchorNode)
|
||||||
transformableNode.select()
|
|
||||||
return transformableNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Planet(val value: String) {
|
enum class Planet(val value: String) {
|
||||||
@ -149,4 +144,4 @@ enum class Planet(val value: String) {
|
|||||||
fun threadLog(message: String) = Log.d(
|
fun threadLog(message: String) = Log.d(
|
||||||
"COROUTINES",
|
"COROUTINES",
|
||||||
"[${Thread.currentThread().name}]: $message"
|
"[${Thread.currentThread().name}]: $message"
|
||||||
)
|
)
|
||||||
|
Loading…
Reference in New Issue
Block a user