private void addControls() {
    int right = _application.getWidth();
    int bottom = _application.getHeight();

    addTextArea();

    SceneUtilities.addButton(
        this,
        "images/backButton.png",
        new Vector3D(60, bottom - 35),
        new TapAction() {
          @Override
          public void onTap() {
            _application.popScene();
          }
        });
    _okButton =
        SceneUtilities.addButton(
            this,
            "images/okButton.png",
            new Vector3D(right / 2, bottom - bottom / 3),
            new TapAction() {
              @Override
              public void onTap() {
                acceptPayment();
              }
            });
    _okButton.setEnabled(false);
  }
  // Global input processor listener implementation (IMTInputEventListener)
  public boolean processInputEvent(MTInputEvent inEvt) {
    if (inEvt instanceof MTFiducialInputEvt) {
      MTFiducialInputEvt fEvt = (MTFiducialInputEvt) inEvt;
      int fID = fEvt.getFiducialId();
      Vector3D position = fEvt.getPosition();

      AbstractShape comp;
      switch (fEvt.getId()) {
        case MTFiducialInputEvt.INPUT_STARTED:
          // Create a new component for the fiducial
          AbstractShape newComp = createComponent(fID, position);
          fiducialIDToComp.put(fID, newComp); // Map id to component
          // Move component to fiducial position
          newComp.setPositionGlobal(position);
          // Save the absolute rotation angle in the component for late
          newComp.setUserData("angle", fEvt.getAngle());
          // Rotate the component
          newComp.rotateZ(
              newComp.getCenterPointRelativeToParent(),
              AbstractMTApplication.degrees(fEvt.getAngle()));
          // Add the component to the canvas to draw it
          getCanvas().addChild(newComp);
          break;
        case MTFiducialInputEvt.INPUT_UPDATED:
          // Retrieve the corresponding component for the fiducial ID from the map
          comp = fiducialIDToComp.get(fID);
          if (comp != null) {
            // Set the new position
            comp.setPositionGlobal(position);
            // Set the rotation (we have to do a little more here because
            // mt4j does incremental rotations instead of specifying an absolute angle)
            float oldAngle = (Float) comp.getUserData("angle"); // retrieve the "old" angle
            float newAngle = fEvt.getAngle();
            if (oldAngle != newAngle) {
              float diff = newAngle - oldAngle;
              comp.setUserData("angle", newAngle);
              diff =
                  AbstractMTApplication.degrees(diff); // our rotation expects degrees (not radians)
              comp.rotateZ(comp.getCenterPointRelativeToParent(), diff);
            }
          }
          break;
        case MTFiducialInputEvt.INPUT_ENDED:
          comp = fiducialIDToComp.get(fID);
          if (comp != null) {
            comp.destroy();
            fiducialIDToComp.remove(fID);
          }
          break;
        default:
          break;
      }
    }
    return false;
  }
예제 #3
0
 private void reset() {
   if (ball.getUserData("resetted") == null) { // To make sure that we call destroy only once
     ball.setUserData("resetted", true);
     app.invokeLater(
         new Runnable() {
           public void run() {
             IPhysicsComponent a = (IPhysicsComponent) ball;
             a.getBody()
                 .setXForm(
                     new Vec2(
                         getMTApplication().width / 2f / scale,
                         getMTApplication().height / 2f / scale),
                     a.getBody().getAngle());
             //					a.getBody().setLinearVelocity(new Vec2(0,0));
             a.getBody()
                 .setLinearVelocity(
                     new Vec2(ToolsMath.getRandom(-8, 8), ToolsMath.getRandom(-8, 8)));
             a.getBody().setAngularVelocity(0);
             ball.setUserData("resetted", null);
           }
         });
   }
   this.scorePlayer1 = 0;
   this.scorePlayer2 = 0;
   this.updateScores();
 }
 private void enablePaymentButton(final boolean enable) {
   _application.invokeLater(
       new Runnable() {
         @Override
         public void run() {
           _okButton.setEnabled(enable);
         }
       });
 }
예제 #5
0
  public void drawBrush(int steps, Vector3D whatToAdd) {
    int i = 0;
    do {
      i++;
      cPos.addLocal(whatToAdd);
      // Draw new brush into FBO at correct position
      Vector3D diff = cPos.getSubtracted(drawLine.getCenterPointLocal());

      drawApp.pushMatrix();
      drawApp.translate(diff.x, diff.y);
      // Draw brush

      drawLine.setStrokeColor(lineColor);
      drawLine.setFillColor(lineColor);
      drawLine.drawComponent(drawApp.g);

      drawApp.popMatrix();
    } while (i < steps);
  }
 private void showText(final String text) {
   _application.invokeLater(
       new Runnable() {
         @Override
         public void run() {
           _textArea.setText(text);
           _textArea.setPositionGlobal(
               new Vector3D(_application.getWidth() / 2, _application.getHeight() / 3));
         }
       });
 }
  /**
   * Instantiates a new mT scene window.
   *
   * @param applet the applet
   * @param scene the scene
   * @param borderWidth the border width
   * @param borderHeight the border height
   * @param fboWidth the fbo width
   * @param fboHeight the fbo height
   */
  public MTSceneWindow(
      final AbstractMTApplication applet,
      final Iscene scene,
      float borderWidth,
      float borderHeight,
      int fboWidth,
      int fboHeight) {
    //		super(0-borderWidth, 0-borderHeight, applet.width+2*borderWidth,
    // applet.height+2*borderHeight, applet);
    super(
        applet,
        0 - borderWidth,
        0 - borderHeight,
        0,
        MT4jSettings.getInstance().getWindowWidth() + 2 * borderWidth,
        MT4jSettings.getInstance().getWindowHeight() + 2 * borderHeight,
        30,
        30);

    this.setStrokeColor(new MTColor(0, 0, 0));

    sceneTexture = new MTSceneTexture(applet, 0, 0, fboWidth, fboHeight, scene);
    sceneTexture.setStrokeColor(new MTColor(0, 0, 0));
    this.addChild(sceneTexture);

    // Add the scene to the scene list in the Application
    // FIXME add the scene later to the MTApplication because if we add the scene
    // before any other scene is added it becomes the active scene which we dont want
    if (applet.getSceneCount() == 0) {
      applet.invokeLater(
          new Runnable() {
            public void run() {
              applet.addScene(sceneTexture.getScene());
            }
          });
    } else {
      applet.addScene(sceneTexture.getScene());
    }

    sceneTexture.addStateChangeListener(
        StateChange.COMPONENT_DESTROYED,
        new StateChangeListener() {
          public void stateChanged(StateChangeEvent evt) {
            destroy();
          }
        });

    if (closeButtonImage == null) {
      closeButtonImage =
          applet.loadImage(
              MT4jSettings.getInstance().getDefaultImagesPath()
                  +
                  //			"close_32.png")
                  //			"126182-simple-black-square-icon-alphanumeric-circled-x3_cr.png"
                  //			"124241-matte-white-square-icon-alphanumeric-circled-x3_cr.png"
                  //			"124241-matte-white-square-icon-alphanumeric-circled-x3128.png"
                  "closeButton64.png");
    }
    MTImageButton closeButton = new MTImageButton(applet, closeButtonImage);
    closeButton.addGestureListener(
        TapProcessor.class,
        new IGestureEventListener() {
          public boolean processGestureEvent(MTGestureEvent ge) {
            TapEvent te = (TapEvent) ge;
            if (te.isTapped()) {
              close();
            }
            return true;
          }
        });
    this.addChild(closeButton);
    closeButton.setNoStroke(true);
    //		closeButton.setSizeXYRelativeToParent(borderWidth - borderWidth/20, borderWidth -
    // borderWidth/20);
    closeButton.setSizeXYRelativeToParent(
        borderWidth - borderWidth / 30, borderWidth - borderWidth / 30);
    //		closeButton.setSizeXYRelativeToParent(borderWidth -0.5f, borderWidth-0.5f);
    closeButton.setPositionRelativeToParent(
        new Vector3D((applet.width + (borderWidth / 2f)), borderHeight - 5));

    if (maximizeButtonImage == null) {
      maximizeButtonImage =
          applet.loadImage(
              MT4jSettings.getInstance().getDefaultImagesPath()
                  +
                  //			"window_app_blank_32.png")
                  //			"127941-simple-black-square-icon-symbols-shapes-maximize-button_cr.png"
                  "maximizeButton64.png");
    }
    MTImageButton maximizeButton = new MTImageButton(applet, maximizeButtonImage);
    maximizeButton.addGestureListener(
        TapProcessor.class,
        new IGestureEventListener() {
          public boolean processGestureEvent(MTGestureEvent ge) {
            TapEvent te = (TapEvent) ge;
            if (te.isTapped()) {
              maximize();
            }
            return true;
          }
        });
    this.addChild(maximizeButton);
    maximizeButton.setNoStroke(true);
    //		maximizeButton.setSizeXYRelativeToParent(borderWidth - borderWidth/10, borderWidth -
    // borderWidth/10);
    maximizeButton.setSizeXYRelativeToParent(
        borderWidth - borderWidth / 30, borderWidth - borderWidth / 30);
    //		maximizeButton.setPositionRelativeToParent(new Vector3D(
    // (applet.width+2*borderWidth)-maximizeButton.getWidthXY(TransformSpace.RELATIVE_TO_PARENT),
    // closeButton.getHeightXY(TransformSpace.RELATIVE_TO_PARENT) + 40));
    //		maximizeButton.setPositionRelativeToParent(new Vector3D( (applet.width+ (borderWidth /2f)),
    // borderHeight + closeButton.getHeightXY(TransformSpace.RELATIVE_TO_PARENT) + 15));
    maximizeButton.setPositionRelativeToParent(
        new Vector3D(
            (applet.width + (borderWidth / 2f)),
            applet.height - closeButton.getHeightXY(TransformSpace.RELATIVE_TO_PARENT) / 2f));
  }
예제 #8
0
  public AirHockeyScene(AbstractMTApplication mtApplication, String name) {
    super(mtApplication, name);
    this.app = mtApplication;
    //		this.setClearColor(new MTColor(120,150,150));
    //		this.setClearColor(new MTColor(190, 190, 170, 255));
    this.setClearColor(new MTColor(0, 0, 0, 255));
    //		this.setClearColor(new MTColor(40, 40, 40, 255));
    this.registerGlobalInputProcessor(new CursorTracer(app, this));

    this.scorePlayer1 = 0;
    this.scorePlayer2 = 0;

    float worldOffset = 10; // Make Physics world slightly bigger than screen borders
    // Physics world dimensions
    AABB worldAABB =
        new AABB(
            new Vec2(-worldOffset, -worldOffset),
            new Vec2((app.width) / scale + worldOffset, (app.height) / scale + worldOffset));
    Vec2 gravity = new Vec2(0, 0);
    boolean sleep = true;
    // Create the pyhsics world
    this.world = new World(worldAABB, gravity, sleep);

    // Update the positions of the components according the the physics simulation each frame
    this.registerPreDrawAction(
        new UpdatePhysicsAction(world, timeStep, constraintIterations, scale));

    physicsContainer = new MTComponent(app);
    // Scale the physics container. Physics calculations work best when the dimensions are small
    // (about 0.1 - 10 units)
    // So we make the display of the container bigger and add in turn make our physics object
    // smaller
    physicsContainer.scale(scale, scale, 1, Vector3D.ZERO_VECTOR);
    this.getCanvas().addChild(physicsContainer);

    // Create borders around the screen
    this.createScreenBorders(physicsContainer);

    // Create gamefield marks
    MTLine line =
        new MTLine(
            mtApplication,
            mtApplication.width / 2f / scale,
            0,
            mtApplication.width / 2f / scale,
            mtApplication.height / scale);
    line.setPickable(false);
    //		line.setStrokeColor(new MTColor(0,0,0));
    line.setStrokeColor(new MTColor(150, 150, 150));
    line.setStrokeWeight(0.5f);
    physicsContainer.addChild(line);

    MTEllipse centerCircle =
        new MTEllipse(
            mtApplication,
            new Vector3D(mtApplication.width / 2f / scale, mtApplication.height / 2f / scale),
            80 / scale,
            80 / scale);
    centerCircle.setPickable(false);
    centerCircle.setNoFill(true);
    //		centerCircle.setStrokeColor(new MTColor(0,0,0));
    centerCircle.setStrokeColor(new MTColor(150, 150, 150));
    centerCircle.setStrokeWeight(0.5f);
    physicsContainer.addChild(centerCircle);

    MTEllipse centerCircleInner =
        new MTEllipse(
            mtApplication,
            new Vector3D(mtApplication.width / 2f / scale, mtApplication.height / 2f / scale),
            10 / scale,
            10 / scale);
    centerCircleInner.setPickable(false);
    centerCircleInner.setFillColor(new MTColor(160, 160, 160));
    //		centerCircleInner.setStrokeColor(new MTColor(150,150,150));
    //		centerCircleInner.setStrokeColor(new MTColor(0,0,0));
    centerCircleInner.setStrokeColor(new MTColor(150, 150, 150));
    centerCircleInner.setStrokeWeight(0.5f);
    physicsContainer.addChild(centerCircleInner);

    // Create the paddles
    PImage paddleTex = mtApplication.loadImage(imagesPath + "paddle.png");
    redCircle =
        new Paddle(
            app,
            new Vector3D(mtApplication.width - 60, mtApplication.height / 2f),
            50,
            world,
            1.0f,
            0.3f,
            0.4f,
            scale);
    redCircle.setTexture(paddleTex);
    redCircle.setFillColor(new MTColor(255, 50, 50));
    redCircle.setNoStroke(true);
    redCircle.setName("red");
    redCircle.setPickable(false);
    physicsContainer.addChild(redCircle);

    blueCircle =
        new Paddle(
            app, new Vector3D(80, mtApplication.height / 2f), 50, world, 1.0f, 0.3f, 0.4f, scale);
    blueCircle.setTexture(paddleTex);
    blueCircle.setFillColor(new MTColor(50, 50, 255));
    blueCircle.setNoStroke(true);
    blueCircle.setName("blue");
    blueCircle.setPickable(false);
    physicsContainer.addChild(blueCircle);

    // Create the ball
    ball =
        new HockeyBall(
            app,
            new Vector3D(mtApplication.width / 2f, mtApplication.height / 2f),
            38,
            world,
            0.5f,
            0.005f,
            0.70f,
            scale);
    //		MTColor ballCol = new MTColor(0,255,0);
    //		ball.setFillColor(ballCol);
    PImage ballTex = mtApplication.loadImage(imagesPath + "puk.png");
    ball.setTexture(ballTex);
    //		ball.setFillColor(new MTColor(160,160,160,255));
    ball.setFillColor(new MTColor(255, 255, 255, 255));
    ball.setNoStroke(true);
    ball.setName("ball");
    physicsContainer.addChild(ball);
    ball.getBody()
        .applyImpulse(
            new Vec2(ToolsMath.getRandom(-8f, 8), ToolsMath.getRandom(-8, 8)),
            ball.getBody().getWorldCenter());

    // Create the GOALS
    HockeyGoal goal1 =
        new HockeyGoal(
            new Vector3D(0, mtApplication.height / 2f),
            50,
            mtApplication.height / 4f,
            mtApplication,
            world,
            0.0f,
            0.1f,
            0.0f,
            scale);
    goal1.setName("goal1");
    goal1.setFillColor(new MTColor(0, 0, 255));
    goal1.setStrokeColor(new MTColor(0, 0, 255));
    physicsContainer.addChild(goal1);

    HockeyGoal goal2 =
        new HockeyGoal(
            new Vector3D(mtApplication.width, mtApplication.height / 2f),
            50,
            mtApplication.height / 4f,
            mtApplication,
            world,
            0.0f,
            0.1f,
            0.0f,
            scale);
    goal2.setName("goal2");
    goal2.setFillColor(new MTColor(255, 0, 0));
    goal2.setStrokeColor(new MTColor(255, 0, 0));
    physicsContainer.addChild(goal2);

    // Make two components for both game field sides to drag the puks upon
    MTRectangle leftSide =
        new MTRectangle(
            app,
            PhysicsHelper.scaleDown(0, scale),
            PhysicsHelper.scaleDown(0, scale),
            PhysicsHelper.scaleDown(app.width / 2f, scale),
            PhysicsHelper.scaleDown(app.height, scale));
    leftSide.setName("left side");
    leftSide.setNoFill(true); // Make it invisible -> only used for dragging
    leftSide.setNoStroke(true);
    leftSide.unregisterAllInputProcessors();
    leftSide.removeAllGestureEventListeners(DragProcessor.class);
    leftSide.registerInputProcessor(new DragProcessor(app));
    leftSide.addGestureListener(DragProcessor.class, new GameFieldHalfDragListener(blueCircle));
    physicsContainer.addChild(0, leftSide);
    MTRectangle rightSide =
        new MTRectangle(
            app,
            PhysicsHelper.scaleDown(app.width / 2f, scale),
            PhysicsHelper.scaleDown(0, scale),
            PhysicsHelper.scaleDown(app.width, scale),
            PhysicsHelper.scaleDown(app.height, scale));
    rightSide.setName("right Side");
    rightSide.setNoFill(true); // Make it invisible -> only used for dragging
    rightSide.setNoStroke(true);
    rightSide.unregisterAllInputProcessors();
    rightSide.removeAllGestureEventListeners(DragProcessor.class);
    rightSide.registerInputProcessor(new DragProcessor(app));
    rightSide.addGestureListener(DragProcessor.class, new GameFieldHalfDragListener(redCircle));
    physicsContainer.addChild(0, rightSide);

    // Display Score UI
    MTComponent uiLayer = new MTComponent(mtApplication, new MTCamera(mtApplication));
    uiLayer.setDepthBufferDisabled(true);
    getCanvas().addChild(uiLayer);
    IFont font = FontManager.getInstance().createFont(mtApplication, "arial", 50, MTColor.WHITE);

    t1 = new MTTextArea(mtApplication, font);
    t1.setPickable(false);
    t1.setNoFill(true);
    t1.setNoStroke(true);
    t1.setPositionGlobal(new Vector3D(5, 30, 0));
    uiLayer.addChild(t1);

    t2 = new MTTextArea(mtApplication, font);
    t2.setPickable(false);
    t2.setNoFill(true);
    t2.setNoStroke(true);
    t2.setPositionGlobal(new Vector3D(mtApplication.width - 65, 30, 0));
    uiLayer.addChild(t2);
    this.updateScores();

    // Set up check for collisions between objects
    this.addWorldContactListener(world);

    /*
    		//Sound
    		if (enableSound){
    			minim = new Minim(mtApplication);
    			wallHit = minim.loadSnippet(MT4jSettings.getInstance().getDataFolderPath() + "sound" + File.separator + "paddleBallHit.wav");
    //			paddleBallClash = minim.loadSample(MT4jSettings.getInstance().getDataFolderPath() + "sound" + File.separator + "paddleBallHit.wav", 2048);
    //			goalHit = minim.loadSnippet(MT4jSettings.getInstance().getDataFolderPath() + "sound" + File.separator + "goal.wav");
    //			goalHit.play();
    			paddleHit = minim.loadSnippet(MT4jSettings.getInstance().getDataFolderPath() + "sound" + File.separator + "wallHit.wav");
    		}
    		*/
  }
  /**
   * Instantiates a new model display scene.
   *
   * @param mtApplication the mt application
   * @param name the name
   */
  public Space3DScene(AbstractMTApplication mtApplication, String name) {
    super(mtApplication, name);
    this.pa = mtApplication;

    if (!MT4jSettings.getInstance().isOpenGlMode()) {
      System.err.println("Scene only usable when using the OpenGL renderer! - See settings.txt");
      return;
    }

    this.setClearColor(new MTColor(200, 200, 200, 255));
    this.registerGlobalInputProcessor(new CursorTracer(pa, this));

    // Add a background image for the scene
    this.getCanvas()
        .addChild(new MTBackgroundImage(pa, pa.loadImage(imagesPath + "3040.jpg"), true));

    // Init light settings
    MTLight.enableLightningAndAmbient(pa, 150, 150, 150, 255);
    // Create a light source //I think GL_LIGHT0 is used by processing!
    //		MTLight light = new MTLight(pa, GL.GL_LIGHT3, new Vector3D(0,0,0));
    MTLight light = new MTLight(pa, GL.GL_LIGHT3, new Vector3D(pa.width / 5f, -pa.height / 10f, 0));

    // Set up a material to react to the light
    GLMaterial material = new GLMaterial(PlatformUtil.getGL());
    material.setAmbient(new float[] {.1f, .1f, .1f, 1f});
    material.setDiffuse(new float[] {1.0f, 1.0f, 1.0f, 1f});
    material.setEmission(new float[] {.0f, .0f, .0f, 1f});
    material.setSpecular(new float[] {1.0f, 1.0f, 1.0f, 1f}); // almost white: very reflective
    material.setShininess(127); // 0=no shine,  127=max shine

    // Create the earth
    earth = new MTSphere(pa, "earth", 40, 40, 80, TextureMode.Projected); // TextureMode.Polar);
    earth.setLight(light);
    earth.setMaterial(material);
    earth.rotateX(earth.getCenterPointRelativeToParent(), -90);
    earth.setTexture(
        new GLTexture(
            pa,
            imagesPath + "worldMap.jpg",
            new GLTextureSettings(
                TEXTURE_TARGET.TEXTURE_2D,
                SHRINKAGE_FILTER.Trilinear,
                EXPANSION_FILTER.Bilinear,
                WRAP_MODE.CLAMP_TO_EDGE,
                WRAP_MODE.CLAMP_TO_EDGE)));
    earth.generateAndUseDisplayLists();
    earth.setPositionGlobal(
        new Vector3D(
            pa.width / 2f,
            pa.height / 2f,
            250)); // earth.setPositionGlobal(new Vector3D(200, 200, 250));
    // Animate earth rotation
    new Animation(
            "rotation animation", new MultiPurposeInterpolator(0, 360, 17000, 0, 1, -1), earth)
        .addAnimationListener(
            new IAnimationListener() {
              public void processAnimationEvent(AnimationEvent ae) {
                earth.rotateY(earth.getCenterPointLocal(), ae.getDelta(), TransformSpace.LOCAL);
              }
            })
        .start();

    // Put planets in a group that can be manipulated by gestures
    // so the rotation of the planets doesent get changed by the gestures
    MTComponent group = new MTComponent(mtApplication);
    group.setComposite(
        true); // This makes the group "consume" all picking and gestures of the children
    group.registerInputProcessor(new DragProcessor(mtApplication));
    group.addGestureListener(DragProcessor.class, new DefaultDragAction());
    group.addGestureListener(DragProcessor.class, new InertiaDragAction(80, 0.8f, 10));
    group.registerInputProcessor(new RotateProcessor(mtApplication));
    group.addGestureListener(RotateProcessor.class, new DefaultRotateAction());
    // Scale the earth from the center. Else it might get distorted
    group.registerInputProcessor(new ScaleProcessor(mtApplication));
    group.addGestureListener(
        ScaleProcessor.class,
        new IGestureEventListener() {
          public boolean processGestureEvent(MTGestureEvent ge) {
            ScaleEvent se = (ScaleEvent) ge;
            earth.scaleGlobal(
                se.getScaleFactorX(),
                se.getScaleFactorY(),
                se.getScaleFactorX(),
                earth.getCenterPointGlobal());
            return false;
          }
        });
    this.getCanvas().addChild(group);
    group.addChild(earth);

    // Create the moon
    final MTSphere moonSphere = new MTSphere(pa, "moon", 35, 35, 25, TextureMode.Polar);
    moonSphere.setMaterial(material);
    moonSphere.translate(new Vector3D(earth.getRadius() + moonSphere.getRadius() + 50, 0, 0));
    moonSphere.setTexture(
        new GLTexture(
            pa,
            imagesPath + "moonmap1k.jpg",
            new GLTextureSettings(
                TEXTURE_TARGET.RECTANGULAR,
                SHRINKAGE_FILTER.Trilinear,
                EXPANSION_FILTER.Bilinear,
                WRAP_MODE.CLAMP_TO_EDGE,
                WRAP_MODE.CLAMP_TO_EDGE)));
    moonSphere.generateAndUseDisplayLists();
    moonSphere.unregisterAllInputProcessors();
    // Rotate the moon around the earth
    new Animation(
            "moon animation", new MultiPurposeInterpolator(0, 360, 12000, 0, 1, -1), moonSphere)
        .addAnimationListener(
            new IAnimationListener() {
              public void processAnimationEvent(AnimationEvent ae) {
                moonSphere.rotateZ(
                    earth.getCenterPointLocal(), ae.getDelta(), TransformSpace.RELATIVE_TO_PARENT);
              }
            })
        .start();
    // Rotate the moon around ints own center
    new Animation(
            "moon animation around own axis",
            new MultiPurposeInterpolator(0, 360, 9000, 0, 1, -1),
            moonSphere)
        .addAnimationListener(
            new IAnimationListener() {
              public void processAnimationEvent(AnimationEvent ae) {
                moonSphere.rotateZ(
                    moonSphere.getCenterPointLocal(), -3 * ae.getDelta(), TransformSpace.LOCAL);
                moonSphere.rotateY(
                    moonSphere.getCenterPointLocal(), 0.5f * ae.getDelta(), TransformSpace.LOCAL);
              }
            })
        .start();
    earth.addChild(moonSphere);
  }