Hacktoberfest 2026: los issues que los mantenedores marcaron para octubre, abiertos y aptos para principiantes. Explorar issues de Hacktoberfest

[flame_3d] Nested Object3D without computeLocalAabb is culled using its local position

Abierto
#4,165 0 comentarios 0 reacciones 1 asignado Ver en GitHub

Los mantenedores suelen responder en 1 día

@ufrshubham ya está trabajando en esto.

Desde el 29/10/2026.

Evaluación

Este issue todavía no se ha evaluado.

Descripción

bug flame_3d
What happened?

A Component3D without geometry (one that doesn't override computeLocalAabb) and without children gets a degenerate AABB: a single point. That point is set from the component's local position instead of its world position:

if (!initialized) {
  // No geometry and no children, so degenerate to point at position.
  _cachedAabb
    ..min.setFrom(position)
    ..max.setFrom(position);
}

For a component directly in the world the local and world positions are the same, so this works. Once it is nested under a parent with a transform, it's frustum culled at the wrong place. For an Object3D that draws something itself without overriding computeLocalAabb, this means it disappears while it is in view (or is drawn while it isn't).

This doesn't affect MeshComponent or ModelComponent, since they override computeLocalAabb, but it does affect custom Object3D subclasses that draw in draw without reporting bounds.

What do you expect?

The degenerate AABB should be at the component's world position, like every other AABB, which is already transformed with worldTransformMatrix.

How can we reproduce this?
  1. Run the main.dart below with flutter run --enable-flutter-gpu (on Windows also add --enable-impeller).
  2. Both cubes are CustomCubes, an Object3D that draws a cube without overriding computeLocalAabb. The red one is directly in the world, the green one is nested under a Group at (0, 0, 20) with a local position of (-1.5, 1, -20), so its world position is (-1.5, 1, 0).
  3. Only the red cube is rendered. The green cube's AABB is a point at (-1.5, 1, -20), which is behind the camera.

Nothing else is moved or added after the first frame, so this is independent of the cached AABBs not being invalidated.

What steps should take to fix this?

Use the world translation for the degenerate AABB:

if (!initialized) {
  // No geometry and no children, so degenerate to point at the world
  // position.
  final worldPosition = worldTransformMatrix.getTranslation();
  _cachedAabb
    ..min.setFrom(worldPosition)
    ..max.setFrom(worldPosition);
}
Do have an example of where the bug occurs?
import 'package:flame/game.dart';
import 'package:flame_3d/camera.dart';
import 'package:flame_3d/components.dart';
import 'package:flame_3d/game.dart';
import 'package:flame_3d/graphics.dart';
import 'package:flame_3d/resources.dart';
import 'package:flutter/widgets.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await GpuBackend.initialize();
  runApp(GameWidget(game: ReproGame()));
}

class ReproGame extends FlameGame3D {
  ReproGame()
    : super(
        camera: CameraComponent3D(
          position: Vector3(0, 2, -6),
          target: Vector3(0, 1, 0),
        ),
      );

  @override
  Future<void> onLoad() async {
    world.addAll([
      // Red: not nested, world position (1.5, 1, 0). Rendered.
      CustomCube(color: const Color(0xFFFF0000), position: Vector3(1.5, 1, 0)),
      // Green: nested, world position (-1.5, 1, 0). Not rendered.
      Group(
        position: Vector3(0, 0, 20),
        children: [
          CustomCube(
            color: const Color(0xFF00FF00),
            position: Vector3(-1.5, 1, -20),
          ),
        ],
      ),
    ]);
  }
}

class Group extends Component3D {
  Group({super.position, super.children});
}

/// An [Object3D] that draws a cube, without overriding `computeLocalAabb`.
class CustomCube extends Object3D {
  CustomCube({required Color color, super.position})
    : mesh = CuboidMesh(
        size: Vector3.all(1),
        material: UnlitMaterial(albedoColor: color),
      );

  final Mesh mesh;

  @override
  void draw(covariant RenderContext3D context) {
    context
      ..model.setFrom(worldTransformMatrix)
      ..drawMesh(mesh);
  }
}
Relevant log output
No errors or warnings are logged.
Execute in a terminal and put output into the code block below
Flutter 3.47.8 • channel stable • https://github.com/flutter/flutter.git
Framework • revision 737ba8f885 (6 days ago) • 2026-10-22 21:37:31 -0700
Engine • hash dfd2e6cb7d319db05e3bf1503f3e2ac60f6288a2 (revision cf636b1dfb) (6 days ago) • 2026-10-23 02:13:38.000Z
Tools • Dart 3.13.7 • DevTools 2.60.0
Affected platforms

All

Other information

Reproduced on Windows (Impeller with the OpenGL ES backend), but the culling logic is platform independent.

Other culling issues found at the same time, which can each be reproduced and fixed independently: #4163 and #4164 (cached AABBs not being invalidated).

Are you interested in working on a PR for this?
  • I want to work on this
Lenguaje dominante
Dart
Estrellas
10.8k
Forks
1.1k
Merge medio
1 d 14 h
PR fusionados (30 d)
62

Preparar el entorno

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de flame-engine/flame

Todos los issues de flame-engine/flame

Issues similares

Más issues de Dart

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.