官方例子---SolarSystem

2019-01-22  本文已影响0人  爱做梦的严重精神病患者

与HelloSceneform的区别

1.SolarActivity

 首先,同样是检测设备支持,之后加载布局、获取视图实例:

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (!DemoUtils.checkIsSupportedDeviceOrFinish(this)) {
      // Not a supported device.
      return;
    }

    setContentView(R.layout.activity_solar);
    arSceneView = findViewById(R.id.ar_scene_view);
}

 与HellowSceneform不同的是,SolarSystem使用的视图是ArSceneView,而不是ArFragment。使用ArFragment会自动配置ArSceneView和ArSession等。

 接下来,创建渲染对象:

// Build all the planet models.
    CompletableFuture<ModelRenderable> sunStage =
        ModelRenderable.builder().setSource(this, Uri.parse("Sol.sfb")).build();
    CompletableFuture<ModelRenderable> mercuryStage =
        ModelRenderable.builder().setSource(this, Uri.parse("Mercury.sfb")).build();
...
    // Build a renderable from a 2D View.
    CompletableFuture<ViewRenderable> solarControlsStage =
        ViewRenderable.builder().setView(this, R.layout.solar_controls).build();

CompletableFuture.allOf(
            sunStage,
            mercuryStage,
           ...
            solarControlsStage)
        .handle(
            (notUsed, throwable) -> {
              // When you build a Renderable, Sceneform loads its resources in the background while
              // returning a CompletableFuture. Call handle(), thenAccept(), or check isDone()
              // before calling get().

              if (throwable != null) {
                DemoUtils.displayError(this, "Unable to load renderable", throwable);
                return null;
              }

              try {
                sunRenderable = sunStage.get();
                mercuryRenderable = mercuryStage.get();
              ...
                solarControlsRenderable = solarControlsStage.get();

                // Everything finished loading successfully.
                hasFinishedLoading = true;

              } catch (InterruptedException | ExecutionException ex) {
                DemoUtils.displayError(this, "Unable to load renderable", ex);
              }

              return null;
            });

 创建可渲染对象都是利用CompletableFuture<?>。其中在SolarSystem中分别创建了3D和2D的模型,差别在于分别是CompleteableFuture<ModelRenderable>和CompleteableFuture<ViewRenderable>。同时,构建方法也不同,ModelRenderable.builder().setSource()和ViewRenderable.builder().setView()。

 然后,添加监听器,响应手指在屏幕上的各种动作:

gestureDetector =
        new GestureDetector(
            this,
            new GestureDetector.SimpleOnGestureListener() {
              @Override
              public boolean onSingleTapUp(MotionEvent e) {
                onSingleTap(e);
                return true;
              }

              @Override
              public boolean onDown(MotionEvent e) {
                return true;
              }
            });

private void onSingleTap(MotionEvent tap) {
    if (!hasFinishedLoading) {
      // We can't do anything yet.
      return;
    }

    Frame frame = arSceneView.getArFrame();
    if (frame != null) {
      if (!hasPlacedSolarSystem && tryPlaceSolarSystem(tap, frame)) {
        hasPlacedSolarSystem = true;
      }
    }
  }

 其中具体的逻辑判断比较复杂,涉及的东西较多,主要就是以下3个方法:

private boolean tryPlaceSolarSystem(MotionEvent tap, Frame frame) {
    if (tap != null && frame.getCamera().getTrackingState() == TrackingState.TRACKING) {
      for (HitResult hit : frame.hitTest(tap)) {
        Trackable trackable = hit.getTrackable();
        if (trackable instanceof Plane && ((Plane) trackable).isPoseInPolygon(hit.getHitPose())) {
          // Create the Anchor.
          Anchor anchor = hit.createAnchor();
          AnchorNode anchorNode = new AnchorNode(anchor);
          anchorNode.setParent(arSceneView.getScene());
          Node solarSystem = createSolarSystem();
          anchorNode.addChild(solarSystem);
          return true;
        }
      }
    }

    return false;
  }
 private Node createSolarSystem() {
    //创建一个基础节点
    Node base = new Node();

    //创建太阳节点
    Node sun = new Node();
    sun.setParent(base);
    sun.setLocalPosition(new Vector3(0.0f, 0.5f, 0.0f));

    Node sunVisual = new Node();
    sunVisual.setParent(sun);
    //创建sunVisual的模型对象
    sunVisual.setRenderable(sunRenderable);
    sunVisual.setLocalScale(new Vector3(0.5f, 0.5f, 0.5f));

    Node solarControls = new Node();
    solarControls.setParent(sun);
    //创建控制器的模型对象
    solarControls.setRenderable(solarControlsRenderable);
    solarControls.setLocalPosition(new Vector3(0.0f, 0.25f, 0.0f));

    //获取ViewRenderable的View对象
    View solarControlsView = solarControlsRenderable.getView();
    SeekBar orbitSpeedBar = solarControlsView.findViewById(R.id.orbitSpeedBar);
    orbitSpeedBar.setProgress((int) (solarSettings.getOrbitSpeedMultiplier() * 10.0f));
    orbitSpeedBar.setOnSeekBarChangeListener(
        new SeekBar.OnSeekBarChangeListener() {
          @Override
          public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            float ratio = (float) progress / (float) orbitSpeedBar.getMax();
            solarSettings.setOrbitSpeedMultiplier(ratio * 10.0f);
          }

          @Override
          public void onStartTrackingTouch(SeekBar seekBar) {}

          @Override
          public void onStopTrackingTouch(SeekBar seekBar) {}
        });

    SeekBar rotationSpeedBar = solarControlsView.findViewById(R.id.rotationSpeedBar);
    rotationSpeedBar.setProgress((int) (solarSettings.getRotationSpeedMultiplier() * 10.0f));
    rotationSpeedBar.setOnSeekBarChangeListener(
        new SeekBar.OnSeekBarChangeListener() {
          @Override
          public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
            float ratio = (float) progress / (float) rotationSpeedBar.getMax();
            solarSettings.setRotationSpeedMultiplier(ratio * 10.0f);
          }

          @Override
          public void onStartTrackingTouch(SeekBar seekBar) {}

          @Override
          public void onStopTrackingTouch(SeekBar seekBar) {}
        });

    // Toggle the solar controls on and off by tapping the sun.
    sunVisual.setOnTapListener(
        (hitTestResult, motionEvent) -> solarControls.setEnabled(!solarControls.isEnabled()));

    //创建模型对象
    createPlanet("Mercury", sun, 0.4f, 47f, mercuryRenderable, 0.019f);

    createPlanet("Venus", sun, 0.7f, 35f, venusRenderable, 0.0475f);

    Node earth = createPlanet("Earth", sun, 1.0f, 29f, earthRenderable, 0.05f);

    createPlanet("Moon", earth, 0.15f, 100f, lunaRenderable, 0.018f);

    createPlanet("Mars", sun, 1.5f, 24f, marsRenderable, 0.0265f);

    createPlanet("Jupiter", sun, 2.2f, 13f, jupiterRenderable, 0.16f);

    createPlanet("Saturn", sun, 3.5f, 9f, saturnRenderable, 0.1325f);

    createPlanet("Uranus", sun, 5.2f, 7f, uranusRenderable, 0.1f);

    createPlanet("Neptune", sun, 6.1f, 5f, neptuneRenderable, 0.074f);

    return base;
  }
 private Node createPlanet(String name, Node parent, float auFromParent,
  float orbitDegreesPerSecond, ModelRenderable renderable, float planetScale) {
    // Orbit is a rotating node with no renderable positioned at the sun.
    // The planet is positioned relative to the orbit so that it appears to rotate around the sun.
    // This is done instead of making the sun rotate so each planet can orbit at its own speed.
    RotatingNode orbit = new RotatingNode(solarSettings, true);
    orbit.setDegreesPerSecond(orbitDegreesPerSecond);
    orbit.setParent(parent);

    // Create the planet and position it relative to the sun.
    Planet planet = new Planet(this, name, planetScale, renderable, solarSettings);
    planet.setParent(orbit);
    planet.setLocalPosition(new Vector3(auFromParent * AU_TO_METERS, 0.0f, 0.0f));

    return planet;
  }

 其中createSolarSystem()和createPlanet()方法相对复杂,需要进一步拆解。而tryPlaceSolarSystem()方法主要的功能就是将建立好的SolarSystem放入场景之中,不难看懂。

 对于自转和公转都是靠ObjectAnimator实现,具体实现不详细展开。

 个人理解:Node都存在parent,可以把parent理解为一个空间坐标系。在这个程序例子中,orbit为planet的parent,orbit的绕Y轴转形成了公转,而planet的绕Y轴转形成了自转。


个人理解图.jpg
上一篇下一篇

猜你喜欢

热点阅读