Flutter三维模型加载插件three_js_tjs_loader的使用

Flutter三维模型加载插件three_js_tjs_loader的使用

three_js_tjs_loaders

这是一个允许用户将three.js JSON文件添加到项目的three.js模型加载器类型。

Gif suzanne modle rotating.

该插件是three.js和three_dart的dart版本,最初由[@mrdoob]创建,并且有一个由[@wasabia]转换的dart分支。

开始使用

在您的pubspec.yaml文件中添加以下依赖项,同时还需要其他部分的three_js_math, three_js_core, three_js_animations, 和 three_js_core_loaders

dependencies:
  three_js_core: ^x.y.z
  three_js_math: ^x.y.z
  three_js_tjs_loader: ^x.y.z

初始化

late Scene scene;

void init() {
  scene = Scene();
  scene.background = Color.fromHex32(0xf0f0f0);
      
  final loader = ObjectLoader();
  final tjsl = await loader.fromAsset('assets/${fileName}.json');
  scene.add(tjsl);
}

void update() {
  controls.update();
}

使用

此项目是为three_js提供的threejs json模型加载器。

示例

以下是使用three_js_tjs_loader的完整示例代码:

import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'package:three_js_core/three_js_core.dart' as three;
import 'package:three_js_math/three_js_math.dart' as tmath;
import 'package:three_js_tjs_loader/three_js_tjs_loader.dart';

enum Method {
  instance,
  merged,
  native
}

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const WebglInstancingPerformance(),
    );
  }
}

class WebglInstancingPerformance extends StatefulWidget {
  const WebglInstancingPerformance({super.key});

  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<WebglInstancingPerformance> {
  late three.ThreeJS threeJs;

  [@override](/user/override)
  void initState() {
    threeJs = three.ThreeJS(
      onSetupComplete: () { setState(() {}); },
      setup: setup,
      settings: three.Settings(
        renderOptions: {
          "minFilter": tmath.LinearFilter,
          "magFilter": tmath.LinearFilter,
          "format": tmath.RGBAFormat,
          "samples": 4
        }
      )
    );
    super.initState();
  }

  [@override](/user/override)
  void dispose() {
    threeJs.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return threeJs.build();
  }

  late three.Material material;

  final Map<String, dynamic> api = {
    'method': Method.instance,
    'count': 1000
  };

  Future<void> setup() async {
    threeJs.camera = three.PerspectiveCamera(70, threeJs.width / threeJs.height, 1, 100);
    threeJs.camera.position.z = 30;

    threeJs.scene = three.Scene();
    threeJs.scene.background = tmath.Color.fromHex32(0xffffff);

    material = three.MeshNormalMaterial();

    // 加载缓冲几何体
    BufferGeometryLoader()
      .fromAsset('assets/suzanne_buffergeometry.json')
      .then((geometry) {
        geometry as three.BufferGeometry;
        material = three.MeshNormalMaterial();
        geometry.computeVertexNormals();

        switch (api['method']) {
          case Method.instance:
            makeInstanced(geometry);
            break;
          // case Method.merged:
          //   makeMerged(geometry);
          //   break;
          case Method.native:
            makeNaive(geometry);
            break;
        }
      });

    threeJs.addAnimationEvent((dt) {
      threeJs.scene.rotation.x += 0.002;
      threeJs.scene.rotation.y += 0.001;
    });
  }

  void makeInstanced(three.BufferGeometry geometry) {
    final matrix = tmath.Matrix4();
    final mesh = three.InstancedMesh(geometry, material, api['count']);

    for (int i = 0; i < api['count']; i++) {
      randomizeMatrix(matrix);
      mesh.setMatrixAt(i, matrix);
    }

    threeJs.scene.add(mesh);  
  }

  void makeNaive(three.BufferGeometry geometry) {
    final matrix = tmath.Matrix4();

    for (int i = 0; i < api['count']; i++) {
      final mesh = three.Mesh(geometry, material);
      randomizeMatrix(matrix);
      mesh.applyMatrix4(matrix);
      threeJs.scene.add(mesh);
    }
  }

  final position = tmath.Vector3();
  final rotation = tmath.Euler(0, 0, 0);
  final quaternion = tmath.Quaternion();
  final scale = tmath.Vector3();

  void randomizeMatrix(tmath.Matrix4 matrix) {
    position.x = math.Random().nextDouble() * 40 - 20;
    position.y = math.Random().nextDouble() * 40 - 20;
    position.z = math.Random().nextDouble() * 40 - 20;

    rotation.x = math.Random().nextDouble() * 2 * math.pi;
    rotation.y = math.Random().nextDouble() * 2 * math.pi;
    rotation.z = math.Random().nextDouble() * 2 * math.pi;

    quaternion.setFromEuler(rotation, false);

    scale.x = scale.y = scale.z = math.Random().nextDouble() * 1;

    matrix.compose(position, quaternion, scale);
  }
}

更多关于Flutter三维模型加载插件three_js_tjs_loader的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter三维模型加载插件three_js_tjs_loader的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,我可以为你提供一个关于如何在Flutter中使用three_js_tjs_loader插件来加载三维模型的代码示例。不过需要注意的是,three_js 本身是一个基于WebGL的JavaScript库,而Flutter是一个跨平台的UI框架,通常用于开发移动和桌面应用。直接在Flutter中使用three_js涉及到一些Webview或者PlatformView的集成工作。

假设你已经找到了一个Flutter插件(如three_js_tjs_loader,尽管这不是一个官方的或广泛认知的插件名,这里假设它提供了类似功能),或者你需要通过PlatformView来集成three.js,下面是一个简化的例子来说明如何可能实现这个功能。

1. 使用WebView集成three.js

由于Flutter直接操作WebGL较为复杂,通常我们会使用webview_flutter插件来加载一个包含three.js的HTML页面。

首先,添加webview_flutter到你的pubspec.yaml文件中:

dependencies:
  flutter:
    sdk: flutter
  webview_flutter: ^2.0.10 # 检查最新版本号

然后,你可以创建一个包含three.js和模型加载逻辑的HTML文件(例如assets/threejs_model.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Three.js Model Loader</title>
    <style>
        body { margin: 0; }
        canvas { display: block; }
    </style>
</head>
<body>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <script>
        // 基本的Three.js场景设置和模型加载逻辑
        let scene, camera, renderer, cube;
        function init() {
            scene = new THREE.Scene();
            camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
            renderer = new THREE.WebGLRenderer();
            renderer.setSize(window.innerWidth, window.innerHeight);
            document.body.appendChild(renderer.domElement);

            const geometry = new THREE.BoxGeometry();
            const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
            cube = new THREE.Mesh(geometry, material);
            scene.add(cube);

            camera.position.z = 5;

            animate();
        }

        function animate() {
            requestAnimationFrame(animate);
            cube.rotation.x += 0.01;
            cube.rotation.y += 0.01;
            renderer.render(scene, camera);
        }

        // 在这里添加你的模型加载逻辑,例如使用GLTFLoader
        // import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
        // const loader = new GLTFLoader();
        // loader.load('path/to/your/model.glb', function (gltf) {
        //     scene.add(gltf.scene);
        // }, undefined, function (error) {
        //     console.error(error);
        // });

        init();
    </script>
</body>
</html>

在Flutter中加载这个HTML文件:

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Three.js Model Loader'),
        ),
        body: WebViewExample(),
      ),
    );
  }
}

class WebViewExample extends StatefulWidget {
  @override
  _WebViewExampleState createState() => _WebViewExampleState();
}

class _WebViewExampleState extends State<WebViewExample> {
  late WebViewController _controller;

  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: Uri.dataFromString(
        '''
        <html>
          <head>
            <title>Local HTML</title>
          </head>
          <body>
            <iframe src="assets/threejs_model.html" style="width:100%;height:100%;" frameborder="0"></iframe>
          </body>
        </html>
        ''',
        mimeType: 'text/html',
        encoding: Encoding.getByName('utf-8')
      ).toString(),
      javascriptMode: JavascriptMode.unrestricted,
      onWebViewCreated: (WebViewController webViewController) {
        _controller = webViewController;
      },
    );
  }
}

注意:上面的代码示例中,由于WebView不能直接加载本地assets文件,所以使用了iframe标签并假设HTML文件可以通过网络访问。如果HTML文件必须作为资产包含在应用中,你可能需要先将HTML内容作为字符串读取,然后动态地将其注入到WebView中。

2. 使用PlatformView直接集成three.js(高级)

对于更复杂的集成,你可能需要创建一个原生平台视图(PlatformView)来直接在Flutter中渲染three.js内容。这涉及到在iOS和Android平台上分别编写原生代码来创建和呈现WebGL内容,然后将其嵌入到Flutter视图中。这是一个高级话题,通常超出了简单示例的范围。

希望这个示例能帮助你开始使用three.js在Flutter中加载三维模型。如果你有更多关于特定插件或集成方法的问题,欢迎继续提问!

回到顶部