def get_color(point): # Color c[34]B, can we use 4 for water? color_grass = [10, 150, 50] color_mountain = [255, 255, 255] color_sand = [200, 200, 0] mountain_height = 1.1 # Slight offset from ocean, so that some land is colored as sand grass_height = 1.005 mag = math_helper.get_mag(point) if mag > mountain_height: main_color = color_mountain elif mag > grass_height: main_color = color_grass else: main_color = color_sand return main_color
def generate_icosahedron(radius=1): """ Generates an icosahedron, returns a list of coordinates """ # TODO - Looks like radius isnt working correctly # TODO - Might be able to remove radius from t t = radius * ((1.0 + math.sqrt(5.0)) / 2.0) t = (1.0 + math.sqrt(5.0)) / 2.0 points = [[-1, t, 0], [1, t, 0], [-1, -t, 0], [1, -t, 0], [0, -1, t], [0, 1, t], [0, -1, -t], [0, 1, -t], [t, 0, -1], [t, 0, 1], [-t, 0, -1], [-t, 0, 1] ] # Normalize all verts # TODO - Could be written better points_out = [] for coord in points: # TODO - Add radius to scale scale = 1.0 scale = radius b = [] mag = math_helper.get_mag(coord) # Refactor into list comp for point in coord: b.append((point / mag) * scale) points_out.append(b) return points_out
def generate_edges(icosahedron, divs, main_displacement, roughness): """ Return a list of edges along each face of the icosahedron """ # If theres an easier way to express edges, we can generalise the solution and increase the resolution of the system # Edges that connect the icosahedron together edges = get_icosahedron_edges() # This stage generates the edges, need to do another generation to fill out triangles final_edges = {} for i in edges: final_edges[i] = [icosahedron[i[0]], icosahedron[i[1]]] # Reset starting displacement for each edge displacement = main_displacement # This needs to be moved to its own function for divisions in range(divs): displacement *= 2 ** -roughness n = 1 for j in range(len(final_edges[i]) - 1): mid = math_helper.get_midpoint(final_edges[i][n - 1], final_edges[i][n]) mag = math_helper.get_mag(mid) scale = random.uniform(mag - displacement, mag + displacement) / mag a = mid[0] * scale b = mid[1] * scale c = mid[2] * scale final_edges[i].insert(n, [a, b, c]) n += 2 # Create the reverse edge final_edges[(i[1], i[0])] = list(reversed(final_edges[i])) return final_edges
def generate_surface(divs, edges, roughness=0, displacement=0): """ Modified version of the midpoint displacement algorithm, or diamond square algorithm, made to work with a triangular section of terrian (Face of an icosahedron). divs: The number of divisions required on the face edges: Edges of the face, can be single points for corners or entire edge. Other values will cause an exception [[Edge from A to B], [Edge from B to C], [Edge from C to A]] where each edge is an array of [x, y, z] coordinates B |\ | \ | \ | \ | \ |_____\ A C AB - [[0,0] -> [0,n]] BC - [[0,n] -> [n,0]] CA - [[n,0] -> [0,0]] roughness: Controls the decay of surface displacement over successive steps displacement: Controls the magnitude of the change in surface Returns the generated surface as a triangular array """ max_points = 2 ** divs + 1 surface = [] for i in range(max_points): surface.append([]) for j in range(max_points - i): surface[i].append(None) # AB if len(edges[0]) == 1 and len(edges[1]) == 1 and len(edges[2]): # A surface[0][0] = edges[0][0] # B surface[0][max_points-1] = edges[1][0] # C surface[max_points-1][0] = edges[2][0] elif len(edges[0]) == max_points and len(edges[1]) == max_points and len(edges[2]) == max_points: # AB for i in range(0, max_points): surface[0][i] = edges[0][i] # BC for i in range(0, max_points): # TODO - This one is iterating backwards surface[max_points - 1 - i][i] = edges[1][i] # CA for i in range(0, max_points): surface[max_points - 1 - i][0] = edges[2][i] else: raise Exception('Incorrect number of points in edge, should be either entire edge or single point') step = max_points for d in range(divs): # Control the displacement decay of the algorithm displacement *= 2 ** -roughness step //= 2 for i, x in enumerate(range(0, max_points, step)): for j, y in enumerate(range(0, len(surface[x]), step)): if surface[x][y] is None: # TODO - Could use a diagram # Select points to calculate the midpoint, the nearest points within the grid are dependant on # the current step size and the parity on the x axis. # The nearest point is then either horizontal line, vertical line or a diagonal line # Horizontal line if j % 2 == 0: upper = surface[x + step][y] lower = surface[x - step][y] else: # Vertical line if i % 2 == 0: upper = surface[x][y + step] lower = surface[x][y - step] # Diagonal line else: upper = surface[x + step][y - step] lower = surface[x - step][y + step] # Find the x,y,z midpoint between the two points mid = math_helper.get_midpoint(upper, lower) # Find the magnitude of the vector mag = math_helper.get_mag(mid) # Scale the vector around its current position and normalize it scale = random.uniform(mag - displacement, mag + displacement) / mag a = mid[0] * scale b = mid[1] * scale c = mid[2] * scale surface[x][y] = [a, b, c] return surface
def __init__(self): self.throttle = 500 self.icosahedron = generator.generate_icosahedron() self.ocean_batch = pyglet.graphics.Batch() self.cloud_batch = pyglet.graphics.Batch() self.sun_batch = pyglet.graphics.Batch() self.chunks = {} self.queue = Queue.Queue() self.background_stars = stars.Stars() divs = 6 main_displacement = 0.3 roughness = 1.0 # First apply random steps to each vertex new_icosahedron = self.icosahedron final_edges = generator.generate_edges(new_icosahedron, divs, main_displacement, roughness) faces = generator.get_icosahedron_faces() ocean_icosahedron = generator.generate_icosahedron() new_ocean = [] for face in faces: # Not the most elegant solution a = ocean_icosahedron[face[0]] b = ocean_icosahedron[face[1]] c = ocean_icosahedron[face[2]] surface = generator.generate_surface(divs, [[a], [b], [c]]) new_ocean.append(surface) ocean_color = [0, 0, 255, 170] verts = [] max_points = 2 ** divs + 1 for face in new_ocean: for i in range(0, max_points - 1): # Create a counter k, this is used in place of j when we draw the triangle at the end of the row k = 0 for j in range(max_points - i - 2): # Add both triangle in the square to the render batch # # b______d . # |\ | |\ # | \ | | \ # | \ 2 | | \ # | 1 \ | | 3 \ # | \ | | \ # a|_____\|c ........ |_____\ a = face[i][j] b = face[i][j + 1] c = face[i + 1][j] d = face[i + 1][j + 1] # Triangle 1 verts += a + b + c # Triangle 2, bdc to maintain clockwise winding order verts += b + d + c k += 1 # Add remaining triangle at the end of the row, Triangle 3 a = face[i][k] b = face[i][k + 1] c = face[i + 1][k] verts += a + b + c num_triangles = len(verts) / 3 # As water color is uniform, add all colors at the end colors = ocean_color * num_triangles # Example of group, class overrides functions, functions within pyglet are all pass # https: // github.com / reidrac / pyglet - obj - batch / blob / master / obj_batch.py group = pyglet.graphics.Group # print group. # water_group = material.Material() water_group = None self.ocean_batch.add(num_triangles, pyglet.gl.GL_TRIANGLES, water_group, ('v3f', verts), ('n3f', verts), ('c4B', colors)) cloud_icosahedron = generator.generate_icosahedron(2) new_clouds = [] new_cloud_density = [] cloud_displacement = 0.9 cloud_roughness = 1 scale_div = 2 cloud_cutoff = 0 cloud_edges = generator.generate_edges(new_icosahedron, divs, cloud_displacement, cloud_roughness) for face in faces: # Not the most elegant solution a = cloud_icosahedron[face[0]] b = cloud_icosahedron[face[1]] c = cloud_icosahedron[face[2]] edges = [cloud_edges[(face[0], face[1])], \ # Note, this edge is backwards cloud_edges[(face[2], face[1])], \ cloud_edges[(face[2], face[0])]] #surface = generator.generate_surface(divs, [[a], [b], [c]]) # Need to pre generate the edges for this one surface = generator.generate_surface(divs, [[a], [b], [c]]) new_clouds.append(surface) surface_density = generator.generate_surface(divs, edges, cloud_displacement, cloud_roughness) new_cloud_density.append(surface_density) # Need a function to generate density # Add the water to the render batch cloud_color = [0, 250, 255, 170] verts = [] max_points = 2 ** divs + 1 for f, face in enumerate(new_clouds): for i in range(0, max_points - 1): # Create a counter k, this is used in place of j when we draw the triangle at the end of the row k = 0 for j in range(max_points - i - 2): # Add both triangle in the square to the render batch # # b______d . # |\ | |\ # | \ | | \ # | \ 2 | | \ # | 1 \ | | 3 \ # | \ | | \ # a|_____\|c ........ |_____\ a = face[i][j] b = face[i][j+1] c = face[i+1][j] d = face[i+1][j+1] verts = [] verts += a + b + c color = [] # get mag new_cloud_density[f][i][j] + new_cloud_density[f][i][j + 1] + new_cloud_density[f][i + 1][j] mag1 = int((255 / scale_div) * math_helper.get_mag(new_cloud_density[f][i][j])) mag2 = int((255 / scale_div) * math_helper.get_mag(new_cloud_density[f][i][j + 1])) mag3 = int((255 / scale_div) * math_helper.get_mag(new_cloud_density[f][i + 1][j])) if mag1 < cloud_cutoff: mag1 = 0 if mag2 < cloud_cutoff: mag2 = 0 if mag3 < cloud_cutoff: mag3 = 0 color = [255, 255, 255, mag1, 255, 255, 255, mag2, 255, 255, 255, mag3] self.cloud_batch.add(3, pyglet.gl.GL_TRIANGLES, None, ('v3f', verts), ('n3f', verts),('c4B', color)) #('c4B', [0, 255, 255, 180])) # Triangle 1 #verts += a + b + c # Triangle 2, bdc to maintain clockwise winding order verts = [] verts += b + d + c color = [] mag1 = int((255/scale_div) * math_helper.get_mag(new_cloud_density[f][i][j + 1])) mag2 = int((255/scale_div) * math_helper.get_mag(new_cloud_density[f][i+1][j + 1])) mag3 = int((255/scale_div) * math_helper.get_mag(new_cloud_density[f][i+1][j])) if mag1 < cloud_cutoff: mag1 = 0 if mag2 < cloud_cutoff: mag2 = 0 if mag3 < cloud_cutoff: mag3 = 0 color = [255, 255, 255, mag1, 255, 255, 255, mag2, 255, 255, 255, mag3] #Get mag - new_cloud_density[f][i][j + 1] + new_cloud_density[f][i + 1][j + 1] + new_cloud_density[f][i + 1][j] #color = [0, 255, 255, 180]*3 self.cloud_batch.add(3, pyglet.gl.GL_TRIANGLES, None, ('v3f', verts), ('n3f', verts),('c4B', color)) #('c4B', [0, 255, 255, 180])) # Triangle 1 k += 1 # Add remaining triangle at the end of the row, Triangle 3 a = face[i][k] b = face[i][k + 1] c = face[i + 1][k] # verts += a + b + c verts = [] verts += a + b + c color = [] # TODO - Remember to use k here, not j mag1 = int((255 / scale_div) * math_helper.get_mag(new_cloud_density[f][i][k])) mag2 = int((255 / scale_div) * math_helper.get_mag(new_cloud_density[f][i][k + 1])) mag3 = int((255 / scale_div) * math_helper.get_mag(new_cloud_density[f][i + 1][k])) if mag1 < cloud_cutoff: mag1 = 0 if mag2 < cloud_cutoff: mag2 = 0 if mag3 < cloud_cutoff: mag3 = 0 color = [255, 255, 255, mag1, 255, 255, 255, mag2, 255, 255, 255, mag3] self.cloud_batch.add(3, pyglet.gl.GL_TRIANGLES, None, ('v3f', verts),('n3f', verts), ('c4B', color)) #num_triangles = len(verts)/3 # TODO - Maybe create solar system class sun_icosahedron = generator.generate_icosahedron(10) print sun_icosahedron new_sun = [] for face in faces: a = sun_icosahedron[face[0]] b = sun_icosahedron[face[1]] c = sun_icosahedron[face[2]] surface = generator.generate_surface(divs, [[a], [b], [c]]) new_sun.append(surface) sun_color = [255, 255, 50] verts = [] max_points = 2 ** divs + 1 for face in new_sun: for i in range(0, max_points - 1): # Create a counter k, this is used in place of j when we draw the triangle at the end of the row k = 0 for j in range(max_points - i - 2): # Add both triangle in the square to the render batch # # b______d . # |\ | |\ # | \ | | \ # | \ 2 | | \ # | 1 \ | | 3 \ # | \ | | \ # a|_____\|c ........ |_____\ a = face[i][j] b = face[i][j + 1] c = face[i + 1][j] d = face[i + 1][j + 1] # Triangle 1 verts += a + b + c # Triangle 2, bdc to maintain clockwise winding order verts += b + d + c k += 1 # Add remaining triangle at the end of the row, Triangle 3 a = face[i][k] b = face[i][k + 1] c = face[i + 1][k] verts += a + b + c num_triangles = len(verts) / 3 # As water color is uniform, add all colors at the end colors = sun_color * num_triangles # Update x position for i in range(0, len(verts), 3): verts[i] += 20 self.sun_batch.add(num_triangles, pyglet.gl.GL_TRIANGLES, None, ('v3f', verts), ('n3f', verts), ('c3B', colors)) # Seems to work if I up the number of processes above the cpu count # Should either remove multiprocessing, or find a way to make it work correctly pool = Pool(processes=7) # process per core # Output isn't in same order self.chunks = pool.map(chunk.Chunk(divs, final_edges, main_displacement, roughness), faces) # Needs to be done outside of multiprocess, as some parts of pyglet arnt thread safe for i in self.chunks: i.generate_batch()