def size_inside_square_for_texture(texture: core.Texture, square_size: float): width = texture.get_x_size() height = texture.get_y_size() if width < 1 or height < 1: return None if width > height: frame_height = (height / width) * square_size frame_width = square_size else: frame_height = square_size frame_width = (width / height) * square_size return core.Vec2(frame_width, frame_height)
def setup_terrain(self): """ Terrain info Units are meters, which is preferable when working with Bullet. """ self.terrain_scale = LVector3(512, 512, 100) self.terrain_pos = LVector3(-256, -256, -70) # sample values for a 4096 x 4096px heightmap. #self.terrain_scale = LVector3(4096, 4096, 1000) #self.terrain_pos = LVector3(-2048, -2048, -70) """ Diamond_subdivision is an alternating triangulation scheme and may produce better results. """ use_diamond_subdivision = True """ Construct the terrain Without scaling, any ShaderTerrainMesh is 1x1x1 units. """ self.terrain_node = ShaderTerrainMesh() """ Set a heightfield, the heightfield should be a 16-bit png and have a quadratic size of a power of two. """ heightfield = Texture() heightfield.read(self.heightfield_fn) heightfield.set_keep_ram_image(True) self.terrain_node.heightfield = heightfield # Display characteristic values of the heightfield texture #minpoint, maxpoint, avg = LPoint3(), LPoint3(), LPoint3() #heightfield.calc_min_max(minpoint, maxpoint) #heightfield.calc_average_point(avg, 0.5, 0.5, 0.5) #print("avg: {} min: {} max: {}".format(avg.x, minpoint.x, maxpoint.x)) """ Set the target triangle width. For a value of 10.0 for example, the ShaderTerrainMesh will attempt to make every triangle 10 pixels wide on screen. """ self.terrain_node.target_triangle_width = 10.0 if use_diamond_subdivision: """ This has to be specified before calling .generate() The default is false. """ load_prc_file_data("", "stm-use-hexagonal-layout true") self.terrain_node.generate() """ Attach the terrain to the main scene and set its scale. With no scale set, the terrain ranges from (0, 0, 0) to (1, 1, 1) """ self.terrain = self.render.attach_new_node(self.terrain_node) self.terrain.set_scale(self.terrain_scale) self.terrain.set_pos(self.terrain_pos) """ Set a vertex and a fragment shader on the terrain. The ShaderTerrainMesh only works with an applied shader. """ terrain_shader = Shader.load(Shader.SL_GLSL, "samples/shader-terrain/terrain.vert.glsl", "samples/shader-terrain/terrain.frag.glsl") self.terrain.set_shader(terrain_shader) self.terrain.set_shader_input("camera", base.camera) # Set some texture on the terrain grass_tex = self.loader.load_texture( "samples/shader-terrain/textures/grass.png") grass_tex.set_minfilter(SamplerState.FT_linear_mipmap_linear) grass_tex.set_anisotropic_degree(16) self.terrain.set_texture(grass_tex) """ Set up the DynamicHeightfield (it's a type of PfmFile). We load the same heightfield image as with ShaderTerrainMesh. """ self.DHF = DynamicHeightfield() self.DHF.read(self.heightfield_fn) """ Set up empty PfmFiles to prepare stuff in that is going to dynamically modify our terrain. """ self.StagingPFM = PfmFile() self.RotorPFM = PfmFile() """ Set up the BulletHeightfieldShape (=collision terrain) and give it some sensible physical properties. """ self.HFS = BulletHeightfieldShape(self.DHF, self.terrain_scale.z, STM=True) if use_diamond_subdivision: self.HFS.set_use_diamond_subdivision(True) HFS_rigidbody = BulletRigidBodyNode("BulletTerrain") HFS_rigidbody.set_static(True) friction = 2.0 HFS_rigidbody.set_anisotropic_friction( LVector3(friction, friction, friction/1.3)) HFS_rigidbody.set_restitution(0.3) HFS_rigidbody.add_shape(self.HFS) self.world.attach(HFS_rigidbody) HFS_NP = NodePath(HFS_rigidbody) HFS_NP.reparent_to(self.worldNP) """ This aligns the Bullet terrain with the ShaderTerrainMesh rendered terrain. It will be exact as long as the terrain vertex shader from the STM sample is used and no additional tessellation shader. For Bullet (as for other physics engines) the origin of objects is at the center. """ HFS_NP.set_pos(self.terrain_pos + self.terrain_scale/2) HFS_NP.set_sx(self.terrain_scale.x / heightfield.get_x_size()) HFS_NP.set_sy(self.terrain_scale.y / heightfield.get_y_size()) # Disables Bullet debug rendering for the terrain, because it is slow. #HFS_NP.node().set_debug_enabled(False) """ Finally, link the ShaderTerrainMesh and the BulletHeightfieldShape to the DynamicHeightfield. From now on changes to the DynamicHeightfield will propagate to the (visible) ShaderTerrainMesh and the (collidable) BulletHeightfieldShape. """ self.HFS.set_dynamic_heightfield(self.DHF) self.terrain_node.set_dynamic_heightfield(self.DHF)