I’m working on a game engine project with a custom data structure list thing based off of the std.MultiArrayList (basically a multiarraylist sparse set, I’m thinking about making a separate thread getting feedback on it as I plan to stick with it until any serious issues come up). Once I changed all my data over to using these lists, I noticed some strange behavior with the rendering (note: I do not know if this was a problem before as I switched to this new data structure before ever testing removing objects). Two things are happening:
-
When I use my remove function, say at index 10, whatever object is at half of that index (5) will also disappear when rendering (the data for index 5 is still there, it just doesn’t render on to screen) (image 1).
-
(the far bigger problem) around half of the objects I add to the list just don’t render at all (image 2, there should be 11 total) (to get the screenshot for #1 I had to append far more than 10 objects, like 20-30).
I do not believe it has anything to do with my sparse set implementation as the stored data seems to be as expected in these conditions (I can confirm this after printing all objects in the list, and printing the vertices/indices that go through functions like updateBuffers()
for both problem #1 and #2).
Relevant code (can provide more if requested):
main.zig
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
try window.init();
defer window.deinit();
try renderer.init();
defer renderer.deinit(allocator);
for (0..10) |i| {
const delta: f32 = @floatFromInt(i);
_ = try renderer.newObject(
allocator,
&[_]renderer.Vertex{
.{ .position = .{ -0.5, 0, 0.5 }, .color = .{ 1, 0, 0 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ -0.5, 0, -0.5 }, .color = .{ 0, 1, 0 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ 0.5, 0, -0.5 }, .color = .{ 0, 0, 1 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ 0.5, 0, 0.5 }, .color = .{ 1, 1, 0 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ 0.0, 0.8, 0 }, .color = .{ 1, 1, 1 }, .tex_coord = .{ 0, 0 } },
},
&[_]usize{
0, 1, 2,
0, 2, 3,
0, 1, 4,
1, 2, 4,
2, 3, 4,
3, 0, 4
},
.{ 0, 0 + delta, 0 },
.{ 0 + (4*delta), 0, 0 },
);
}
_ = try renderer.newObject(
allocator,
&[_]renderer.Vertex{
.{ .position = .{ -0.5, 0, 0.5 }, .color = .{ 0, 0, 1 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ -0.5, 0, -0.5 }, .color = .{ 0, 0, 1 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ 0.5, 0, -0.5 }, .color = .{ 0, 0, 1 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ 0.5, 0, 0.5 }, .color = .{ 0, 0, 1 }, .tex_coord = .{ 0, 0 } },
.{ .position = .{ 0.0, 0.8, 0 }, .color = .{ 0, 0, 1 }, .tex_coord = .{ 0, 0 } },
},
&[_]usize{
0, 1, 2,
0, 2, 3,
0, 1, 4,
1, 2, 4,
2, 3, 4,
3, 0, 4
},
.{ 0, 0, 0 },
.{ 0, 0, 0 },
);
// temp player camera
var cam = renderer.Camera{
.position = .{ 0, -0.5, -5 },
.rotation = .{ 0, 0, 0, 1 },
.fov = 90,
.near_plane = 0.001, // temp
.far_plane = 10000, // temp
.owner = null,
};
// update buffers
try renderer.updateBuffers();
// main loop
try window.update(&cam);
// cleanup
try network.deinit(allocator);
}
updateBuffers()
pub fn updateBuffers() !void {
const vertex_count: isize = @intCast(renderer.vertices.len);
const index_count: isize = @intCast(renderer.indices.len);
// not sure how performant this approach is, might change later
// update the vertex buffer with the new vertices
gl.BufferData(gl.ARRAY_BUFFER, @sizeOf(renderer.Vertex) * vertex_count, null, gl.DYNAMIC_DRAW);
for (renderer.vertices.getOrder()) |i| {
const vertex: renderer.Vertex = renderer.vertices.getAssertExists(i);
gl.BufferSubData(gl.ARRAY_BUFFER, @sizeOf(renderer.Vertex) * @as(isize, @intCast(i)), @sizeOf(renderer.Vertex), &vertex);
}
// update the index buffer with the new indices
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, @sizeOf(u32) * index_count, null, gl.DYNAMIC_DRAW);
for (renderer.indices.getOrder()) |i| {
const index: usize = renderer.indices.getAssertExists(i);
gl.BufferSubData(gl.ELEMENT_ARRAY_BUFFER, @sizeOf(u32) * @as(isize, @intCast(i)), @sizeOf(u32), &index);
}
}
update()
// setup model matrix for each mesh and draw the vertices
for (renderer.objects.getOrder()) |i| {
const object = renderer.objects.getAssertExists(i);
var model: zmath.Mat = zmath.identity();
// rot
model = zmath.matFromRollPitchYaw(object.rotation[0] * (std.math.pi / 180.0), object.rotation[1] * (std.math.pi / 180.0), object.rotation[2] * (std.math.pi / 180.0)); // converts to quaternion under the hood
// test local rotation
model = zmath.mul(model, zmath.rotationY(delta_rotation * (std.math.pi / 180.0)));
// pos
model = zmath.mul(model, zmath.translation(object.position[0], object.position[1], object.position[2]));
gl.UniformMatrix4fv(model_location, 1, gl.FALSE, zmath.arrNPtr(&model));
gl.DrawElements(gl.TRIANGLES, @intCast(object.index_count), gl.UNSIGNED_INT, object.index_start * @sizeOf(usize)); // whyd the last argument change from *usize to usize????
}
newObject()
pub fn newObject(allocator: std.mem.Allocator, new_vertices: []const Vertex, new_indices: []const usize, position: [3]f32, rotation: [3]f32) !Object {
const object = Object {
.position = position,
.rotation = rotation,
.vertex_start = @enumFromInt(vertices.len),
.vertex_count = @intCast(new_vertices.len),
.index_start = @intCast(indices.len),
.index_count = @intCast(new_indices.len),
.material = @enumFromInt(0), // not implemented yet
};
for (new_vertices) |vertex| {
try vertices.append(allocator, vertex);
}
for (new_indices) |index| {
try indices.append(allocator, index + @intFromEnum(object.vertex_start));
}
// add the object to the object list
try objects.append(allocator, object);
return object;
}
removeObject()
pub fn removeObject(index: usize) !void {
const object: Object = try objects.get(index);
for (@intFromEnum(object.vertex_start)..(@intFromEnum(object.vertex_start) + object.vertex_count)) |i| {
try vertices.remove(i);
}
for (object.index_start..(object.index_start + object.index_count)) |i| {
try indices.remove(i);
}
try objects.remove(index);
}
Also, another thing I’ve noticed is that the position data is behaving abnormally. For example, the first 10 objects I add will gradually increase vertically by 1 unit (as programmed, using a delta value in for loop) (again, only 5 of them render). Then once I add the 11th object (outside of the for loop, colored blue this time) its position just doesn’t matter at all. I can set the 11th object’s position to literally anything and it will not respect the value I give it. It will always be at the top of how ever many rendered.
If any more information or code would be helpful in figuring this out, please just let me know what specifically you want to see and I can reply with it.