uvsphere.zig (5069B)
1 const std = @import("std"); 2 3 const Mesh = @import("mesh.zig"); 4 const f32x3_normalize = @import("camera.zig").f32x3_normalize; 5 const f32x3_mul = @import("camera.zig").f32x3_mul; 6 7 const f32x3 = @Vector(3, f32); 8 const f32x4 = @Vector(4, f32); 9 10 const pi: f32 = 3.14159; 11 12 pub fn generateUVSphere( 13 n_rows: u32, 14 n_cols: u32, 15 allocator: std.mem.Allocator, 16 ) !Mesh { 17 const positions = try generatePositions(n_rows, n_cols, allocator); 18 const normals = try generateNormals(positions, allocator); 19 const tex_coords = try generateTexCoordsEqualArea(normals, allocator); 20 const tangents = try generateTangents(positions, allocator); 21 const bitangents = try generateBitangents(normals, tangents, allocator); 22 23 const n_vertices: u32 = @intCast(positions.len); 24 const indices = try generateIndices(n_rows, n_cols, n_vertices, allocator); 25 26 return .{ 27 .positions = positions, 28 .normals = normals, 29 .tex_coords = tex_coords, 30 .indices = indices, 31 .tangents = tangents, 32 .bitangents = bitangents, 33 }; 34 } 35 36 fn generatePositions( 37 n_rows: u32, 38 n_cols: u32, 39 allocator: std.mem.Allocator, 40 ) ![]f32x4 { 41 var positions = std.ArrayList(f32x4).init(allocator); 42 defer positions.deinit(); 43 // top 44 try positions.append(f32x4{ 0.0, 1.0, 0.0, 1.0 }); 45 // middle 46 for (1..n_rows) |i| { 47 const phi = pi * @as(f32, @floatFromInt(i)) / @as(f32, @floatFromInt(n_rows)); 48 for (0..n_cols) |j| { 49 const theta = 2.0 * pi * @as(f32, @floatFromInt(j)) / @as(f32, @floatFromInt(n_cols)); 50 const x = @sin(phi) * @cos(theta); 51 const y = @cos(phi); 52 const z = @sin(phi) * @sin(theta); 53 try positions.append(f32x4{ x, y, z, 1.0 }); 54 } 55 } 56 // bottom 57 try positions.append(f32x4{ 0.0, -1.0, 0.0, 1.0 }); 58 return try positions.toOwnedSlice(); 59 } 60 61 fn generateNormals( 62 positions: []f32x4, 63 allocator: std.mem.Allocator, 64 ) ![]f32x3 { 65 const normals = try allocator.alloc(f32x3, positions.len); 66 for (positions, normals) |p, *n| { 67 n.* = f32x3_normalize(.{ p[0], p[1], p[2] }); 68 } 69 return normals; 70 } 71 72 fn generateTangents( 73 positions: []f32x4, 74 allocator: std.mem.Allocator, 75 ) ![]f32x3 { 76 const up = f32x3{ 0, 1, 0 }; 77 const tangents = try allocator.alloc(f32x3, positions.len); 78 for (positions, tangents) |p, *t| { 79 t.* = f32x3_normalize(f32x3_mul(up, .{ p[0], p[1], p[2] })); 80 } 81 tangents[0] = .{ 1, 0, 0 }; 82 tangents[tangents.len - 1] = .{ 1, 0, 0 }; 83 return tangents; 84 } 85 86 fn generateBitangents( 87 normals: []f32x3, 88 tangents: []f32x3, 89 allocator: std.mem.Allocator, 90 ) ![]f32x3 { 91 const bitangents = try allocator.alloc(f32x3, normals.len); 92 for (normals, tangents, bitangents) |n, t, *b| { 93 b.* = f32x3_mul(n, t); 94 } 95 bitangents[0] = .{ 0, 0, 1 }; 96 bitangents[bitangents.len - 1] = .{ 0, 0, 1 }; 97 return bitangents; 98 } 99 100 fn generateTexCoordsEqualArea( 101 normals: []f32x3, 102 allocator: std.mem.Allocator, 103 ) ![]f32x3 { 104 const tex_coords = try allocator.alloc(f32x3, normals.len); 105 for (normals, tex_coords) |n, *tc| { 106 tc.* = .{ 107 (std.math.atan2(n[0], -n[2]) / pi + 1.0) * 0.5, 108 std.math.asin(n[1]) / pi + 0.5, 109 0.0, 110 }; 111 } 112 return tex_coords; 113 } 114 115 fn generateTexCoordsEquirectangular( 116 normals: []f32x3, 117 allocator: std.mem.Allocator, 118 ) ![]f32x3 { 119 const tex_coords = try allocator.alloc(f32x3, normals.len); 120 for (normals, tex_coords) |n, *tc| { 121 tc.* = .{ 122 // equirectangular 123 std.math.atan2(n[0], n[2]) / (2.0 * pi) + 0.5, 124 n[1] * 0.5 + 0.5, 125 0.0, 126 }; 127 } 128 } 129 130 fn generateIndices( 131 n_rows: u32, 132 n_cols: u32, 133 n_vertices: u32, 134 allocator: std.mem.Allocator, 135 ) ![]u32 { 136 var indices = std.ArrayList(u32).init(allocator); 137 defer indices.deinit(); 138 // top 139 for (0..n_cols) |i| { 140 const a = (i + 1) % n_rows + 1; 141 const b = i + 1; 142 try indices.append(0); 143 try indices.append(@intCast(a)); 144 try indices.append(@intCast(b)); 145 } 146 // bottom 147 for (0..n_cols) |i| { 148 const a = i + n_cols * (n_rows - 2) + 1; 149 const b = (i + 1) % n_cols + n_cols * (n_rows - 2) + 1; 150 try indices.append(@intCast(n_vertices - 1)); 151 try indices.append(@intCast(a)); 152 try indices.append(@intCast(b)); 153 } 154 // center 155 for (0..n_rows - 2) |j| { 156 const x = j * n_cols + 1; 157 const y = (j + 1) * n_cols + 1; 158 for (0..n_cols) |i| { 159 const a = x + i; 160 const b = x + (i + 1) % n_cols; 161 const c = y + (i + 1) % n_cols; 162 const d = y + i; 163 try indices.append(@intCast(a)); 164 try indices.append(@intCast(b)); 165 try indices.append(@intCast(c)); 166 try indices.append(@intCast(a)); 167 try indices.append(@intCast(c)); 168 try indices.append(@intCast(d)); 169 } 170 } 171 return try indices.toOwnedSlice(); 172 }