def drawMainInventory(client: ClientState, canvas): # FIXME: player = client.getPlayer() assert(player is not None) for i in range(9, 36): slot = player.inventory[i] (x, y, _) = getSlotCenterAndSize(client, i) drawSlot(client, canvas, x, y, slot)
def updatePlayerPos(client: ClientState): player: Player = client.player playerChunkPos = world.toChunkLocal(player.getBlockPos())[0] playerChunkPos = ChunkPos(playerChunkPos.x, 0, playerChunkPos.z) # W makes the player go forward, S makes them go backwards, # and pressing both makes them stop! z = float(client.w) - float(client.s) # Likewise for side to side movement x = float(client.d) - float(client.a) ticks = min((time.perf_counter() - client.lastFrameTime) / 0.05, 2) if playerChunkPos in client.world.chunks and client.world.chunks[ playerChunkPos].isTicking: if x != 0.0 or z != 0.0: mag = math.sqrt(x * x + z * z) x /= mag z /= mag newX = math.cos(client.cameraYaw) * x - math.sin( client.cameraYaw) * z newZ = math.sin(client.cameraYaw) * x + math.cos( client.cameraYaw) * z x, z = newX, newZ x *= player.walkSpeed z *= player.walkSpeed #player.tick(app, app.world, app.entities, 0.0, 0.0) if player.flying: if client.space: player.velocity[1] = 0.2 elif client.shift: player.velocity[1] = -0.2 else: player.velocity[1] = 0.0 collideY(client.world, player, ticks, useGravity=False) if player.onGround: player.velocity[0] = x player.velocity[2] = z else: player.velocity[0] += x / 10.0 player.velocity[2] += z / 10.0 player.velocity[0] *= 0.9 player.velocity[2] *= 0.9 collideXZ(client.world, player, ticks) client.cameraPos = copy.copy(player.pos) client.cameraPos[1] += player.height
def drawHud(client: ClientState, canvas, startTime): # Indicates the center of the screen canvas.create_oval(client.width / 2 - 1, client.height / 2 - 1, client.width / 2 + 1, client.height / 2 + 1) drawHealthbar(client, canvas) drawHotbar(client, canvas) if client.showDebugInfo: clientTickTime = sum(client.tickTimes) / len(client.tickTimes) * 1000.0 serverTickTime = sum(client.serverTickTimes) / len(client.serverTickTimes) * 1000.0 drawTextOutlined(canvas, 10, 30, text=f'Tick: (C) {clientTickTime:.2f}ms (S) {serverTickTime:.2f}ms', anchor='nw') global frameTimes global frameTimeIdx endTime = time.time() frameTimes[frameTimeIdx] = (endTime - startTime) frameTimeIdx += 1 frameTimeIdx %= len(frameTimes) frameTime = sum(frameTimes) / len(frameTimes) * 1000.0 drawTextOutlined(canvas, 10, 10, text=f'Frame Time: {frameTime:.2f}ms', anchor='nw') drawTextOutlined(canvas, 10, 50, text=f"Eyes: {client.cameraPos[0]:.2f}, {client.cameraPos[1]:.2f}, {client.cameraPos[2]:.2f}", anchor='nw') chunkX = math.floor(client.cameraPos[0] / 16) chunkY = math.floor(client.cameraPos[1] / world.CHUNK_HEIGHT) chunkZ = math.floor(client.cameraPos[2] / 16) drawTextOutlined(canvas, 10, 140, text=f'Chunk coords: {chunkX}, {chunkY}, {chunkZ}', anchor='nw') # FIXME: player = client.getPlayer() if player is not None: drawTextOutlined(canvas, 10, 90, text=f"Feet: {player.pos[0]:.2f}, {player.pos[1]:.2f}, {player.pos[2]:.2f}", anchor='nw') feetPos = (client.cameraPos[0], client.cameraPos[1] - player.height + 0.1, client.cameraPos[2]) feetBlockPos = world.nearestBlockPos(feetPos[0], feetPos[1], feetPos[2]) (ckPos, ckLocal) = world.toChunkLocal(feetBlockPos) if ckPos in client.world.chunks: lightLevel = client.world.getLightLevel(feetBlockPos) blockLightLevel = client.world.getBlockLightLevel(world.nearestBlockPos(feetPos[0], feetPos[1], feetPos[2])) drawTextOutlined(canvas, 10, 190, text=f'Sky {lightLevel}, Block {blockLightLevel}', anchor='nw') biome = client.world.chunks[ckPos].biomes[ckLocal.x // 4, ckLocal.y // 4, ckLocal.z // 4] drawTextOutlined(canvas, 10, 230, text=f'Biome: {biome.name}', anchor='nw')
def clientTick(client: ClientState, instData): startTime = time.time() client.time += 1 if not client.local: chunkPos, _ = world.toChunkLocal(client.player.getBlockPos()) client.world.tickets[chunkPos] = 1 client.world.loadUnloadChunks(instData) client.world.addChunkDetails(instData, needUrgent=False) client.world.flushLightChanges() player: Player = client.player if not player.onGround: player.velocity[1] -= GRAVITY playerChunkPos = world.toChunkLocal(player.getBlockPos())[0] playerChunkPos = ChunkPos(playerChunkPos.x, 0, playerChunkPos.z) for entity in client.entities: entChunkPos = world.toChunkLocal(entity.getBlockPos())[0] entChunkPos = ChunkPos(entChunkPos.x, 0, entChunkPos.z) if entChunkPos not in client.world.chunks or not client.world.chunks[ entChunkPos].isTicking: continue entity.clientTick() #collide(client, entity) endTime = time.time() client.tickTimes[client.tickTimeIdx] = (endTime - startTime) client.tickTimeIdx += 1 client.tickTimeIdx %= len(client.tickTimes) client.lastTickTime = endTime
def drawHotbar(client: ClientState, canvas): # FIXME: player = client.getPlayer() assert(player is not None) #slotWidth = CLIENT_DATA.itemTextures['air'].width + 7 slotWidth = getSlotCenterAndSize(client, 0)[2] margin = 10 for (i, slot) in enumerate(player.inventory[:9]): x = (i - 4) * slotWidth + client.width / 2 y = client.height - margin - slotWidth / 2 drawSlot(client, canvas, x, y, slot) x = (player.hotbarIdx - 4) * slotWidth + client.width / 2 y = client.height - margin - slotWidth canvas.create_rectangle(x - slotWidth / 2, y, x + slotWidth / 2, y + slotWidth, outline='white')
def __init__(self, cid, sock, addr): self.state = ClientState() self.connection = ClientConnection(cid, sock, addr)
def renderInstancesGl(client: ClientState, canvas): glViewport(0, 0, client.width, client.height) view = glViewMat(client.cameraPos, client.cameraYaw, client.cameraPitch) th = math.tan(0.5 * math.radians(70.0)); zf = 100.0; zn = 0.1; projection = np.array([ [(client.height / client.width) / th, 0.0, 0.0, 0.0], [0.0, 1.0 / th, 0.0, 0.0], [0.0, 0.0, zf / (zf - zn), 1.0], [0.0, 0.0, -(zf * zn) / (zf - zn), 0.0], ], dtype='float32') #glBindVertexArray(app.cubeVao) chunkMeshes = [] for (pos, chunk) in client.world.chunks.items(): if not chunk.isVisible: continue [cx, cy, cz] = chunk.pos cx *= 16 cy *= world.CHUNK_HEIGHT cz *= 16 for i in range(len(chunk.meshes)): chunkMeshes.append((chunk.meshes[i], chunk.transMeshes[i], pos, i)) breakingBlockAmount = 0.0 if hasattr(client, 'player'): player = client.getPlayer() if client.breakingBlock != 0.0 and player is not None: toolStack = player.inventory[player.hotbarIdx].stack if toolStack.isEmpty(): tool = '' else: tool = toolStack.item blockId = client.world.getBlock(client.breakingBlockPos) if blockId != 'air': hardness = getHardnessAgainst(blockId, tool) breakingBlockAmount = client.breakingBlock / hardness b = math.floor(breakingBlockAmount * 10.0) b = min(b, len(CLIENT_DATA.breakTextures) - 1) else: b = 0 CLIENT_DATA.chunkProgram.useProgram() glUniformMatrix4fv(CLIENT_DATA.chunkProgram.getUniformLocation("view"), 1, GL_FALSE, view) #type:ignore glUniformMatrix4fv(CLIENT_DATA.chunkProgram.getUniformLocation("projection"), 1, GL_FALSE, projection) #type:ignore glUniform1f(CLIENT_DATA.chunkProgram.getUniformLocation("atlasWidth"), CLIENT_DATA.atlasWidth) glUniform1i(CLIENT_DATA.chunkProgram.getUniformLocation("gameTime"), client.time) glUniform1i(CLIENT_DATA.chunkProgram.getUniformLocation("blockTexture"), 0) glUniform1i(CLIENT_DATA.chunkProgram.getUniformLocation("breakTexture"), 1) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, CLIENT_DATA.textureAtlas) glActiveTexture(GL_TEXTURE1) glBindTexture(GL_TEXTURE_2D, CLIENT_DATA.breakTextures[b]) (cp, lp) = world.toChunkLocal(client.breakingBlockPos) breakBlockIdx = lp.x * 16 * 16 + (lp.y % world.MESH_HEIGHT) * 16 + lp.z breakBlockLoc = CLIENT_DATA.chunkProgram.getUniformLocation("breakBlockIdx") glBindFramebuffer(GL_FRAMEBUFFER, CLIENT_DATA.translucentFb) glViewport(0, 0, client.width, client.height) glClearColor(0.2, 0.1, 0.1, 1.0) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) #type:ignore for _m, transMesh, pos, i in chunkMeshes: if breakingBlockAmount > 0.0 and cp == pos and (lp.y // world.MESH_HEIGHT) == i: glUniform1i(breakBlockLoc, breakBlockIdx) else: glUniform1i(breakBlockLoc, -1) glBindVertexArray(transMesh.vao) glDrawArrays(GL_TRIANGLES, 0, transMesh.dataLen) glBindFramebuffer(GL_FRAMEBUFFER, 0) for mesh, _t, pos, i in chunkMeshes: if breakingBlockAmount > 0.0 and cp == pos and (lp.y // world.MESH_HEIGHT) == i: glUniform1i(breakBlockLoc, breakBlockIdx) else: glUniform1i(breakBlockLoc, -1) glBindVertexArray(mesh.vao) glDrawArrays(GL_TRIANGLES, 0, mesh.dataLen) CLIENT_DATA.transProgram.useProgram() glUniform1i(CLIENT_DATA.transProgram.getUniformLocation("transTexture"), 0) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, CLIENT_DATA.transColorTex) glUniform1i(CLIENT_DATA.transProgram.getUniformLocation("depthTexture"), 1) glActiveTexture(GL_TEXTURE1) glBindTexture(GL_TEXTURE_2D, CLIENT_DATA.transDepthTex) glBindVertexArray(CLIENT_DATA.fullscreenVao) glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, ctypes.c_void_p(0)) #type:ignore drawEntities(client, view, projection) # https://learnopengl.com/Advanced-OpenGL/Cubemaps glDisable(GL_CULL_FACE) glDepthFunc(GL_LEQUAL) CLIENT_DATA.skyProgram.useProgram() view[3, 0:3] = 0.0 glUniformMatrix4fv(CLIENT_DATA.skyProgram.getUniformLocation("view"), 1, GL_FALSE, view) #type:ignore glUniformMatrix4fv(CLIENT_DATA.skyProgram.getUniformLocation("projection"), 1, GL_FALSE, projection) #type:ignore glUniform1i(CLIENT_DATA.skyProgram.getUniformLocation("gameTime"), client.time) glUniform1i(CLIENT_DATA.skyProgram.getUniformLocation("sunTex"), 0) glActiveTexture(GL_TEXTURE0) glBindTexture(GL_TEXTURE_2D, CLIENT_DATA.sunTex) glBindVertexArray(CLIENT_DATA.skyboxVao) glDrawArrays(GL_TRIANGLES, 0, 36) glBindVertexArray(0) glEnable(GL_CULL_FACE) glDepthFunc(GL_LESS)
def cullInstance(client: ClientState, toCamMat: ndarray, inst: Instance, blockPos: Optional[BlockPos]) -> List[Tuple[Any, Face, Color]]: """ This converts the instance's vertices to points in camera space, and then: For all blocks, the following happens: - Faces pointing away from the camera are removed - Faces that are hidden 'underground' are removed - The color of each face is adjusted based on lighting - ~~A "fog" is applied~~ NOT IMPLEMENTED! TODO: For anything else: - Normal back face culling is applied Then the faces are clipped, which may remove, modify, or split faces. Then a list of faces, their vertices, and their colors are returned """ vertices = [toCamMat @ v for v in inst.worldSpaceVertices()] faces = [] skipNext = False for (faceIdx, (face, color)) in enumerate(zip(inst.model.faces, inst.texture)): if skipNext: skipNext = False continue if blockPos is not None: if not inst.visibleFaces[faceIdx]: continue if isBackBlockFace(client, blockPos, faceIdx): skipNext = True continue light = blockFaceLight(client, blockPos, faceIdx) r = int(color[1:3], base=16) g = int(color[3:5], base=16) b = int(color[5:7], base=16) brightness = (light + 1) / 16 r *= brightness g *= brightness b *= brightness if blockPos == client.breakingBlockPos and client.breakingBlock != 0.0: avg = (r + g + b) / 3.0 player = client.getPlayer() assert(player is not None) toolSlot = player.inventory[player.hotbarIdx] if toolSlot.isEmpty(): tool = '' else: tool = toolSlot.stack.item hardness = getHardnessAgainst(client.world.getBlock(blockPos), tool) desaturation = client.breakingBlock / hardness r += (avg - r) * desaturation g += (avg - g) * desaturation b += (avg - b) * desaturation r = max(0.0, min(255.0, r)) g = max(0.0, min(255.0, g)) b = max(0.0, min(255.0, b)) color = '#{:02X}{:02X}{:02X}'.format(int(r), int(g), int(b)) else: # Backface culling (surprisingly expensive) ''' backFace = isBackFace( vertices[face[0]], vertices[face[1]], vertices[face[2]] ) if backFace: continue ''' for clippedFace in clip(client, vertices, face): faces.append([vertices, clippedFace, color]) return faces