SolarSystem.qml Example File

planets-qml/SolarSystem.qml
 /****************************************************************************
 **
 ** Copyright (C) 2016 The Qt Company Ltd.
 ** Contact: https://www.qt.io/licensing/
 **
 ** This file is part of the Qt3D module of the Qt Toolkit.
 **
 ** $QT_BEGIN_LICENSE:BSD$
 ** Commercial License Usage
 ** Licensees holding valid commercial Qt licenses may use this file in
 ** accordance with the commercial license agreement provided with the
 ** Software or, alternatively, in accordance with the terms contained in
 ** a written agreement between you and The Qt Company. For licensing terms
 ** and conditions see https://www.qt.io/terms-conditions. For further
 ** information use the contact form at https://www.qt.io/contact-us.
 **
 ** BSD License Usage
 ** Alternatively, you may use this file under the terms of the BSD license
 ** as follows:
 **
 ** "Redistribution and use in source and binary forms, with or without
 ** modification, are permitted provided that the following conditions are
 ** met:
 **   * Redistributions of source code must retain the above copyright
 **     notice, this list of conditions and the following disclaimer.
 **   * Redistributions in binary form must reproduce the above copyright
 **     notice, this list of conditions and the following disclaimer in
 **     the documentation and/or other materials provided with the
 **     distribution.
 **   * Neither the name of The Qt Company Ltd nor the names of its
 **     contributors may be used to endorse or promote products derived
 **     from this software without specific prior written permission.
 **
 **
 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 **
 ** $QT_END_LICENSE$
 **
 ****************************************************************************/

 import QtQuick 2.0 as QQ2
 import Qt3D.Core 2.0
 import Qt3D.Render 2.0
 import Qt3D.Input 2.0
 import Qt3D.Logic 2.0
 import Qt3D.Extras 2.0

 import "planets.js" as Planets

 Entity {
     id: sceneRoot

     property bool ready: false

     property real cameraNear: 0
     property real xLookAtOffset: 0
     property real yLookAtOffset: 0
     property real zLookAtOffset: 0
     property real xCameraOffset: 0
     property real yCameraOffset: 0
     property real zCameraOffset: 0

     property var planetData
     property var planets: []

     property vector3d defaultUp: Qt.vector3d(0, 1, 0)
     property vector3d defaultCameraPosition: Qt.vector3d(Planets.solarDistance,
                                                          Planets.solarDistance,
                                                          Planets.solarDistance)
     property vector3d tiltAxis: Qt.vector3d(0, 0, 1)
     property vector3d rollAxis: Qt.vector3d(0, 1, 0)
     property real cameraDistance: 1
     property vector3d oldCameraPosition
     property vector3d oldFocusedPlanetPosition

     property color ambientStrengthStarfield: "#000000"
     property color ambientStrengthSun: "#ffffff"
     property color ambientStrengthClouds: "#000000"
     property color ambientStrengthRing: "#111111"
     property color ambientStrengthPlanet: "#222222"
     property real shininessSpecularMap: 50.0
     property real shininessClouds: 10.0
     property real shininessBasic: 1.0

     property real saturnRingInnerRadius
     property real saturnRingOuterRadius
     property real uranusRingInnerRadius
     property real uranusRingOuterRadius

     // Time variables
     property int year: 2000
     property int month: 1
     property int day: 1
     // Time scale formula based on http://www.stjarnhimlen.se/comp/ppcomp.html
     property real startD: 367 * year - 7 * (year + (month + 9) / 12) / 4 + 275 * month / 9 + day - 730530
     property real oldTimeD: startD
     property real currTimeD: startD
     property real deltaTimeD: 0
     property real daysPerFrame
     property real daysPerFrameScale

     property real planetScale

     property bool focusedScaling: false
     property int focusedMinimumScale: 20
     property real actualScale

     // Animate solar system with LogicComponent
     FrameAction {
         onTriggered: {
             frames++
             animate(focusedPlanet)
         }
     }

     PlanetsLight {
         id: light
         ratio: width / height
     }

     Camera {
         id: camera
         projectionType: CameraLens.PerspectiveProjection
         fieldOfView: 45
         aspectRatio: width / height
         nearPlane: 2500000.0
         farPlane: 20000000.0
         position: defaultCameraPosition
         upVector: defaultUp
         viewCenter: Qt.vector3d( xLookAtOffset, yLookAtOffset, zLookAtOffset )
     }

     FirstPersonCameraController { camera: camera }

     components: [
         PlanetFrameGraph {
             id: framegraph
             viewCamera: camera
             lightCamera: light.lightCamera
         },
         InputSettings {}
     ]

     PlanetEffect {
         id: effectD
         light: light
     }

     PlanetEffect {
         id: effectDB
         light: light
         vertexES: "qrc:/shaders/es2/planetDB.vert"
         fragmentES: "qrc:/shaders/es2/planetDB.frag"
         vertexGL: "qrc:/shaders/gl3/planetDB.vert"
         fragmentGL: "qrc:/shaders/gl3/planetDB.frag"
     }

     PlanetEffect {
         id: effectDSB
         light: light
         vertexES: "qrc:/shaders/es2/planetDB.vert"
         fragmentES: "qrc:/shaders/es2/planetDSB.frag"
         vertexGL: "qrc:/shaders/gl3/planetDB.vert"
         fragmentGL: "qrc:/shaders/gl3/planetDSB.frag"
     }

     PlanetEffect {
         id: cloudEffect
         light: light
         vertexES: "qrc:/shaders/es2/planetD.vert"
         fragmentES: "qrc:/shaders/es2/planetDS.frag"
         vertexGL: "qrc:/shaders/gl3/planetD.vert"
         fragmentGL: "qrc:/shaders/gl3/planetDS.frag"
     }

     SunEffect {
         id: sunEffect
     }

     ShadowEffect {
         id: shadowMapEffect
         shadowTexture: framegraph.shadowTexture
         light: light
     }

     QQ2.Component.onCompleted: {
         planetData = Planets.loadPlanetData()
         // Push in the correct order
         planets.push(sun)
         planets.push(mercury)
         planets.push(venus)
         planets.push(earth)
         planets.push(mars)
         planets.push(jupiter)
         planets.push(saturn)
         planets.push(uranus)
         planets.push(neptune)
         planets.push(moon)
         // TODO: Once support for creating meshes from arrays is implemented take these into use
         //saturnRing.makeRing()
         //uranusRing.makeRing()
         saturnRingOuterRadius = planetData[Planets.SATURN].radius + Planets.saturnOuterRadius
         saturnRingInnerRadius = planetData[Planets.SATURN].radius + 6.630
         uranusRingOuterRadius = planetData[Planets.URANUS].radius + Planets.uranusOuterRadius
         uranusRingInnerRadius = planetData[Planets.URANUS].radius + 2
         ready = true
         changeScale(1200)
         changeSpeed(0.2)
         setLookAtOffset(Planets.SUN)
     }

     QQ2.NumberAnimation {
         id: lookAtOffsetAnimation
         target: sceneRoot
         properties: "xLookAtOffset, yLookAtOffset, zLookAtOffset"
         to: 0
         easing.type: Easing.InOutQuint
         duration: 1250
     }

     QQ2.NumberAnimation {
         id: cameraOffsetAnimation
         target: sceneRoot
         properties: "xCameraOffset, yCameraOffset, zCameraOffset"
         to: 0
         easing.type: Easing.InOutQuint
         duration: 2500
     }

     QQ2.Behavior on cameraNear {
         QQ2.PropertyAnimation {
             easing.type: Easing.InOutQuint
             duration: 2500
         }
     }

     function changePlanetFocus(oldPlanet, focusedPlanet) {
         setOldPlanet(oldPlanet, focusedPlanet)
         setLookAtOffset(focusedPlanet)
         setCameraOffset(oldPlanet, focusedPlanet)
         lookAtOffsetAnimation.restart()
         cameraOffsetAnimation.restart()
     }

     function setOldPlanet(oldPlanet, focusedPlanet) {
         oldCameraPosition = camera.position

         var planet = 0
         if (oldPlanet !== Planets.SOLAR_SYSTEM)
             planet = oldPlanet
         oldFocusedPlanetPosition = Qt.vector3d(planets[planet].x,
                                                planets[planet].y,
                                                planets[planet].z)
         checkScaling(focusedPlanet)
     }

     function setScale(value, focused) {
         // Save actual scale
         if (!focused)
             actualScale = value

         // Limit minimum scaling in focus mode to avoid jitter caused by rounding errors
         if (value <= focusedMinimumScale && (focusedScaling || focused))
             planetScale = focusedMinimumScale
         else
             planetScale = actualScale
         return planetScale
     }

     function checkScaling(focusedPlanet) {
         if (focusedPlanet !== Planets.SOLAR_SYSTEM) {
             // Limit minimum scaling in focus mode to avoid jitter caused by rounding errors
             if (actualScale <= focusedMinimumScale) {
                 planetScale = focusedMinimumScale
                 changeScale(focusedMinimumScale, true)
             }
             focusedScaling = true
         } else if (focusedScaling === true) {
             // Restore normal scaling
             focusedScaling = false
             changeScale(actualScale, false)
         }
     }

     function setLookAtOffset(focusedPlanet) {
         var offset = oldFocusedPlanetPosition

         var planet = 0
         if (focusedPlanet !== Planets.SOLAR_SYSTEM)
             planet = focusedPlanet

         var focusedPlanetPosition = Qt.vector3d(planets[planet].x,
                                                 planets[planet].y,
                                                 planets[planet].z)
         offset = offset.minus(focusedPlanetPosition)

         xLookAtOffset = offset.x
         yLookAtOffset = offset.y
         zLookAtOffset = offset.z
     }

     function setCameraOffset(oldPlanet, focusedPlanet) {
         var offset = oldCameraPosition

         var planet = 0
         if (focusedPlanet !== Planets.SOLAR_SYSTEM)
             planet = focusedPlanet

         var newCameraPosition = getNewCameraPosition(focusedPlanet, Planets.getOuterRadius(planet))

         if (focusedPlanet !== Planets.SUN)
             offset = offset.minus(newCameraPosition)

         if (oldPlanet === Planets.SOLAR_SYSTEM && focusedPlanet === Planets.SUN) {
             xCameraOffset = Math.abs(offset.x)
             yCameraOffset = Math.abs(offset.y)
             zCameraOffset = Math.abs(offset.z)
         } else { // from a planet to another
             xCameraOffset = offset.x
             yCameraOffset = offset.y
             zCameraOffset = offset.z
         }
     }

     function getNewCameraPosition(focusedPlanet, radius) {
         var position
         if (focusedPlanet === Planets.SOLAR_SYSTEM) {
             position = defaultCameraPosition
             position = position.times(cameraDistance)
         } else if (focusedPlanet === Planets.SUN) {
             position = Qt.vector3d(radius * planetScale * 2,
                                    radius * planetScale * 2,
                                    radius * planetScale * 2)
             position = position.times(cameraDistance)
         } else {
             var vec1 = Qt.vector3d(planets[focusedPlanet].x,
                                    planets[focusedPlanet].y,
                                    planets[focusedPlanet].z)
             var vec2 = defaultUp
             vec1 = vec1.normalized()
             vec2 = vec2.crossProduct(vec1)
             vec2 = vec2.times(radius * planetScale * cameraDistance * 4)
             vec2 = vec2.plus(Qt.vector3d(planets[focusedPlanet].x,
                                          planets[focusedPlanet].y,
                                          planets[focusedPlanet].z))
             vec1 = Qt.vector3d(0, radius * planetScale, 0)
             vec2 = vec2.plus(vec1)
             position = vec2
         }
         return position
     }

     function advanceTime(focusedPlanet) {
         if (focusedPlanet === Planets.SOLAR_SYSTEM)
             daysPerFrame = daysPerFrameScale * 10
         else
             daysPerFrame = daysPerFrameScale * planetData[focusedPlanet].period / 100.0

         // Advance the time in days
         oldTimeD = currTimeD
         currTimeD = currTimeD + daysPerFrame
         deltaTimeD = currTimeD - oldTimeD
     }

     function positionPlanet(i) {
         var planet = planetData[i]
         var target = planets[i]

         if (i !== Planets.SUN) {
             // Calculate the planet orbital elements from the current time in days
             var N = (planet.N1 + planet.N2 * currTimeD) * Math.PI / 180
             var iPlanet = (planet.i1 + planet.i2 * currTimeD) * Math.PI / 180
             var w = (planet.w1 + planet.w2 * currTimeD) * Math.PI / 180
             var a = planet.a1 + planet.a2 * currTimeD
             var e = planet.e1 + planet.e2 * currTimeD
             var M = (planet.M1 + planet.M2 * currTimeD) * Math.PI / 180
             var E = M + e * Math.sin(M) * (1.0 + e * Math.cos(M))

             var xv = a * (Math.cos(E) - e)
             var yv = a * (Math.sqrt(1.0 - e * e) * Math.sin(E))
             var v = Math.atan2(yv, xv)

             // Calculate the distance (radius)
             // TODO: Math.hypot() is ES6 and QML JS is only ES5 currently. A patch to QtQml is
             // required to get Math.hypot() to work.
             //var r = Math.hypot(xv, yv)
             var r = Math.sqrt(Math.pow(xv, 2) + Math.pow(yv, 2))

             // From http://www.davidcolarusso.com/astro/
             // Modified to compensate for the right handed coordinate system of OpenGL
             var xh = r * (Math.cos(N) * Math.cos(v + w)
                           - Math.sin(N) * Math.sin(v + w) * Math.cos(iPlanet))
             var zh = -r * (Math.sin(N) * Math.cos(v + w)
                            + Math.cos(N) * Math.sin(v + w) * Math.cos(iPlanet))
             var yh = r * (Math.sin(w + v) * Math.sin(iPlanet))

             // Apply the position offset from the center of orbit to the bodies
             var centerOfOrbit = planet.centerOfOrbit
             target.x = planets[centerOfOrbit].x + xh * Planets.auScale
             target.y = planets[centerOfOrbit].y + yh * Planets.auScale
             target.z = planets[centerOfOrbit].z + zh * Planets.auScale
         }
         // Calculate the rotation (roll) of the bodies. Tilt does not change.
         target.roll += (deltaTimeD / planet.period) * 360 // In degrees
     }

     function changeScale(scale, focused) {
         if (!ready)
             return

         var scaling = setScale(scale, focused)
         sun.r = planetData[Planets.SUN].radius * scaling / 100
         mercury.r = planetData[Planets.MERCURY].radius * scaling
         venus.r = planetData[Planets.VENUS].radius * scaling
         earth.r = planetData[Planets.EARTH].radius * scaling
         earthClouds.r = planetData[Planets.EARTH].radius * scaling * 1.02
         moon.r = planetData[Planets.MOON].radius * scaling
         mars.r = planetData[Planets.MARS].radius * scaling
         jupiter.r = planetData[Planets.JUPITER].radius * scaling
         saturn.r = planetData[Planets.SATURN].radius * scaling
         saturnRing.outerRadius = saturnRingOuterRadius * scaling
         saturnRing.innerRadius = saturnRingInnerRadius * scaling
         uranus.r = planetData[Planets.URANUS].radius * scaling
         uranusRing.outerRadius = uranusRingOuterRadius * scaling
         uranusRing.innerRadius = uranusRingInnerRadius * scaling
         neptune.r = planetData[Planets.NEPTUNE].radius * scaling
     }

     function changeSpeed(speed) {
         daysPerFrameScale = speed
     }

     function changeCameraDistance(distance) {
         cameraDistance = distance
     }

     function animate(focusedPlanet) {
         if (!ready)
             return

         advanceTime(focusedPlanet)
         for (var i = 0; i <= Planets.NUM_SELECTABLE_PLANETS; i++)
             positionPlanet(i)

         updateCamera(focusedPlanet)
     }

     function updateCamera(focusedPlanet) {
         // Get the appropriate near plane position for the camera and animate it with QML animations
         var outerRadius = Planets.getOuterRadius(focusedPlanet)
         cameraNear = outerRadius
         camera.nearPlane = cameraNear
         light.near = cameraNear

         // Calculate position
         var cameraPosition = getNewCameraPosition(focusedPlanet, outerRadius)
         var cameraOffset = Qt.vector3d(xCameraOffset, yCameraOffset, zCameraOffset)
         cameraPosition = cameraPosition.plus(cameraOffset)

         // Calculate look-at point
         var lookAtPlanet = Planets.SUN
         if (focusedPlanet !== Planets.SOLAR_SYSTEM)
             lookAtPlanet = focusedPlanet
         var cameraLookAt = Qt.vector3d(planets[lookAtPlanet].x,
                                        planets[lookAtPlanet].y,
                                        planets[lookAtPlanet].z)
         var lookAtOffset = Qt.vector3d(xLookAtOffset, yLookAtOffset, zLookAtOffset)
         cameraLookAt = cameraLookAt.plus(lookAtOffset)

         // Set position and look-at
         camera.viewCenter = cameraLookAt
         camera.position = Qt.vector3d(cameraPosition.x, cameraPosition.y, cameraPosition.z)
         camera.upVector = defaultUp
     }

     //
     // STARFIELD
     //

     Entity {
         id: starfieldEntity

         Mesh {
             id: starfield
             source: "qrc:/meshes/starfield.obj"
         }

         PlanetMaterial {
             id: materialStarfield
             effect: effectD
             ambientLight: ambientStrengthStarfield
             specularColor: Qt.rgba(0.0, 0.0, 0.0, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/galaxy_starfield.jpg"
             shininess: 1000000.0
         }

         property Transform transformStarfield: Transform {
             scale: 8500000
             translation: Qt.vector3d(0, 0, 0)
         }

         components: [ starfield, materialStarfield, transformStarfield ]
     }

     //
     // SUN
     //

     Entity {
         id: sunEntity

         Planet {
             id: sun
             tilt: planetData[Planets.SUN].tilt
         }

         PlanetMaterial {
             id: materialSun
             effect: sunEffect
             ambientLight: ambientStrengthSun
             diffuseMap: "qrc:/images/solarsystemscope/sunmap.jpg"
         }

         property Transform transformSun: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(sun.x, sun.y, sun.z))
                 m.rotate(sun.tilt, tiltAxis)
                 m.rotate(sun.roll, rollAxis)
                 m.scale(sun.r)
                 return m
             }
         }

         components: [ sun, materialSun, transformSun ]
     }

     //
     // PLANETS
     //

     // MERCURY

     Entity {
         id: mercuryEntity

         Planet {
             id: mercury
             tilt: planetData[Planets.MERCURY].tilt
         }

         PlanetMaterial {
             id: materialMercury
             effect: effectDB
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/mercurymap.jpg"
             normalMap: "qrc:/images/solarsystemscope/mercurynormal.jpg"
             shininess: shininessSpecularMap
         }

         property Transform transformMercury: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(mercury.x, mercury.y, mercury.z))
                 m.rotate(mercury.tilt, tiltAxis)
                 m.rotate(mercury.roll, rollAxis)
                 m.scale(mercury.r)
                 return m
             }
         }

         components: [ mercury, materialMercury, transformMercury ]
     }

     // VENUS

     Entity {
         id: venusEntity

         Planet {
             id: venus
             tilt: planetData[Planets.VENUS].tilt
         }

         PlanetMaterial {
             id: materialVenus
             effect: effectDB
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/venusmap.jpg"
             normalMap: "qrc:/images/solarsystemscope/venusnormal.jpg"
             shininess: shininessSpecularMap
         }

         property Transform transformVenus: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(venus.x, venus.y, venus.z))
                 m.rotate(venus.tilt, tiltAxis)
                 m.rotate(venus.roll, rollAxis)
                 m.scale(venus.r)
                 return m
             }
         }

         components: [ venus, materialVenus, transformVenus ]
     }

     // EARTH

     Entity {
         id: earthEntity

         Planet {
             id: earth
             tilt: planetData[Planets.EARTH].tilt
         }

         PlanetMaterial {
             id: materialEarth
             effect: effectDSB
             ambientLight: ambientStrengthPlanet
             diffuseMap: "qrc:/images/solarsystemscope/earthmap2k.jpg"
             specularMap: "qrc:/images/solarsystemscope/earthspec2k.jpg"
             normalMap: "qrc:/images/solarsystemscope/earthnormal2k.jpg"
             shininess: shininessSpecularMap
         }

         property Transform transformEarth: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(earth.x, earth.y, earth.z))
                 m.rotate(earth.tilt, tiltAxis)
                 m.rotate(earth.roll, rollAxis)
                 m.scale(earth.r)
                 return m
             }
         }

         components: [ earth, materialEarth, transformEarth ]
     }

     // EARTH CLOUDS

     Entity {
         id: earthCloudsEntity

         Planet {
             id: earthClouds
             tilt: planetData[Planets.EARTH].tilt
         }

         PlanetMaterial {
             id: materialEarthClouds
             effect: cloudEffect
             ambientLight: ambientStrengthClouds
             diffuseMap: "qrc:/images/solarsystemscope/earthcloudmapcolortrans.png"
             specularMap: "qrc:/images/solarsystemscope/earthcloudmapspec.jpg"
             shininess: shininessClouds
             opacity: 0.2
         }

         property Transform transformEarthClouds: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(earth.x, earth.y, earth.z))
                 m.rotate(earth.tilt, tiltAxis)
                 m.rotate(earth.roll / 1.2, rollAxis)
                 m.scale(earthClouds.r)
                 return m
             }
         }

         components: [ earthClouds, materialEarthClouds, transformEarthClouds ]
     }

     // MOON

     Entity {
         id: moonEntity

         Planet {
             id: moon
             tilt: planetData[Planets.MOON].tilt
         }

         PlanetMaterial {
             id: materialMoon
             effect: effectDB
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/moonmap2k.jpg"
             normalMap: "qrc:/images/solarsystemscope/moonnormal2k.jpg"
             shininess: shininessSpecularMap
         }

         property Transform transformMoon: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(moon.x, moon.y, moon.z))
                 m.rotate(moon.tilt, tiltAxis)
                 m.rotate(moon.roll, rollAxis)
                 m.scale(moon.r)
                 return m
             }
         }

         components: [ moon, materialMoon, transformMoon ]
     }

     // MARS

     Entity {
         id: marsEntity

         Planet {
             id: mars
             tilt: planetData[Planets.MARS].tilt
         }

         PlanetMaterial {
             id: materialMars
             effect: effectDB
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/marsmap2k.jpg"
             normalMap: "qrc:/images/solarsystemscope/marsnormal2k.jpg"
             shininess: shininessSpecularMap
         }

         property Transform transformMars: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(mars.x, mars.y, mars.z))
                 m.rotate(mars.tilt, tiltAxis)
                 m.rotate(mars.roll, rollAxis)
                 m.scale(mars.r)
                 return m
             }
         }

         components: [ mars, materialMars, transformMars ]
     }

     // JUPITER

     Entity {
         id: jupiterEntity

         Planet {
             id: jupiter
             tilt: planetData[Planets.JUPITER].tilt
         }

         PlanetMaterial {
             id: materialJupiter
             effect: effectD
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/jupitermap.jpg"
             shininess: shininessBasic
         }

         property Transform transformJupiter: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(jupiter.x, jupiter.y, jupiter.z))
                 m.rotate(jupiter.tilt, tiltAxis)
                 m.rotate(jupiter.roll, rollAxis)
                 m.scale(jupiter.r)
                 return m
             }
         }

         components: [ jupiter, materialJupiter, transformJupiter ]
     }

     // SATURN

     Entity {
         id: saturnEntity

         Planet {
             id: saturn
             tilt: planetData[Planets.SATURN].tilt
         }

         PlanetMaterial {
             id: materialSaturn
             effect: shadowMapEffect
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/saturnmap.jpg"
             shininess: shininessBasic
         }

         property Transform transformSaturn: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(saturn.x, saturn.y, saturn.z))
                 m.rotate(saturn.tilt, tiltAxis)
                 m.rotate(saturn.roll, rollAxis)
                 m.scale(saturn.r)
                 return m
             }
         }

         components: [ saturn, materialSaturn, transformSaturn ]
     }

     // SATURN RING

     Entity {
         id: saturnRingEntity

         Ring {
             id: saturnRing
             innerRadius: saturnRingInnerRadius
             outerRadius: saturnRingOuterRadius
         }

         PlanetMaterial {
             id: materialSaturnRing
             effect: shadowMapEffect
             ambientLight: ambientStrengthRing
             specularColor: Qt.rgba(0.01, 0.01, 0.01, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/saturnringcolortrans.png"
             shininess: shininessBasic
             opacity: 0.4
         }

         property Transform transformSaturnRing: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(saturn.x, saturn.y, saturn.z))
                 m.rotate(saturn.tilt, tiltAxis)
                 m.rotate(saturn.roll / 10, rollAxis)
                 m.scale((saturnRing.innerRadius + saturnRing.outerRadius) / 1.75)
                 return m
             }
         }

         components: [ saturnRing, materialSaturnRing, transformSaturnRing ]
     }

     // URANUS

     Entity {
         id: uranusEntity

         Planet {
             id: uranus
             tilt: planetData[Planets.URANUS].tilt
         }

         PlanetMaterial {
             id: materialUranus
             effect: shadowMapEffect
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/uranusmap.jpg"
             shininess: shininessBasic
         }

         property Transform transformUranus: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(uranus.x, uranus.y, uranus.z))
                 m.rotate(uranus.tilt, tiltAxis)
                 m.rotate(uranus.roll, rollAxis)
                 m.scale(uranus.r)
                 return m
             }
         }

         components: [ uranus, materialUranus, transformUranus ]
     }

     // URANUS RING

     Entity {
         id: uranusRingEntity

         Ring {
             id: uranusRing
             innerRadius: uranusRingInnerRadius
             outerRadius: uranusRingOuterRadius
         }

         PlanetMaterial {
             id: materialUranusRing
             effect: shadowMapEffect
             ambientLight: ambientStrengthRing
             specularColor: Qt.rgba(0.01, 0.01, 0.01, 1.0)
             diffuseMap: "qrc:/images/nasa/uranusringcolortrans.png"
             shininess: shininessBasic
             opacity: 0.4
         }

         property Transform transformUranusRing: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(uranus.x, uranus.y, uranus.z))
                 m.rotate(uranus.tilt, tiltAxis)
                 m.rotate(uranus.roll / 10, rollAxis)
                 m.scale((uranusRing.innerRadius + uranusRing.outerRadius) / 1.75)
                 return m
             }
         }

         components: [ uranusRing, materialUranusRing, transformUranusRing ]
     }

     // NEPTUNE

     Entity {
         id: neptuneEntity

         Planet {
             id: neptune
             tilt: planetData[Planets.NEPTUNE].tilt
         }

         PlanetMaterial {
             id: materialNeptune
             effect: effectD
             ambientLight: ambientStrengthPlanet
             specularColor: Qt.rgba(0.2, 0.2, 0.2, 1.0)
             diffuseMap: "qrc:/images/solarsystemscope/neptunemap.jpg"
             shininess: shininessBasic
         }

         property Transform transformNeptune: Transform {
             matrix: {
                 var m = Qt.matrix4x4()
                 m.translate(Qt.vector3d(neptune.x, neptune.y, neptune.z))
                 m.rotate(neptune.tilt, tiltAxis)
                 m.rotate(neptune.roll, rollAxis)
                 m.scale(neptune.r)
                 return m
             }
         }

         components: [ neptune, materialNeptune, transformNeptune ]
     }
 }