├── .gitignore ├── 01_hello_world.py ├── 02_camera_control.py ├── 03_sounds.py ├── 04_sequence.py ├── 05_interaction.py ├── 06_physics.py ├── 07_gui.py ├── 08_deploy.py ├── LICENSE ├── advanced ├── deferred_shading │ └── dummy.txt ├── environment_maps │ ├── main.py │ └── shaders │ │ ├── ibl_f.glsl │ │ ├── ibl_v.glsl │ │ ├── skybox_f.glsl │ │ └── skybox_v.glsl ├── instancing_on_terrain │ ├── main.py │ └── shaders │ │ ├── grass_f.glsl │ │ ├── grass_v.glsl │ │ ├── skybox_f.glsl │ │ ├── skybox_v.glsl │ │ ├── terrain_f.glsl │ │ └── terrain_v.glsl ├── npr_shading │ └── dummy.txt ├── pbr_materials │ └── dummy.txt ├── post_process │ └── dummy.txt ├── readme.md └── terrain │ └── dummy.txt ├── models ├── a_p3d_chan_idle.egg.pz ├── a_p3d_chan_run.egg.pz ├── a_p3d_chan_step_l.egg.pz ├── a_p3d_chan_step_r.egg.pz ├── a_p3d_chan_walk.egg.pz ├── act_p3d_chan.egg.pz ├── box.egg ├── crate.egg.pz ├── grass.egg ├── monkey.egg ├── room_industrial.egg.pz └── texture │ ├── crate_1.png │ ├── crate_1_ng.png │ ├── crate_2.png │ ├── crate_2_ng.png │ ├── cubemap │ ├── georgentor.txo │ ├── green_point_park.txo │ ├── indoor_pool.txo │ ├── machine_shop.txo │ ├── mutianyu.txo │ ├── qwantani.txo │ ├── studio_small.txo │ └── wooden_lounge.txo │ ├── grass.png │ ├── industrial.png │ ├── industrial_ng.png │ ├── panda_chan_c.png │ ├── panda_chan_material.png │ ├── panda_chan_ng.png │ └── terrain │ ├── grass_c.png │ ├── grass_n.png │ ├── rock_c.png │ ├── rock_n.png │ ├── snow_c.png │ ├── snow_n.png │ ├── terrain_atr.png │ ├── terrain_grass.png │ └── terrain_height.png ├── readme.md ├── tutorial_01 └── base_app.py ├── tutorial_02 └── keys.ini ├── tutorial_03 └── dummy.txt ├── tutorial_04 └── dummy.txt ├── tutorial_05 └── dummy.txt ├── tutorial_06 └── dummy.txt ├── tutorial_07 └── dummy.txt ├── tutorial_08 └── dummy.txt └── utilities └── hdri_to_cubemap ├── blue_noise.png ├── box.egg ├── cubemap └── dummy.txt ├── hdri └── paul_lobe_haus_4k.png ├── hdri_to_cubemap.py ├── shaders ├── skybox_f.glsl └── skybox_v.glsl └── temp └── dummy.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.pyo 3 | -------------------------------------------------------------------------------- /01_hello_world.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This sample shows how to: 3 | -import and use basic Panda3D classes 4 | -use configuration variables 5 | -load 3d models 6 | -move, scale, rotate 3d models 7 | -change textures 8 | -change colors 9 | -setup lights 10 | - ??? 11 | -profit! 12 | ''' 13 | 14 | ''' Import the most used panda3d classes 15 | You could just as well use: 16 | from panda3d.core import * 17 | but star imports are un-pythonic -_-''' 18 | import panda3d.core as p3d 19 | 20 | ''' 'direct' is the part of Panda3D written in Python ('core' is C++ with Python bindings) 21 | ShowBase is a singelton class that will setup a window, task manager, and all the 22 | other, common things that you may (or may not) want to use. 23 | You don't need to use it, but you'll probably want to use or use it even if you don't want to. 24 | It also installs itself and a bunch of other things into builtins, so that 25 | key objects (like the 'render' node) are accessible anywhere in your code, 26 | in these samples I will try not to use these, because again - un-pythonic -_-' ''' 27 | from direct.showbase.ShowBase import ShowBase 28 | 29 | # To save some space and keyboard wear and tear, we'll add some things under shorter name 30 | FT_MIPMAP = p3d.SamplerState.FT_linear_mipmap_linear 31 | FT_LINEAR = p3d.SamplerState.FT_linear 32 | TS_NORMAL = p3d.TextureStage.M_normal 33 | TS_NORMAL_GLOSS = p3d.TextureStage.M_normal_gloss 34 | TS_MODULATE = p3d.TextureStage.M_modulate 35 | 36 | ''' Some configuration: 37 | Panda3D uses .prc files to configure some stuff 38 | but you don't need to load external files, you can put the values in code 39 | Do notice how 'load_prc_file_data' works: 40 | there's an empty string as the first argument, that's not an error 41 | Full list of options is here: https://www.panda3d.org/manual/?title=List_of_All_Config_Variables''' 42 | # It's 2019, make the window 720p let's not embarrass ourself with a 800x600 window 43 | p3d.load_prc_file_data('', 'win-size 1280 720') 44 | # Add some multisamples to enable MSAA 45 | p3d.load_prc_file_data('', 'multisamples 1') 46 | # We also want a framerate meter... 47 | p3d.load_prc_file_data('', 'show-frame-rate-meter 1') 48 | # ...and we want to disable v-sync else the framerate meter will just show 60.0 fps 49 | p3d.load_prc_file_data('', 'sync-video 0') 50 | # There's a whole lot of options, let's just set a window title just for the fun of it 51 | p3d.load_prc_file_data('', 'window-title Panda3D - Hello World') 52 | 53 | 54 | 55 | class App(ShowBase): 56 | def __init__(self): 57 | ''' init the base class, this will give us access to all the ShowBase functionality 58 | If you are using python 2.x you could use: 59 | ShowBase.__init__(self) 60 | but a better solution is to use Python 3.x, 61 | Here's a link, go ahead, download it, I'm not going anywhere... 62 | https://www.python.org/downloads/ ''' 63 | super().__init__() 64 | 65 | ''' By default the background is a dull grey, let's make it black 66 | colors in Panda3D usually are red, green, blue and alpha values 67 | (in a 0.0-1.0 range, where 0.0 is black and 1.1 is white ) 68 | but in this case we only have red, green and blue''' 69 | self.set_background_color(0.0, 0.0, 0.0) 70 | 71 | # Load a model of a empty room 72 | self.room = self.loader.load_model('models/room_industrial') 73 | ''' loader returns a node (NodePath), to make it visible 74 | we need to parent it to 'render' -the root node of the scene graph 75 | we could use just 'render' but apparently that's un-pythonic''' 76 | self.room.reparent_to(self.render) 77 | 78 | ''' Now we need to add some crates 79 | can't have games without crates, I'm almost sure there's a law for that 80 | we'll load a model and copy it a few times''' 81 | crate_model = self.loader.load_model('models/crate') 82 | ''' The crate should be a 1x1x1 cube, but *someone* who knows nothing 83 | about Blender made the model (it's all wezu's fault!) 84 | and it's just a wee bit too small- wee need to fix that ''' 85 | # First resize it, just a bit 86 | crate_model.set_scale(1.033) 87 | ''' We don't want this extra scale to persist, so we flatten the model 88 | flatten_light() will just apply the transformations (movement scale, rotation) 89 | to the vertex of the model 90 | flatten_strong() will try to merge meshes into as few batches as it can 91 | flatten_medium() will do something in between ''' 92 | crate_model.flatten_light() 93 | '''We'll make a list of crates so it will be easy to access them later 94 | copy_to() makes a copy of a node, parents it to a given node 95 | and returns the NodePath with that copied node - we'll use that 96 | list comprehension version for the python savvy...''' 97 | self.crates = [crate_model.copy_to(self.render) for _ in range(5)] 98 | '''...for the non-savvy: the above code is equivalent to: 99 | self.crates = [] 100 | for i in range(5): 101 | self.crates.append(crate_model.copy_to(self.render))''' 102 | 103 | ''' Now we have the crates all in one place, overlapping - that's no good 104 | let's put crate #1 on top of create #0, and twist it a bit 105 | the crates are 1x1x1 cubes with the pivot cantered at the bottom, 106 | so placing them is easy 107 | Panda3D uses a Z-up coordinate system (by default) 108 | 109 | Z Y 110 | | / 111 | | / 112 | |/____ 113 | X 114 | X is right ---> 115 | 116 | /\ 117 | / 118 | Y is forward / 119 | 120 | ^ 121 | | 122 | Z is up | 123 | ''' 124 | # First move it up 125 | self.crates[1].set_pos(0,0,1) 126 | ''' HPR stands for Heading, Pitch, Roll 127 | Heading is the rotation around a vertical axis, 128 | eg. the way a top spins, the way Earth spins, looking left and right 129 | Pitch is the rotation around a horizontal axis, 130 | eg. the way your jaw moves, the way a bike throttle in the handle works, looking up and down 131 | Roll is the rotation in the.. em.. the other axis 132 | eg. the way clock hands move''' 133 | # Rotate it a bit, 10 degrees should be fine 134 | self.crates[1].set_hpr(10,0,0) 135 | # Let's move crate #2 to the side and a bit back 136 | self.crates[2].set_pos(1,-0.3,0) 137 | # Let's make the #3 and #4 crates bigger, and also move them 138 | self.crates[3].set_pos(3,-3,0) 139 | self.crates[4].set_pos(3.1,-1.35,0) 140 | self.crates[3].set_scale(1.6) 141 | self.crates[4].set_scale(1.3) 142 | ''' Still looks lame. Why don't we change the textures on the small crates? 143 | I think the crate uses 2 textures - a diffuse(color) texture and a normal (bump) texture 144 | let's just see what textures the crates have and change the textures if we find anything worth changing 145 | we will be using some enums defined near the top 146 | you can use p3d.SamplerState.FT_linear_mipmap_linear in place of FT_MIPMAP 147 | but first - load new textures''' 148 | new_diffuse_tex = self.loader.load_texture('models/texture/crate_2.png') 149 | # loader uses some reasonable defaults, but let's play with the filter type a 150 | new_diffuse_tex.set_minfilter(FT_MIPMAP) 151 | new_diffuse_tex.set_magfilter(FT_LINEAR) 152 | # Same for normal map, but pass the filter types as arguments 153 | # both give the same result, use what you prefer 154 | new_normal_tex = self.loader.load_texture('models/texture/crate_2_ng.png', 155 | minfilter = FT_MIPMAP, 156 | magfilter = FT_LINEAR) 157 | # Change the textures only on the last two crates 158 | for crate in self.crates[-2:]: 159 | # Model have textures in texture stages 160 | # so we iterate over all the texture stages of the model 161 | for tex_stage in crate.find_all_texture_stages(): 162 | if crate.find_texture(tex_stage):#test if there is any texture to override 163 | # texture stages have modes 164 | if tex_stage.get_mode() in (TS_NORMAL, TS_NORMAL_GLOSS): 165 | # we found ourself a normal map - replace! 166 | crate.set_texture(tex_stage, new_normal_tex, 1) 167 | if tex_stage.get_mode() == TS_MODULATE: 168 | # we found ourself a diffuse map - replace! 169 | crate.set_texture(tex_stage, new_diffuse_tex, 1) 170 | ''' Now it looks... wait, what? 171 | The texture we loaded is grey! Someone overrode it, and now it's broken! 172 | No panic - we'll just add some color to the models. 173 | make crate #3 a nice yellowish-brown color''' 174 | self.crates[3].set_color((0.8, 0.696, 0.496, 1.0), 1) 175 | # Make crate #4 a nice brown color 176 | self.crates[4].set_color((0.66, 0.55, 0.46, 1.0), 1) 177 | # That still looks bad, but at least you now know how to change colors 178 | 179 | '''ShowBase creates a camera for us, so we can use it without any extra setup 180 | ...well almost, for this scene we don't want the player to move the camera 181 | by default the camera can be moved and rotated using the mouse, 182 | we'll disable that mouse-look, using the worst named function ever:''' 183 | self.disable_mouse() # <- this disables the camera mouse control not the mouse! 184 | # Now we can place the camera in a good spot 185 | self.camera.set_pos(-7.0, -4.5, 4.5) 186 | # We could use self.camera.set_hpr() to orient the camera, but it's 187 | # simpler to just point it at one of the crates, (or a point in space) 188 | self.camera.look_at(self.crates[4]) 189 | ''' Lets also change the field of view (FOV) for the camera 190 | ShowBase already has a reference to the default lens used by the default camera 191 | that lens is available under the name base.camLens or self.camLens 192 | that name uses the old camelCase naming convention and we want to avoid that 193 | Let's get the lens from the default camera 194 | here's the catch - the *actual* camera is *not* self.camera (or base.camera) 195 | self.camera is a extra node (NodePath class) that has the camera (Camera class) 196 | ...well, kind of, the camera is also wrapped in a NodePath, hence the .node() call 197 | ShowBase keeps a this under the name self.cam''' 198 | self.cam_lens = self.cam.node().get_lens() 199 | # The fov may change with the size of the window, 200 | # we'll just increase it to 125% of what it was (both horizontal and vertical) 201 | fov = self.cam_lens.get_fov() 202 | self.cam_lens.set_fov(fov*1.25) 203 | 204 | #Lights 205 | # First we need an ambient light, else everything not illuminated will be black 206 | self.ambient_light = self.render.attach_new_node(p3d.AmbientLight('ambient')) 207 | # Remember the use of .node() from earlier? 208 | self.ambient_light.node().set_color((0.1, 0.1, 0.1, 1.0)) 209 | # Tell Panda3D to actually use this light for anything parented to render 210 | self.render.set_light(self.ambient_light) 211 | # Next a directional light 212 | self.dir_light = self.render.attach_new_node(p3d.DirectionalLight('directional')) 213 | self.dir_light.node().set_color((0.1, 0.1, 0.25, 1.0)) 214 | ''' You can think of the light direction vector as: 215 | 'how much is the light coming from the left, back, and bottom?' 216 | on a -1.0 - 1.0 scale''' 217 | light_from_left = 0.2 # the light is a bit from the left 218 | light_from_back = 0.4 # the light is a bit from the back 219 | light_from_bottom = -1.0 # the light is coming from the top 220 | light_vec = p3d.Vec3(light_from_left,light_from_back,light_from_bottom) 221 | light_vec.normalize() # <- not needed, but it won't hurt 222 | self.dir_light.node().set_direction(light_vec) 223 | self.render.set_light(self.dir_light) 224 | # Last we add a spotlight, just for shadows 225 | self.spot_light = self.render.attach_new_node(p3d.Spotlight('spot')) 226 | self.spot_light.node().set_color((1.0, 1.0, 1.0, 1.0)) 227 | # Make a light cast shadows, and set the shadow map resolution to 1024x1024 228 | self.spot_light.node().set_shadow_caster(True, 1024, 1024) 229 | ''' To get a good resolution for the shadow depth map 230 | you need to fit the near and far planes of the light (lense) 231 | the more precise the bounds, the better the shadow quality''' 232 | self.spot_light.node().get_lens().set_near_far(0.1, 20.0) 233 | # Let's set the light cone to be narrower 234 | self.spot_light.node().get_lens().set_fov(25) 235 | # and also to have a smoother falloff 236 | self.spot_light.node().set_exponent(120.0) 237 | # spotlights unlike ambient and directional lights need to be 238 | # placed and oriented in the scene 239 | self.spot_light.set_pos(-8, 0, 8) 240 | self.spot_light.look_at(self.crates[3]) 241 | self.render.set_light(self.spot_light) 242 | 243 | # Enable the shader generator, 244 | # it creates Cg shaders for per pixel lights and shadows 245 | # It's an older code but it checks out 246 | self.render.set_shader_auto() 247 | # Enable MSAA 248 | self.render.set_antialias(p3d.AntialiasAttrib.M_multisample) 249 | 250 | # run all the code 251 | if __name__ == "__main__": 252 | app=App() 253 | app.run() 254 | -------------------------------------------------------------------------------- /02_camera_control.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This sample shows how to control the camera, and also how to: 3 | - respond to key events 4 | - using tasks 5 | - track the mouse cursor 6 | - configure keys using a .ini file 7 | - relative motion mumbo-jumbo 8 | ''' 9 | import panda3d.core as p3d 10 | p3d.load_prc_file_data('', '''win-size 1280 720 11 | multisamples 1 12 | show-frame-rate-meter 1 13 | sync-video 0''') 14 | 15 | #we re use the code from tutorial_01 16 | #it sets up Panda3D, the scene, and lights 17 | from tutorial_01.base_app import BaseApp 18 | 19 | import configparser 20 | 21 | class App(BaseApp): 22 | def __init__(self): 23 | super().__init__() 24 | #set a window title 25 | self.set_window_title('Panda3D - Camera Control. Use mouse!') 26 | #setup the scene and lights 27 | self.setup_scene() 28 | self.setup_lights() 29 | #let's make it a bit brighter 30 | self.ambient_light.node().set_color((0.3, 0.3, 0.3, 1.0)) 31 | 32 | # we will control the camera using a rig of 2 nodes 33 | # the 'node' will be the point in space where the camera looks at 34 | # the 'gimbal' will act as an arm that the camera is attached to 35 | self.camera_node = self.render.attach_new_node('camera_node') 36 | self.camera_gimbal = self.camera_node.attach_new_node('gimbal') 37 | # the camera will orbit around the camera_node, here we'll set 38 | # how far that orbit is from the focus point 39 | camera_offset =(0, 5, 5) 40 | self.camera.set_pos(self.render, camera_offset) 41 | self.camera.look_at(self.camera_node) 42 | #we use 'wrt_reparent_to' to keep the offset we just set 43 | self.camera.wrt_reparent_to(self.camera_gimbal) 44 | #with the setup done, we want to move the camera to a different spot 45 | self.camera_node.set_pos(0, -2, 1) 46 | #and rotate 180 47 | self.camera_node.set_h(180) 48 | 49 | #configuration time 50 | # how fast can the camera rotate? 51 | self.camera_rotation_speed = 100.0 52 | # how fast can the camera move? 53 | self.camera_move_speed = 10.0 54 | # how fast can the camera zoom? 55 | self.camera_zoom_speed = 10.0 56 | self.camera_zoom_damping = 2.0 57 | # how much can the camera tilt? (change 'p' in HPR sense) 58 | self.camera_p_limit = p3d.Vec2(-45, 45) 59 | #how much can the camera zoom in or out? 60 | self.zoom_limit=[3, 20] 61 | # controls: 62 | # we'll read the keys from an .ini file 63 | # I'm not using the default .prc PAnda3D configuration file format 64 | # because most players will not even recognize .prc as a human readable, 65 | # configuration file, .ini on the other hand are common in games 66 | key_config = configparser.ConfigParser() 67 | key_config.read('tutorial_02/keys.ini') 68 | #we want mouse wheel to control zoom 69 | # the way the mouse wheel works is just like a button 70 | # it just fires the 'wheel_up' or 'wheel_down' events when the wheel is spun 71 | # we will zoom in (or out) some amount whenever the wheel is moved 72 | # self.zoom will tell us how much we still need to move and in what direction 73 | # the actual moving of the camera will happen in the camera_update task 74 | self.zoom = 0 75 | #the 'accept' function tells the underlying DirectObject to listen to some event 76 | # and run some function (method) when it happens 77 | # in this case the event is "key_config['camera_zoom']['zoom_in']" - and that should be just 78 | #"key_config['camera_zoom']['zoom_in']" should just be 'wheel_up' 79 | # our function is 'self.zoom_control' 80 | # the last argument is a list of ... em arguments passed to our function 81 | # in other words: 82 | # when a player spins the mouse wheel, panda3d will send a event named 'wheel_up' 83 | # and our class will respond to it by calling 'self.zoom_control(1.0)' 84 | self.accept(key_config['camera_zoom']['zoom_in'], self.zoom_control, [1.0] ) 85 | self.accept(key_config['camera_zoom']['zoom_out'], self.zoom_control, [-1.0] ) 86 | # for other keys we want to know if the player is holding down a key 87 | # self.key_down will be a dict that will have the names of events as keys 88 | # and True as the value if a key is pressed else False 89 | self.key_down={} 90 | for event, key in key_config['camera_keys'].items(): 91 | self.key_down[event] = False 92 | self.accept(key, self.key_down.__setitem__, [event, True]) 93 | #the keys can me 'compound' like 'shift-mouse1' or 'alt-mouse1' 94 | # the problem is - there is no -up event for such key-combos 95 | # we will have to react just to the first key 96 | # if we'd have a key bind on shift things would break :( 97 | if '-' in key: 98 | self.accept(key.split('-')[0]+'-up', self.key_down.__setitem__, [event, False]) 99 | else: 100 | self.accept(key+'-up', self.key_down.__setitem__, [event, False]) 101 | # we'll later check if this set None and skip moving the mouse 102 | # until we have a valid value 103 | self.last_mouse_pos = None 104 | 105 | #our App class inherits from the BaseApp class.. 106 | # BaseApp inherits from ShowBase... 107 | # ShowBase inherits from DirectObject... 108 | # ...so we can use DirectObject methods like add_task() 109 | #The alternative is using taskMgr.add() and that's what 110 | # DirectObject is actually doing, but this is nicer (?) 111 | #A task is simply a function called auto-magically each frame 112 | self.add_task(self.camera_update, 'camera_update') 113 | 114 | def zoom_control(self, amount): 115 | self.zoom=amount 116 | 117 | def camera_update(self, task): 118 | '''This function is a task run each frame, 119 | it controls the camera movement/rotation/zoom''' 120 | #dt is 'delta time' - how much time elapsed since the last time the task ran 121 | #we will use it to keep the motion independent from the framerate 122 | dt = globalClock.get_dt() 123 | #we'll be tracking the mouse 124 | #first we need to check if the cursor is in the window 125 | if self.mouseWatcherNode.has_mouse(): 126 | #let's see where the mouse cursor is now 127 | mouse_pos = self.mouseWatcherNode.get_mouse() 128 | #let's see how much it moved from last time, or if this is the first frame 129 | if self.last_mouse_pos is None: 130 | self.last_mouse_pos = p3d.Vec2(mouse_pos) 131 | return task.again 132 | mouse_delta = mouse_pos- self.last_mouse_pos 133 | #and let's remember where it is this frame so we can 134 | #check next frame where it was 135 | self.last_mouse_pos = p3d.Vec2(mouse_pos) 136 | #camera zoom 137 | if self.zoom != 0.0: 138 | #let's see how far the camera is from the pivot point 139 | distance=self.camera.get_distance(self.camera_node) 140 | #we don't want it to be too close nor to far away 141 | if (distance > self.zoom_limit[0] and self.zoom > 0.0) or \ 142 | (distance < self.zoom_limit[1] and self.zoom < 0.0): 143 | #move the camera away or closer to the pivot point 144 | #we do that by moving the camera relative to itself 145 | self.camera.set_y(self.camera, self.zoom*dt*self.camera_zoom_speed) 146 | 147 | if self.zoom >= 0.0: 148 | self.zoom-=dt*self.camera_zoom_damping 149 | else: 150 | self.zoom+=dt*self.camera_zoom_damping 151 | else: 152 | self.zoom=0.0 153 | if self.key_down['rotate']: 154 | h = self.camera_node.get_h()- mouse_delta.x*self.camera_rotation_speed 155 | self.camera_node.set_h(h) 156 | p = self.camera_gimbal.get_p()- mouse_delta.y*self.camera_rotation_speed 157 | p = min(max(p, self.camera_p_limit.x), self.camera_p_limit.y) 158 | self.camera_gimbal.set_p(p) 159 | if self.key_down['relative_move']: 160 | pos = p3d.Vec3(mouse_delta.x, 0, mouse_delta.y) 161 | pos = self.camera_node.get_pos(self.camera) - pos*self.camera_move_speed 162 | self.camera_node.set_pos(self.camera, pos) 163 | elif self.key_down['move']: 164 | pos=p3d.Vec3(mouse_delta.x, mouse_delta.y, 0) 165 | self.camera_node.set_pos(self.camera_node, pos*self.camera_move_speed) 166 | return task.again 167 | 168 | 169 | 170 | app=App() 171 | app.run() 172 | -------------------------------------------------------------------------------- /03_sounds.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/03_sounds.py -------------------------------------------------------------------------------- /04_sequence.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/04_sequence.py -------------------------------------------------------------------------------- /05_interaction.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/05_interaction.py -------------------------------------------------------------------------------- /06_physics.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/06_physics.py -------------------------------------------------------------------------------- /07_gui.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/07_gui.py -------------------------------------------------------------------------------- /08_deploy.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/08_deploy.py -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Except where otherwise noted, all models and textures are licensed under a Creative Commons Attribution 3.0 Unported (CC BY 3.0) License: 2 | https://creativecommons.org/licenses/by/3.0/ 3 | 4 | All sourcecode is Public Domain/CC0: 5 | https://creativecommons.org/publicdomain/zero/1.0/ 6 | 7 | 8 | -------------------------------------------------------------------------------- /advanced/deferred_shading/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /advanced/environment_maps/main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This demo shows how to use cube maps for 3 | -skyboxes 4 | -reflections 5 | -ambient lighting 6 | 7 | The cube maps used in this demo are made from equirectangular environment 8 | maps (hdri), but only use standard bit depth(8bpp), have a fuzzy blur 9 | applied to lower lods and are compressed using dxt1. 10 | 11 | You can check the 'utilities' folder to check the scpript used to make 12 | the cube maps. 13 | 14 | Instructions: 15 | - Press *SPACE* to change the environment map 16 | - Default mouse diver for camera movement 17 | ''' 18 | import os 19 | import panda3d.core as p3d 20 | p3d.load_prc_file_data('', 'framebuffer-srgb 1') 21 | p3d.load_prc_file_data('', 'multisamples 1') 22 | from direct.showbase.ShowBase import ShowBase 23 | 24 | FT_LINEAR = p3d.SamplerState.FT_linear 25 | FT_NEAREST = p3d.SamplerState.FT_nearest 26 | FT_MIPMAP = p3d.SamplerState.FT_linear_mipmap_linear 27 | GLSL = p3d.Shader.SLGLSL 28 | F_SRGB =p3d.Texture.F_srgb 29 | 30 | class App(ShowBase): 31 | def __init__(self): 32 | super().__init__(self) 33 | #move the cam back without breaking the default mouse driver 34 | self.trackball.node().set_pos(0, 20, 0) 35 | #MSAA just for making thinkgs look good 36 | self.render.set_antialias(p3d.AntialiasAttrib.M_multisample) 37 | 38 | self.env_maps=[] 39 | for filename in os.listdir('../../models/texture/cubemap'): 40 | self.env_maps.append('../../models/texture/cubemap/'+filename) 41 | self.curren_env_map=0 42 | #the map_sky will be used for the skybox 43 | #the other one, blurred and at a lower resolution as the environment map 44 | self.map_env=self.loader.load_texture(self.env_maps[0]) 45 | 46 | #make sure it's sRGB 47 | if p3d.ConfigVariableBool('framebuffer-srgb').get_value(): 48 | self.map_env.set_format(F_SRGB) 49 | #apply texture to render 50 | self.render.set_shader_input('env_map', self.map_env) 51 | #camera pos is needed by the shaders 52 | self.render.set_shader_input("camera", self.cam) 53 | #load a model and do some setup 54 | model=self.loader.load_model('../../models/monkey') 55 | model.set_shader(p3d.Shader.load(GLSL, 'shaders/ibl_v.glsl', 'shaders/ibl_f.glsl'), 1) 56 | model.set_shader_input('roughness', 0.0) 57 | model.set_shader_input('metallic', 1.0) 58 | model.set_shader_input('color', p3d.Vec3(0.8, 0.8, 0.8)) 59 | #copy it a few times 60 | monkeys=[model.copy_to(render) for _ in range(15)] 61 | #move and apply different values 62 | for i, monkey in enumerate(monkeys[:5]): 63 | monkey.set_pos((i*3)-6, 0, 0) 64 | monkey.set_shader_input('roughness', i/4.0) 65 | for i, monkey in enumerate(monkeys[5:10]): 66 | monkey.set_pos((i*3)-6, 5, 0) 67 | monkey.set_shader_input('roughness', i/4.0) 68 | monkey.set_shader_input('metallic', 0.5) 69 | for i, monkey in enumerate(monkeys[10:]): 70 | monkey.set_pos((i*3)-6, 10, 0) 71 | monkey.set_shader_input('roughness', i/4.0) 72 | monkey.set_shader_input('metallic', 0.0) 73 | #make skybox 74 | self.sky_box=self.loader.load_model('../../models//box') 75 | self.sky_box.reparent_to(self.render) 76 | self.sky_box.set_scale(10) 77 | self.sky_box.set_shader(p3d.Shader.load(GLSL, 'shaders/skybox_v.glsl', 'shaders/skybox_f.glsl'), 1) 78 | self.sky_box.set_shader_input('blur', 0.0) 79 | self.sky_box.set_bin('background', 100) 80 | self.sky_box.set_depth_test(False) 81 | self.sky_box.set_depth_write(False) 82 | #add a task to update the skybox 83 | self.taskMgr.add(self.update, 'update') 84 | 85 | #press space to change the environment texture 86 | self.accept('space', self.cycle_map) 87 | 88 | def cycle_map(self): 89 | '''Change the environment map''' 90 | self.curren_env_map+=1 91 | if self.curren_env_map >= len(self.env_maps): 92 | self.curren_env_map=0 93 | self.map_env=self.loader.load_texture(self.env_maps[self.curren_env_map]) 94 | if p3d.ConfigVariableBool('framebuffer-srgb').get_value(): 95 | self.map_env.set_format(F_SRGB) 96 | self.render.set_shader_input('env_map', self.map_env) 97 | 98 | def update(self, task): 99 | ''' Per frame update task''' 100 | self.sky_box.set_pos(self.cam.get_pos(render)) 101 | return task.cont 102 | 103 | app=App() 104 | app.run() 105 | -------------------------------------------------------------------------------- /advanced/environment_maps/shaders/ibl_f.glsl: -------------------------------------------------------------------------------- 1 | #version 130 2 | //NOTE: This shader is *NOT* PBR! 3 | uniform samplerCube env_map; 4 | uniform float roughness; 5 | uniform float metallic; 6 | uniform vec3 color; 7 | 8 | 9 | in vec3 V; 10 | in vec3 N; 11 | 12 | out vec4 final_color; 13 | 14 | void main() 15 | { 16 | vec3 n=normalize(N); 17 | vec3 v=normalize(V); 18 | //uv for the reflection 19 | vec3 r = normalize(reflect(v, n)); 20 | 21 | //f0 for fresnel, internet says 2-5%, oh well.. 22 | vec3 f0=vec3(0.01+roughness*0.07); 23 | //use a lower (blurred) LOD for higher roughness 24 | vec3 base_reflection=textureLod(env_map, r, (roughness)*10.0).rgb; 25 | 26 | //modulate reflection based on roughness??? 27 | vec3 reflection=base_reflection*(1.0-roughness); 28 | reflection*=mix(vec3(pow(1.0-roughness, 2.0)), color, metallic); 29 | 30 | vec3 fresnel = mix(f0, base_reflection, pow(1.0 - dot(n,-v), 5.0)); 31 | fresnel*=mix(vec3(1.0), color, metallic); 32 | //modulate fresnel based on roughness.. maybe? but not like this 33 | fresnel*=pow(((1.0-roughness)*0.4)+0.6, 2.0); 34 | vec3 light=textureLod(env_map, n, 8.8).rgb; 35 | //light=pow(light*1.22, vec3(2.0)); 36 | 37 | vec3 diffuse = mix( (color*light), vec3(0.0), metallic); 38 | 39 | final_color=vec4(diffuse+fresnel+reflection, 1.0); 40 | 41 | 42 | 43 | 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /advanced/environment_maps/shaders/ibl_v.glsl: -------------------------------------------------------------------------------- 1 | //GLSL 2 | #version 130 3 | in vec4 p3d_Vertex; 4 | in vec3 p3d_Normal; 5 | 6 | uniform mat4 p3d_ModelViewProjectionMatrix; 7 | uniform mat4 p3d_ModelMatrix; 8 | uniform mat4 p3d_ModelMatrixInverseTranspose; 9 | 10 | uniform vec3 wspos_camera; 11 | 12 | out vec3 V; 13 | out vec3 N; 14 | 15 | void main() 16 | { 17 | gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; 18 | 19 | N=(p3d_ModelMatrixInverseTranspose* vec4(p3d_Normal, 1.0)).xyz; 20 | V= vec3(p3d_ModelMatrix * p3d_Vertex - vec4(wspos_camera, 1.0)).xyz; 21 | } 22 | -------------------------------------------------------------------------------- /advanced/environment_maps/shaders/skybox_f.glsl: -------------------------------------------------------------------------------- 1 | #version 130 2 | uniform samplerCube env_map; 3 | uniform float blur; 4 | in vec3 V; 5 | 6 | out vec4 final_color; 7 | 8 | void main() 9 | { vec3 color =textureLod(env_map, normalize(V), blur).rgb; 10 | final_color=vec4(color, 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /advanced/environment_maps/shaders/skybox_v.glsl: -------------------------------------------------------------------------------- 1 | //GLSL 2 | #version 130 3 | in vec4 p3d_Vertex; 4 | 5 | uniform mat4 p3d_ModelViewProjectionMatrix; 6 | 7 | out vec3 V; 8 | 9 | void main() 10 | { 11 | gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; 12 | V=p3d_Vertex.xyz; 13 | } 14 | -------------------------------------------------------------------------------- /advanced/instancing_on_terrain/main.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This demo shows how to use instancing to place a lot of models on a height map terrain. 3 | 4 | We'll use a samplerBuffer (Buffer Texture) to pack the position(offset) of all the grass instances, 5 | if there's more data to pack (rotation, scale, color, etc) a floating point texture 6 | may be a better choice because the buffer may be limited to 65536 texels. 7 | We're rendering 'only' ~21.5k instances of the grass so there should be no problems. 8 | 9 | The grass shader has some additional features: 10 | - animation (for show) 11 | - discarding far away grass (for performance, but test show it has no impact on speed) 12 | ''' 13 | import panda3d.core as p3d 14 | p3d.load_prc_file_data('', '''framebuffer-srgb 1 15 | sync-video 0 16 | show-frame-rate-meter 1 17 | multisamples 1''') 18 | from direct.showbase.ShowBase import ShowBase 19 | 20 | from operator import itemgetter 21 | import struct 22 | import random 23 | 24 | FT_LINEAR = p3d.SamplerState.FT_linear 25 | FT_MIPMAP = p3d.SamplerState.FT_linear_mipmap_linear 26 | GLSL = p3d.Shader.SLGLSL 27 | F_SRGB = p3d.Texture.F_srgb 28 | F_SRGBA = p3d.Texture.F_srgb_alpha 29 | 30 | class App(ShowBase): 31 | def __init__(self): 32 | super().__init__(self) 33 | # MSAA just for making thinkgs look good 34 | self.render.set_antialias(p3d.AntialiasAttrib.M_multisample) 35 | 36 | # ShaderTerrainMesh used for terrain 37 | self.terrain_node = p3d.ShaderTerrainMesh() 38 | heightfield = self.loader.load_texture('../../models/texture/terrain/terrain_height.png') 39 | self.terrain_node.heightfield = heightfield 40 | # self.terrain_node.target_triangle_width = 10.0 41 | self.terrain_node.generate() 42 | self.terrain = self.render.attach_new_node(self.terrain_node) 43 | self.terrain.set_scale(512, 512, 100) 44 | self.terrain.set_pos(-256, -256, -30) 45 | self.terrain.set_shader(p3d.Shader.load(GLSL, 'shaders/terrain_v.glsl', 'shaders/terrain_f.glsl'), 1) 46 | # load terrain textures 47 | grass = self.loader.load_texture('../../models/texture/terrain/grass_c.png', 48 | minfilter = FT_MIPMAP, magfilter = FT_LINEAR) 49 | rock = self.loader.load_texture('../../models/texture/terrain/rock_c.png', 50 | minfilter = FT_MIPMAP, magfilter = FT_LINEAR) 51 | snow = self.loader.load_texture('../../models/texture/terrain/snow_c.png', 52 | minfilter = FT_MIPMAP, magfilter = FT_LINEAR) 53 | attribute = self.loader.load_texture('../../models/texture/terrain/terrain_atr.png') 54 | # make sure textures are sRGB 55 | if p3d.ConfigVariableBool('framebuffer-srgb').get_value(): 56 | grass.set_format(F_SRGB) 57 | rock.set_format(F_SRGB) 58 | snow.set_format(F_SRGB) 59 | attribute.set_format(F_SRGB) 60 | self.terrain.set_shader_input('grass_map', grass) 61 | self.terrain.set_shader_input('rock_map', rock) 62 | self.terrain.set_shader_input('snow_map', snow) 63 | self.terrain.set_shader_input('attribute_map', attribute) 64 | self.terrain.set_shader_input('camera', self.camera) 65 | 66 | # make the grass map more gpu friendly: 67 | grass_map = p3d.PNMImage() 68 | grass_map.read('../../models/texture/terrain/terrain_grass.png') 69 | grass_list=[] 70 | x_size = grass_map.get_read_x_size() 71 | y_size = grass_map.get_read_y_size() 72 | for x in range(x_size): 73 | for y in range(y_size): 74 | if grass_map.get_bright(x,y) > 0.5: 75 | # terrain_node.uv_to_world() will give as the position of the grass 76 | # but we need to flip the y because OpenGL UVs... 77 | # we also want to add a bit of random to avoid 'grass grid' 78 | uv_x = x/x_size 79 | uv_y = 1.0-y/y_size 80 | uv_x += random.uniform(-0.001, 0.001) 81 | uv_y += random.uniform(-0.001, 0.001) 82 | uv_x = max(0.0, min(1.0, uv_x)) 83 | uv_y = max(0.0, min(1.0, uv_y)) 84 | grass_list.append(self.terrain_node.uv_to_world(uv_x, uv_y) ) 85 | # pack the grass_list into a buffer_texture 86 | grass_buf_tex = p3d.Texture('texbuffer') 87 | grass_buf_tex.setup_buffer_texture(len(grass_list)+1, 88 | p3d.Texture.T_float, 89 | p3d.Texture.F_rgb32, 90 | p3d.GeomEnums.UH_static) 91 | image = memoryview(grass_buf_tex.modify_ram_image()) 92 | for i, pos in enumerate(grass_list): 93 | off = i * 12 94 | image[off:off+12] = struct.pack('fff', pos[0], pos[1], pos[2]) 95 | # load the grass model 96 | grass_model=self.loader.load_model('../../models/grass') 97 | # fix texture for srgb 98 | if p3d.ConfigVariableBool('framebuffer-srgb').get_value(): 99 | for tex_stage in grass_model.find_all_texture_stages(): 100 | tex = grass_model.find_texture(tex_stage) 101 | if tex: 102 | tex.set_format(F_SRGBA) 103 | grass_model.set_texture(tex_stage, tex, 1) 104 | grass_model.set_shader(p3d.Shader.load(GLSL, 'shaders/grass_v.glsl', 'shaders/grass_f.glsl'), 1) 105 | grass_model.set_shader_input('grass_buf_tex', grass_buf_tex) 106 | grass_model.set_shader_input('clip_distance', 125.0) 107 | grass_model.set_instance_count(len(grass_list)) 108 | grass_model.reparent_to(self.render) 109 | # alpha testing will be done in the shader 110 | grass_model.set_transparency(p3d.TransparencyAttrib.M_none, 1) 111 | # set the bounds so it stays visible 112 | grass_model.node().set_bounds( p3d.BoundingBox( (0,0,0), max( grass_list,key=itemgetter(1) ) ) ) 113 | grass_model.node().set_final(1) 114 | 115 | #make skybox 116 | self.sky_box=self.loader.load_model('../../models//box') 117 | self.sky_box.reparent_to(self.render) 118 | self.sky_box.set_scale(10) 119 | self.sky_box.set_shader(p3d.Shader.load(GLSL, 'shaders/skybox_v.glsl', 'shaders/skybox_f.glsl'), 1) 120 | self.sky_box.set_shader_input('blur', 0.0) 121 | self.sky_box.set_bin('background', 100) 122 | self.sky_box.set_depth_test(False) 123 | self.sky_box.set_depth_write(False) 124 | self.render.set_shader_input("camera", self.cam) 125 | self.render.set_shader_input('env_map', self.loader.load_texture('../../models/texture/cubemap/qwantani.txo')) 126 | #add a task to update the skybox 127 | self.taskMgr.add(self.update, 'update') 128 | 129 | def update(self, task): 130 | ''' Per frame update task''' 131 | self.sky_box.set_pos(self.cam.get_pos(render)) 132 | return task.cont 133 | 134 | app=App() 135 | app.run() 136 | -------------------------------------------------------------------------------- /advanced/instancing_on_terrain/shaders/grass_f.glsl: -------------------------------------------------------------------------------- 1 | #version 150 2 | in vec2 UV; 3 | in float DISTANCE_TO_CAMERA; 4 | 5 | out vec4 final_color; 6 | 7 | uniform sampler2D p3d_Texture0; 8 | uniform float clip_distance; 9 | 10 | void main() 11 | { 12 | //discard based on distance 13 | if (DISTANCE_TO_CAMERA > clip_distance) 14 | { 15 | discard; 16 | } 17 | 18 | vec4 color = texture(p3d_Texture0,UV); 19 | //discard based on alpha, because modern OGL has no alpha testing 20 | if (color.a < 0.5) 21 | { 22 | discard; 23 | } 24 | final_color=color; 25 | } 26 | -------------------------------------------------------------------------------- /advanced/instancing_on_terrain/shaders/grass_v.glsl: -------------------------------------------------------------------------------- 1 | #version 150 2 | in vec4 p3d_Vertex; 3 | in vec2 p3d_MultiTexCoord0; 4 | 5 | uniform samplerBuffer grass_buf_tex; 6 | uniform mat4 p3d_ModelViewProjectionMatrix; 7 | uniform mat4 p3d_ModelViewMatrix; 8 | uniform float osg_FrameTime; 9 | 10 | out vec2 UV; 11 | out float DISTANCE_TO_CAMERA; 12 | 13 | void main() 14 | { 15 | vec4 vertex=p3d_Vertex; 16 | //animation 17 | float anim_co=vertex.z*0.2; 18 | float animation =sin(0.7*osg_FrameTime+float(gl_InstanceID))*sin(1.7*osg_FrameTime+float(gl_InstanceID))*anim_co; 19 | vertex.xy += animation; 20 | //position offset read from the buffer texture, see python code 21 | vec3 pos_offset= texelFetch(grass_buf_tex, gl_InstanceID).xyz; 22 | vertex.xyz+=pos_offset; 23 | gl_Position = p3d_ModelViewProjectionMatrix *vertex; 24 | 25 | UV = p3d_MultiTexCoord0; 26 | DISTANCE_TO_CAMERA= -vec4(p3d_ModelViewMatrix* vertex).z; 27 | } 28 | -------------------------------------------------------------------------------- /advanced/instancing_on_terrain/shaders/skybox_f.glsl: -------------------------------------------------------------------------------- 1 | #version 130 2 | uniform samplerCube env_map; 3 | uniform float blur; 4 | in vec3 V; 5 | 6 | out vec4 final_color; 7 | 8 | void main() 9 | { vec3 color =textureLod(env_map, normalize(V), blur).rgb; 10 | final_color=vec4(color, 1.0); 11 | } 12 | -------------------------------------------------------------------------------- /advanced/instancing_on_terrain/shaders/skybox_v.glsl: -------------------------------------------------------------------------------- 1 | //GLSL 2 | #version 130 3 | in vec4 p3d_Vertex; 4 | 5 | uniform mat4 p3d_ModelViewProjectionMatrix; 6 | 7 | out vec3 V; 8 | 9 | void main() 10 | { 11 | gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; 12 | V=p3d_Vertex.xyz; 13 | } 14 | -------------------------------------------------------------------------------- /advanced/instancing_on_terrain/shaders/terrain_f.glsl: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | // This is the terrain fragment shader. There is a lot of code in here 4 | // which is not necessary to render the terrain, but included for convenience - 5 | // Like generating normals from the heightmap or a simple fog effect. 6 | 7 | // Most of the time you want to adjust this shader to get your terrain the look 8 | // you want. The vertex shader most likely will stay the same. 9 | 10 | in vec2 terrain_uv; 11 | in vec3 vtx_pos; 12 | out vec4 color; 13 | 14 | uniform struct { 15 | sampler2D data_texture; 16 | sampler2D heightfield; 17 | int view_index; 18 | int terrain_size; 19 | int chunk_size; 20 | } ShaderTerrainMesh; 21 | 22 | uniform sampler2D attribute_map; 23 | uniform sampler2D grass_map; 24 | uniform sampler2D rock_map; 25 | uniform sampler2D snow_map; 26 | 27 | uniform vec3 wspos_camera; 28 | 29 | // Compute normal from the heightmap, this assumes the terrain is facing z-up 30 | vec3 get_terrain_normal() { 31 | const float terrain_height = 50.0; 32 | vec3 pixel_size = vec3(1.0, -1.0, 0) / textureSize(ShaderTerrainMesh.heightfield, 0).xxx; 33 | float u0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.yz).x * terrain_height; 34 | float u1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.xz).x * terrain_height; 35 | float v0 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zy).x * terrain_height; 36 | float v1 = texture(ShaderTerrainMesh.heightfield, terrain_uv + pixel_size.zx).x * terrain_height; 37 | vec3 tangent = normalize(vec3(1.0, 0, u1 - u0)); 38 | vec3 binormal = normalize(vec3(0, 1.0, v1 - v0)); 39 | return normalize(cross(tangent, binormal)); 40 | } 41 | 42 | 43 | 44 | void main() { 45 | vec3 attr=texture(attribute_map, terrain_uv).rgb; 46 | vec3 grass=texture(grass_map, terrain_uv*40.0).rgb; 47 | vec3 rock=texture(rock_map, terrain_uv*40.0).rgb; 48 | vec3 snow=texture(snow_map, terrain_uv*40.0).rgb; 49 | vec3 diffuse = rock*attr.r + snow*attr.b + grass*attr.g; 50 | 51 | vec3 normal = get_terrain_normal(); 52 | 53 | // Add some fake lighting - you usually want to use your own lighting code here 54 | vec3 sun_vec = normalize(vec3(0.7, 0.2, 0.6)); 55 | vec3 light = max(0.0, dot(normal, sun_vec))*vec3(0.9, 0.9, 0.6) + vec3(0.1, 0.1, 0.2); 56 | //shading += ; 57 | 58 | 59 | // Fake fog 60 | //float dist = distance(vtx_pos, wspos_camera); 61 | //float fog_factor = smoothstep(0, 1, dist / 10000.0); 62 | //shading = mix(shading, vec3(0.7, 0.7, 0.8), fog_factor); 63 | 64 | color = vec4(diffuse*light, 1.0); 65 | 66 | } 67 | -------------------------------------------------------------------------------- /advanced/instancing_on_terrain/shaders/terrain_v.glsl: -------------------------------------------------------------------------------- 1 | #version 150 2 | 3 | // This is the default terrain vertex shader. Most of the time you can just copy 4 | // this and reuse it, and just modify the fragment shader. 5 | 6 | in vec4 p3d_Vertex; 7 | uniform mat4 p3d_ModelViewProjectionMatrix; 8 | uniform mat4 p3d_ModelMatrix; 9 | uniform vec3 wspos_camera; 10 | uniform struct { 11 | sampler2D data_texture; 12 | sampler2D heightfield; 13 | int view_index; 14 | int terrain_size; 15 | int chunk_size; 16 | } ShaderTerrainMesh; 17 | 18 | out vec2 terrain_uv; 19 | out vec3 vtx_pos; 20 | out vec3 view_vec; 21 | 22 | void main() { 23 | 24 | // Terrain data has the layout: 25 | // x: x-pos, y: y-pos, z: size, w: clod 26 | vec4 terrain_data = texelFetch(ShaderTerrainMesh.data_texture, 27 | ivec2(gl_InstanceID, ShaderTerrainMesh.view_index), 0); 28 | 29 | // Get initial chunk position in the (0, 0, 0), (1, 1, 0) range 30 | vec3 chunk_position = p3d_Vertex.xyz; 31 | 32 | // CLOD implementation 33 | float clod_factor = smoothstep(0, 1, terrain_data.w); 34 | chunk_position.xy -= clod_factor * fract(chunk_position.xy * ShaderTerrainMesh.chunk_size / 2.0) 35 | * 2.0 / ShaderTerrainMesh.chunk_size; 36 | 37 | // Scale the chunk 38 | chunk_position *= terrain_data.z * float(ShaderTerrainMesh.chunk_size) 39 | / float(ShaderTerrainMesh.terrain_size); 40 | chunk_position.z *= ShaderTerrainMesh.chunk_size; 41 | 42 | // Offset the chunk, it is important that this happens after the scale 43 | chunk_position.xy += terrain_data.xy / float(ShaderTerrainMesh.terrain_size); 44 | 45 | // Compute the terrain UV coordinates 46 | terrain_uv = chunk_position.xy; 47 | 48 | // Sample the heightfield and offset the terrain - we do not need to multiply 49 | // the height with anything since the terrain transform is included in the 50 | // model view projection matrix. 51 | chunk_position.z += texture(ShaderTerrainMesh.heightfield, terrain_uv).x; 52 | gl_Position = p3d_ModelViewProjectionMatrix * vec4(chunk_position, 1); 53 | 54 | // Output the vertex world space position - in this case we use this to render 55 | // the fog. 56 | vtx_pos = (p3d_ModelMatrix * vec4(chunk_position, 1)).xyz; 57 | 58 | view_vec= vec3(p3d_ModelMatrix * vec4(chunk_position, 1) - vec4(wspos_camera, 1.0)).xyz; 59 | } 60 | -------------------------------------------------------------------------------- /advanced/npr_shading/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /advanced/pbr_materials/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /advanced/post_process/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /advanced/readme.md: -------------------------------------------------------------------------------- 1 | The samples in this directory are for (somewhat) advanced users. 2 | Both Python and GLSL knowledge is probably needed. 3 | 4 | There will be fewer explanations in comments about setting up common p3d stuff, 5 | the code here will focus on specific problems and working solutions for them. 6 | -------------------------------------------------------------------------------- /advanced/terrain/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /models/a_p3d_chan_idle.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/a_p3d_chan_idle.egg.pz -------------------------------------------------------------------------------- /models/a_p3d_chan_run.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/a_p3d_chan_run.egg.pz -------------------------------------------------------------------------------- /models/a_p3d_chan_step_l.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/a_p3d_chan_step_l.egg.pz -------------------------------------------------------------------------------- /models/a_p3d_chan_step_r.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/a_p3d_chan_step_r.egg.pz -------------------------------------------------------------------------------- /models/a_p3d_chan_walk.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/a_p3d_chan_walk.egg.pz -------------------------------------------------------------------------------- /models/act_p3d_chan.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/act_p3d_chan.egg.pz -------------------------------------------------------------------------------- /models/box.egg: -------------------------------------------------------------------------------- 1 | { Z-Up } 2 | unit_box { 3 | unit_box.verts { 4 | 0 { 5 | -1 -1 -1 6 | { 1 0 } 7 | { 0 0 1 } 8 | } 9 | 1 { 10 | 1 -1 -1 11 | { 0 0 } 12 | { 0 0 1 } 13 | } 14 | 2 { 15 | 1 1 -1 16 | { 0 1 } 17 | { 0 0 1 } 18 | } 19 | 3 { 20 | -1 1 -1 21 | { 1 1 } 22 | { 0 0 1 } 23 | } 24 | 4 { 25 | -1 -1 1 26 | { 0 0 } 27 | { 0 0 -1 } 28 | } 29 | 5 { 30 | -1 1 1 31 | { 0 1 } 32 | { 0 0 -1 } 33 | } 34 | 6 { 35 | 1 1 1 36 | { 1 1 } 37 | { 0 0 -1 } 38 | } 39 | 7 { 40 | 1 -1 1 41 | { 1 0 } 42 | { 0 0 -1 } 43 | } 44 | 8 { 45 | -1 -1 -1 46 | { 0 0 } 47 | { 0 1 0 } 48 | } 49 | 9 { 50 | -1 -1 1 51 | { 0 1 } 52 | { 0 1 0 } 53 | } 54 | 10 { 55 | 1 -1 1 56 | { 1 1 } 57 | { 0 1 0 } 58 | } 59 | 11 { 60 | 1 -1 -1 61 | { 1 0 } 62 | { 0 1 0 } 63 | } 64 | 12 { 65 | 1 -1 -1 66 | { 0 0 } 67 | { -1 0 0 } 68 | } 69 | 13 { 70 | 1 -1 1 71 | { 0 1 } 72 | { -1 0 0 } 73 | } 74 | 14 { 75 | 1 1 1 76 | { 1 1 } 77 | { -1 0 0 } 78 | } 79 | 15 { 80 | 1 1 -1 81 | { 1 0 } 82 | { -1 0 0 } 83 | } 84 | 16 { 85 | 1 1 -1 86 | { 0 0 } 87 | { 0 -1 0 } 88 | } 89 | 17 { 90 | 1 1 1 91 | { 0 1 } 92 | { 0 -1 0 } 93 | } 94 | 18 { 95 | -1 1 1 96 | { 1 1 } 97 | { 0 -1 0 } 98 | } 99 | 19 { 100 | -1 1 -1 101 | { 1 0 } 102 | { 0 -1 0 } 103 | } 104 | 20 { 105 | -1 1 -1 106 | { 0 0 } 107 | { 1 0 0 } 108 | } 109 | 21 { 110 | -1 1 1 111 | { 0 1 } 112 | { 1 0 0 } 113 | } 114 | 22 { 115 | -1 -1 1 116 | { 1 1 } 117 | { 1 0 0 } 118 | } 119 | 23 { 120 | -1 -1 -1 121 | { 1 0 } 122 | { 1 0 0 } 123 | } 124 | } 125 | { 126 | { 0 1 2 { unit_box.verts } } 127 | } 128 | { 129 | { 2 3 0 { unit_box.verts } } 130 | } 131 | { 132 | { 4 5 6 { unit_box.verts } } 133 | } 134 | { 135 | { 6 7 4 { unit_box.verts } } 136 | } 137 | { 138 | { 8 9 10 { unit_box.verts } } 139 | } 140 | { 141 | { 10 11 8 { unit_box.verts } } 142 | } 143 | { 144 | { 12 13 14 { unit_box.verts } } 145 | } 146 | { 147 | { 14 15 12 { unit_box.verts } } 148 | } 149 | { 150 | { 16 17 18 { unit_box.verts } } 151 | } 152 | { 153 | { 18 19 16 { unit_box.verts } } 154 | } 155 | { 156 | { 20 21 22 { unit_box.verts } } 157 | } 158 | { 159 | { 22 23 20 { unit_box.verts } } 160 | } 161 | } 162 | 163 | -------------------------------------------------------------------------------- /models/crate.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/crate.egg.pz -------------------------------------------------------------------------------- /models/grass.egg: -------------------------------------------------------------------------------- 1 | { Z-up } 2 | Tex1 { 3 | "texture/grass.png" 4 | wrap { clamp } 5 | minfilter { linear_mipmap_linear } 6 | magfilter { linear_mipmap_linear } 7 | envtype { modulate } 8 | alpha { binary } 9 | } 10 | 11 | Plane { 12 | Plane { 13 | 14 | 0 {-0.600000 0.000000 0.000000 15 | { 0.000000 -1.000000 0.000000 } 16 | { 17 | 0.217027 0.000000 18 | } 19 | } 20 | 1 {0.600000 0.000000 0.000000 21 | { 0.000000 -1.000000 0.000000 } 22 | { 23 | 0.742667 0.000000 24 | } 25 | } 26 | 2 {0.600000 0.000000 2.000000 27 | { 0.000000 -1.000000 0.000000 } 28 | { 29 | 0.742667 0.835679 30 | } 31 | } 32 | 3 {-0.600000 0.000000 2.000000 33 | { 0.000000 -1.000000 0.000000 } 34 | { 35 | 0.217027 0.835679 36 | } 37 | } 38 | 4 {0.600000 0.000000 -0.000000 39 | { 0.000000 1.000000 -0.000000 } 40 | { 41 | 0.742667 0.000000 42 | } 43 | } 44 | 5 {-0.600000 -0.000000 0.000000 45 | { 0.000000 1.000000 -0.000000 } 46 | { 47 | 0.217027 0.000000 48 | } 49 | } 50 | 6 {-0.600000 0.000000 2.000000 51 | { 0.000000 1.000000 -0.000000 } 52 | { 53 | 0.217027 0.835679 54 | } 55 | } 56 | 7 {0.600000 0.000000 2.000000 57 | { 0.000000 1.000000 -0.000000 } 58 | { 59 | 0.742667 0.835679 60 | } 61 | } 62 | 8 {0.000000 -0.600000 0.000000 63 | { 1.000000 0.000000 0.000000 } 64 | { 65 | 0.217027 0.000000 66 | } 67 | } 68 | 9 {0.000000 0.600000 -0.000000 69 | { 1.000000 0.000000 0.000000 } 70 | { 71 | 0.742667 0.000000 72 | } 73 | } 74 | 10 {0.000000 0.600000 2.000000 75 | { 1.000000 0.000000 0.000000 } 76 | { 77 | 0.742667 0.835679 78 | } 79 | } 80 | 11 {0.000000 -0.600000 2.000000 81 | { 1.000000 0.000000 0.000000 } 82 | { 83 | 0.217027 0.835679 84 | } 85 | } 86 | 12 {-0.000000 0.600000 -0.000000 87 | { -1.000000 0.000000 0.000000 } 88 | { 89 | 0.742667 0.000000 90 | } 91 | } 92 | 13 {0.000000 -0.600000 0.000000 93 | { -1.000000 0.000000 0.000000 } 94 | { 95 | 0.217027 0.000000 96 | } 97 | } 98 | 14 {0.000000 -0.600000 2.000000 99 | { -1.000000 0.000000 0.000000 } 100 | { 101 | 0.217027 0.835679 102 | } 103 | } 104 | 15 {-0.000000 0.600000 2.000000 105 | { -1.000000 0.000000 0.000000 } 106 | { 107 | 0.742667 0.835679 108 | } 109 | }} 110 | 111 | 112 | { 113 | { Tex1 } 114 | {0.000000 -1.000000 0.000000} 115 | { 0 1 2 3 { Plane }} 116 | } 117 | { 118 | { Tex1 } 119 | {-0.000000 1.000000 -0.000000} 120 | { 4 5 6 7 { Plane }} 121 | } 122 | { 123 | { Tex1 } 124 | {1.000000 0.000000 0.000000} 125 | { 8 9 10 11 { Plane }} 126 | } 127 | { 128 | { Tex1 } 129 | {-1.000000 -0.000000 0.000000} 130 | { 12 13 14 15 { Plane }} 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /models/room_industrial.egg.pz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/room_industrial.egg.pz -------------------------------------------------------------------------------- /models/texture/crate_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/crate_1.png -------------------------------------------------------------------------------- /models/texture/crate_1_ng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/crate_1_ng.png -------------------------------------------------------------------------------- /models/texture/crate_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/crate_2.png -------------------------------------------------------------------------------- /models/texture/crate_2_ng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/crate_2_ng.png -------------------------------------------------------------------------------- /models/texture/cubemap/georgentor.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/georgentor.txo -------------------------------------------------------------------------------- /models/texture/cubemap/green_point_park.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/green_point_park.txo -------------------------------------------------------------------------------- /models/texture/cubemap/indoor_pool.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/indoor_pool.txo -------------------------------------------------------------------------------- /models/texture/cubemap/machine_shop.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/machine_shop.txo -------------------------------------------------------------------------------- /models/texture/cubemap/mutianyu.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/mutianyu.txo -------------------------------------------------------------------------------- /models/texture/cubemap/qwantani.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/qwantani.txo -------------------------------------------------------------------------------- /models/texture/cubemap/studio_small.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/studio_small.txo -------------------------------------------------------------------------------- /models/texture/cubemap/wooden_lounge.txo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/cubemap/wooden_lounge.txo -------------------------------------------------------------------------------- /models/texture/grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/grass.png -------------------------------------------------------------------------------- /models/texture/industrial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/industrial.png -------------------------------------------------------------------------------- /models/texture/industrial_ng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/industrial_ng.png -------------------------------------------------------------------------------- /models/texture/panda_chan_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/panda_chan_c.png -------------------------------------------------------------------------------- /models/texture/panda_chan_material.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/panda_chan_material.png -------------------------------------------------------------------------------- /models/texture/panda_chan_ng.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/panda_chan_ng.png -------------------------------------------------------------------------------- /models/texture/terrain/grass_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/grass_c.png -------------------------------------------------------------------------------- /models/texture/terrain/grass_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/grass_n.png -------------------------------------------------------------------------------- /models/texture/terrain/rock_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/rock_c.png -------------------------------------------------------------------------------- /models/texture/terrain/rock_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/rock_n.png -------------------------------------------------------------------------------- /models/texture/terrain/snow_c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/snow_c.png -------------------------------------------------------------------------------- /models/texture/terrain/snow_n.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/snow_n.png -------------------------------------------------------------------------------- /models/texture/terrain/terrain_atr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/terrain_atr.png -------------------------------------------------------------------------------- /models/texture/terrain/terrain_grass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/terrain_grass.png -------------------------------------------------------------------------------- /models/texture/terrain/terrain_height.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/models/texture/terrain/terrain_height.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | This is an attempt to create new and (hopefully better) samples for the Panda3D game engine. 2 | 3 | Contributions are welcome. 4 | -------------------------------------------------------------------------------- /tutorial_01/base_app.py: -------------------------------------------------------------------------------- 1 | ''' 2 | This is a variation of the code from 01_hello_world.py 3 | It it meant to be expanded in future tutorial/samples. 4 | This version lacks most comments, see 01_hello_world.py 5 | 6 | New things that are not in 01_hello_world.py: 7 | -changing the title of the window at runtime 8 | -adding dirs to the model path 9 | -changing texture format for sRGB 10 | ''' 11 | 12 | import panda3d.core as p3d 13 | from direct.showbase.ShowBase import ShowBase 14 | 15 | FT_MIPMAP = p3d.SamplerState.FT_linear_mipmap_linear 16 | FT_LINEAR = p3d.SamplerState.FT_linear 17 | TS_NORMAL = p3d.TextureStage.M_normal 18 | TS_NORMAL_GLOSS = p3d.TextureStage.M_normal_gloss 19 | TS_MODULATE = p3d.TextureStage.M_modulate 20 | F_SRGB = p3d.Texture.F_srgb 21 | F_SRGBA = p3d.Texture.F_srgb_alpha 22 | F_RGB = p3d.Texture.F_rgb 23 | F_RGBA = p3d.Texture.F_rgba 24 | M_MSAA= p3d.AntialiasAttrib.M_multisample 25 | 26 | 27 | class BaseApp(ShowBase): 28 | def __init__(self): 29 | super().__init__() 30 | # we are running from a sub directory 31 | # but we still want to load the models/textures without the extra '../' 32 | # so we put the up directory on the model path 33 | p3d.get_model_path().append_directory('..') 34 | 35 | self.disable_mouse() 36 | self.set_background_color(0.0, 0.0, 0.0) 37 | self.render.set_shader_auto() 38 | self.render.set_antialias(M_MSAA) 39 | 40 | def set_window_title(self, title): 41 | '''Change the title of the window''' 42 | win_props = p3d.WindowProperties() 43 | win_props.set_title(title) 44 | self.win.request_properties(win_props) 45 | 46 | def setup_lights(self): 47 | '''Adds lights to the scene ''' 48 | # ambient 49 | self.ambient_light = self.render.attach_new_node(p3d.AmbientLight('ambient')) 50 | self.ambient_light.node().set_color((0.1, 0.1, 0.1, 1.0)) 51 | self.render.set_light(self.ambient_light) 52 | # directional 53 | self.dir_light = self.render.attach_new_node(p3d.DirectionalLight('directional')) 54 | self.dir_light.node().set_color((0.1, 0.1, 0.25, 1.0)) 55 | self.dir_light.node().set_direction(p3d.Vec3(0.2,0.4,-1.0)) 56 | self.render.set_light(self.dir_light) 57 | # spot 58 | self.spot_light = self.render.attach_new_node(p3d.Spotlight('spot')) 59 | self.spot_light.node().set_color((1.0, 1.0, 1.0, 1.0)) 60 | self.spot_light.node().set_shadow_caster(True, 1024, 1024) 61 | self.spot_light.node().get_lens().set_near_far(0.1, 20.0) 62 | self.spot_light.node().get_lens().set_fov(25) 63 | self.spot_light.node().set_exponent(120.0) 64 | self.spot_light.set_pos(-8, 0, 8) 65 | self.spot_light.look_at(p3d.Point3(3,-3,0)) 66 | self.render.set_light(self.spot_light) 67 | 68 | 69 | def setup_scene(self): 70 | '''Creates a industrial style room with 5 boxes ''' 71 | self.room = self.loader.load_model('models/room_industrial') 72 | self.room.reparent_to(self.render) 73 | 74 | crate_model = self.loader.load_model('models/crate') 75 | crate_model.set_scale(1.033) 76 | crate_model.flatten_light() 77 | 78 | crate_transforms=[ 79 | {}, 80 | {'pos':(0,0,1), 'hpr':(10,0,0)}, 81 | {'pos':(1,-0.3,0)}, 82 | {'pos':(3,-3,0), 'scale':(1.6), 'color':(0.8, 0.696, 0.496, 1.0), 83 | 'tex':'models/texture/crate_2.png','tex_n':'models/texture/crate_2_ng.png'}, 84 | {'pos':(3.1,-1.35,0), 'scale':(1.3), 'color':(0.66, 0.55, 0.46, 1.0), 85 | 'tex':'models/texture/crate_2.png','tex_n':'models/texture/crate_2_ng.png'} 86 | ] 87 | self.crates = [crate_model.copy_to(self.render) for _ in range(5)] 88 | for crate, transform in zip(self.crates, crate_transforms): 89 | if 'pos' in transform: 90 | crate.set_pos(transform['pos']) 91 | if 'hpr' in transform: 92 | crate.set_hpr(transform['hpr']) 93 | if 'scale' in transform: 94 | crate.set_scale(transform['scale']) 95 | if 'color' in transform: 96 | crate.set_color(transform['color'], 1) 97 | if 'tex' in transform: 98 | self.replace_texture(crate, transform['tex']) 99 | if 'tex_n' in transform: 100 | self.replace_texture(crate, transform['tex_n'], stage=TS_NORMAL_GLOSS) 101 | 102 | def replace_texture(self, model, texture, stage=TS_MODULATE, minfilter = FT_MIPMAP, magfilter = FT_LINEAR): 103 | '''Replace the texture on a model ''' 104 | new_tex=self.loader.load_texture(texture) 105 | new_tex.set_minfilter(FT_MIPMAP) 106 | new_tex.set_magfilter(FT_LINEAR) 107 | 108 | if p3d.ConfigVariableBool('framebuffer-srgb').get_value() and stage==TS_MODULATE: 109 | tex_format=new_tex.get_format() 110 | if tex_format == F_RGB: 111 | new_tex.set_format(F_SRGB) 112 | elif tex_format == F_RGBA: 113 | new_tex.set_format(F_SRGBA) 114 | 115 | for tex_stage in model.find_all_texture_stages(): 116 | if model.find_texture(tex_stage): 117 | if tex_stage.get_mode() == stage: 118 | model.set_texture(tex_stage, new_tex, 1) 119 | 120 | 121 | # run all the code 122 | if __name__ == "__main__": 123 | p3d.load_prc_file_data('', '''win-size 1280 720') 124 | multisamples 1 125 | show-frame-rate-meter 1 126 | sync-video 0''') 127 | 128 | app=BaseApp() 129 | app.set_window_title('Panda3D - Hello World') 130 | app.setup_scene() 131 | app.setup_lights() 132 | 133 | app.camera.set_pos(-7.0, -4.5, 4.5) 134 | app.camera.look_at(app.crates[4]) 135 | app.cam_lens=app.cam.node().get_lens() 136 | fov=app.cam_lens.get_fov() 137 | app.cam_lens.set_fov(fov*1.25) 138 | 139 | app.run() 140 | -------------------------------------------------------------------------------- /tutorial_02/keys.ini: -------------------------------------------------------------------------------- 1 | [camera_keys] 2 | forward = w 3 | back = s 4 | left = a 5 | right = d 6 | up = q 7 | down = z 8 | relative_move = shift-mouse1 9 | move = mouse1 10 | rotate = mouse3 11 | 12 | [camera_zoom] 13 | zoom_in = wheel_up 14 | zoom_out = wheel_down 15 | -------------------------------------------------------------------------------- /tutorial_03/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /tutorial_04/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /tutorial_05/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /tutorial_06/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /tutorial_07/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /tutorial_08/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/blue_noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/utilities/hdri_to_cubemap/blue_noise.png -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/box.egg: -------------------------------------------------------------------------------- 1 | { Z-Up } 2 | unit_box { 3 | unit_box.verts { 4 | 0 { 5 | -1 -1 -1 6 | { 1 0 } 7 | { 0 0 1 } 8 | } 9 | 1 { 10 | 1 -1 -1 11 | { 0 0 } 12 | { 0 0 1 } 13 | } 14 | 2 { 15 | 1 1 -1 16 | { 0 1 } 17 | { 0 0 1 } 18 | } 19 | 3 { 20 | -1 1 -1 21 | { 1 1 } 22 | { 0 0 1 } 23 | } 24 | 4 { 25 | -1 -1 1 26 | { 0 0 } 27 | { 0 0 -1 } 28 | } 29 | 5 { 30 | -1 1 1 31 | { 0 1 } 32 | { 0 0 -1 } 33 | } 34 | 6 { 35 | 1 1 1 36 | { 1 1 } 37 | { 0 0 -1 } 38 | } 39 | 7 { 40 | 1 -1 1 41 | { 1 0 } 42 | { 0 0 -1 } 43 | } 44 | 8 { 45 | -1 -1 -1 46 | { 0 0 } 47 | { 0 1 0 } 48 | } 49 | 9 { 50 | -1 -1 1 51 | { 0 1 } 52 | { 0 1 0 } 53 | } 54 | 10 { 55 | 1 -1 1 56 | { 1 1 } 57 | { 0 1 0 } 58 | } 59 | 11 { 60 | 1 -1 -1 61 | { 1 0 } 62 | { 0 1 0 } 63 | } 64 | 12 { 65 | 1 -1 -1 66 | { 0 0 } 67 | { -1 0 0 } 68 | } 69 | 13 { 70 | 1 -1 1 71 | { 0 1 } 72 | { -1 0 0 } 73 | } 74 | 14 { 75 | 1 1 1 76 | { 1 1 } 77 | { -1 0 0 } 78 | } 79 | 15 { 80 | 1 1 -1 81 | { 1 0 } 82 | { -1 0 0 } 83 | } 84 | 16 { 85 | 1 1 -1 86 | { 0 0 } 87 | { 0 -1 0 } 88 | } 89 | 17 { 90 | 1 1 1 91 | { 0 1 } 92 | { 0 -1 0 } 93 | } 94 | 18 { 95 | -1 1 1 96 | { 1 1 } 97 | { 0 -1 0 } 98 | } 99 | 19 { 100 | -1 1 -1 101 | { 1 0 } 102 | { 0 -1 0 } 103 | } 104 | 20 { 105 | -1 1 -1 106 | { 0 0 } 107 | { 1 0 0 } 108 | } 109 | 21 { 110 | -1 1 1 111 | { 0 1 } 112 | { 1 0 0 } 113 | } 114 | 22 { 115 | -1 -1 1 116 | { 1 1 } 117 | { 1 0 0 } 118 | } 119 | 23 { 120 | -1 -1 -1 121 | { 1 0 } 122 | { 1 0 0 } 123 | } 124 | } 125 | { 126 | { 0 1 2 { unit_box.verts } } 127 | } 128 | { 129 | { 2 3 0 { unit_box.verts } } 130 | } 131 | { 132 | { 4 5 6 { unit_box.verts } } 133 | } 134 | { 135 | { 6 7 4 { unit_box.verts } } 136 | } 137 | { 138 | { 8 9 10 { unit_box.verts } } 139 | } 140 | { 141 | { 10 11 8 { unit_box.verts } } 142 | } 143 | { 144 | { 12 13 14 { unit_box.verts } } 145 | } 146 | { 147 | { 14 15 12 { unit_box.verts } } 148 | } 149 | { 150 | { 16 17 18 { unit_box.verts } } 151 | } 152 | { 153 | { 18 19 16 { unit_box.verts } } 154 | } 155 | { 156 | { 20 21 22 { unit_box.verts } } 157 | } 158 | { 159 | { 22 23 20 { unit_box.verts } } 160 | } 161 | } 162 | 163 | -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/cubemap/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/hdri/paul_lobe_haus_4k.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wezu/p3d_samples/2e8b56881bbc95fa4e2f09c5d40b7b6e3cffa04b/utilities/hdri_to_cubemap/hdri/paul_lobe_haus_4k.png -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/hdri_to_cubemap.py: -------------------------------------------------------------------------------- 1 | import os 2 | from panda3d.core import * 3 | loadPrcFileData("", "framebuffer-multisample 1") 4 | loadPrcFileData("", "multisamples 8") 5 | from direct.showbase import ShowBase 6 | 7 | base=ShowBase.ShowBase() 8 | render.setAntialias(AntialiasAttrib.MMultisample) 9 | 10 | sky_box=loader.load_model('box') 11 | sky_box.reparent_to(render) 12 | sky_box.set_scale(10) 13 | sky_box.set_shader(Shader.load(Shader.SLGLSL, 'shaders/skybox_v.glsl', 'shaders/skybox_f.glsl'), 1) 14 | sky_box.set_shader_input('noise_tex', loader.load_texture('blue_noise.png', minfilter = SamplerState.FT_nearest, magfilter = SamplerState.FT_nearest)) 15 | sky_box.set_bin('background', 100) 16 | sky_box.set_depth_test(False) 17 | sky_box.set_depth_write(False) 18 | 19 | for filename in os.listdir('hdri'): 20 | source = filename[:-4] 21 | print(source) 22 | sky_map=loader.load_texture('hdri/'+source+'.png') 23 | sky_map.set_magfilter(SamplerState.FT_linear_mipmap_linear) 24 | sky_map.set_minfilter(SamplerState.FT_linear_mipmap_linear) 25 | sky_box.set_shader_input('sky_map', sky_map) 26 | sky_box.set_shader_input('sky_lod', 0.0) 27 | sky_box.set_shader_input('value', 0.0) 28 | 29 | lod=0 30 | resolution = 1024 31 | while resolution >= 1: 32 | base.saveCubeMap('temp/'+source+'_'+str(lod)+'_#.png', size = resolution) 33 | resolution=resolution//2 34 | lod+=1 35 | sky_box.set_shader_input('sky_lod', float(lod*1.5)) 36 | sky_box.set_shader_input('value', lod*0.04) 37 | base.graphicsEngine.renderFrame() 38 | base.graphicsEngine.renderFrame() 39 | base.graphicsEngine.renderFrame() 40 | 41 | cubemap=loader.loadCubeMap('temp/'+source+'_#_#.png', readMipmaps = True, minfilter = SamplerState.FT_linear_mipmap_linear, magfilter = SamplerState.FT_linear_mipmap_linear) 42 | cubemap.set_compression(Texture.CM_dxt1) 43 | cubemap.write('cubemap/'+source+'.txo') 44 | 45 | #base.run() 46 | -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/shaders/skybox_f.glsl: -------------------------------------------------------------------------------- 1 | #version 130 2 | uniform sampler2D sky_map; 3 | uniform sampler2D noise_tex; 4 | uniform float sky_lod; 5 | uniform float value; 6 | in vec3 V; 7 | 8 | out vec4 final_color; 9 | 10 | 11 | 12 | const vec2 invAtan = vec2(0.1591, 0.3183); 13 | vec2 SampleSphericalMap(vec3 v) 14 | { 15 | vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); 16 | uv *= invAtan; 17 | uv += 0.5; 18 | return uv; 19 | } 20 | 21 | vec3 rgb2hsv(vec3 c) 22 | { 23 | vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); 24 | vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g)); 25 | vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r)); 26 | 27 | float d = q.x - min(q.w, q.y); 28 | float e = 1.0e-10; 29 | return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); 30 | } 31 | 32 | vec3 hsv2rgb(vec3 c) 33 | { 34 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 35 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 36 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 37 | } 38 | 39 | void main() 40 | { 41 | vec2 noise_size=textureSize(noise_tex, 0).xy/(sky_lod+1.0); 42 | vec2 noise_uv=(gl_FragCoord.xy/noise_size); 43 | vec3 noise=texture(noise_tex, noise_uv).rgb; 44 | 45 | vec2 uv1 = SampleSphericalMap(normalize(V+(noise*0.004*sky_lod))); 46 | vec2 uv2 = SampleSphericalMap(normalize(V+(noise*0.009*sky_lod))); 47 | 48 | vec3 color = mix(textureLod(sky_map, uv1, sky_lod).rgb, textureLod(sky_map, uv2, sky_lod*0.7).rgb, 0.5); 49 | 50 | vec3 hsv=rgb2hsv(color); 51 | hsv.z*=1.0+value; 52 | 53 | final_color=vec4(hsv2rgb(hsv), 1.0); 54 | 55 | } 56 | -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/shaders/skybox_v.glsl: -------------------------------------------------------------------------------- 1 | //GLSL 2 | #version 130 3 | in vec4 p3d_Vertex; 4 | 5 | uniform mat4 p3d_ModelViewProjectionMatrix; 6 | 7 | out vec3 V; 8 | 9 | void main() 10 | { 11 | gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; 12 | V=p3d_Vertex.xzy; 13 | } 14 | -------------------------------------------------------------------------------- /utilities/hdri_to_cubemap/temp/dummy.txt: -------------------------------------------------------------------------------- 1 | This file exist only to have empty directories in the repo. Please ignore. 2 | --------------------------------------------------------------------------------