from libs import * # https://iquilezles.org/articles/intersectors/ def intersect_tri(ro, rd, tv): e1 = tv[1] - tv[0] e2 = tv[2] - tv[0] to = ro - tv[0] n = np.cross(e1, e2) q = np.cross(to, rd) d = 1.0 / np.dot(rd, n) u = d * np.dot(-q, e2) v = d * np.dot(q, e1) t = d * np.dot(-n, to) if u > 0.0 and v > 0.0 and u + v < 1.0 and t > 1e-3: return t class Entity: def __init__(self, verts, col, pos, **kwargs): self.verts = vec(verts) self.col = vec(col) self.pos = vec(pos) self.draw_mode = kwargs.get("mode", GL_TRIANGLE_STRIP) self.hud = kwargs.get("hud", False) self.orient = np.identity(4, dtype=FLOAT) self.onclick = lambda s, b, n: print(f"clicked {s}:{n} with button {b}") self.vao = None self.vbos = [[None, 0], [None, 0]] def col_list(self): return np.repeat(vec([self.col]), len(self.verts), 0) def update_attr(self, i, l=None): if l is None: l = self.verts if i == 0 else self.col_list() if self.vao: glBindBuffer(GL_ARRAY_BUFFER, self.vbos[i][0]) if l.shape[0] != self.vbos[i][1]: glBufferData(GL_ARRAY_BUFFER, l.nbytes, l, GL_DYNAMIC_DRAW) self.vbos[i][1] = l.shape[0] else: glBufferSubData(GL_ARRAY_BUFFER, 0, l.nbytes, l) def build_vao(self): self.vao = glGenVertexArrays(1) glBindVertexArray(self.vao) for (i, l) in enumerate((self.verts, self.col_list())): self.vbos[i][0] = glGenBuffers(1) self.update_attr(i, l) glEnableVertexAttribArray(i) glVertexAttribPointer(i, l.shape[1], GL_FLOAT, GL_FALSE, 0, None) def translate(self): m = np.identity(4, dtype=FLOAT) m[:3, 3] = self.pos return m def rotate(self, theta, axis): x, y, z = axis c, s = np.cos(theta), np.sin(theta) t = 1 - c self.orient = vec([ [t*x*x + c, t*x*y - s*z, t*x*z + s*y, 0], [t*x*y + s*z, t*y*y + c, t*y*z - s*x, 0], [t*x*z - s*y, t*y*z + s*x, t*z*z + c, 0], [0, 0, 0, 1] ]) @ self.orient def click(self, button, face): self.onclick(self, button, face) def raycast(self, ro, rd): if self.hud or self.draw_mode == GL_LINES: return elif self.draw_mode == GL_TRIANGLE_STRIP: tl = ((i, self.verts[i:i + 3]) for i in range(len(self.verts) - 2)) elif self.draw_mode == GL_TRIANGLES: tl = ((i, self.verts[3 * i:3 * (i + 1)]) for i in range(len(self.verts) // 3)) else: raise NotImplementedError d, n = None, None for (i, t) in tl: r = intersect_tri(ro, rd, np.dot(t, self.orient[:3, :3].T) + self.pos) if r and (d is None or r < d): d, n = r, i return d, n