como posso aplicar uma transformação mais simples, em vez dessa complexa?

Minha implementação de transformação simples que eu quero:

    // Inside the paint method
for (int i = 0; i < vertices.length; i++) {
  final Vector4 transformedVertex = transform.transformed(
      Vector4(vertices[i].x, vertices[i].y, vertices[i].z, 1.0));
  positions[i * 2] = transformedVertex.x / transformedVertex.w; // X
  positions[i * 2 + 1] = transformedVertex.y / transformedVertex.w; // Y

  Offset t = texcoords[i];

  // Corrected texture coordinates to fit the entire texture over the sphere
  double u = t.dx * texture.width; // Map U coordinate by texture width
  double v = t.dy * texture.height; // Map V coordinate by texture height

  renderTexcoords[i * 2] = u; // U
  renderTexcoords[i * 2 + 1] = v; // V
}



implementação atual:

import 'dart:async';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:vector_math/vector_math_64.dart' hide Colors;
import 'dart:math' as math;

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('3D Sphere with Fixed Texture')),
        body: Center(child: SphereWidget()),
        backgroundColor: const Color.fromARGB(255, 52, 13, 245),
      ),
    );
  }
}

class SphereWidget extends StatefulWidget {
  @override
  _SphereWidgetState createState() => _SphereWidgetState();
}

class _SphereWidgetState extends State<SphereWidget> {
  double angle = 0;
  ui.Image? texture;

  @override
  void initState() {
    super.initState();
    loadImageFromAsset('assets/1.jpg');
    rotate();
  }

  void rotate() {
    Timer.periodic(Duration(milliseconds: 16), (timer) {
      setState(() {
        angle += 0.5 * (math.pi / 180); // Incrementa 0.5 graus em radianos
        if (angle > 2 * math.pi) {
          angle -= 2 * math.pi; // Mantém o ângulo entre 0 e 2π
        }
      });
    });
  }

  Future<void> loadImageFromAsset(String path) async {
    final completer = Completer<ui.Image>();
    final image = AssetImage(path);
    image.resolve(ImageConfiguration()).addListener(
      ImageStreamListener((info, _) {
        completer.complete(info.image);
      }),
    );
    texture = await completer.future;
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    if (texture == null) {
      return Container();
    }
    return CustomPaint(
      size: const Size(
          1000, 1000), // Ajuste o tamanho da esfera conforme necessário
      painter: SpherePainter(texture!, angle),
    );
  }
}

class SpherePainter extends CustomPainter {
  final ui.Image texture;
  final double angle;

  // Geometria da esfera
  static List<Vector3> staticVertices = [];
  static List<Offset> staticTexcoords = [];
  static List<List<int>> staticIndices = [];
  static bool isSphereGenerated = false;

  final Matrix4 transform = Matrix4.identity();
  final double radius = 100;
  final int latSegments = 50; // Ajustado para melhor performance
  final int lonSegments = 50;

  SpherePainter(this.texture, this.angle) {
    if (!isSphereGenerated) {
      _generateSphere();
      isSphereGenerated = true;
    }
  }

  void _generateSphere() {
    for (int y = 0; y <= latSegments; ++y) {
      final double v = y / latSegments;
      final double sv = math.sin(v * math.pi);
      final double cv = math.cos(v * math.pi);
      for (int x = 0; x <= lonSegments; ++x) {
        final double u = x / lonSegments;
        staticVertices.add(Vector3(
          radius * math.cos(u * math.pi * 2.0) * sv, // X
          radius * cv, // Y
          radius * math.sin(u * math.pi * 2.0) * sv, // Z
        ));
        staticTexcoords.add(Offset(u, v)); // Coordenadas UV normalizadas
      }
    }

    // Preenche os índices dos triângulos com ordem anti-horária consistente
    for (int y = 0; y < latSegments; ++y) {
      for (int x = 0; x < lonSegments; ++x) {
        final int current = (lonSegments + 1) * y + x;
        final int next = current + 1;
        final int below = (lonSegments + 1) * (y + 1) + x;
        final int belowNext = below + 1;

        staticIndices
            .add([current, below, next]); // Triângulo inferior esquerdo
        staticIndices
            .add([next, below, belowNext]); // Triângulo superior direito
      }
    }
  }

  void updateTransform() {
    // Apenas uma rotação em torno do eixo Y
    transform.setIdentity();
    transform.rotateY(angle);
  }

  @override
  void paint(Canvas canvas, Size size) {
    canvas.translate(size.width / 2, size.height / 2);
    updateTransform();

    final Float32List positions = Float32List(staticVertices.length * 2);
    final Float32List renderTexcoords = Float32List(staticTexcoords.length * 2);

    // Transformar os vértices
    List<Vector4> transformedVertices =
        List<Vector4>.filled(staticVertices.length, Vector4.zero());
    for (int i = 0; i < staticVertices.length; i++) {
      transformedVertices[i] = transform.transformed(Vector4(
          staticVertices[i].x, staticVertices[i].y, staticVertices[i].z, 1.0));
      // Projeção em perspectiva
      double perspective = 300.0;
      double w = perspective / (perspective + transformedVertices[i].z);
      positions[i * 2] = transformedVertices[i].x * w; // X
      positions[i * 2 + 1] = transformedVertices[i].y * w; // Y

      Offset t = staticTexcoords[i];

      // Corrected texture coordinates to fit the entire texture over the sphere
      double u = t.dx * texture.width; // Map U coordinate by texture width
      double v = t.dy * texture.height; // Map V coordinate by texture height

      renderTexcoords[i * 2] = u; // U
      renderTexcoords[i * 2 + 1] = v; // V
    }

    // Lista para armazenar triângulos visíveis
    List<Triangle> visibleTriangles = [];

    // Calcular e filtrar triângulos com base nas normais (Back-Face Culling)
    for (int i = 0; i < staticIndices.length; i++) {
      final int idx0 = staticIndices[i][0];
      final int idx1 = staticIndices[i][1];
      final int idx2 = staticIndices[i][2];

      Vector4 v0 = transformedVertices[idx0];
      Vector4 v1 = transformedVertices[idx1];
      Vector4 v2 = transformedVertices[idx2];

      // Vetores das arestas do triângulo
      Vector3 edge1 = Vector3(v1.x - v0.x, v1.y - v0.y, v1.z - v0.z);
      Vector3 edge2 = Vector3(v2.x - v0.x, v2.y - v0.y, v2.z - v0.z);

      // Normal do triângulo
      Vector3 normal = edge1.cross(edge2).normalized();

      // Vetor de visão assumindo câmera no eixo Z positivo
      Vector3 viewVector = Vector3(0, 0, 1);

      // Produto escalar entre normal e vetor de visão
      double dotProduct = normal.dot(viewVector);

      // Se o dot product for positivo, o triângulo está voltado para a câmera
      if (dotProduct > 0) {
        // Calcular profundidade média para ordenação
        double depth = (v0.z + v1.z + v2.z) / 3.0;
        visibleTriangles.add(Triangle([idx0, idx1, idx2], depth));
      }
    }

    // Ordenar triângulos do mais distante para o mais próximo
    visibleTriangles.sort((a, b) => b.depth.compareTo(a.depth));

    // Preparar buffers para Vertices.raw
    List<int> renderedIndices = [];
    List<double> renderedPositions = [];
    List<double> renderedTexcoordsList = [];

    // Mapeamento de vértices únicos para evitar duplicações
    Map<int, int> vertexMap = {};
    int currentIndex = 0;

    for (var tri in visibleTriangles) {
      for (int idx in tri.indices) {
        if (!vertexMap.containsKey(idx)) {
          vertexMap[idx] = currentIndex++;
          renderedPositions.add(positions[idx * 2]);
          renderedPositions.add(positions[idx * 2 + 1]);
          renderedTexcoordsList.add(renderTexcoords[idx * 2]);
          renderedTexcoordsList.add(renderTexcoords[idx * 2 + 1]);
        }
        renderedIndices.add(vertexMap[idx]!);
      }
    }

    if (renderedIndices.isEmpty) {
      return; // Nenhum triângulo visível para desenhar
    }

    final Uint16List indexBuffer = Uint16List.fromList(renderedIndices);
    final Float32List positionsBuffer = Float32List.fromList(renderedPositions);
    final Float32List texcoordsBuffer =
        Float32List.fromList(renderedTexcoordsList);

    final verticesData = Vertices.raw(
      VertexMode.triangles,
      positionsBuffer,
      textureCoordinates: texcoordsBuffer,
      indices: indexBuffer,
    );

    final Float64List matrix4 = Matrix4.identity().storage;
    final paint = Paint()
      ..shader = ImageShader(
        texture,
        TileMode.clamp,
        TileMode.clamp,
        matrix4,
      );

    // Desenhar os triângulos visíveis ordenados por profundidade
    canvas.drawVertices(verticesData, BlendMode.srcOver, paint);
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return true;
  }
}

class Triangle {
  final List<int> indices;
  final double depth;

  Triangle(this.indices, this.depth);
}