官方例子---SolarSystem

与HelloSceneform的区别

  • HelloSceneform使用的包是ux:sceneform-ux:1.6.0,并且在布局上使用了ArFragment,自动完成了一些相关配置(初步估计有Camera权限申请)。

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 翻译自“Collection View Programming Guide for iOS” 0 关于iOS集合视...
    lakerszhy阅读 9,363评论 1 22
  • 在接下来的五章中,您将构建一个名为 WorldTrotter 的应用程序。 完成后,此应用程序将转换华氏温度和摄氏...
    titvax阅读 4,619评论 0 3
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 13,887评论 1 32
  • edenmandom 生活諺語解說 這個男人擁有這隻狗,牠不能行走。因此他每天用輪椅帶牠出去散步。我找不到任何詞句...
    黑貓子阅读 1,418评论 0 0
  • 做寿司啦,做寿司啦我高兴地喊道,原来今天要做寿司。寿司的日本的料理,看起来十分诱人,我就叫妈妈在手机上给我买了一份...
    岁婉钰阅读 1,526评论 0 0

友情链接更多精彩内容