three_js_advanced_loaders 0.1.1 copy "three_js_advanced_loaders: ^0.1.1" to clipboard
three_js_advanced_loaders: ^0.1.1 copied to clipboard

Flutter advanced loaders package e.g.(gltf, glb and fbx) converted from threejs and three_dart package.

example/lib/main.dart

import 'dart:async';
import 'dart:math' as math;
import 'package:flutter/material.dart';

import 'package:three_js_core/three_js_core.dart' as three;
import 'package:three_js_animations/three_js_animations.dart';
import 'package:three_js_helpers/three_js_helpers.dart';
import 'package:three_js_advanced_loaders/three_js_advanced_loaders.dart';
import 'package:three_js_controls/three_js_controls.dart';
import 'package:three_js_math/three_js_math.dart' as tmath;

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

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

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const WebglAnimationMultiple(),
    );
  }
}

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

  @override
  createState() => _State();
}

class _State extends State<WebglAnimationMultiple> {
  late three.ThreeJS threeJs;

  @override
  void initState() {
    threeJs = three.ThreeJS(
      settings: three.Settings(
        useSourceTexture: true
      ),
      onSetupComplete: (){setState(() {});},
      setup: setup
    );
    super.initState();
  }
  @override
  void dispose() {
    threeJs.dispose();
    controls.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return threeJs.build();
  }

  late OrbitControls controls;

  late List<Map<String, dynamic>> models;
  late List<Map<String, dynamic>> units;
  final List<AnimationMixer> mixers = []; // All the three.AnimationMixer objects for all the animations in the scene
  int numLoadedModels = 0;

  Future<void> setup() async {
    models = [
      {"name": "Soldier"},
      {"name": "Parrot"},
    ];

    // Here we define instances of the models that we want to place in the scene, their position, scale and the animations
    // that must be played.
    units = [
      {
        "modelName": "Soldier", // Will use the 3D model from file models/gltf/Soldier.glb
        "meshName": "vanguard_Mesh", // Name of the main mesh to animate
        "position": {
          "x": 0,
          "y": 0,
          "z": 0
        }, // Where to put the unit in the scene
        "scale": 1, // Scaling of the unit. 1.0 means: use original size, 0.1 means "10 times smaller", etc.
        "animationName": "Idle" // Name of animation to run
      },
      {
        "modelName": "Soldier",
        "meshName": "vanguard_Mesh",
        "position": {"x": 3, "y": 0, "z": 0},
        "scale": 2,
        "animationName": "Walk"
      },
      {
        "modelName": "Soldier",
        "meshName": "vanguard_Mesh",
        "position": {"x": 1, "y": 0, "z": 0},
        "scale": 1,
        "animationName": "Run"
      },
      {
        "modelName": "Parrot",
        "meshName": "mesh_0",
        "position": {"x": -4, "y": 0, "z": 0},
        "rotation": {"x": 0, "y": math.pi, "z": 0},
        "scale": 0.01,
        "animationName": "parrot_A_"
      },
      {
        "modelName": "Parrot",
        "meshName": "mesh_0",
        "position": {"x": -2, "y": 0, "z": 0},
        "rotation": {"x": 0, "y": math.pi / 2, "z": 0},
        "scale": 0.02,
        "animationName": null
      },
    ];

    setup2();
    loadModels();
  }

  void setup2() {
    threeJs.camera = three.PerspectiveCamera(45, threeJs.width / threeJs.height, 1, 10000);
    threeJs.camera.position.setValues(3, 6, -10);
    threeJs.camera.lookAt(tmath.Vector3(0, 1, 0));

    threeJs.scene = three.Scene();
    threeJs.scene.background = tmath.Color.fromHex32(0xa0a0a0);
    threeJs.scene.fog = three.Fog(0xa0a0a0, 10, 22);

    final hemiLight = three.HemisphereLight(0xffffff, 0x444444);
    hemiLight.position.setValues(0, 20, 0);
    threeJs.scene.add(hemiLight);

    final dirLight = three.DirectionalLight(0xffffff);
    dirLight.position.setValues(-3, 10, -10);
    dirLight.castShadow = true;
    dirLight.shadow!.camera!.top = 10;
    dirLight.shadow!.camera!.bottom = -10;
    dirLight.shadow!.camera!.left = -10;
    dirLight.shadow!.camera!.right = 10;
    dirLight.shadow!.camera!.near = 0.1;
    dirLight.shadow!.camera!.far = 40;
    threeJs.scene.add(dirLight);

    controls = OrbitControls(threeJs.camera, threeJs.globalKey);

    // ground
    final groundMesh = three.Mesh(three.PlaneGeometry(40, 40), three.MeshPhongMaterial.fromMap({"color": 0x999999, "depthWrite": false}));

    groundMesh.rotation.x = -math.pi / 2;
    groundMesh.receiveShadow = true;
    threeJs.scene.add(groundMesh);
  }

  void loadModels() {
    for (int i = 0; i < models.length; ++i) {
      final m = models[i];

      loadGltfModel(m, () {
        ++numLoadedModels;

        if (numLoadedModels == models.length) {
          three.console.info("All models loaded, time to instantiate units...");
          instantiateUnits();
        }
      });
    }
  }

  void instantiateUnits() {
    int numSuccess = 0;

    for (int i = 0; i < units.length; ++i) {
      final u = units[i];
      final model = getModelByName(u["modelName"]);

      if (model != null) {
        final clonedScene = SkeletonUtils.clone(model["scene"]);

        if (clonedScene != null) {
          // three.Scene is cloned properly, let's find one mesh and launch animation for it
          final clonedMesh = clonedScene.getObjectByName(u["meshName"]);

          if (clonedMesh != null) {
            final mixer = startAnimation(
                clonedMesh,
                List<AnimationClip>.from(model["animations"]),
                u["animationName"]);

            // Save the animation mixer in the list, will need it in the animation loop
            mixers.add(mixer);
            numSuccess++;
          }
          
          threeJs.scene.add(clonedScene);

          if (u["position"] != null) {
            clonedScene.position.setValues(
                u["position"]["x"].toDouble(), u["position"]["y"].toDouble(), u["position"]["z"].toDouble());
          }

          if (u["scale"] != null) {
            clonedScene.scale.setValues(u["scale"].toDouble(), u["scale"].toDouble(), u["scale"].toDouble());
          }

          if (u["rotation"] != null) {
            clonedScene.rotation.x = u["rotation"]["x"].toDouble();
            clonedScene.rotation.y = u["rotation"]["y"].toDouble();
            clonedScene.rotation.z = u["rotation"]["z"].toDouble();
          }
        }
      } else {
        three.console.info("Can not find model ${u["modelName"]}");
      }
    }

    three.console.info(" Successfully instantiated $numSuccess units ");

    threeJs.addAnimationEvent((dt){
      for (int i = 0; i < mixers.length; ++i) {
        mixers[i].update(dt);
      }

      controls.update();
    });
  }

  AnimationMixer startAnimation(
    three.Object3D skinnedMesh, 
    List<AnimationClip> animations, 
    String animationName
  ) {
    final mixer = AnimationMixer(skinnedMesh);
    final clip = AnimationClip.findByName(animations, animationName);

    if (clip != null) {
      final action = mixer.clipAction(clip);
      action!.play();
    }

    return mixer;
  }

  Map<String, dynamic>? getModelByName(String name) {
    for (int i = 0; i < models.length; ++i) {
      if (models[i]["name"] == name) {
        return models[i];
      }
    }

    return null;
  }

  void loadGltfModel(model, onLoaded) {
    final loader = GLTFLoader();
    final modelName = "assets/${model["name"]}.gltf";

    loader.fromAsset(modelName).then((gltf) {
      final scene = gltf!.scene;

      model["animations"] = gltf.animations;
      model["scene"] = scene;

      gltf.scene.traverse((object) {
        if (object is three.Mesh) {
          object.castShadow = true;
        }
      });

      three.console.info("Done loading model ${model["name"]} ");
      onLoaded();
    });
  }
}
3
likes
150
points
1.01k
downloads

Publisher

unverified uploader

Weekly Downloads

Flutter advanced loaders package e.g.(gltf, glb and fbx) converted from threejs and three_dart package.

Repository (GitHub)

Topics

#gltf #collada #usda #flutter3d #model3d

Documentation

API reference

License

MIT (license)

Dependencies

archive, flutter, three_js_animations, three_js_controls, three_js_core, three_js_core_loaders, three_js_curves, three_js_geometry, three_js_math, xml

More

Packages that depend on three_js_advanced_loaders