def build_curve(self): """ Rebuilds the curve based on the controll point values """ sorted_points = sorted(self._cv_points, key=lambda v: v[0]) first_point = sorted_points[0] fitter = CurveFitter() # Duplicate curve at the beginning for i in range(self._border_points): end_point = self._cv_points[(-i + self._border_points - 1) % len(self._cv_points)] end_point = first_point fitter.add_xyz(0.0, Vec3(0, end_point[1], 0)) # Append the actual points for point in self._cv_points: # Clamp point x position to avoid artifacts at the beginning point_t = max(0.01, point[0]) fitter.add_xyz(point_t, Vec3(point_t, point[1], 0)) # Duplicate curve at the end for i in range(self._border_points): start_point = self._cv_points[i % len(self._cv_points)] start_point = first_point fitter.add_xyz(1.0, Vec3(1, start_point[1], 0)) fitter.sort_points() fitter.wrap_hpr() fitter.compute_tangents(1.0) self._curve = fitter.make_hermite()
def create(self): if self.remove_fireflies: self.target_firefly = self.create_target("RemoveFireflies") self.target_firefly.add_color_attachment(bits=16) self.target_firefly.prepare_buffer() self.scene_target_img = Image.create_2d("BloomDownsample", 0, 0, "RGBA16") self.scene_target_img.set_minfilter( SamplerState.FT_linear_mipmap_linear) self.scene_target_img.set_magfilter(SamplerState.FT_linear) self.scene_target_img.set_wrap_u(SamplerState.WM_clamp) self.scene_target_img.set_wrap_v(SamplerState.WM_clamp) self.scene_target_img.set_clear_color(Vec4(0.1, 0.0, 0.0, 1.0)) self.scene_target_img.clear_image() self.target_extract = self.create_target("ExtractBrightSpots") self.target_extract.prepare_buffer() self.target_extract.set_shader_input("DestTex", self.scene_target_img, False, True, -1, 0) if self.remove_fireflies: self.target_extract.set_shader_input("ShadedScene", self.target_firefly.color_tex, 1000) self.downsample_targets = [] self.upsample_targets = [] # Downsample passes for i in range(self.num_mips): scale_multiplier = 2**(1 + i) target = self.create_target("Downsample:Step-" + str(i)) target.size = -scale_multiplier, -scale_multiplier target.prepare_buffer() target.set_shader_input("sourceMip", i) target.set_shader_input("SourceTex", self.scene_target_img) target.set_shader_input("DestTex", self.scene_target_img, False, True, -1, i + 1) self.downsample_targets.append(target) # Upsample passes for i in range(self.num_mips): scale_multiplier = 2**(self.num_mips - i - 1) target = self.create_target("Upsample:Step-" + str(i)) target.size = -scale_multiplier, -scale_multiplier target.prepare_buffer() target.set_shader_input("FirstUpsamplePass", i == 0) target.set_shader_input("sourceMip", self.num_mips - i) target.set_shader_input("SourceTex", self.scene_target_img) target.set_shader_input("DestTex", self.scene_target_img, False, True, -1, self.num_mips - i - 1) self.upsample_targets.append(target) self.target_apply = self.create_target("ApplyBloom") self.target_apply.add_color_attachment(bits=16) self.target_apply.prepare_buffer() self.target_apply.set_shader_input("BloomTex", self.scene_target_img)
def find_and_reserve_region(self, tile_width, tile_height): for x in range(self._num_tiles - tile_height + 1): for y in range(self._num_tiles - tile_width + 1): if self.region_is_free(x, y, tile_width, tile_height): self.reserve_region(x, y, tile_width, tile_height) return LVecBase4i(x, y, tile_width, tile_height) print("Failed to find a free region of size", tile_width, "x", tile_height) return LVecBase4i(-1)
def create(self): if self.remove_fireflies: self.target_firefly = self.create_target("RemoveFireflies") self.target_firefly.add_color_attachment(bits=16) self.target_firefly.prepare_buffer() self.scene_target_img = Image.create_2d( "BloomDownsample", Globals.resolution.x, Globals.resolution.y, "R11G11B10") self.scene_target_img.set_minfilter(SamplerState.FT_linear_mipmap_linear) self.scene_target_img.set_magfilter(SamplerState.FT_linear) self.scene_target_img.set_wrap_u(SamplerState.WM_clamp) self.scene_target_img.set_wrap_v(SamplerState.WM_clamp) self.scene_target_img.set_clear_color(Vec4(0.1, 0.0, 0.0, 1.0)) self.scene_target_img.clear_image() self.target_extract = self.create_target("ExtractBrightSpots") self.target_extract.prepare_buffer() self.target_extract.set_shader_input("DestTex", self.scene_target_img, False, True, -1, 0) if self.remove_fireflies: self.target_extract.set_shader_input("ShadedScene", self.target_firefly.color_tex, 1000) self.downsample_targets = [] self.upsample_targets = [] # Downsample passes for i in range(self.num_mips): scale_multiplier = 2 ** (1 + i) target = self.create_target("Downsample:Step-" + str(i)) target.size = -scale_multiplier, -scale_multiplier target.prepare_buffer() target.set_shader_input("sourceMip", i) target.set_shader_input("SourceTex", self.scene_target_img) target.set_shader_input("DestTex", self.scene_target_img, False, True, -1, i + 1) self.downsample_targets.append(target) # Upsample passes for i in range(self.num_mips): scale_multiplier = 2 ** (self.num_mips - i - 1) target = self.create_target("Upsample:Step-" + str(i)) target.size = -scale_multiplier, -scale_multiplier target.prepare_buffer() target.set_shader_input("FirstUpsamplePass", i == 0) target.set_shader_input("sourceMip", self.num_mips - i) target.set_shader_input("SourceTex", self.scene_target_img) target.set_shader_input("DestTex", self.scene_target_img, False, True, -1, self.num_mips - i - 1) self.upsample_targets.append(target) self.target_apply = self.create_target("ApplyBloom") self.target_apply.add_color_attachment(bits=16) self.target_apply.prepare_buffer() self.target_apply.set_shader_input("BloomTex", self.scene_target_img)
def find_consecutive_slots(self, num_consecutive): if num_consecutive == 1: return self.find_slot() for i in range(len(self._data)): any_taken = False for k in range(num_consecutive): if self._data[i + k]: any_taken = True break if not any_taken: return i return -1
def update_shadow_sources(self): sources_to_update = [] for source in self._shadow_sources.begin(): # if source and source.get_needs_update(): # sources_to_update.append(source) if source: bounds = source.get_bounds() distance_to_camera = (self._camera_pos - bounds.get_center() ) - bounds.get_radius() if distance_to_camera < self._shadow_update_distance: sources_to_update.append(source) else: if source.has_region(): self._shadow_manager.get_atlas().free_region( source.get_region()) source.clear_region() def get_source_score(source): dist = (source.get_bounds().get_center() - self._camera_pos).length() return -dist + (10**10 if source.has_region() else 0) sorted_sources = list(sorted(sources_to_update, key=get_source_score)) atlas = self._shadow_manager.get_atlas() update_slots = min(len(sorted_sources), self._shadow_manager.get_num_update_slots_left()) for i in range(update_slots): if sorted_sources[i].has_region(): atlas.free_region(sorted_sources[i].get_region()) for i in range(update_slots): source = sorted_sources[i] if not self._shadow_manager.add_update(source): print( "ERROR: Shadow manager ensured update slot, but slot is taken!" ) break region_size = atlas.get_required_tiles(source.get_resolution()) new_region = atlas.find_and_reserve_region(region_size, region_size) new_uv_region = atlas.region_to_uv(new_region) source.set_region(new_region, new_uv_region) source.set_needs_update(False) self.gpu_update_source(source)
def generate_dataset_texture_into(self, dest_tex, layer_index): resolution_vertical = dest_tex.get_y_size() resolution_horizontal = dest_tex.get_x_size() dest = PNMImage(resolution_vertical, resolution_horizontal, 1, 65535) for vert in range(resolution_vertical): for horiz in range(resolution_horizontal): vert_angle = vert / (resolution_vertical-1.0) vert_angle = math.cos(vert_angle * math.pi) * 90.0 + 90.0 horiz_angle = horiz / (resolution_horizontal-1.0) * 360.0 candela = self.get_candela_value(vert_angle, horiz_angle) dest.set_xel(vert, horiz, candela) dest_tex.load(dest, layer_index, 0)
def generate_dataset_texture_into(self, dest_tex, layer_index): resolution_vertical = dest_tex.get_y_size() resolution_horizontal = dest_tex.get_x_size() dest = PNMImage(resolution_vertical, resolution_horizontal, 1, 65535) for vert in range(resolution_vertical): for horiz in range(resolution_horizontal): vert_angle = vert / (resolution_vertical - 1.0) vert_angle = math.cos(vert_angle * math.pi) * 90.0 + 90.0 horiz_angle = horiz / (resolution_horizontal - 1.0) * 360.0 candela = self.get_candela_value(vert_angle, horiz_angle) dest.set_xel(vert, horiz, candela) dest_tex.load(dest, layer_index, 0)
def create_stats(self): """ Creates the stats overlay """ self.overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self.debug_lines = [] for i in range(6): self.debug_lines.append(TextNode( pos=Vec2(0, -i * 0.046), parent=self.overlay_node, align="right", color=Vec3(1)))
def create(self): self.target = self.create_target("ShadowMap") self.target.size = self.split_resolution * self.num_splits, self.split_resolution self.target.add_depth_attachment(bits=32) self.target.prepare_render(None) # Remove all unused display regions internal_buffer = self.target.internal_buffer internal_buffer.remove_all_display_regions() internal_buffer.get_display_region(0).set_active(False) internal_buffer.disable_clears() # Set a clear on the buffer instead on all regions internal_buffer.set_clear_depth(1) internal_buffer.set_clear_depth_active(True) # Prepare the display regions for i in range(self.num_splits): region = internal_buffer.make_display_region( i / self.num_splits, i / self.num_splits + 1 / self.num_splits, 0, 1) region.set_sort(25 + i) region.disable_clears() region.set_active(True) self.split_regions.append(region)
def init_tiles(self): self._num_tiles = self._size // self._tile_size def row(): return [False for i in range(self._num_tiles)] # pylint: disable=unused-variable self._flags = [row() for j in range(self._num_tiles)] # pylint: disable=unused-variable
def remove_light(self, light): assert light is not None if not light.has_slot(): print("ERROR: Could not detach light, light was not attached!") return self._lights.free_slot(light.get_slot()) self.gpu_remove_light(light) light.remove_slot() if light.get_casts_shadows(): for i in range(light.get_num_shadow_sources()): source = light.get_shadow_source(i) if source.has_slot(): self._shadow_sources.free_slot(source.get_slot()) if source.has_region(): self._shadow_manager.get_atlas().free_region( source.get_region()) source.clear_region() self.gpu_remove_consecutive_sources(light.get_shadow_source(0), light.get_num_shadow_sources()) light.clear_shadow_sources()
def _setup_camera_rig(self): """ Setups the cameras to render a cubemap """ directions = (Vec3(1, 0, 0), Vec3(-1, 0, 0), Vec3(0, 1, 0), Vec3(0, -1, 0), Vec3(0, 0, 1), Vec3(0, 0, -1)) # Prepare the display regions for i in range(6): region = self.target.internal_buffer.make_display_region( i / 6, i / 6 + 1 / 6, 0, 1) region.set_sort(25 + i) region.set_active(True) region.disable_clears() lens = PerspectiveLens() lens.set_fov(90) lens.set_near_far(0.001, 1.0) camera = Camera("EnvmapCam-" + str(i), lens) camera_np = self.rig_node.attach_new_node(camera) camera_np.look_at(camera_np, directions[i]) region.set_camera(camera_np) self.regions.append(region) self.cameras.append(camera_np) self.cameras[0].set_r(90) self.cameras[1].set_r(-90) self.cameras[3].set_r(180) self.cameras[5].set_r(180) # Register cameras for camera_np in self.cameras: self._pipeline.tag_mgr.register_envmap_camera(camera_np.node())
def _create_stats(self): """ Creates the stats overlay """ self._overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self._overlay_node.set_pos(Globals.base.get_aspect_ratio() - 0.07, 1, 1.0 - 0.07) self._debug_lines = [] for i in range(5): self._debug_lines.append(TextNode( pos=Vec2(0, -i * 0.046), parent=self._overlay_node, pixel_size=16, align="right", color=Vec3(1)))
def update_shadow_sources(self): sources_to_update = [] for source in self._shadow_sources.begin(): # if source and source.get_needs_update(): # sources_to_update.append(source) if source: bounds = source.get_bounds() distance_to_camera = (self._camera_pos - bounds.get_center()) - bounds.get_radius() if distance_to_camera < self._shadow_update_distance: sources_to_update.append(source) else: if source.has_region(): self._shadow_manager.get_atlas().free_region(source.get_region()) source.clear_region() def get_source_score(source): dist = (source.get_bounds().get_center() - self._camera_pos).length() return -dist + (10**10 if source.has_region() else 0) sorted_sources = list(sorted(sources_to_update, key=get_source_score)) atlas = self._shadow_manager.get_atlas() update_slots = min( len(sorted_sources), self._shadow_manager.get_num_update_slots_left()) for i in range(update_slots): if sorted_sources[i].has_region(): atlas.free_region(sorted_sources[i].get_region()) for i in range(update_slots): source = sorted_sources[i] if not self._shadow_manager.add_update(source): print("ERROR: Shadow manager ensured update slot, but slot is taken!") break region_size = atlas.get_required_tiles(source.get_resolution()) new_region = atlas.find_and_reserve_region(region_size, region_size) new_uv_region = atlas.region_to_uv(new_region) source.set_region(new_region, new_uv_region) source.set_needs_update(False) self.gpu_update_source(source)
def create_stats(self): """ Creates the stats overlay """ self.overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self.debug_lines = [] num_lines = 6 if self.advanced_info else 1 for i in range(num_lines): self.debug_lines.append(TextNode( pos=Vec2(0, -i * 0.046), parent=self.overlay_node, align="right", color=Vec3(0.7, 1, 1))) self.debug_lines[0].color = Vec4(1, 1, 0, 1)
def create_stats(self): """ Creates the stats overlay """ self.overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self.debug_lines = [] for i in range(6): self.debug_lines.append( TextNode(pos=Vec2(0, -i * 0.046), parent=self.overlay_node, align="right", color=Vec3(1)))
def _create_stats(self): """ Creates the stats overlay """ self._overlay_node = Globals.base.aspect2d.attach_new_node("Overlay") self._overlay_node.set_pos(Globals.base.get_aspect_ratio() - 0.07, 1, 1.0 - 0.07) self._debug_lines = [] for i in range(6): self._debug_lines.append( TextNode(pos=Vec2(0, -i * 0.046), parent=self._overlay_node, pixel_size=16, align="right", color=Vec3(1)))
def update(self): for i in range(len(self._queued_updates), self._max_updates): self._cameras[i].set_active(False) self._display_regions[i].set_active(False) for i, source in enumerate(self._queued_updates): self._cameras[i].set_active(True) self._display_regions[i].set_active(True) self._cameras[i].get_lens().set_user_mat(source.get_mvp()) uv = source.get_uv_region() self._display_regions[i].set_dimensions(uv.x, uv.x + uv.z, uv.y, uv.y + uv.w) self._queued_updates = []
def _setup_textures(self): """ Prepares all bound textures """ for i in range(self._aux_count): self._targets["aux_{}".format(i)] = Texture( self.debug_name + "_aux{}".format(i)) for tex in itervalues(self._targets): tex.set_wrap_u(SamplerState.WM_clamp) tex.set_wrap_v(SamplerState.WM_clamp) tex.set_anisotropic_degree(0) tex.set_x_size(self._size.x) tex.set_y_size(self._size.y) tex.set_minfilter(SamplerState.FT_linear) tex.set_magfilter(SamplerState.FT_linear)
def _setup_textures(self): """ Prepares all bound textures """ for i in range(self._aux_count): self._targets["aux_{}".format(i)] = Texture(self.debug_name + "_aux{}".format(i)) for tex in itervalues(self._targets): tex.set_wrap_u(SamplerState.WM_clamp) tex.set_wrap_v(SamplerState.WM_clamp) tex.set_anisotropic_degree(0) tex.set_x_size(self._size.x) tex.set_y_size(self._size.y) tex.set_minfilter(SamplerState.FT_linear) tex.set_magfilter(SamplerState.FT_linear)
def _create(self): """ Creates the internally used buffer """ self._setup_textures() window_props, buffer_props = self._make_properties() self._internal_buffer = self.engine.make_output( self._source_window.get_pipe(), self.debug_name, 1, buffer_props, window_props, GraphicsPipe.BF_refuse_window | GraphicsPipe.BF_resizeable, self._source_window.gsg, self._source_window) if not self._internal_buffer: self.error("Failed to create buffer") return if self._depth_bits: self._internal_buffer.add_render_texture( self.depth_tex, GraphicsOutput.RTM_bind_or_copy, GraphicsOutput.RTP_depth) if max(self._color_bits) > 0: self._internal_buffer.add_render_texture( self.color_tex, GraphicsOutput.RTM_bind_or_copy, GraphicsOutput.RTP_color) aux_prefix = { 8: "RTP_aux_rgba_{}", 16: "RTP_aux_hrgba_{}", 32: "RTP_aux_float_{}", }[self._aux_bits] for i in range(self._aux_count): target_mode = getattr(GraphicsOutput, aux_prefix.format(i)) self._internal_buffer.add_render_texture( self.aux_tex[i], GraphicsOutput.RTM_bind_or_copy, target_mode) if not self.sort: RenderTarget.CURRENT_SORT += 20 self.sort = RenderTarget.CURRENT_SORT RenderTarget.NUM_ALLOCATED_BUFFERS += 1 self._internal_buffer.set_sort(self.sort) self._internal_buffer.disable_clears() self._internal_buffer.get_display_region(0).disable_clears() self._internal_buffer.get_overlay_display_region().disable_clears() self._internal_buffer.get_overlay_display_region().set_active(False) RenderTarget.REGISTERED_TARGETS.append(self) return True
def setup_shadows(self, light): light.init_shadow_sources() light.update_shadow_sources() num_sources = light.get_num_shadow_sources() base_slot = self._shadow_sources.find_consecutive_slots(num_sources) if base_slot < 0: print("ERROR: Failed to find slot for shadow sources!") return for i in range(num_sources): source = light.get_shadow_source(i) source.set_needs_update(True) slot = base_slot + i self._shadow_sources.reserve_slot(slot, source) source.set_slot(slot)
def update(self): # First, disable all targets for target in itervalues(self._targets): target.active = False # Check for updated faces for i in range(6): if self._pipeline.task_scheduler.is_scheduled("envprobes_capture_envmap_face" + str(i)): self.regions[i].set_active(True) # Check for filtering if self._pipeline.task_scheduler.is_scheduled("envprobes_filter_and_store_envmap"): self.target_store.active = True self.target_store_diff.active = True self.filter_diffuse_target.active = True for target in self.filter_targets: target.active = True
def create(self): """ Creates the gui components """ screen_w, screen_h = Globals.native_resolution.x, Globals.native_resolution.y self.fullscreen_node = Globals.base.pixel2dp.attach_new_node("LoadingScreen") self.fullscreen_node.set_bin("fixed", 10) self.fullscreen_node.set_depth_test(False) scale_w = screen_w / 1920.0 scale_h = screen_h / 1080.0 scale = max(scale_w, scale_h) self.fullscreen_bg = Sprite( image=self.image_source, x=(screen_w - 1920.0 * scale) // 2, y=(screen_h - 1080.0 * scale) // 2, w=int(1920 * scale), h=int(1080 * scale), parent=self.fullscreen_node, near_filter=False) for _ in range(2): Globals.base.graphicsEngine.render_frame()
def create(self): """ Creates the gui components """ screen_w, screen_h = Globals.base.win.get_x_size(), Globals.base.win.get_y_size() self.fullscreen_node = Globals.base.pixel2dp.attach_new_node("LoadingScreen") self.fullscreen_node.set_bin("fixed", 10) self.fullscreen_node.set_depth_test(False) scale_w = screen_w / 1920.0 scale_h = screen_h / 1080.0 scale = max(scale_w, scale_h) self.fullscreen_bg = Sprite( image="/$$rp/data/gui/loading_screen_bg.txo", x=(screen_w-1920.0*scale)//2, y=(screen_h-1080.0*scale)//2, w=int(1920 * scale), h=int(1080 * scale), parent=self.fullscreen_node, near_filter=False) for _ in range(2): Globals.base.graphicsEngine.render_frame()
def on_pipeline_created(self): self.debug("Initializing pssm ..") # Construct a dummy node to parent the rig to self.node = Globals.base.render.attach_new_node("PSSMCameraRig") self.node.hide() # Construct the actual PSSM rig self.camera_rig = PSSMCameraRig(self.get_setting("split_count")) # noqa # pylint: disable=undefined-variable self.camera_rig.set_sun_distance(self.get_setting("sun_distance")) self.camera_rig.set_pssm_distance(self.get_setting("max_distance")) self.camera_rig.set_logarithmic_factor( self.get_setting("logarithmic_factor")) self.camera_rig.set_border_bias(self.get_setting("border_bias")) self.camera_rig.set_use_stable_csm(True) self.camera_rig.set_use_fixed_film_size(True) self.camera_rig.set_resolution(self.get_setting("resolution")) self.camera_rig.reparent_to(self.node) # Attach the cameras to the shadow stage for i in range(self.get_setting("split_count")): camera_np = self.camera_rig.get_camera(i) camera_np.node().set_scene(Globals.base.render) region = self.shadow_stage.split_regions[i] region.set_camera(camera_np) # camera_np.node().show_frustum() # Make sure the pipeline knows about our camera, so it can apply # the correct bitmasks self._pipeline.tag_mgr.register_camera("shadow", camera_np.node()) # Accept a shortcut to enable / disable the update of PSSM Globals.base.accept("u", self.toggle_update_enabled) # Set inputs self.pssm_stage.set_shader_inputs( pssm_mvps=self.camera_rig.get_mvp_array(), pssm_nearfar=self.camera_rig.get_nearfar_array()) if self.is_plugin_enabled("volumetrics"): handle = self.get_plugin_instance("volumetrics") handle.stage.set_shader_inputs( pssm_mvps=self.camera_rig.get_mvp_array(), pssm_nearfar=self.camera_rig.get_nearfar_array())
def create(self): current_tex = None self.blur_targets = [] for i in range(3): target_h = self.create_target("BlurH-" + str(i)) target_h.add_color_attachment(bits=16) target_h.prepare_buffer() target_h.set_shader_input("direction", LVecBase2i(1, 0)) if current_tex is not None: target_h.set_shader_input("ShadedScene", current_tex, override=True) current_tex = target_h.color_tex target_v = self.create_target("BlurV-" + str(i)) target_v.add_color_attachment(bits=16) target_v.prepare_buffer() target_v.set_shader_input("ShadedScene", current_tex, override=True) target_v.set_shader_input("direction", LVecBase2i(0, 1)) current_tex = target_v.color_tex self.blur_targets += [target_h, target_v] self.final_tex = current_tex
def on_pipeline_created(self): self.debug("Initializing pssm ..") # Construct a dummy node to parent the rig to self.node = Globals.base.render.attach_new_node("PSSMCameraRig") self.node.hide() # Construct the actual PSSM rig self.camera_rig = PSSMCameraRig(self.get_setting("split_count")) # noqa # pylint: disable=undefined-variable self.camera_rig.set_sun_distance(self.get_setting("sun_distance")) self.camera_rig.set_pssm_distance(self.get_setting("max_distance")) self.camera_rig.set_logarithmic_factor(self.get_setting("logarithmic_factor")) self.camera_rig.set_border_bias(self.get_setting("border_bias")) self.camera_rig.set_use_stable_csm(True) self.camera_rig.set_use_fixed_film_size(True) self.camera_rig.set_resolution(self.get_setting("resolution")) self.camera_rig.reparent_to(self.node) # Attach the cameras to the shadow stage for i in range(self.get_setting("split_count")): camera_np = self.camera_rig.get_camera(i) camera_np.node().set_scene(Globals.base.render) region = self.shadow_stage.split_regions[i] region.set_camera(camera_np) # camera_np.node().show_frustum() # Make sure the pipeline knows about our camera, so it can apply # the correct bitmasks self._pipeline.tag_mgr.register_camera("shadow", camera_np.node()) # Accept a shortcut to enable / disable the update of PSSM Globals.base.accept("u", self.toggle_update_enabled) # Set inputs self.pssm_stage.set_shader_inputs( pssm_mvps=self.camera_rig.get_mvp_array(), pssm_nearfar=self.camera_rig.get_nearfar_array()) if self.is_plugin_enabled("volumetrics"): handle = self.get_plugin_instance("volumetrics") handle.stage.set_shader_inputs( pssm_mvps=self.camera_rig.get_mvp_array(), pssm_nearfar=self.camera_rig.get_nearfar_array())
def init(self): for i in range(self._max_updates): camera = Camera("ShadowCam-" + str(i)) camera.set_lens(MatrixLens()) camera.set_active(False) camera.set_scene(self._scene_parent) self._tag_state_mgr.register_shadow_camera(camera) self._camera_nps.append(self._scene_parent.attach_new_node(camera)) self._cameras.append(camera) region = self._atlas_graphics_output.make_display_region() region.set_sort(1000) region.set_clear_depth_active(True) region.set_clear_depth(1.0) region.set_clear_color_active(False) region.set_camera(self._camera_nps[i]) region.set_active(False) self._display_regions.append(region) self._atlas = ShadowAtlas(self._atlas_size)
def get_vertical_candela_value(self, horizontal_angle_idx, vertical_angle): if vertical_angle < 0.0: return 0.0 if vertical_angle > self._vertical_angles[len(self._vertical_angles) - 1]: return 0.0 for vertical_index in range(1, len(self._vertical_angles)): curr_angle = self._vertical_angles[vertical_index] if curr_angle > vertical_angle: prev_angle = self._vertical_angles[vertical_index - 1] prev_value = self.get_candela_value_from_index( vertical_index - 1, horizontal_angle_idx) curr_value = self.get_candela_value_from_index( vertical_index, horizontal_angle_idx) lerp = (vertical_angle - prev_angle) / (curr_angle - prev_angle) assert lerp >= 0.0 and lerp <= 1.0 return curr_value * lerp + prev_value * (1.0 - lerp) return 0.0
def init(self): for i in range(self._max_updates): camera = Camera("ShadowCam-" + str(i)) camera.set_lens(MatrixLens()) camera.set_active(False) camera.set_scene(self._scene_parent) self._tag_state_mgr.register_camera("shadow", camera) self._camera_nps.append(self._scene_parent.attach_new_node(camera)) self._cameras.append(camera) region = self._atlas_graphics_output.make_display_region() region.set_sort(1000) region.set_clear_depth_active(True) region.set_clear_depth(1.0) region.set_clear_color_active(False) region.set_camera(self._camera_nps[i]) region.set_active(False) self._display_regions.append(region) self._atlas = ShadowAtlas(self._atlas_size)
def create(self): """ Creates the gui components """ screen_w, screen_h = Globals.native_resolution.x, Globals.native_resolution.y self.fullscreen_node = Globals.base.pixel2dp.attach_new_node( "LoadingScreen") self.fullscreen_node.set_bin("fixed", 10) self.fullscreen_node.set_depth_test(False) scale_w = screen_w / 1920.0 scale_h = screen_h / 1080.0 scale = max(scale_w, scale_h) self.fullscreen_bg = Sprite(image=self.image_source, x=(screen_w - 1920.0 * scale) // 2, y=(screen_h - 1080.0 * scale) // 2, w=int(1920 * scale), h=int(1080 * scale), parent=self.fullscreen_node, near_filter=False) for _ in range(2): Globals.base.graphicsEngine.render_frame()
def _setup_camera_rig(self): """ Setups the cameras to render a cubemap """ directions = (Vec3(1, 0, 0), Vec3(-1, 0, 0), Vec3(0, 1, 0), Vec3(0, -1, 0), Vec3(0, 0, 1), Vec3(0, 0, -1)) # Prepare the display regions for i in range(6): region = self.target.internal_buffer.make_display_region( i / 6, i / 6 + 1 / 6, 0, 1) region.set_sort(25 + i) region.set_active(True) region.disable_clears() # Set the correct clears region.set_clear_depth_active(True) region.set_clear_depth(1.0) region.set_clear_color_active(True) region.set_clear_color(Vec4(0)) lens = PerspectiveLens() lens.set_fov(90) lens.set_near_far(0.001, 1.0) camera = Camera("EnvmapCam-" + str(i), lens) camera_np = self.rig_node.attach_new_node(camera) camera_np.look_at(camera_np, directions[i]) region.set_camera(camera_np) self.regions.append(region) self.cameras.append(camera_np) self.cameras[0].set_r(90) self.cameras[1].set_r(-90) self.cameras[3].set_r(180) self.cameras[5].set_r(180) # Register cameras for camera_np in self.cameras: self._pipeline.tag_mgr.register_camera("envmap", camera_np.node())
def remove_light(self, light): assert light is not None if not light.has_slot(): print("ERROR: Could not detach light, light was not attached!") return self._lights.free_slot(light.get_slot()) self.gpu_remove_light(light) light.remove_slot() if light.get_casts_shadows(): for i in range(light.get_num_shadow_sources()): source = light.get_shadow_source(i) if source.has_slot(): self._shadow_sources.free_slot(source.get_slot()) if source.has_region(): self._shadow_manager.get_atlas().free_region(source.get_region()) source.clear_region() self.gpu_remove_consecutive_sources( light.get_shadow_source(0), light.get_num_shadow_sources()) light.clear_shadow_sources()
def begin(self): for i in range(self._max_index + 1): if self._data[i]: yield self._data[i]
def free_consecutive_slots(self, slot, num_consecutive): for i in range(num_consecutive): self.free_slot(slot + i)
def compute(self): """ Precomputes the scattering """ self.debug("Precomputing ...") exec_cshader = self.exec_compute_shader # Transmittance exec_cshader(self.shaders["transmittance"], {"dest": self.textures["transmittance"]}, (self.trans_w, self.trans_h, 1)) # Delta E exec_cshader( self.shaders["delta_e"], { "transmittanceSampler": self.textures["transmittance"], "dest": self.textures["delta_e"] }, (self.sky_w, self.sky_h, 1)) # Delta S exec_cshader( self.shaders["delta_sm_sr"], { "transmittanceSampler": self.textures["transmittance"], "destDeltaSR": self.textures["delta_sr"], "destDeltaSM": self.textures["delta_sm"] }, (self.res_mu_s_nu, self.res_mu, self.res_r), (8, 8, 8)) # Copy deltaE to irradiance texture exec_cshader( self.shaders["copy_irradiance"], { "k": 0.0, "deltaESampler": self.textures["delta_e"], "dest": self.textures["irradiance"] }, (self.sky_w, self.sky_h, 1)) # Copy delta s into inscatter texture exec_cshader( self.shaders["copy_inscatter"], { "deltaSRSampler": self.textures["delta_sr"], "deltaSMSampler": self.textures["delta_sm"], "dest": self.textures["inscatter"] }, (self.res_mu_s_nu, self.res_mu, self.res_r), (8, 8, 8)) for order in range(2, 5): first = order == 2 # Delta J exec_cshader( self.shaders["delta_j"], { "transmittanceSampler": self.textures["transmittance"], "deltaSRSampler": self.textures["delta_sr"], "deltaSMSampler": self.textures["delta_sm"], "deltaESampler": self.textures["delta_e"], "dest": self.textures["delta_j"], "first": first }, (self.res_mu_s_nu, self.res_mu, self.res_r), (8, 8, 8)) # Delta E exec_cshader( self.shaders["irradiance_n"], { "transmittanceSampler": self.textures["transmittance"], "deltaSRSampler": self.textures["delta_sr"], "deltaSMSampler": self.textures["delta_sm"], "dest": self.textures["delta_e"], "first": first }, (self.sky_w, self.sky_h, 1)) # Delta Sr exec_cshader( self.shaders["delta_sr"], { "transmittanceSampler": self.textures["transmittance"], "deltaJSampler": self.textures["delta_j"], "dest": self.textures["delta_sr"], "first": first }, (self.res_mu_s_nu, self.res_mu, self.res_r), (8, 8, 8)) # Add delta E to irradiance exec_cshader( self.shaders["add_delta_e"], { "deltaESampler": self.textures["delta_e"], "dest": self.textures["irradiance"], }, (self.sky_w, self.sky_h, 1)) # Add deltaSr to inscatter texture exec_cshader( self.shaders["add_delta_sr"], { "deltaSSampler": self.textures["delta_sr"], "dest": self.textures["inscatter"] }, (self.res_mu_s_nu, self.res_mu, self.res_r), (8, 8, 8)) # Make stages available for stage in [self.handle.display_stage, self.handle.envmap_stage]: stage.set_shader_inputs( InscatterSampler=self.textures["inscatter"], transmittanceSampler=self.textures["transmittance"], IrradianceSampler=self.textures["irradiance"])
def prepare_scene(self, scene): """ Prepares a given scene, by converting panda lights to render pipeline lights. This also converts all empties with names starting with 'ENVPROBE' to environment probes. Conversion of blender to render pipeline lights is done by scaling their intensity by 100 to match lumens. Additionally, this finds all materials with the 'TRANSPARENT' shading model, and sets the proper effects on them to ensure they are rendered properly. This method also returns a dictionary with handles to all created objects, that is lights, environment probes, and transparent objects. This can be used to store them and process them later on, or delete them when a newer scene is loaded.""" lights = [] for light in scene.find_all_matches("**/+PointLight"): light_node = light.node() rp_light = PointLight() rp_light.pos = light.get_pos(Globals.base.render) rp_light.radius = light_node.max_distance rp_light.energy = 20.0 * light_node.color.w rp_light.color = light_node.color.xyz rp_light.casts_shadows = light_node.shadow_caster rp_light.shadow_map_resolution = light_node.shadow_buffer_size.x rp_light.inner_radius = 0.4 self.add_light(rp_light) light.remove_node() lights.append(rp_light) for light in scene.find_all_matches("**/+Spotlight"): light_node = light.node() rp_light = SpotLight() rp_light.pos = light.get_pos(Globals.base.render) rp_light.radius = light_node.max_distance rp_light.energy = 20.0 * light_node.color.w rp_light.color = light_node.color.xyz rp_light.casts_shadows = light_node.shadow_caster rp_light.shadow_map_resolution = light_node.shadow_buffer_size.x rp_light.fov = light_node.exponent / math.pi * 180.0 lpoint = light.get_mat(Globals.base.render).xform_vec((0, 0, -1)) rp_light.direction = lpoint self.add_light(rp_light) light.remove_node() lights.append(rp_light) envprobes = [] for np in scene.find_all_matches("**/ENVPROBE*"): probe = self.add_environment_probe() probe.set_mat(np.get_mat()) probe.border_smoothness = 0.0001 probe.parallax_correction = True np.remove_node() envprobes.append(probe) tristrips_warning_emitted = False transparent_objects = [] for geom_np in scene.find_all_matches("**/+GeomNode"): geom_node = geom_np.node() geom_count = geom_node.get_num_geoms() for i in range(geom_count): state = geom_node.get_geom_state(i) geom = geom_node.get_geom(i) needs_conversion = False for prim in geom.get_primitives(): if isinstance(prim, GeomTristrips): needs_conversion = True if not tristrips_warning_emitted: self.warn( "At least one GeomNode (", geom_node.get_name(), "and possible more..) contains tristrips.") self.warn( "Due to a NVIDIA Driver bug, we have to convert them to triangles now." ) self.warn( "Consider exporting your models with the Bam Exporter to avoid this." ) tristrips_warning_emitted = True break if needs_conversion: geom_node.modify_geom(i).decompose_in_place() if not state.has_attrib(MaterialAttrib): self.warn("Geom", geom_node, "has no material! Please fix this.") continue material = state.get_attrib(MaterialAttrib).get_material() shading_model = material.emission.x # SHADING_MODEL_TRANSPARENT if shading_model == 3: if geom_count > 1: self.error( "Transparent materials must be on their own geom!\n" "If you are exporting from blender, split them into\n" "seperate meshes, then re-export your scene. The\n" "problematic mesh is: " + geom_np.get_name()) continue self.set_effect(geom_np, "/$$rp/effects/default.yaml", { "render_forward": True, "render_gbuffer": False }, 100) return { "lights": lights, "envprobes": envprobes, "transparent_objects": transparent_objects }
def free_region(self, region): self._num_used_tiles -= region.z * region.w for x in range(region.z): for y in range(region.w): self._flags[region.x + x][region.y + y] = False
def region_is_free(self, x, y, w, h): for x_offset in range(w): for y_offset in range(h): if self._flags[x + x_offset][y + y_offset]: return False return True
def _load_and_parse_file(self, pth): """ Loads a .IES file from a given filename, returns an IESDataset which is used by the load function later on. """ self.debug("Loading ies profile from", pth) try: with open(pth, "r") as handle: lines = handle.readlines() except IOError as msg: self.error("Failed to open", pth, ":", msg) return None lines = [i.strip() for i in lines] # Parse version header self._check_version_header(lines.pop(0)) # Parse arbitrary amount of keywords keywords = self._extract_keywords(lines) # noqa # Next line should be TILT=NONE according to the spec if lines.pop(0) != "TILT=NONE": raise InvalidIESProfileException("Expected TILT=NONE line, but none found!") # From now on, lines do not matter anymore, instead everything is # space seperated new_parts = (' '.join(lines)).replace(",", " ").split() def read_int(): return int(new_parts.pop(0)) def read_float(): return float(new_parts.pop(0)) # Amount of Lamps if read_int() != 1: raise InvalidIESProfileException("Only 1 Lamp supported!") # Extract various properties lumen_per_lamp = read_float() # noqa candela_multiplier = read_float() # noqa num_vertical_angles = read_int() num_horizontal_angles = read_int() if num_vertical_angles < 1 or num_horizontal_angles < 1: raise InvalidIESProfileException("Invalid of vertical/horizontal angles!") photometric_type = read_int() # noqa unit_type = read_int() # Check for a correct unit type, should be 1 for meters and 2 for feet if unit_type not in [1, 2]: raise InvalidIESProfileException("Invalid unit type") width = read_float() # noqa length = read_float() # noqa height = read_float() # noqa ballast_factor = read_float() # noqa future_use = read_float() # Unused field for future usage # noqa input_watts = read_float() # noqa # Read vertical angles vertical_angles = [read_float() for i in range(num_vertical_angles)] horizontal_angles = [read_float() for i in range(num_horizontal_angles)] candela_values = [] candela_scale = 0.0 for i in range(num_horizontal_angles): vertical_data = [read_float() for i in range(num_vertical_angles)] candela_scale = max(candela_scale, max(vertical_data)) candela_values += vertical_data # Rescale values, divide by maximum candela_values = [i / candela_scale for i in candela_values] if len(new_parts) != 0: self.warn("Unhandled data at file-end left:", new_parts) # Dont abort here, some formats like those from ERCO Leuchten GmbH # have an END keyword, just ignore everything after the data was # read in. dataset = IESDataset() dataset.set_vertical_angles(self._list_to_pta(vertical_angles)) dataset.set_horizontal_angles(self._list_to_pta(horizontal_angles)) dataset.set_candela_values(self._list_to_pta(candela_values)) # Testing code to write out the LUT # from panda3d.core import Texture # tex = Texture("temp") # tex.setup_3d_texture(512, 512, 1, Image.T_float, Image.F_r16) # dataset.generate_dataset_texture_into(tex, 0) # tex.write("generated.png") return dataset
def _draw(self, painter): """ Internal method to draw the widget """ canvas_width = self.width() - self._legend_border canvas_height = self.height() - self._legend_border - self._bar_h # Draw field background # painter.setPen(QtGui.QColor(200, 200, 200)) # painter.setBrush(QtGui.QColor(230, 230, 230)) # painter.drawRect(0, 0, self.width() - 1, self.height() - 1) # Draw legend # Compute amount of horizontal / vertical lines num_vert_lines = 12 # 24 / 12 = 2, one entry per 2 hours line_spacing_x = (self.width() - self._legend_border) / num_vert_lines line_spacing_y = (self.height() - self._legend_border - self._bar_h) / 20.0 num_horiz_lines = int( math.ceil(canvas_height / float(line_spacing_y)) + 1) # Draw vertical lines painter.setPen(QtGui.QColor(200, 200, 200)) for i in range(num_vert_lines + 1): line_pos = i * line_spacing_x + self._legend_border - 1 painter.drawLine(line_pos, self._bar_h, line_pos, canvas_height + self._bar_h) # Draw horizontal lines painter.setPen(QtGui.QColor(200, 200, 200)) for i in range(num_horiz_lines): line_pos = canvas_height - i * line_spacing_y + self._bar_h painter.drawLine(self._legend_border, line_pos, self.width(), line_pos) # Draw vetical legend labels painter.setPen(QtGui.QColor(120, 120, 120)) for i in range(num_horiz_lines): line_pos = canvas_height - i * line_spacing_y + self._bar_h # painter.drawText(6, line_pos + 3, str(round(float(i) / (num_horiz_lines-1), 2))) painter.drawText( 6, line_pos + 3, self._unit_processor(float(i) / (num_horiz_lines - 1))) # Draw horizontal legend labels for i in range(num_vert_lines + 1): line_pos = i * line_spacing_x + self._legend_border offpos_x = -14 if i == 0: offpos_x = -2 elif i == num_vert_lines: offpos_x = -27 time_string = str(int( float(i) / num_vert_lines * 24)).zfill(2) + ":00" painter.drawText(line_pos + offpos_x, canvas_height + self._bar_h + 18, time_string) # Draw curve for index, curve in enumerate(self._curves): painter.setPen(QtGui.QColor(*curve.color)) last_value = 0 for i in range(canvas_width): rel_offset = i / (canvas_width - 1.0) curve_height = self._get_y_value_for( curve.get_value(rel_offset)) if i == 0: last_value = curve_height painter.drawLine(self._legend_border + i - 1, last_value, self._legend_border + i, curve_height) last_value = curve_height # Draw the CV points of the curve painter.setBrush(QtGui.QColor(240, 240, 240)) for cv_index, (x, y) in enumerate(curve.control_points): offs_x = x * canvas_width + self._legend_border offs_y = (1 - y) * canvas_height + self._bar_h if (self._selected_point and self._selected_point[0] == index and self._selected_point[1] == cv_index): painter.setPen(QtGui.QColor(255, 0, 0)) else: painter.setPen(QtGui.QColor(100, 100, 100)) painter.drawRect(offs_x - self._cv_point_size, offs_y - self._cv_point_size, 2 * self._cv_point_size, 2 * self._cv_point_size) # Draw bar background bar_half_height = 4 bar_top_pos = 10 painter.setBrush(QtGui.QColor(255, 0, 0)) painter.setPen(QtGui.QColor(110, 110, 110)) painter.drawRect(self._legend_border - 1, bar_top_pos - 1, self.width() - self._legend_border, 2 * bar_half_height + 2) # Draw bar if len(self._curves) == 0: return if len(self._curves) == 1: bar_curve = self._curves[0] else: bar_curve = self._curves[0:3] for i in range(canvas_width - 1): xpos = self._legend_border + i relv = float(i) / float(canvas_width) if len(self._curves) == 1: val = max(0, min(255, int(bar_curve.get_value(relv) * 255.0))) painter.setPen(QtGui.QColor(val, val, val)) else: r = max(0, min(255, int(bar_curve[0].get_value(relv) * 255.0))) g = max(0, min(255, int(bar_curve[1].get_value(relv) * 255.0))) b = max(0, min(255, int(bar_curve[2].get_value(relv) * 255.0))) painter.setPen(QtGui.QColor(r, g, b)) painter.drawLine(xpos, bar_top_pos, xpos, bar_top_pos + 2 * bar_half_height) # Draw selected time if self._drag_point: painter.setBrush(QtGui.QColor(200, 200, 200)) painter.setPen(QtGui.QColor(90, 90, 90)) offs_x = max( 0, min(canvas_width + 10, self._drag_time * canvas_width + self._legend_border - 19)) offs_y = self.height() - self._legend_border minutes = int(self._drag_time * 24 * 60) hours = minutes / 60 minutes = minutes % 60 painter.drawRect(offs_x, self.height() - self._legend_border + 5, 40, 20) painter.drawText(offs_x + 7, offs_y + 20, "{:02}:{:02}".format(hours, minutes)) painter.setPen(QtGui.QColor(150, 150, 150)) painter.drawLine(offs_x + 19, bar_top_pos + 15, offs_x + 19, self.height() - self._legend_border + 5) # Display current time pen = QtGui.QPen() pen.setColor(QtGui.QColor(255, 100, 100)) pen.setStyle(QtCore.Qt.DashLine) painter.setPen(pen) xoffs = self._legend_border + self._current_time * (canvas_width - 1) painter.drawLine(xoffs, self._bar_h, xoffs, self._bar_h + canvas_height) # Draw usage hints painter.setPen(QtGui.QColor(100, 100, 100)) painter.drawText( 5, self.height() - 2, "Click on the curve to add new control points, click and drag " "existing points to move them.")
def prepare_scene(self, scene): """ Prepares a given scene, by converting panda lights to render pipeline lights """ # TODO: IES profiles ies_profile = self.load_ies_profile("soft_display.ies") # pylint: disable=W0612 lights = [] for light in scene.find_all_matches("**/+PointLight"): light_node = light.node() rp_light = PointLight() rp_light.pos = light.get_pos(Globals.base.render) rp_light.radius = light_node.max_distance rp_light.energy = 100.0 * light_node.color.w rp_light.color = light_node.color.xyz rp_light.casts_shadows = light_node.shadow_caster rp_light.shadow_map_resolution = light_node.shadow_buffer_size.x rp_light.inner_radius = 0.8 self.add_light(rp_light) light.remove_node() lights.append(rp_light) for light in scene.find_all_matches("**/+Spotlight"): light_node = light.node() rp_light = SpotLight() rp_light.pos = light.get_pos(Globals.base.render) rp_light.radius = light_node.max_distance rp_light.energy = 100.0 * light_node.color.w rp_light.color = light_node.color.xyz rp_light.casts_shadows = light_node.shadow_caster rp_light.shadow_map_resolution = light_node.shadow_buffer_size.x rp_light.fov = light_node.exponent / math.pi * 180.0 lpoint = light.get_mat(Globals.base.render).xform_vec((0, 0, -1)) rp_light.direction = lpoint self.add_light(rp_light) light.remove_node() lights.append(rp_light) envprobes = [] # Add environment probes for np in scene.find_all_matches("**/ENVPROBE*"): probe = self.add_environment_probe() probe.set_mat(np.get_mat()) probe.border_smoothness = 0.001 probe.parallax_correction = True np.remove_node() envprobes.append(probe) # Find transparent objects and set the right effect for geom_np in scene.find_all_matches("**/+GeomNode"): geom_node = geom_np.node() geom_count = geom_node.get_num_geoms() for i in range(geom_count): state = geom_node.get_geom_state(i) if not state.has_attrib(MaterialAttrib): self.warn("Geom", geom_node, "has no material!") continue material = state.get_attrib(MaterialAttrib).get_material() shading_model = material.emission.x # SHADING_MODEL_TRANSPARENT if shading_model == 3: if geom_count > 1: self.error("Transparent materials must have their own geom!") continue self.set_effect( geom_np, "effects/default.yaml", {"render_forward": True, "render_gbuffer": False}, 100) # SHADING_MODEL_FOLIAGE elif shading_model == 5: # XXX: Maybe only enable alpha testing for foliage unless # specified otherwise pass return {"lights": lights, "envprobes": envprobes}
def _draw(self, painter): """ Internal method to draw the widget """ canvas_width = self.width() - self._legend_border canvas_height = self.height() - self._legend_border - self._bar_h # Draw field background # painter.setPen(QtGui.QColor(200, 200, 200)) # painter.setBrush(QtGui.QColor(230, 230, 230)) # painter.drawRect(0, 0, self.width() - 1, self.height() - 1) # Draw legend # Compute amount of horizontal / vertical lines num_vert_lines = 12 # 24 / 12 = 2, one entry per 2 hours line_spacing_x = (self.width() - self._legend_border) / num_vert_lines line_spacing_y = (self.height() - self._legend_border - self._bar_h) / 20.0 num_horiz_lines = int(math.ceil(canvas_height / float(line_spacing_y)) + 1) # Draw vertical lines painter.setPen(QtGui.QColor(200, 200, 200)) for i in range(num_vert_lines + 1): line_pos = i*line_spacing_x + self._legend_border - 1 painter.drawLine(line_pos, self._bar_h, line_pos, canvas_height + self._bar_h) # Draw horizontal lines painter.setPen(QtGui.QColor(200, 200, 200)) for i in range(num_horiz_lines): line_pos = canvas_height - i*line_spacing_y + self._bar_h painter.drawLine(self._legend_border, line_pos, self.width(), line_pos) # Draw vetical legend labels painter.setPen(QtGui.QColor(120, 120, 120)) for i in range(num_horiz_lines): line_pos = canvas_height - i*line_spacing_y + self._bar_h # painter.drawText(6, line_pos + 3, str(round(float(i) / (num_horiz_lines-1), 2))) painter.drawText(6, line_pos + 3, self._unit_processor(float(i) / (num_horiz_lines-1))) # Draw horizontal legend labels for i in range(num_vert_lines + 1): line_pos = i*line_spacing_x + self._legend_border offpos_x = -14 if i == 0: offpos_x = -2 elif i == num_vert_lines: offpos_x = -27 time_string = str(int(float(i) / num_vert_lines * 24)).zfill(2) + ":00" painter.drawText(line_pos + offpos_x, canvas_height + self._bar_h + 18, time_string) # Draw curve for index, curve in enumerate(self._curves): painter.setPen(QtGui.QColor(*curve.color)) last_value = 0 for i in range(canvas_width): rel_offset = i / (canvas_width - 1.0) curve_height = self._get_y_value_for(curve.get_value(rel_offset)) if i == 0: last_value = curve_height painter.drawLine(self._legend_border + i-1, last_value, self._legend_border + i, curve_height) last_value = curve_height # Draw the CV points of the curve painter.setBrush(QtGui.QColor(240, 240, 240)) for cv_index, (x, y) in enumerate(curve.control_points): offs_x = x * canvas_width + self._legend_border offs_y = (1-y) * canvas_height + self._bar_h if self._selected_point and self._selected_point[0] == index and self._selected_point[1] == cv_index: painter.setPen(QtGui.QColor(255, 0, 0)) else: painter.setPen(QtGui.QColor(100, 100, 100)) painter.drawRect(offs_x - self._cv_point_size, offs_y - self._cv_point_size, 2*self._cv_point_size, 2*self._cv_point_size) # Draw bar background bar_half_height = 4 bar_top_pos = 10 painter.setBrush(QtGui.QColor(255, 0, 0)) painter.setPen(QtGui.QColor(110, 110, 110)) painter.drawRect(self._legend_border - 1, bar_top_pos - 1, self.width() - self._legend_border, 2*bar_half_height + 2) # Draw bar if len(self._curves) == 0: return if len(self._curves) == 1: bar_curve = self._curves[0] else: bar_curve = self._curves[0:3] for i in range(canvas_width - 1): xpos = self._legend_border + i relv = float(i) / float(canvas_width) if len(self._curves) == 1: val = max(0, min(255, int(bar_curve.get_value(relv) * 255.0))) painter.setPen(QtGui.QColor(val, val, val)) else: r = max(0, min(255, int(bar_curve[0].get_value(relv) * 255.0))) g = max(0, min(255, int(bar_curve[1].get_value(relv) * 255.0))) b = max(0, min(255, int(bar_curve[2].get_value(relv) * 255.0))) painter.setPen(QtGui.QColor(r, g, b)) painter.drawLine(xpos, bar_top_pos, xpos, bar_top_pos + 2 * bar_half_height) # Draw selected time if self._drag_point: painter.setBrush(QtGui.QColor(200, 200, 200)) painter.setPen(QtGui.QColor(90, 90, 90)) offs_x = max(0, min(canvas_width + 10, self._drag_time * canvas_width + self._legend_border - 19)) offs_y = self.height() - self._legend_border minutes = int(self._drag_time * 24 * 60) hours = minutes / 60 minutes = minutes % 60 painter.drawRect(offs_x, self.height() - self._legend_border + 5, 40, 20) painter.drawText(offs_x + 7, offs_y + 20, "{:02}:{:02}".format(hours, minutes)) painter.setPen(QtGui.QColor(150, 150, 150)) painter.drawLine(offs_x + 19, bar_top_pos + 15, offs_x + 19, self.height() - self._legend_border + 5) # Display current time pen = QtGui.QPen() pen.setColor(QtGui.QColor(255, 100, 100)) pen.setStyle(QtCore.Qt.DashLine) painter.setPen(pen) xoffs = self._legend_border + self._current_time * (canvas_width-1) painter.drawLine(xoffs, self._bar_h, xoffs, self._bar_h + canvas_height) # Draw usage hints painter.setPen(QtGui.QColor(100, 100, 100)) painter.drawText(5, self.height() - 2, "Click on the curve to add new control points, click and drag existing points to move them.")
def _load_and_parse_file(self, pth): """ Loads a .IES file from a given filename, returns an IESDataset which is used by the load function later on. """ self.debug("Loading ies profile from", pth) try: with open(pth, "r") as handle: lines = handle.readlines() except IOError as msg: self.error("Failed to open", pth, ":", msg) return None lines = [i.strip() for i in lines] # Parse version header self._check_version_header(lines.pop(0)) # Parse arbitrary amount of keywords keywords = self._extract_keywords(lines) # Next line should be TILT=NONE according to the spec if lines.pop(0) != "TILT=NONE": raise InvalidIESProfileException("Expected TILT=NONE line, but none found!") # From now on, lines do not matter anymore, instead everything is # space seperated new_parts = (' '.join(lines)).replace(",", " ").split() read_int = lambda: int(new_parts.pop(0)) read_float = lambda: float(new_parts.pop(0)) # Amount of Lamps if read_int() != 1: raise InvalidIESProfileException("Only 1 Lamp supported!") # Extract various properties lumen_per_lamp = read_float() candela_multiplier = read_float() num_vertical_angles = read_int() num_horizontal_angles = read_int() if num_vertical_angles < 1 or num_horizontal_angles < 1: raise InvalidIESProfileException("Invalid of vertical/horizontal angles!") photometric_type = read_int() unit_type = read_int() # Check for a correct unit type, should be 1 for meters and 2 for feet if unit_type not in [1, 2]: raise InvalidIESProfileException("Invalid unit type") width = read_float() length = read_float() height = read_float() ballast_factor = read_float() future_use = read_float() # Unused field for future usage input_watts = read_float() # Read vertical angles vertical_angles = [read_float() for i in range(num_vertical_angles)] horizontal_angles = [read_float() for i in range(num_horizontal_angles)] candela_values = [] candela_scale = 0.0 for i in range(num_horizontal_angles): vertical_data = [read_float() for i in range(num_vertical_angles)] candela_scale = max(candela_scale, max(vertical_data)) candela_values += vertical_data # Rescale values, divide by maximum candela_values = [i / candela_scale for i in candela_values] if len(new_parts) != 0: self.warn("Unhandled data at file-end left:", new_parts) # Dont abort here, some formats like those from ERCO Leuchten GmbH # have an END keyword, just ignore everything after the data was # read in. dataset = IESDataset() dataset.set_vertical_angles(self._list_to_pta(vertical_angles)) dataset.set_horizontal_angles(self._list_to_pta(horizontal_angles)) dataset.set_candela_values(self._list_to_pta(candela_values)) # Testing code to write out the LUT # from panda3d.core import Texture # tex = Texture("temp") # tex.setup_3d_texture(512, 512, 1, Image.T_float, Image.F_r16) # dataset.generate_dataset_texture_into(tex, 0) # tex.write("generated.png") return dataset