camera static method

Geometry camera({
  1. double bodyWidth = 0.6,
  2. double bodyHeight = 0.7,
  3. double bodyDepth = 1.4,
  4. double lensRadius = 0.3,
  5. double lensLength = 0.4,
  6. bool normals = true,
  7. bool uvs = true,
})

Implementation

static Geometry camera({
  double bodyWidth = 0.6,    // X-axis (medium width)
  double bodyHeight = 0.7,   // Y-axis (medium height)
  double bodyDepth = 1.4,    // Z-axis (LONG dimension - camera body extends back)
  double lensRadius = 0.3,
  double lensLength = 0.4,
  bool normals = true,
  bool uvs = true,
}) {
  List<double> verticesList = [];
  List<double> normalsList = [];
  List<double> uvsList = [];
  List<int> indices = [];

  // Helper function to add a vertex with normal and UV
  void addVertex(double x, double y, double z, double nx, double ny, double nz, double u, double v) {
    verticesList.addAll([x, y, z]);
    if (normals) normalsList.addAll([nx, ny, nz]);
    if (uvs) uvsList.addAll([u, v]);
  }

  int currentIndex = 0;

  // === CAMERA BODY (Rectangular box) ===
  // Now: width=1.0, height=0.6, depth=1.4 (long)
  // The front face (Z=+halfDepth) is the short face where lens attaches
  double halfWidth = bodyWidth / 2;   // 0.5 (medium)
  double halfHeight = bodyHeight / 2; // 0.3 (short)
  double halfDepth = bodyDepth / 2;   // 0.7 (long - extends backward)

  // Front face (SHORT face - where lens attaches) - Z = +halfDepth
  addVertex(-halfWidth, -halfHeight, halfDepth, 0, 0, 1, 0, 0);  // 0
  addVertex(halfWidth, -halfHeight, halfDepth, 0, 0, 1, 1, 0);   // 1
  addVertex(halfWidth, halfHeight, halfDepth, 0, 0, 1, 1, 1);    // 2
  addVertex(-halfWidth, halfHeight, halfDepth, 0, 0, 1, 0, 1);   // 3

  // Back face (SHORT face) - Z = -halfDepth
  addVertex(halfWidth, -halfHeight, -halfDepth, 0, 0, -1, 0, 0); // 4
  addVertex(-halfWidth, -halfHeight, -halfDepth, 0, 0, -1, 1, 0); // 5
  addVertex(-halfWidth, halfHeight, -halfDepth, 0, 0, -1, 1, 1); // 6
  addVertex(halfWidth, halfHeight, -halfDepth, 0, 0, -1, 0, 1);  // 7

  // Top face (LONG face) - Y = +halfHeight
  addVertex(-halfWidth, halfHeight, halfDepth, 0, 1, 0, 0, 0);   // 8
  addVertex(halfWidth, halfHeight, halfDepth, 0, 1, 0, 1, 0);    // 9
  addVertex(halfWidth, halfHeight, -halfDepth, 0, 1, 0, 1, 1);   // 10
  addVertex(-halfWidth, halfHeight, -halfDepth, 0, 1, 0, 0, 1);  // 11

  // Bottom face (LONG face) - Y = -halfHeight
  addVertex(-halfWidth, -halfHeight, -halfDepth, 0, -1, 0, 0, 0); // 12
  addVertex(halfWidth, -halfHeight, -halfDepth, 0, -1, 0, 1, 0);  // 13
  addVertex(halfWidth, -halfHeight, halfDepth, 0, -1, 0, 1, 1);   // 14
  addVertex(-halfWidth, -halfHeight, halfDepth, 0, -1, 0, 0, 1);  // 15

  // Right face (LONG face) - X = +halfWidth
  addVertex(halfWidth, -halfHeight, halfDepth, 1, 0, 0, 0, 0);   // 16
  addVertex(halfWidth, -halfHeight, -halfDepth, 1, 0, 0, 1, 0);  // 17
  addVertex(halfWidth, halfHeight, -halfDepth, 1, 0, 0, 1, 1);   // 18
  addVertex(halfWidth, halfHeight, halfDepth, 1, 0, 0, 0, 1);    // 19

  // Left face (LONG face) - X = -halfWidth
  addVertex(-halfWidth, -halfHeight, -halfDepth, -1, 0, 0, 0, 0); // 20
  addVertex(-halfWidth, -halfHeight, halfDepth, -1, 0, 0, 1, 0);  // 21
  addVertex(-halfWidth, halfHeight, halfDepth, -1, 0, 0, 1, 1);   // 22
  addVertex(-halfWidth, halfHeight, -halfDepth, -1, 0, 0, 0, 1);  // 23

  // Body indices
  List<int> bodyIndices = [
    // Front face
    0, 1, 2, 0, 2, 3,
    // Back face
    4, 5, 6, 4, 6, 7,
    // Top face
    8, 9, 10, 8, 10, 11,
    // Bottom face
    12, 13, 14, 12, 14, 15,
    // Right face
    16, 17, 18, 16, 18, 19,
    // Left face
    20, 21, 22, 20, 22, 23
  ];

  indices.addAll(bodyIndices);
  currentIndex = 24;

  // === CONICAL LENS ===
  int segments = 16;
  double lensApexZ = -halfDepth;  // Apex touches the front face (short face)
  double lensBaseZ = -halfDepth - lensLength;  // Base extends outward along Z-axis

  // Lens apex (tip of the cone - touching the camera body at center of front face)
  addVertex(0, 0, lensApexZ, 0, 0, -1, 0.5, 0);
  int apexIndex = currentIndex;
  currentIndex++;

  // Lens base circle (the wide part extending outward)
  List<int> baseIndices = [];
  for (int i = 0; i < segments; i++) {
    double theta = i * 2 * pi / segments;
    double x = lensRadius * cos(theta);
    double y = lensRadius * sin(theta);

    // Calculate normal for cone side (pointing outward from cone surface)
    double normalX = x / lensRadius;  // Normalized radial component
    double normalY = y / lensRadius;
    double normalZ = lensRadius / lensLength;  // Axial component based on cone slope

    // Normalize the normal vector
    double normalLength = sqrt(normalX * normalX + normalY * normalY + normalZ * normalZ);
    normalX /= normalLength;
    normalY /= normalLength;
    normalZ /= normalLength;

    addVertex(x, y, lensBaseZ, normalX, normalY, normalZ, i / segments, 1);
    baseIndices.add(currentIndex);
    currentIndex++;
  }

  // Create cone side triangles
  for (int i = 0; i < segments; i++) {
    int current = baseIndices[i];
    int next = baseIndices[(i + 1) % segments];

    // Triangle from apex to base edge (counter-clockwise when viewed from outside)
    indices.addAll([apexIndex, next, current]);
  }

  // === LENS BASE (flat circular face at the wide end) ===
  // Center of lens base
  addVertex(0, 0, lensBaseZ, 0, 0, 1, 0.5, 0.5);
  int baseCenterIndex = currentIndex;
  currentIndex++;

  // Base circle vertices (separate from cone vertices for proper normals)
  List<int> baseFaceIndices = [];
  for (int i = 0; i < segments; i++) {
    double theta = i * 2 * pi / segments;
    double x = lensRadius * cos(theta);
    double y = lensRadius * sin(theta);

    double u = 0.5 + 0.5 * cos(theta);
    double v = 0.5 + 0.5 * sin(theta);

    addVertex(x, y, lensBaseZ, 0, 0, 1, u, v);
    baseFaceIndices.add(currentIndex);
    currentIndex++;
  }

  // Create base face triangles (facing outward from camera)
  for (int i = 0; i < segments; i++) {
    int current = baseFaceIndices[i];
    int next = baseFaceIndices[(i + 1) % segments];

    // Triangle from center to edge (counter-clockwise when viewed from outside)
    indices.addAll([baseCenterIndex, current, next]);
  }

  Float32List vertices = Float32List.fromList(verticesList);
  Float32List? _normals = normals ? Float32List.fromList(normalsList) : null;
  Float32List? _uvs = uvs ? Float32List.fromList(uvsList) : null;

  return Geometry(vertices, Uint16List.fromList(indices), normals: _normals, uvs: _uvs);
}