def calculate_polycount(bsp_file): # triangle fan (num_tris + 2 = num_verts) so (num_edges - 2 = num_tris) face_tris = sum([f['numedges'] - 2 for f in bsp_file.FACES]) disp_tris = 0 try: disp_tris = len(list(bsp_file.DISP_TRIS)) except AttributeError: ... # render_bsp.py vert & index counts vertices = [] indices = [] currentIndex = 0 for face in bsp_file.FACES: if face["dispinfo"] == -1: faceVerts = bsp_file.verts_of(face) faceIndices = calcTriFanIndices(faceVerts, currentIndex) else: power = bsp_file.DISP_INFO[face['dispinfo']]['power'] faceVerts = bsp_tool.disp_tris(bsp_file.dispverts_of(face), power) faceIndices = bsp_tool.disp_tris(range((2 ** power + 1) ** 2), power) vertices += faceVerts indices += faceIndices currentIndex = faceIndices[len(faceIndices) - 1] + 1 return len(bsp_file.BRUSHES), len(bsp_file.FACES), face_tris, disp_tris, face_tris + disp_tris, len(vertices), len(indices)
def main(width, height, bsp): SDL_Init(SDL_INIT_VIDEO) window = SDL_CreateWindow(bytes(bsp.filename, 'utf-8'), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_BORDERLESS) #SDL_WINDOW_FULLSCREEN glContext = SDL_GL_CreateContext(window) glColor(0, 0, 0, 0) gluPerspective(90, width / height, 0.1, 4096 * 4) glPointSize(2) glPolygonMode(GL_BACK, GL_LINE) glEnable(GL_DEPTH_TEST) glColor(1, 1, 1) glFrontFace(GL_CW) FACE = iter(bsp.FACES) current_face = next(FACE) current_face_verts = bsp.verts_of(current_face) all_faces = [] all_faces_map = [] start = 0 for face in bsp.FACES: if face['dispinfo'] == -1: f_verts = bsp.verts_of(face) out = f_verts[:3] f_verts = f_verts[3:] for vert in f_verts: out += [out[0], out[-1], vert] f_verts = out f_verts_len = len(f_verts) all_faces_map.append((start, f_verts_len)) start += f_verts_len else: power = bsp.DISP_INFO[face['dispinfo']]['power'] f_verts = bsp_tool.disp_tris(bsp.dispverts_of(face), power) all_faces += f_verts ## all_faces = list(itertools.chain(*all_faces)) NODE = iter(filter(lambda x: x['children'][1] < 0, bsp.NODES)) NODE = sorted(bsp.NODES, key=lambda node: sum( [x[1] for x in node_faces(node, bsp, all_faces_map)])) current_node = NODE[0] current_node_index = 0 draw_calls = node_faces(current_node, bsp, all_faces_map) current_node_aabb = aabb(current_node['mins'], current_node['maxs']) cnff = current_node['firstface'] current_node_faces = all_faces_map[cnff:cnff + current_node['numfaces']] try: cn_start = current_node_faces[0][0] cn_count = sum([x[1] for x in current_node_faces]) except: cn_start = 0 cn_count = 0 all_nodes = list(map(lambda x: aabb(x['mins'], x['maxs']), bsp.NODES)) all_leaves = list(map(lambda x: aabb(x['mins'], x['maxs']), bsp.LEAVES)) print(bsp.filename.upper(), end=' ') print('{:,}KB BSP'.format(bsp.bytesize // 1024), '>>>', end=' ') print('{:,} TRIS'.format(len(all_faces) // 9), end=' & ') print('{:,}KB VRAM'.format((len(all_faces) * 4) // 1024)) print('{:,} NODES'.format(len(bsp.NODES))) # shader & vertex buffer would go here SDL_GL_SetSwapInterval(0) SDL_CaptureMouse(SDL_TRUE) SDL_WarpMouseInWindow(window, width // 2, height // 2) SDL_SetRelativeMouseMode(SDL_TRUE) SDL_SetWindowGrab(window, SDL_TRUE) cam_spawn = vector.vec3(0, 0, 0) init_speed = 128 VIEW_CAMERA = camera.freecam(cam_spawn, None, init_speed) mousepos = vector.vec2() keys = [] tickrate = 120 event = SDL_Event() oldtime = time() while True: while SDL_PollEvent(ctypes.byref(event)) != 0: if event.type == SDL_QUIT or event.key.keysym.sym == SDLK_ESCAPE and event.type == SDL_KEYDOWN: SDL_GL_DeleteContext(glContext) SDL_DestroyWindow(window) SDL_Quit() return False if event.type == SDL_KEYDOWN: if event.key.keysym.sym not in keys: keys.append(event.key.keysym.sym) if event.type == SDL_KEYUP: while event.key.keysym.sym in keys: keys.remove(event.key.keysym.sym) if event.type == SDL_MOUSEMOTION: mousepos += vector.vec2(event.motion.xrel, event.motion.yrel) SDL_WarpMouseInWindow(window, width // 2, height // 2) if event.type == SDL_MOUSEWHEEL: VIEW_CAMERA.speed += event.wheel.y * 32 if event.type == SDL_MOUSEBUTTONDOWN: if event.button.button not in keys: keys.append(event.button.button) if event.type == SDL_MOUSEBUTTONUP: while event.button.button in keys: keys.remove(event.button.button) dt = time() - oldtime while dt >= 1 / tickrate: VIEW_CAMERA.update(mousepos, keys, 1 / tickrate) if SDLK_BACKQUOTE in keys: #NODES print(current_node, draw_calls, sep='\n\n') cn_center = (current_node_aabb.mins + current_node_aabb.maxs) / 2 VIEW_CAMERA.position = cn_center while SDLK_BACKQUOTE in keys: keys.remove(SDLK_BACKQUOTE) if SDLK_r in keys: VIEW_CAMERA = camera.freecam(cam_spawn, None, init_speed) if SDLK_LSHIFT in keys: VIEW_CAMERA.speed += VIEW_CAMERA.speed * .125 if SDLK_LCTRL in keys: VIEW_CAMERA.speed -= VIEW_CAMERA.speed * .125 if SDLK_LEFT in keys or SDL_BUTTON_LEFT in keys: current_node_index -= 1 current_node = NODE[current_node_index] draw_calls = node_faces(current_node, bsp, all_faces_map) current_node_aabb = aabb(current_node['mins'], current_node['maxs']) VIEW_CAMERA.position = current_node_aabb.center cnff = current_node['firstface'] current_node_faces = all_faces_map[cnff:cnff + current_node['numfaces']] try: cn_start = current_node_faces[0][0] cn_count = sum([x[1] for x in current_node_faces]) except: cn_start = 0 cn_count = 0 while SDLK_LEFT in keys: keys.remove(SDLK_LEFT) while SDL_BUTTON_LEFT in keys: keys.remove(SDL_BUTTON_LEFT) if SDLK_RIGHT in keys or SDL_BUTTON_RIGHT in keys: current_node_index += 1 current_node = NODE[current_node_index] draw_calls = node_faces(current_node, bsp, all_faces_map) current_node_aabb = aabb(current_node['mins'], current_node['maxs']) VIEW_CAMERA.position = current_node_aabb.center cnff = current_node['firstface'] current_node_faces = all_faces_map[cnff:cnff + current_node['numfaces']] try: cn_start = current_node_faces[0][0] cn_count = sum([x[1] for x in current_node_faces]) except: cn_start = 0 cn_count = 0 while SDLK_RIGHT in keys: keys.remove(SDLK_RIGHT) while SDL_BUTTON_RIGHT in keys: keys.remove(SDL_BUTTON_RIGHT) dt -= 1 / tickrate oldtime = time() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glPushMatrix() VIEW_CAMERA.set() glPolygonMode(GL_FRONT, GL_LINE) ## glUseProgram(0) glColor(1, 0, 1) glBegin(GL_LINES) current_node_aabb.draw() glEnd() glColor(1, 1, 1) glDrawArrays(GL_TRIANGLES, cn_start, cn_count) ## glUseProgram(bsp_shader) glColor(1, .5, 0) glPolygonMode(GL_FRONT, GL_FILL) glPolygonMode(GL_BACK, GL_LINE) try: for draw_call in draw_calls: glDrawArrays(GL_TRIANGLES, draw_call[0], draw_call[1]) except: pass #CENTER POINT ## glUseProgram(0) ## glBegin(GL_LINES) ## glColor(1, 0, 0) ## glVertex(0, 0, 0) ## glVertex(128, 0, 0) ## glColor(0, 1, 0) ## glVertex(0, 0, 0) ## glVertex(0, 128, 0) ## glColor(0, 0, 1) ## glVertex(0, 0, 0) ## glVertex(0, 0, 128) ## glEnd() glPopMatrix() SDL_GL_SwapWindow(window)
def main(width, height, bsp): SDL_Init(SDL_INIT_VIDEO) window = SDL_CreateWindow(bytes(bsp.filename, 'utf-8'), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL) #| SDL_WINDOW_BORDERLESS) #SDL_WINDOW_FULLSCREEN glContext = SDL_GL_CreateContext(window) glClearColor(0, .5, 1, 0) try: light_environment = [e for e in bsp.ENTITIES if e.classname == 'light_environment'][0] light_environment.angles = tuple(map(float, light_environment.angles.split(' '))) light_environment.pitch = float(light_environment.pitch) light_environment._light = tuple(map(int, light_environment._light.split(' '))) light_environment._ambient = tuple(map(int, light_environment._ambient.split(' '))) sun_vector = vec3(1, 0, 0).rotate(light_environment.angles[2], -light_environment.pitch, light_environment.angles[1]) sun_colour = (*[x / 255 for x in light_environment._light[:3]], light_environment._light[-1]) # vec4 (R, G, B) + Strength sun_ambient = (*[x / 255 for x in light_environment._ambient[:3]], light_environment._ambient[-1]) # vec4 (R, G, B) + Strength glClearColor(*sun_ambient[:3], 0) except: # no light_environment in .bsp sun_vector = vec3(1, 0, 0).rotate(0, 35, 108) # GOLDRUSH sun_colour = (1.00, 0.89, 0.73, 600) sun_ambient = (0.46, 0.45, 0.55, 350) glClearColor(*sun_ambient[:3], 0) gluPerspective(90, width / height, 0.1, 4096 * 4) glPointSize(4) glPolygonMode(GL_BACK, GL_LINE) glEnable(GL_DEPTH_TEST) glFrontFace(GL_CW) glEnable(GL_CULL_FACE) glColor(1, 1, 1) filtered_faces = [f for f in bsp.FACES if f.light_offset != -1] # no sky or trigger ## filtered_faces = [f for f in bsp.FACES if f.disp_info == -1] # disp only ## filtered_faces = [f for f in bsp.FACES if f.light_offset != -1 and x.disp_info == -1] # no sky, trigger or disp ## filtered_faces = [f for f in bsp.FACES if f.styles == (-1, -1, -1, -1)] # unlit? faces ## filtered_faces = bsp.FACES # no filter face_count = len(filtered_faces) current_face_index = 0 current_face = filtered_faces[current_face_index] current_face_verts = [v[0] for v in bsp.verts_of(current_face)] all_faces = [] all_faces_map = [] # [(start, length), ...] start = 0 t1 = time() for face in filtered_faces: if face.disp_info == -1: f_verts = bsp.verts_of(face) # add to vertex buffer here and fan the indices out = f_verts[:3] f_verts = f_verts[3:] for vert in f_verts: out += [out[0], out[-1], vert] f_verts = out f_verts_len = len(f_verts) all_faces_map.append((start, f_verts_len)) start += f_verts_len else: # face is a displacement power = bsp.DISP_INFO[face.disp_info].power f_verts = bsp.dispverts_of(face) f_verts = bsp_tool.disp_tris(f_verts, power) f_verts_len = len(f_verts) all_faces_map.append((start, f_verts_len)) start += f_verts_len all_faces += f_verts slow_faces = all_faces.copy() all_faces = list(itertools.chain(*itertools.chain(*all_faces))) all_faces_size = len(all_faces) vertices = all_faces indices = range(all_faces_size) ## print('compressing vertex buffer...') ## vertices = [] ## indices = [] ## currentIndex = 0 ## for face in filtered_faces: ## if face.disp_info == -1: ## faceVerts = bsp.verts_of(face) ## faceIndices = calcTriFanIndices(faceVerts, currentIndex) ## else: ## power = bsp.DISP_INFO[face.disp_info].power ## faceVerts = bsp_tool.disp_tris(bsp.dispverts_of(face), power) ## faceIndices = bsp_tool.disp_tris(range((2 ** power + 1) ** 2), power) ## vertices += faceVerts ## indices += faceIndices ## currentIndex = faceIndices[-1] + 1 # ? ## vertices = list(itertools.chain(*itertools.chain(*vertices))) RGB_LIGHTING = [] for RGBE_texel in struct.iter_unpack('3Bb', bsp.RAW_LIGHTING): ## RGB_texel = vec3(RGBE_texel[:3]) * (2 ** RGBE_texel[3]) ## RGB_texel = [clamp(int(x), 0, 255) for x in RGB_texel] RGB_LIGHTING.append(struct.pack('3Bb', *RGBE_texel))#, RGBE_texel[3])) RGB_LIGHTING = b''.join(RGB_LIGHTING) lightmap = [] # store on GPU (TextureArray?) for face in filtered_faces: lmap_start = face.light_offset if lmap_start != -1: bounds = face.lightmap_texture_size_in_luxels bounds = [x + 1 for x in bounds] num_styles = sum([1 if x is not -1 else 0 for x in face.styles]) lmap_end = lmap_start + bounds[0] * bounds[1] * 4 * num_styles lmap_bytes = RGB_LIGHTING[lmap_start:lmap_end] lightmap.append([lmap_bytes, bounds]) t2 = time() print(bsp.filename.upper(), end=' ') print(f'{bsp.bytesize // 1024:,}KB BSP', end=' >>> ') print(f'{len(all_faces) // 9:,} TRIS', end=' & ') print(f'{(len(all_faces) * 4) // 1024:,}KB VRAM') print(f'ASSEMBLED IN {(t2 - t1) * 1000:,.3f}ms') print() # SHADERS (check GLSL version) USING_ES = False try: vertShader = compileShader(open('shaders/bsp_lightmap.v', 'rb'), GL_VERTEX_SHADER) fragShader = compileShader(open('shaders/bsp_lightmap.f', 'rb'), GL_FRAGMENT_SHADER) except Exception as exc: # requires PyOpenGL changes described in older version of this repo USING_ES = True # if OpenGL 4.5 is not supported, switch to GLES 3.0 vertShader = compileShader(open('shaders/bsp_faces_300_es.v', 'rb'), GL_VERTEX_SHADER) fragShader = compileShader(open('shaders/bsp_faces_300_es.f', 'rb'), GL_FRAGMENT_SHADER) raise exc # need to log error if issue is not GLSL version bsp_shader = compileProgram(vertShader, fragShader) glLinkProgram(bsp_shader) glUseProgram(bsp_shader) # must call UseProgram before setting uniforms # UNIFORMS if USING_ES: # GLES vertex attribs attrib_position = glGetAttribLocation(bsp_shader, 'vertexPosition') attrib_normal = glGetAttribLocation(bsp_shader, 'vertexNormal') attrib_texture_uv = glGetAttribLocation(bsp_shader, 'vertexTexCoord') attrib_lightmap_uv = glGetAttribLocation(bsp_shader, 'vertexLightCoord') attrib_colour_uv = glGetAttribLocation(bsp_shader, 'vertexColour') ## ProjectionMatrixLoc = glGetUniformLocation(bsp_shader, 'ProjectionMatrix') ## # https://www.khronos.org/opengl/wiki/GluPerspective_code ## glUniformMatrix4f?(ProjectionMatrixLoc, ?, GL_FALSE, ...) # bad input? else: # glsl 450 core uniforms glUniform3f(glGetUniformLocation(bsp_shader, 'sun_vector'), *sun_vector) glUniform4f(glGetUniformLocation(bsp_shader, 'sun_colour'), *sun_colour) glUniform4f(glGetUniformLocation(bsp_shader, 'sun_ambient'), *sun_ambient) VERTEX_BUFFER, INDEX_BUFFER = glGenBuffers(2) glBindBuffer(GL_ARRAY_BUFFER, VERTEX_BUFFER) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER) glBufferData(GL_ELEMENT_ARRAY_BUFFER, len(indices) * 4, np.array(indices, dtype=np.uint64), GL_STATIC_DRAW) # INDICES glBufferData(GL_ARRAY_BUFFER, len(vertices) * 4, np.array(vertices, dtype=np.float32), GL_STATIC_DRAW) # VERTICES glEnableVertexAttribArray(0) #vertexPosition glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 52, GLvoidp(0)) glEnableVertexAttribArray(1) #vertexNormal glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 52, GLvoidp(12)) glEnableVertexAttribArray(2) #vertexTexcoord glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 52, GLvoidp(24)) glEnableVertexAttribArray(3) #vertexLightmapCoord glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 52, GLvoidp(32)) glEnableVertexAttribArray(4) #reflectivityColour glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 52, GLvoidp(40)) # displacement alpha (seperate format or shared?) glEnable(GL_TEXTURE_2D) glActiveTexture(GL_TEXTURE0) # texture = open('materials/obsolete.bmp', 'rb') texture = open('materials/dev/reflectivity_100.bmp', 'rb') texture.seek(54) # glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB4, 256, 256, 0, GL_BGR, GL_UNSIGNED_BYTE, texture.read()) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB4, 512, 512, 0, GL_BGR, GL_UNSIGNED_BYTE, texture.read()) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE) texture.close() del texture SDL_GL_SetSwapInterval(0) SDL_CaptureMouse(SDL_TRUE) SDL_WarpMouseInWindow(window, width // 2, height // 2) SDL_SetRelativeMouseMode(SDL_TRUE) SDL_SetWindowGrab(window, SDL_TRUE) cam_spawn = vec3(0, 0, 32) init_speed = 128 VIEW_CAMERA = utils.camera.freecam(cam_spawn, None, init_speed) props = [e for e in bsp.ENTITIES if 'prop' in e.classname] mousepos = vec2() keys = [] tickrate = 120 oldtime = time() event = SDL_Event() while True: while SDL_PollEvent(ctypes.byref(event)) != 0: if event.type == SDL_QUIT or event.key.keysym.sym == SDLK_ESCAPE and event.type == SDL_KEYDOWN: SDL_GL_DeleteContext(glContext) SDL_DestroyWindow(window) SDL_Quit() return bsp if event.type == SDL_KEYDOWN: if event.key.keysym.sym not in keys: keys.append(event.key.keysym.sym) if event.type == SDL_KEYUP: while event.key.keysym.sym in keys: keys.remove(event.key.keysym.sym) if event.type == SDL_MOUSEMOTION: mousepos += vec2(event.motion.xrel, event.motion.yrel) SDL_WarpMouseInWindow(window, width // 2, height // 2) if event.type == SDL_MOUSEWHEEL: VIEW_CAMERA.speed += event.wheel.y * 32 if event.type == SDL_MOUSEBUTTONDOWN: if event.button.button not in keys: keys.append(event.button.button) if event.type == SDL_MOUSEBUTTONUP: while event.button.button in keys: keys.remove(event.button.button) dt = time() - oldtime while dt >= 1 / tickrate: VIEW_CAMERA.update(mousepos, keys, 1 / tickrate) sun_vector = sun_vector.rotate(.05, 0, 0) glUseProgram(bsp_shader) glUniform3f(glGetUniformLocation(bsp_shader, 'sun_vector'), *sun_vector) # update projection matrix (GLES only) if SDLK_BACKQUOTE in keys: face_values = [f"{s}: {getattr(face, s)}" for s in face.__slots__] print(f'{bsp.filename}.FACES[{bsp.FACES.index(current_face)}]', *face_values, sep='\n') fe, ne = current_face.first_edge, current_face.num_edges se_loop = bsp.SURFEDGES[fe:fe + ne] e_loop = [bsp.EDGES[e] for e in se_loop] face_verts = [bsp.VERTICES[v] for v in itertools.chain(*e_loop)][::2] print(*[f"{v.x:.3f}, {v.y:.3f}, {v.z:.3f}" for v in face_verts], sep="\n") face_center = sum(map(vec3, current_face_verts), vec3()) / len(current_face_verts) face_normal = bsp.PLANES[current_face.plane_num].normal VIEW_CAMERA.position = face_center + vec3(face_normal) * 32 while SDLK_BACKQUOTE in keys: keys.remove(SDLK_BACKQUOTE) if SDLK_r in keys: VIEW_CAMERA = utils.camera.freecam(cam_spawn, None, init_speed) if SDLK_LSHIFT in keys: VIEW_CAMERA.speed += 5 if SDLK_LCTRL in keys: VIEW_CAMERA.speed -= 5 if SDLK_LEFT in keys or SDL_BUTTON_LEFT in keys: current_face_index -= 1 current_face = filtered_faces[current_face_index] current_face_verts = [v[0] for v in bsp.verts_of(current_face)] while SDLK_LEFT in keys: keys.remove(SDLK_LEFT) while SDL_BUTTON_LEFT in keys: keys.remove(SDL_BUTTON_LEFT) if SDLK_RIGHT in keys or SDL_BUTTON_RIGHT in keys: # filter to only displacements so I can debug this mess current_face_index += 1 current_face = filtered_faces[current_face_index] current_face_verts = [v[0] for v in bsp.verts_of(current_face)] while SDLK_RIGHT in keys: keys.remove(SDLK_RIGHT) while SDL_BUTTON_RIGHT in keys: keys.remove(SDL_BUTTON_RIGHT) dt -= 1 / tickrate oldtime = time() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glPushMatrix() VIEW_CAMERA.set() glPolygonMode(GL_FRONT, GL_FILL) glUseProgram(bsp_shader) for i, face in enumerate(all_faces_map): texture = lightmap[i] glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8UI, texture[1][0], texture[1][1], 0, GL_RGBA_INTEGER, GL_UNSIGNED_BYTE, texture[0]) glDrawArrays(GL_TRIANGLES, face[0], face[1]) glDrawArrays(GL_TRIANGLES, 0, all_faces_size) # supported in gl3.0 Mesa? ## glDrawElements(GL_TRIANGLES, len(indices), GL_UNSIGNED_INT, GLvoidp(0)) # for when shaders are too much work ## glUseProgram(0) ## for i, f_map in enumerate(all_faces_map): #### texture = lightmap[i] #### glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture[1][0], texture[1][1], 0, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]) ## face = slow_faces[f_map[0]: f_map[0] + f_map[1]] ## glBegin(GL_TRIANGLES) ## for vertex in face: ## pos, normal, uv, uv2, colour = vertex ## glColor(*colour) ## glTexCoord(*uv) ## glVertex(*pos) ## glEnd() # CENTER MARKER ## glUseProgram(0) ## glBegin(GL_LINES) ## glColor(1, 0, 0) ## glVertex(0, 0, 0) ## glVertex(128, 0, 0) ## glColor(0, 1, 0) ## glVertex(0, 0, 0) ## glVertex(0, 128, 0) ## glColor(0, 0, 1) ## glVertex(0, 0, 0) ## glVertex(0, 0, 128) ## glEnd() glUseProgram(0) # SELECTED FACE glDisable(GL_TEXTURE_2D) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) glColor(1, 1, 1) glDisable(GL_DEPTH_TEST) glBegin(GL_LINE_LOOP) for vertex in current_face_verts: glVertex(*vertex) glEnd() glBegin(GL_POINTS) for vertex in current_face_verts: glVertex(*vertex) glEnd() # THE SUN glPointSize(24) glBegin(GL_POINTS) glVertex(*(sun_vector * 4096)) glEnd() glPointSize(18) glColor(1, 0, 1) glBegin(GL_POINTS) for p in props: position = [float(s) for s in p.origin.split()] glVertex(*position) glEnd() glPointSize(4) glEnable(GL_DEPTH_TEST) glEnable(GL_TEXTURE_2D) glPopMatrix() SDL_GL_SwapWindow(window)
def main(width, height, bsp): bsp = bsp_tool.bsp(bsp) SDL_Init(SDL_INIT_VIDEO) window = SDL_CreateWindow(bytes(bsp.filename, 'utf-8'), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL) #| SDL_WINDOW_BORDERLESS) #SDL_WINDOW_FULLSCREEN glContext = SDL_GL_CreateContext(window) glClearColor(0, .5, 1, 0) ent_dicts = [] for e in bsp.ENTITIES[1:-1].split('}\n{'): ent_dicts.append(eval('{' + e.replace('" "', '": "').replace('"\n', '", ') + '}')) try: light_environment = [e for e in ent_dicts if e['classname'] == 'light_environment'][0] light_environment['angles'] = tuple(map(float, light_environment['angles'].split(' '))) light_environment['pitch'] = float(light_environment['pitch']) light_environment['_light'] = tuple(map(int, light_environment['_light'].split(' '))) light_environment['_ambient'] = tuple(map(int, light_environment['_ambient'].split(' '))) sun_vector = vec3(1, 0, 0).rotate(light_environment['angles'][2], -light_environment['pitch'], light_environment['angles'][1]) sun_colour = (*[x / 255 for x in light_environment['_light'][:3]], light_environment['_light'][-1]) # vec4 (R, G, B) + Strength sun_ambient = (*[x / 255 for x in light_environment['_ambient'][:3]], light_environment['_ambient'][-1]) # vec4 (R, G, B) + Strength glClearColor(*sun_ambient[:3], 0) except: # no light_environment in .bsp (defaults to goldrush) sun_vector = vec3(1, 0, 0).rotate(0, 35, 108) sun_colour = (1.00, 0.89, 0.73, 600) sun_ambient = (0.46, 0.45, 0.55, 350) glClearColor(0, 0, 0, 0) gluPerspective(90, width / height, 0.1, 4096 * 4) glPointSize(4) glPolygonMode(GL_BACK, GL_LINE) glEnable(GL_DEPTH_TEST) glFrontFace(GL_CW) glEnable(GL_CULL_FACE) glColor(1, 1, 1) filtered_faces = list(filter(lambda x: x['lightofs'] != -1, bsp.FACES)) #no sky or trigger ## filtered_faces = list(filter(lambda x: x['dispinfo'] != -1, bsp.FACES)) #disp only ## filtered_faces = list(filter(lambda x: x['lightofs'] != -1 and x['dispinfo'] == -1, bsp.FACES)) #no sky, trigger or disp ## filtered_faces = list(filter(lambda x: x['styles'] == (-1, -1, -1, -1), bsp.FACES)) ## filtered_faces = bsp.FACES face_count = len(filtered_faces) current_face_index = 0 current_face = filtered_faces[current_face_index] current_face_verts = [v[0] for v in bsp.verts_of(current_face)] all_faces = [] all_faces_map = [] start = 0 t1 = time() for face in filtered_faces: if face['dispinfo'] == -1: f_verts = bsp.verts_of(face) # add to vertex buffer here and fan the indices out = f_verts[:3] f_verts = f_verts[3:] for vert in f_verts: out += [out[0], out[-1], vert] f_verts = out f_verts_len = len(f_verts) all_faces_map.append((start, f_verts_len)) start += f_verts_len else: power = bsp.DISP_INFO[face['dispinfo']]['power'] f_verts = bsp.dispverts_of(face) f_verts = bsp_tool.disp_tris(f_verts, power) all_faces += f_verts slow_faces = all_faces.copy() all_faces = list(itertools.chain(*itertools.chain(*all_faces))) all_faces_size = len(all_faces) vertices = all_faces indices = range(all_faces_size) ## print('compressing vertex buffer...') ## vertices = [] ## indices = [] ## currentIndex = 0 ## for face in filtered_faces: ## if face["dispinfo"] == -1: ## faceVerts = bsp.verts_of(face) ## faceIndices = calcTriFanIndices(faceVerts, currentIndex) ## else: ## power = bsp.DISP_INFO[face['dispinfo']]['power'] ## faceVerts = bsp_tool.disp_tris(bsp.dispverts_of(face), power) ## faceIndices = bsp_tool.disp_tris(range((2 ** power + 1) ** 2), power) ## vertices += faceVerts ## indices += faceIndices ## currentIndex = faceIndices[-1] + 1 # ? ## vertices = list(itertools.chain(*itertools.chain(*vertices))) ## RGB_LIGHTING = [] ## for RGBE_texel in struct.iter_unpack('3Bb', bsp.LIGHTING): ## RGBA_texel = vec3(RGBE_texel[:-1]) * 2 ** RGBE_texel[-1] ## RGBA_texel = [clamp(int(x) // 2, 0, 255) for x in RGBA_texel] ## RGB_LIGHTING.append(struct.pack('3Bb', *RGBA_texel, RGBE_texel[3])) ## RGB_LIGHTING = b''.join(RGB_LIGHTING) ## ## lightmap = [] # store on GPU ## for face in filtered_faces: ## lmap_start = face['lightofs'] ## if lmap_start != -1: ## bounds = face['LightmapTextureSizeinLuxels'] ## bounds = [x + 1 for x in bounds] ## num_styles = sum([1 if x is not -1 else 0 for x in face['styles']]) ## lmap_end = lmap_start + bounds[0] * bounds[1] * 4 * num_styles ## lmap_bytes = RGB_LIGHTING[lmap_start:lmap_end] ## lightmap.append([lmap_bytes, bounds]) t2 = time() print(bsp.filename.upper(), end=' ') print(f'{bsp.bytesize // 1024:,}KB BSP', end=' >>> ') print(f'{len(all_faces) // 9:,} TRIS', end=' & ') print(f'{(len(all_faces) * 4) // 1024:,}KB VRAM') print(f'ASSEMBLED IN {(t2 - t1) * 1000:,.3f}ms') print() # SHADERS (check GLSL version) USING_ES = False try: vertShader = compileShader(open('shaders/bsp_faces.v', 'rb'), GL_VERTEX_SHADER) fragShader = compileShader(open('shaders/bsp_faces.f', 'rb'), GL_FRAGMENT_SHADER) except Exception as exc: # requires PyOpenGL changes described elsewhere USING_ES = True # if OpenGL 4.5 is not supported, switch to GLES 3.0 vertShader = compileShader(open('shaders/bsp_faces_300_es.v', 'rb'), GL_VERTEX_SHADER) fragShader = compileShader(open('shaders/bsp_faces_300_es.f', 'rb'), GL_FRAGMENT_SHADER) raise exc # need error log if issue is not GLSL version bsp_shader = compileProgram(vertShader, fragShader) glLinkProgram(bsp_shader) glUseProgram(bsp_shader) # must UseProgram to set uniforms # UNIFORMS if USING_ES: # GLES vertex attribs attrib_position = glGetAttribLocation(bsp_shader, 'vertexPosition') attrib_normal = glGetAttribLocation(bsp_shader, 'vertexNormal') attrib_texture_uv = glGetAttribLocation(bsp_shader, 'vertexTexCoord') attrib_lightmap_uv = glGetAttribLocation(bsp_shader, 'vertexLightCoord') attrib_colour_uv = glGetAttribLocation(bsp_shader, 'vertexColour') ## ProjectionMatrixLoc = glGetUniformLocation(bsp_shader, 'ProjectionMatrix') ## # https://www.khronos.org/opengl/wiki/GluPerspective_code ## glUniformMatrix4fv(ProjectionMatrixLoc, 1, GL_FALSE, ProjectionMatrix) # bad input? else: # glsl 450 core uniforms glUniform3f(glGetUniformLocation(bsp_shader, 'sun_vector'), *sun_vector) glUniform4f(glGetUniformLocation(bsp_shader, 'sun_colour'), *sun_colour) glUniform4f(glGetUniformLocation(bsp_shader, 'sun_ambient'), *sun_ambient) VERTEX_BUFFER, INDEX_BUFFER = glGenBuffers(2) glBindBuffer(GL_ARRAY_BUFFER, VERTEX_BUFFER) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, INDEX_BUFFER) glBufferData(GL_ELEMENT_ARRAY_BUFFER, len(indices) * 4, np.array(indices, dtype=np.uint32), GL_STATIC_DRAW) # INDICES glBufferData(GL_ARRAY_BUFFER, len(vertices) * 4, np.array(vertices, dtype=np.float32), GL_STATIC_DRAW) # VERTICES glEnableVertexAttribArray(0) #vertexPosition glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 52, GLvoidp(0)) glEnableVertexAttribArray(1) #vertexNormal glVertexAttribPointer(1, 3, GL_FLOAT, GL_TRUE, 52, GLvoidp(12)) glEnableVertexAttribArray(2) #vertexTexcoord glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 52, GLvoidp(24)) glEnableVertexAttribArray(3) #vertexLightmapCoord glVertexAttribPointer(3, 2, GL_FLOAT, GL_FALSE, 52, GLvoidp(32)) glEnableVertexAttribArray(4) #reflectivityColour glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, 52, GLvoidp(40)) # displacement alpha (seperate format or shared?) glEnable(GL_TEXTURE_2D) glActiveTexture(GL_TEXTURE0) # texture = open('materials/obsolete.bmp', 'rb') texture = open('materials/dev/reflectivity_100.bmp', 'rb') texture.seek(54) # glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB4, 256, 256, 0, GL_BGR, GL_UNSIGNED_BYTE, texture.read()) glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB4, 512, 512, 0, GL_BGR, GL_UNSIGNED_BYTE, texture.read()) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) texture.close() del texture SDL_GL_SetSwapInterval(0) SDL_CaptureMouse(SDL_TRUE) SDL_WarpMouseInWindow(window, width // 2, height // 2) SDL_SetRelativeMouseMode(SDL_TRUE) SDL_SetWindowGrab(window, SDL_TRUE) cam_spawn = vec3(0, 0, 32) init_speed = 128 VIEW_CAMERA = camera.freecam(cam_spawn, None, init_speed) ## # http://heatmaps.tf/api.html ## url_tail = '.json?fields=id,timestamp,killer_class,killer_weapon,killer_x,killer_y,killer_z,victim_class,victim_x,victim_y,victim_z,customkill,damagebits,death_flags,team&limit=1024' #### heatmap = json.load(urllib.request.urlopen('http://heatmaps.tf/data/kills/' + bsp.filename[:-4] + url_tail)) # including the limit in the url is great for load times ## heatmap = json.load(open('heatmaps.tf/pl_upward_complete.json')) ## k_class = heatmap['fields'].index('killer_class') ## k_wep = heatmap['fields'].index('killer_weapon') ## MINI_SENTRY = -2 ## SENTRY = -1 ## WORLD = 0 ## SCOUT = 1 ## SNIPER = 2 ## SOLDIER = 3 ## DEMOMAN = 4 ## MEDIC = 5 ## HEAVY = 6 ## PYRO = 7 ## SPY = 8 ## ENGINEER = 9 ## k_x = heatmap['fields'].index('killer_x') ## v_class = heatmap['fields'].index('victim_class') ## v_x = heatmap['fields'].index('victim_x') ## kill_range = lambda kill: (vec3(*kill[v_x:v_x + 3]) - vec3(*kill[k_x:k_x + 3])).magnitude() ## ## filtered_kills = [*filter(lambda k: k[k_wep] == MINI_SENTRY, heatmap['kills'])][:1024] mousepos = vec2() keys = [] tickrate = 120 oldtime = time() event = SDL_Event() while True: while SDL_PollEvent(ctypes.byref(event)) != 0: if event.type == SDL_QUIT or event.key.keysym.sym == SDLK_ESCAPE and event.type == SDL_KEYDOWN: SDL_GL_DeleteContext(glContext) SDL_DestroyWindow(window) SDL_Quit() return bsp if event.type == SDL_KEYDOWN: if event.key.keysym.sym not in keys: keys.append(event.key.keysym.sym) if event.type == SDL_KEYUP: while event.key.keysym.sym in keys: keys.remove(event.key.keysym.sym) if event.type == SDL_MOUSEMOTION: mousepos += vec2(event.motion.xrel, event.motion.yrel) SDL_WarpMouseInWindow(window, width // 2, height // 2) if event.type == SDL_MOUSEWHEEL: VIEW_CAMERA.speed += event.wheel.y * 32 if event.type == SDL_MOUSEBUTTONDOWN: if event.button.button not in keys: keys.append(event.button.button) if event.type == SDL_MOUSEBUTTONUP: while event.button.button in keys: keys.remove(event.button.button) dt = time() - oldtime while dt >= 1 / tickrate: VIEW_CAMERA.update(mousepos, keys, 1 / tickrate) # turning sun sun_vector = sun_vector.rotate(.05, 0, 0) glUseProgram(bsp_shader) glUniform3f(glGetUniformLocation(bsp_shader, 'sun_vector'), *sun_vector) #update projection matrix if SDLK_BACKQUOTE in keys: ## print(VIEW_CAMERA) #FACES fe, ne = current_face['firstedge'], current_face['numedges'] se_loop = bsp.SURFEDGES[fe:fe + ne] e_loop = [bsp.EDGES[e] for e in se_loop] print(f'{bsp.filename}.FACES[{bsp.FACES.index(current_face)}]', '\n'.join([f'{k} = {v}' for k,v in current_face.items()]), sep='\n') print('\n\t', '\n\t'.join([f'{bsp.VERTICES[v]}' for v in itertools.chain(*e_loop)][::2]), sep='', end='\n\n') face_center = sum(map(vec3, current_face_verts), vec3()) / len(current_face_verts) face_normal = bsp.PLANES[current_face['planenum']]['normal'] VIEW_CAMERA.position = face_center + vec3(face_normal) * 32 while SDLK_BACKQUOTE in keys: keys.remove(SDLK_BACKQUOTE) if SDLK_r in keys: VIEW_CAMERA = camera.freecam(cam_spawn, None, init_speed) if SDLK_LSHIFT in keys: VIEW_CAMERA.speed += 5 if SDLK_LCTRL in keys: VIEW_CAMERA.speed -= 5 if SDLK_LEFT in keys or SDL_BUTTON_LEFT in keys: current_face_index -= 1 current_face = filtered_faces[current_face_index] current_face_verts = [v[0] for v in bsp.verts_of(current_face)] while SDLK_LEFT in keys: keys.remove(SDLK_LEFT) while SDL_BUTTON_LEFT in keys: keys.remove(SDL_BUTTON_LEFT) if SDLK_RIGHT in keys or SDL_BUTTON_RIGHT in keys: current_face_index += 1 current_face = filtered_faces[current_face_index] current_face_verts = [v[0] for v in bsp.verts_of(current_face)] while SDLK_RIGHT in keys: keys.remove(SDLK_RIGHT) while SDL_BUTTON_RIGHT in keys: keys.remove(SDL_BUTTON_RIGHT) dt -= 1 / tickrate oldtime = time() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glPushMatrix() VIEW_CAMERA.set() glPolygonMode(GL_FRONT, GL_FILL) glUseProgram(bsp_shader) ## for i, face in enumerate(all_faces_map): ## texture = lightmap[i] ## glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, texture[1][0], texture[1][1], 0, GL_RGBA, GL_UNSIGNED_BYTE, texture[0]) ## glDrawArrays(GL_TRIANGLES, face[0], face[1]) ## glDrawArrays(GL_TRIANGLES, 0, all_faces_size) # supported in gl3.0 Mesa? glDrawElements(GL_TRIANGLES, len(indices), GL_UNSIGNED_INT, GLvoidp(0)) # for when shaders are too much work ## glBegin(GL_TRIANGLES) ## for pos, normal, uv, uv2, colour in slow_faces[:2048]: ## glColor(*colour) ## glTexCoord(*uv) ## glVertex(*pos) ## glEnd() # CENTER MARKER ## glUseProgram(0) ## glBegin(GL_LINES) ## glColor(1, 0, 0) ## glVertex(0, 0, 0) ## glVertex(128, 0, 0) ## glColor(0, 1, 0) ## glVertex(0, 0, 0) ## glVertex(0, 128, 0) ## glColor(0, 0, 1) ## glVertex(0, 0, 0) ## glVertex(0, 0, 128) ## glEnd() glUseProgram(0) glDisable(GL_TEXTURE_2D) glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) glColor(1, 1, 1) glDisable(GL_DEPTH_TEST) ## glBegin(GL_LINE_LOOP) ## for vertex in current_face_verts: ## glVertex(*vertex) ## glEnd() ## glBegin(GL_POINTS) ## for vertex in current_face_verts: ## glVertex(*vertex) ## glEnd() glPointSize(24) glBegin(GL_POINTS) glVertex(*(sun_vector * 4096)) glEnd() glPointSize(4) glEnable(GL_DEPTH_TEST) glEnable(GL_TEXTURE_2D) ## glTranslate(0, 0, 64) ## glBegin(GL_LINES) ## for kill in filtered_kills: ## glColor(*colorsys.hsv_to_rgb(kill[k_class] / 9, 1, .75)) ## glVertex(*kill[k_x:k_x + 3]) ## glColor(*colorsys.hsv_to_rgb(kill[v_class] / 9, 1, 1)) ## glVertex(*kill[v_x:v_x + 3]) ## glEnd() glPopMatrix() SDL_GL_SwapWindow(window)