In fooling around with my own 3D display engine I've been able to get a few modest features working, and I've come close with this latest one - line culling - but I fear in order to do it correctly I'll need to backpedal a little bit and start establishing more attributes in my basic wireframe objects. I'd like to avoid that if possible because I legitimately haven't needed it until now (maybe).
The basic '3d object' class is essentially just lists of lines, points, an RGB value for color, and a few self-transforming methods for basic movement and rotation.
class MyObject(object): def __init__(self, points, lines, color): self.points = points self.lines = lines self.color = color # insert methods for rotation/translation/etc
No vertex order, no collection of 'faces' or 'sides' or anything, just points and lines and a color.
I've tried to introduce a culling method into my view object that uses all the possible triangles that each line could be an edge with, and then checks the dot product of that triangle's outward-facing normal against the imaginary line from the camera to the shared point in the triangle (v0, pt_a, etc).
def line_cull(self, shape): """Determine which lines should be drawn by comparing them against the triangles they could be a part of. If the triangle faces away from the camera, don't draw that line. """ to_draw =  for line in shape.lines: a, b = line # get a list of the other lines that point a exists in other_lines = [m for m in shape.lines if a in m and m is not one] # treat point a as "vertex 0" and get the line from a to the camera a_to_cam = minus(a, self.camera) for line2 in other_lines: # get the correct point c if not a == line2: line2 = line2[::-1] c = line2[-1] # get the vector of ab and ac for a cross product ab = minus(b, a) ac = minus(c, a) # find the middle of the triangle mid = [x / 3.0 for x in plus(a, plus(b, c))] # outwards is the direction vector from the shape's center # to the middle of the triangle. for whatever reason this # has been working better at orienting the normal than just # using the dot product to the center of the object outwards = minus(mid, shape.center) norm = cross(ab, ac) if dot(norm, outwards) < 0: norm = cross(ac, ab) if dot(norm, a_to_cam) < 0: if line not in to_draw: to_draw.append(line) return to_draw
This works for certain shapes a lot better than others. Sticking to platonic solids for now - tetrahedrons and cubes are perfectly drawn, whereas octahedrons (especially rotating ones) have flickering back-end lines.
I did try to include a line about confirming the existence of line BC -- that is, the imaginary 'third line' in the triangle -- and all of a sudden, all my cubes disappeared! Because there would never be a third line in its series of right-angles, and so there was nothing to render anymore.
My question is -- do I have to establish faces/sides/etc in order for this to "just work", or is there a way to suss out which pairings should not be considered, without just excluding certain shapes from being rendered at all?