class Terrain(object): id = 'terrain' def __init__(self, application): self.application = application self.width, self.height = width, height = application.mesh_width, application.mesh_height col = Column() self.input_height = LabelInput('Height', self).append_to(col).input self.material = LabelInput('Material', self).append_to(col).input self.widget = Widget('Terrain', col, id='terrain').append_to(application.workspace) self.default_material = Texture(2, 2, GL_RGBA, data=(140, 140, 140, 255)*4) self.vertex_texture = Texture(width, height, GL_RGBA32F) self.normal_texture = Texture(application.width, application.height, GL_RGBA32F, unit=GL_TEXTURE0) self.vertex_fbo = Framebuffer(self.vertex_texture) self.normal_fbo = Framebuffer(self.normal_texture) self.update_vertex_shader = application.shader('update_vertex.frag') self.update_normals_shader = application.shader('update_normals.frag') self.update_normals_shader.vars.offsets = 1.0/application.width, 1.0/application.height self.reset_vertex = application.shader('reset_vertex.frag') self.reset_normals = application.shader('reset_normals.frag') self.vbo = self.generate_vbo(width, height) self.reset() self.updated = None def export_obj(self, filename): if not filename.endswith('.obj'): filename += '.obj' if self.input_height.source: heightmap = self.input_height.source.texture heightmap.retrieve() width, height = self.application.width, self.application.height with open(filename, 'w') as outfile: heights = map(lambda z: (z, float(z)/height), range(height)) widths = map(lambda x: (x, float(x)/width), range(width)) for iz, z in heights: for ix, x in widths: outfile.write('v %f %f %f\n' % (x, heightmap[ix, iz][0], z)) self.normal_texture.retrieve() for normal in self.normal_texture: outfile.write('vn %f %f %f\n' % tuple(normal[:3])) heights = map(lambda x: float(x)/height, range(height)) widths = map(lambda y: float(y)/width, range(width)) for y in heights: for x in widths: outfile.write('vt %f %f\n' % (x, y)) i_width, i_height = width-1, height-1 for z in range(i_height): for x in range(i_width): p1 = x+z*width p2 = p1+width p4 = p1+1 p3 = p2+1 outfile.write('f %i/%i/%i %i/%i/%i %i/%i/%i\n' % ( p1+1, p1+1, p1+1, p2+1, p2+1, p2+1, p3+1, p3+1, p3+1 )) outfile.write('f %i/%i/%i %i/%i/%i %i/%i/%i\n' % ( p1+1, p1+1, p1+1, p3+1, p3+1, p3+1, p4+1, p4+1, p4+1 )) def export_float_array(self, filename): if not filename.endswith('.farr'): filename += '.farr' if self.input_height.source: heightmap = self.input_height.source.texture heightmap.retrieve() string = string_at(heightmap.buffer, heightmap.width*heightmap.height*4) with open(filename, 'wb') as outfile: outfile.write(string) def generate_vbo(self, width, height): #as an acceleration the arrays could be prefilled in C v4f = (c_float*(width*height*4))() width_factor, height_factor = 1.0/float(width), 1.0/float(height) for z in range(height): for x in range(width): offset = (x+z*width)*4 v4f[offset:offset+4] = x*width_factor, 0, z*height_factor, 1 i_width, i_height = width-1, height-1 indices = (c_uint*(i_width*i_height*6))() for z in range(i_height): for x in range(i_width): offset = (x+z*i_width)*6 p1 = x+z*width p2 = p1+width p4 = p1+1 p3 = p2+1 indices[offset:offset+6] = p1, p2, p3, p1, p3, p4 return VertexObject( pbo = True, indices = indices, dynamic_draw_v4f = v4f, ) def open(self, data, instances): offset = data['offset'] self.widget.rect.x, self.widget.rect.y = offset['x'], offset['y'] self.widget.layout() input_id = data['input_height'] if input_id: node = instances[input_id] connect(node, self.input_height) material_id = data['material'] if material_id: node = instances[material_id] connect(node, self.material) def reset(self): view = self.application.processing_view self.vertex_fbo.textures[0] = self.vertex_texture with nested(view, self.vertex_fbo, self.reset_vertex): quad(self.width, self.height) self.vbo.vertices.copy_from(self.vertex_texture) with nested(view, self.normal_fbo, self.reset_normals): quad(self.application.width, self.application.height) def update(self): view = self.application.processing_view revision = self.revision if self.material.source: self.material.source.update() if self.input_height.source: self.input_height.source.update() if revision != self.updated: self.application.ambient_occlusion.changed = True if self.input_height.source and self.input_height.source.complete: source = self.input_height.source.texture source.unit = GL_TEXTURE0 self.vertex_fbo.textures[0] = self.vertex_texture with nested(view, self.vertex_fbo, source, self.update_vertex_shader): quad(self.width, self.height) self.vbo.vertices.copy_from(self.vertex_texture) with nested(view, self.normal_fbo, source, self.update_normals_shader): quad(self.application.width, self.application.height) else: self.reset() self.application.ambient_occlusion.update() self.updated = revision def get_source(self): if self.input_height.source and self.input_height.source.complete: return self.input_height.source.texture @property def revision(self): if self.input_height.source: height = self.input_height.source.revision else: height = None if self.material.source: material = self.material.source.revision else: material = None return hash((height, material)) def draw(self): glPushMatrix() glTranslatef(-0.5, 0, -0.5) self.normal_texture.unit = GL_TEXTURE0 ambient_occlusion = self.application.ambient_occlusion.result ambient_occlusion.unit = GL_TEXTURE2 if self.material.source and self.material.source.complete: self.material.source.texture.unit = GL_TEXTURE1 with nested(self.normal_texture, self.material.source.texture, ambient_occlusion): self.vbo.draw(GL_TRIANGLES) else: self.default_material.unit = GL_TEXTURE1 with nested(self.normal_texture, self.default_material, ambient_occlusion): self.vbo.draw(GL_TRIANGLES) glPopMatrix()
class Terrain(object): id = 'terrain' def __init__(self, application): self.application = application self.width, self.height = width, height = application.mesh_width, application.mesh_height col = Column() self.input_height = LabelInput('Height', self).append_to(col).input self.material = LabelInput('Material', self).append_to(col).input self.widget = Widget('Terrain', col, id='terrain').append_to(application.workspace) self.default_material = Texture(2, 2, GL_RGBA, data=(140, 140, 140, 255)*4) self.vertex_texture = Texture(width, height, GL_RGBA32F) self.normal_texture = Texture(application.width, application.height, GL_RGBA32F, unit=GL_TEXTURE0) self.vertex_fbo = Framebuffer(self.vertex_texture) self.normal_fbo = Framebuffer(self.normal_texture) self.update_vertex_shader = application.shader('update_vertex.frag') self.update_normals_shader = application.shader('update_normals.frag') self.update_normals_shader.vars.offsets = 1.0/application.width, 1.0/application.height self.reset_vertex = application.shader('reset_vertex.frag') self.reset_normals = application.shader('reset_normals.frag') self.vbo = self.generate_vbo(width, height) self.reset() self.updated = None def export_obj(self, filename): if not filename.endswith('.obj'): filename += '.obj' if self.input_height.source: heightmap = self.input_height.source.texture heightmap.retrieve() width, height = self.application.width, self.application.height with open(filename, 'w') as outfile: heights = map(lambda z: (z, float(z)/height), range(height)) widths = map(lambda x: (x, float(x)/width), range(width)) for iz, z in heights: for ix, x in widths: outfile.write('v %f %f %f\n' % (x, heightmap[ix, iz][0], z)) self.normal_texture.retrieve() for normal in self.normal_texture: outfile.write('vn %f %f %f\n' % tuple(normal[:3])) heights = map(lambda x: float(x)/height, range(height)) widths = map(lambda y: float(y)/width, range(width)) for y in heights: for x in widths: outfile.write('vt %f %f\n' % (x, y)) i_width, i_height = width-1, height-1 for z in range(i_height): for x in range(i_width): p1 = x+z*width p2 = p1+width p4 = p1+1 p3 = p2+1 outfile.write('f %i/%i/%i %i/%i/%i %i/%i/%i\n' % ( p1+1, p1+1, p1+1, p2+1, p2+1, p2+1, p3+1, p3+1, p3+1 )) outfile.write('f %i/%i/%i %i/%i/%i %i/%i/%i\n' % ( p1+1, p1+1, p1+1, p3+1, p3+1, p3+1, p4+1, p4+1, p4+1 )) def export_float_array(self, filename): if not filename.endswith('.png'): filename += '.png' if self.input_height.source: heightmap = self.input_height.source.texture heightmap.retrieve() heightmap.save(filename) #print(heightmap) #source = string_at(heightmap.buffer, sizeof(heightmap.buffer)) #image = Image.frombytes("RGB", (self.width, self.height), source) #image.save(filename) def generate_vbo(self, width, height): #as an acceleration the arrays could be prefilled in C v4f = (c_float*(width*height*4))() width_factor, height_factor = 1.0/float(width), 1.0/float(height) for z in range(height): for x in range(width): offset = (x+z*width)*4 v4f[offset:offset+4] = x*width_factor, 0, z*height_factor, 1 i_width, i_height = width-1, height-1 indices = (c_uint*(i_width*i_height*6))() for z in range(i_height): for x in range(i_width): offset = (x+z*i_width)*6 p1 = x+z*width p2 = p1+width p4 = p1+1 p3 = p2+1 indices[offset:offset+6] = p1, p2, p3, p1, p3, p4 return VertexObject( pbo = True, indices = indices, dynamic_draw_v4f = v4f, ) def open(self, data, instances): offset = data['offset'] self.widget.rect.x, self.widget.rect.y = offset['x'], offset['y'] self.widget.layout() input_id = data['input_height'] if input_id: node = instances[input_id] connect(node, self.input_height) material_id = data['material'] if material_id: node = instances[material_id] connect(node, self.material) def reset(self): view = self.application.processing_view self.vertex_fbo.textures[0] = self.vertex_texture with nested(view, self.vertex_fbo, self.reset_vertex): quad(self.width, self.height) self.vbo.vertices.copy_from(self.vertex_texture) with nested(view, self.normal_fbo, self.reset_normals): quad(self.application.width, self.application.height) def update(self): view = self.application.processing_view revision = self.revision if self.material.source: self.material.source.update() if self.input_height.source: self.input_height.source.update() if revision != self.updated: if self.input_height.source and self.input_height.source.complete: source = self.input_height.source.texture source.unit = GL_TEXTURE0 self.vertex_fbo.textures[0] = self.vertex_texture with nested(view, self.vertex_fbo, source, self.update_vertex_shader): quad(self.width, self.height) self.vbo.vertices.copy_from(self.vertex_texture) with nested(view, self.normal_fbo, source, self.update_normals_shader): quad(self.application.width, self.application.height) else: self.reset() self.updated = revision @property def revision(self): if self.input_height.source: height = self.input_height.source.revision else: height = None if self.material.source: material = self.material.source.revision else: material = None return hash((height, material)) def draw(self): glPushMatrix() glTranslatef(-0.5, 0, -0.5) self.normal_texture.unit = GL_TEXTURE0 if self.material.source and self.material.source.complete: self.material.source.texture.unit = GL_TEXTURE1 with nested(self.normal_texture, self.material.source.texture): self.vbo.draw(GL_TRIANGLES) else: self.default_material.unit = GL_TEXTURE1 with nested(self.normal_texture, self.default_material): self.vbo.draw(GL_TRIANGLES) glPopMatrix()