Skip to content

Sizes are incorrect when displaying multiple ThreeJS widgets #108

@Phelicks

Description

@Phelicks

When displaying multiple 3D scenes, sizes are assigned incorrectly and swapped. In the example, you can see that the second scene is rendered with the smaller size, and the first with the larger one.

Expected

The first scene should be rendered with a size of 100x100, and the second with 200x200.

Issue

The sizes are swapped; the first is rendered with 200x200, and the second with 100x100.
Assigning different renderNumber values to ThreeJS makes no difference.
Tested on MacOS and Android.

Render result

Image

Simple example showing the issue

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:three_js/three_js.dart';

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

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

  @override
  State<ExampleApp> createState() => _MyAppState();
}

class _MyAppState extends State<ExampleApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: ThemeData(primarySwatch: Colors.blue),
      home: Scaffold(
        body: Center(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisAlignment: MainAxisAlignment.spaceEvenly,
            children: [
              const SizedBox(
                width: 100,
                height: 100,
                child: FlutterGame(size: Size(100, 100), text: '100'),
              ),
              const SizedBox(
                width: 200,
                height: 200,
                child: FlutterGame(size: Size(200, 200), text: '200'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

class FlutterGame extends StatefulWidget {
  const FlutterGame({super.key, required this.size, required this.text});

  final Size size;
  final String text;

  @override
  State<FlutterGame> createState() => _FlutterGameState();
}

class _FlutterGameState extends State<FlutterGame> {
  static const url =
      'https://raw.githubusercontent.com/mrdoob/three.js/refs/heads/master/examples/fonts/helvetiker_regular.typeface.json';

  late ThreeJS threeJs;

  @override
  void initState() {
    threeJs = ThreeJS(
      onSetupComplete: () => setState(() {}),
      setup: setup,
      size: widget.size,
    );
    super.initState();
  }

  @override
  void dispose() {
    threeJs.dispose();
    loading.clear();
    super.dispose();
  }

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

  Future<void> setup() async {
    final aspect = threeJs.width / threeJs.height;
    threeJs.camera = PerspectiveCamera(45, aspect, 1, 200);
    threeJs.camera.position.setValues(0, 0, 10);

    threeJs.scene = Scene();

    final pointLight = PointLight(0xffffff, 20);
    pointLight.position.setValues(1, 1, 2);
    threeJs.camera.add(pointLight);

    threeJs.scene.add(threeJs.camera);

    final geometry = TextGeometry(
      widget.text,
      TextGeometryOptions(font: await loadFont(), size: 5, depth: 2),
    );
    geometry.computeBoundingBox();
    final material = MeshStandardMaterial()..color = Color(1, 0, 0);
    final mesh = Mesh(geometry, material);
    mesh.position.x =
        -0.5 * (geometry.boundingBox!.max.x - geometry.boundingBox!.min.x);
    mesh.position.y =
        -0.5 * (geometry.boundingBox!.max.y - geometry.boundingBox!.min.y);

    threeJs.scene.add(mesh);
  }

  Future<TTFFont> loadFont() async {
    final loader = FontLoader();
    final uri = Uri.parse(url);
    final font = await loader.fromNetwork(uri);
    loader.dispose();

    return font!;
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions