├── 3d-sound ├── 3d-sound.py └── knocking.ogg ├── FSM └── fsm.py ├── GUI └── GUI.py ├── LICENSE ├── README.md ├── __init__.py ├── basics └── camera_turning_around_sphere.py ├── boilerplate ├── __init__.py ├── base.py ├── shader.py └── shader │ ├── shader.frag │ └── shader.vert ├── bones ├── bones_model.py ├── main_animation.py ├── main_control_joints.py ├── main_inverse_kinematics.py ├── main_mocap.py ├── main_physics.py └── mocap.py ├── buffer_protocol ├── gizeh_to_panda3d.py ├── matplotlib_to_panda3d.py ├── pygame_to_panda3d.py └── terse_description.py ├── bullet_physics └── bullet_physics.py ├── collision └── collision.py ├── cubemap └── create_cubemap.py ├── extrawindows └── extrawindows.py ├── filter ├── app.py ├── fragment.glsl └── vertex.glsl ├── geometry ├── basic_model.py └── main.py ├── headless.py ├── libRocket ├── demo.rml ├── gnu-freefont_freesans │ ├── AUTHORS │ ├── COPYING │ ├── CREDITS │ ├── ChangeLog │ ├── FreeSans.otf │ ├── FreeSans.ttf │ ├── FreeSansBold.otf │ ├── FreeSansBold.ttf │ ├── FreeSansBoldOblique.otf │ ├── FreeSansBoldOblique.ttf │ ├── FreeSansOblique.otf │ ├── FreeSansOblique.ttf │ ├── INSTALL │ └── README ├── libRocket.py └── main.rcss ├── lod ├── lod.py ├── tree0.blend ├── tree0.egg ├── tree1.blend ├── tree1.egg ├── tree2.blend └── tree2.egg ├── offscreen_filming └── offscreen_filming.py ├── pbr ├── main_complexpbr.py ├── main_no_pbr.py ├── main_simplepbr.py └── pbr_model.py ├── quaternions ├── toy_problem.py └── toy_problem_quat.py ├── render_to_texture ├── plane.blend ├── plane.egg ├── rtt.py ├── test.png ├── twisted.egg └── twisted.png ├── reprojection └── reproject.py ├── selfshadow ├── selfshadow.py ├── tex.png ├── twisted.blend └── twisted.egg ├── shaders ├── all_stages │ ├── shader.data │ ├── shader.frag │ ├── shader.geom │ ├── shader.py │ ├── shader.tesc │ ├── shader.tese │ └── shader.vert ├── compute │ └── main_basic_compute.py └── minimal │ └── shader.py ├── shape_keys ├── box.blend ├── box.egg └── shape_key.py └── skybox ├── display_skybox.py ├── skybox.png └── skybox_1024.egg /3d-sound/3d-sound.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from direct.task import Task 5 | from math import sin, cos, pi 6 | import sys 7 | 8 | from direct.showbase import Audio3DManager 9 | 10 | class MyApp(ShowBase): 11 | def __init__(self): 12 | # The basics 13 | ShowBase.__init__(self) 14 | base.disableMouse() 15 | # Camera / Audio listener node 16 | self.cam_node = self.render.attachNewNode("Camera node") 17 | self.camera.reparentTo(self.cam_node) 18 | self.audio3d = Audio3DManager.Audio3DManager(base.sfxManagerList[0], self.camera) 19 | self.cam_node.set_pos(0, -10, 0) 20 | # Add the model 21 | self.m = self.loader.loadModel("models/smiley") 22 | self.m.reparentTo(self.render) 23 | self.m.setPos(0, 0, 0) 24 | s = self.audio3d.loadSfx('knocking.ogg') 25 | s.setLoop(True) 26 | s.play() 27 | self.audio3d.attachSoundToObject(s, self.m) 28 | # Bookkeeping for the rotation around the model 29 | self.angle = 0.0 30 | self.adjust_angle = 0 31 | # Initial camera setup 32 | self.camera.look_at(self.m) 33 | # Key events and camera movement task 34 | self.accept("arrow_left", self.adjust_turning, [-1.0]) 35 | self.accept("arrow_left-up", self.adjust_turning, [1.0]) 36 | self.accept("arrow_right", self.adjust_turning, [1.0]) 37 | self.accept("arrow_right-up", self.adjust_turning, [-1.0]) 38 | self.accept("escape", sys.exit) 39 | self.taskMgr.add(self.update_camera, 'adjust camera', sort = 10) 40 | def adjust_turning(self, heading): 41 | self.adjust_angle += heading * -50.0 42 | def update_camera(self, task): 43 | if globalClock.getDt() != 0.0: 44 | self.camera.set_h(self.camera, pi * globalClock.getDt() * self.adjust_angle) 45 | return Task.cont 46 | 47 | app = MyApp() 48 | app.run() 49 | -------------------------------------------------------------------------------- /3d-sound/knocking.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/3d-sound/knocking.ogg -------------------------------------------------------------------------------- /FSM/fsm.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from direct.fsm.FSM import FSM 5 | from direct.task import Task 6 | import sys 7 | 8 | # A Finite State Machines state names are defined by the presence of 9 | # enterX() or exitX() methods, where X is the state name. These 10 | # methods will be called when a state is entered or exited. A state 11 | # change can be induced by calling fsm.request("X"). 12 | # If the current state also has a filterX() method, that will be 13 | # called instead by request("arg"), so that arg can be used in 14 | # determining the actual state that the machine should move into. 15 | # filter() should return that states name, or None if no change 16 | # should occur. 17 | # 18 | # FIXME: Explain "arguments to request"=="arguments to enterX" 19 | # FIXME: Explain FSM.defaultTransitions 20 | # FIXME: Catch FSM.RequestDenied 21 | # FIXME: Mention fsm.state and FSM.AlreadyInTransition 22 | # FIXME: Explain defaultFilter 23 | # FIXME: demand(), forceTransition() 24 | 25 | class MyApp(ShowBase): 26 | def __init__(self): 27 | # Basics 28 | ShowBase.__init__(self) 29 | base.disableMouse() 30 | self.camera.set_pos(0, -10, 0) 31 | self.camera.look_at(0, 0, 0) 32 | self.accept("escape", sys.exit) 33 | # State machine 34 | self.fsm = RotationState(self) 35 | self.rotation = 0 36 | # Model, control and rotation task 37 | self.model = self.loader.loadModel("models/smiley") 38 | self.model.reparent_to(self.render) 39 | self.accept("arrow_left", self.fsm.request, ['left']) 40 | self.accept("arrow_right", self.fsm.request, ['right']) 41 | self.taskMgr.add(self.rotate, "rotation") 42 | def set_rotation(self, rotation): 43 | self.rotation = rotation 44 | def rotate(self, task): 45 | self.model.set_h(self.model.get_h() + self.rotation * task.getDt() * 60) 46 | return Task.cont 47 | 48 | class RotationState(FSM): 49 | def __init__(self, statekeeper): 50 | FSM.__init__(self, "RotationStateFSM") 51 | self.statekeeper = statekeeper 52 | self.request('RotationStop') 53 | # State for rotating left 54 | def enterRotationLeft(self): 55 | self.statekeeper.set_rotation(-360.0) 56 | def exitRotationLeft(self): 57 | pass 58 | def filterRotationLeft(self, request, args): 59 | if request == "right": 60 | return "RotationStop" 61 | else: 62 | return None 63 | # State for rotating right 64 | def enterRotationRight(self): 65 | self.statekeeper.set_rotation(360.0) 66 | def exitRotationRight(self): 67 | pass 68 | def filterRotationRight(self, request, args): 69 | if request == "left": 70 | return "RotationStop" 71 | else: 72 | return None 73 | # State for rotating left 74 | def enterRotationStop(self): 75 | self.statekeeper.set_rotation(0.0) 76 | def exitRotationStop(self): 77 | pass 78 | def filterRotationStop(self, request, args): 79 | if request == "left": 80 | return "RotationLeft" 81 | elif request == "right": 82 | return "RotationRight" 83 | else: 84 | return None 85 | 86 | app = MyApp() 87 | app.run() 88 | 89 | -------------------------------------------------------------------------------- /GUI/GUI.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | from direct.showbase.ShowBase import ShowBase 5 | from direct.gui.DirectGui import DirectButton 6 | 7 | s = ShowBase() 8 | 9 | b = DirectButton(text = ("OK", "click!", "rolling over", "disabled"), 10 | text_scale = 0.1, 11 | frameColor = (0.6, 0.6, 0.6, 1.0), 12 | frameSize = (-0.3, 0.3, -0.3, 0.3), 13 | pos = (0.0, 0.0, -0.7)) 14 | 15 | s.accept("escape", sys.exit) 16 | 17 | s.run() 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | As these are minimal examples and do nothing but demonstrate how to use 2 | Panda3D's API, nothing in here should be copyrightable, nor is any 3 | copyright implied. In case of any ambiguity left, please file a bug 4 | report with your legislature. 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | panda\_examples 2 | =============== 3 | 4 | These are examples for functionality of Panda3D. They are meant to be as minimal as possible, each showing exactly one thing, and each be executable right out of the box. The intention is rather to get you started than being complete examples of anything, though in the future further examples will delve into more advanced features. 5 | 6 | 7 | Procedural modeling and animation 8 | --------------------------------- 9 | 10 | In this course we will deal with using code to create geometric models, and animate them. We will not deal with advanced procedural generation algorithms to create complex objects, only with the underlying API that would also be used to turn such complex models into actual data that Panda3D can work on. 11 | 12 | Special thanks go to [Entikan](https://entikan.dimension.sh/) whose trailblazing led to me developing these examples based on his code. 13 | 14 | 15 | ### geometry 16 | 17 | Here we generate a static model purely in code, so as to see what is going on under the hood. In [`basic_model.py`](./geometry/basic_model.py) we learn 18 | * how to define a table for vertices, 19 | * how to fill it with data, 20 | * how to add primitives, in particular triangles. 21 | 22 | [`main.py`](./geometry/main.py) is mostly a convenience so that we can actually look at something. 23 | 24 | 25 | ### pbr 26 | 27 | Building on `geometry`, we discuss the properties of PBR-capable models and their textures. [`pbr_model.py`](./pbr/pbr_model.py) shows 28 | * what vertex columns a PBR model typically uses, 29 | * what materials are and do, 30 | * what information is encoded in textures, and how, 31 | * how to procedurally generate images to load into textures. 32 | 33 | There are three executable progrems this time, where 34 | * [`main_no_pbr.py`](./pbr/main_no_pbr.py) shows how the model looks in 35 | the default renderer, 36 | * [`main_simplepbr.py`](./pbr/main_simplepbr.py) uses Moguri's 37 | [`panda3d-simplepbr` package](https://github.com/Moguri/panda3d-simplepbr), 38 | * and [`main_complexpbr.py`](./pbr/main_complexpbr.py) does the same for 39 | Simulan's 40 | [`panda3d-complexpbr` package](https://github.com/rayanalysis/panda3d-complexpbr). 41 | 42 | 43 | ### bones 44 | 45 | Also building on `geometry`, we create the model of a tentacle, and give it a chain of bones to animate it around. As you will doubtlessly expect by now, [`bones_model.py`](./bones/bones_model.py) does the same old "Create a table, fill it with vertices, add triangles" song and dance, while adding information to the vertices about which bones they should consider for changing their apparent data while the model is being animated. 46 | 47 | There are four paradigms of animation that I am aware of, with advanced procedural animation techniques building on those. There are 48 | * Forward Kinematics, basically just setting bones to translations generated ad hoc in code, shown in [`main_control_joints.py`](./bones/main_control_joints.py), 49 | * Skeletal animation, which is basically the same, using pre-recorded and usually artist-generated animations; Here we use [`main_mocap.py`](./bones/main_mocap.py) to record an animation using [rdb's](https://github.com/rdb/) [`mocap.py`](./bones/mocap.py), and play it back with [`main_animation.py`](./bones/main_animation.py), 50 | * Inverse Kinematics, which is the reverse of Forward Kinematics, in that the code provides a target that a chain of bones should reach for, and leaves it to mathematical tools to move the bones within provided constraints so as to reach the target, demonstrated in [`main_inverse_kinematics.py`](./bones/main_inverse_kinematics.py) using [CCD-IK-Panda3D](https://github.com/Germanunkol/CCD-IK-Panda3D) by [germanunkol](https://github.com/Germanunkol/), 51 | * and Physical Simulation, where we add information about a physical approximation of our model to simulate how it moves under the influence of gravity and collisions by using Panda3D's Bullet integration as in [`main_physics.py`](./bones/main_physics.py). 52 | 53 | 54 | Miscellaneous snippets 55 | ---------------------- 56 | 57 | ### basics 58 | 59 | Let the camera rotate around the camera with your left and right cursor key. 60 | 61 | Features demonstrated: 62 | * Loading a model and attaching it to the scene graph 63 | * Events 64 | * Tasks 65 | * Moving a node 66 | 67 | 68 | ### 3d-sound 69 | 70 | Have an object in the scene emit a sound which is then rendered appropriately. 71 | 72 | This example uses a sound file downloaded at http://www.freesound.org/people/lth_stp/sounds/120490/ created by the sound department of the states theater of Lower Austria (Landestheater Niederösterreich). I have dropped an audio channel (the original is stereo, this version is mono) and converted it to Ogg Vorbis. The file is licensed under the Creative Commons 3.0 license, Attribution-NonCommercial: http://creativecommons.org/licenses/by-nc/3.0/ 73 | 74 | Features demonstrated: 75 | * 3D sound 76 | 77 | Features not yet demonstrated: 78 | * Doppler shift 79 | 80 | 81 | ### bullet\_physics 82 | 83 | Features demonstrated: 84 | * Setting up and stepping a physics simulation 85 | * Adding a Rigid Body 86 | * Apply an impulse 87 | 88 | 89 | ### cubemap 90 | 91 | This one sets up a scene and saves a cube map snapshot of it a six images in industry standard format. This should later come in handy to precreate skyboxes. 92 | 93 | Features demonstrated: 94 | * Save a cube map 95 | 96 | 97 | ### FSM 98 | 99 | This sets up a simple state machine that lets a sphere rotate clockwise, counterclockwise or not at all. Using the left and right cursor keys, you can switch between states. 100 | 101 | Features demonstrated: 102 | * Finite State Machine 103 | 104 | 105 | ### GUI 106 | 107 | Okay, it *does* get a bit ridiculous here. All I did here was plaster a button on the screen. I guess the point here is mostly that DirectGUI widgets reparent themselves to aspect2d automagically, so... Keep in mind to manage your scene graph? 108 | 109 | Features demonstrated: 110 | * DirectGUI elements 111 | 112 | 113 | ### libRocket 114 | 115 | A minimal libRocket GUI 116 | 117 | Features demonstrated: 118 | * Loading a font 119 | * Setting up the Python-side of the GUI 120 | * RML / RCSS 121 | 122 | 123 | ### lod 124 | 125 | Level of detail. I created a model of a tree on tree different detail levels and you're allowed to laugh, but even for programmers art I did them quarter-assed. Anyways, your up and down cursor keys will move you towards and away from the tree, showing how the mesh gets exchanged automagically. 126 | 127 | Features demonstrated: 128 | * Level of Detail nodes 129 | 130 | 131 | ### offscreen\_filming 132 | 133 | If you want to use Panda3D to render frames to image files on the disk 134 | instead of a window on the screen, that is remarkably easy; Just combine 135 | `window-type offscreen` with a window's `screenshot()` method. To turn 136 | the outputted images into a video, you can use 137 | `ffmpeg -framerate 30 -pattern_type glob -i '*.png' -c:v libx264 -pix_fmt yuv420p out.mp4`. 138 | 139 | Features demonstrated: 140 | * Offscreen rendering 141 | * Screenshots 142 | 143 | 144 | ### render\_to\_texture 145 | 146 | You have a camera somewhere in your game world and a screen that shows what's on the camera? You want 2D displays showing something that's easier to model in 3D? Or you have any other purpose for having a Panda3D camera render into a texture? This is the example for you. 147 | 148 | Features demonstrated: 149 | * Set up a secondary scene graph, which will get rendered into textures. 150 | * Create a butter, set its sort order. 151 | * Create a camera and let it render into that buffer. 152 | * Get a texture from the buffer and set it as a "monitors" texture. 153 | 154 | 155 | ### reprojection 156 | 157 | Scene graph position to camera frustum position and back. 158 | 159 | Features demonstrated: 160 | * `Lens.project` 161 | * `Lens.extrude_depth()` 162 | 163 | 164 | ### selfshadow 165 | 166 | Features demonstrated: 167 | * Set up a shadow-casting light. 168 | * Make all objects in the scene graph receive shadows. 169 | 170 | 171 | ### shaders 172 | 173 | Features demonstrated: 174 | * Minimal shader pipeline 175 | * Full shader pipeline 176 | 177 | 178 | ### skybox 179 | 180 | I admit that this is a bit of a weird one. I've assembled an .egg file mostly by hand to tile a texture onto a cube, where the texture coords of each vertex is smack in the middle of the corner pixel of that sides tile, so that mipmapping should be avoided. The point is that with this, you can create high-quality skybox textures. 181 | 182 | A bunch of spheres in the scene show that the skybox gets rendered "behind" objects in the scene, even though the box should be too small to contain the outer spheres. 183 | 184 | Features demonstrated: 185 | * Rendering objects as background 186 | 187 | 188 | ### filter 189 | 190 | Features demonstrated: 191 | * Post-processing effects with fragment shaders 192 | 193 | This is mostly a copy-and-paste job of the Hello World application in Panda3D's manual, with a filter slapped on at the end. The FilterManager causes a scene to not be rendered directly to the scene, but instead to textures on a quad, which then does get fed back into fragment shading. 194 | 195 | 196 | ### buffer\_protocol 197 | 198 | The buffer protocol is a Python feature that lets C extensions efficiently share memory. In the context on Panda3D, this is a viable way to share texture and vertex data with non-Panda3D modules. 199 | 200 | Features demonstrated: 201 | * (gizeh_to_panda3d.py) Creating images on a canvas backed by a numpy array (which is the standard way of storing images in Python), and copying it into a Panda3D texture. Requires Gizeh (a Python module for rendering vector graphics, akin to Cairo). 202 | 203 | 204 | ### boilerplate 205 | 206 | This is code I find helpful when developing experiments with. 207 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/__init__.py -------------------------------------------------------------------------------- /basics/camera_turning_around_sphere.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from direct.task import Task 5 | from math import sin, cos, pi 6 | import sys 7 | 8 | class MyApp(ShowBase): 9 | def __init__(self): 10 | # The basics 11 | ShowBase.__init__(self) 12 | base.disableMouse() 13 | # Add the model 14 | m = self.loader.loadModel("models/smiley") 15 | m.reparentTo(self.render) 16 | m.setPos(0,0,0) 17 | # Bookkeeping for the rotation around the model 18 | self.angle = 0.0 19 | self.pitch = 0.0 20 | self.adjust_angle = 0 21 | self.adjust_pitch = 0 22 | self.last_time = 0.0 23 | # Initial camera setup 24 | self.camera.set_pos(sin(self.angle)*20,-cos(self.angle)*20,0) 25 | self.camera.look_at(0,0,0) 26 | # Key events and camera movement task 27 | self.accept("arrow_left", self.adjust_turning, [-1.0, 0.0]) 28 | self.accept("arrow_left-up", self.adjust_turning, [1.0, 0.0]) 29 | self.accept("arrow_right", self.adjust_turning, [1.0, 0.0]) 30 | self.accept("arrow_right-up", self.adjust_turning, [-1.0, 0.0]) 31 | self.accept("arrow_up", self.adjust_turning, [0.0, 1.0]) 32 | self.accept("arrow_up-up", self.adjust_turning, [0.0, -1.0]) 33 | self.accept("arrow_down", self.adjust_turning, [0.0, -1.0]) 34 | self.accept("arrow_down-up", self.adjust_turning, [0.0, 1.0]) 35 | self.accept("escape", sys.exit) 36 | self.taskMgr.add(self.update_camera, 'adjust camera', sort = 10) 37 | def adjust_turning(self, heading, pitch): 38 | self.adjust_angle += heading 39 | self.adjust_pitch += pitch 40 | def update_camera(self, task): 41 | if task.time != 0.0: 42 | dt = task.time - self.last_time 43 | self.last_time = task.time 44 | self.angle += pi * dt * self.adjust_angle 45 | self.pitch += pi * dt * self.adjust_pitch 46 | # Why /2.001 and not an even 2.0? Because then we'd have to set_Hpr 47 | # explicitly, as look_at can't deduce the heading when the camera is 48 | # exactly above/below the spheres center. 49 | if self.pitch > pi/2.001: 50 | self.pitch = pi/2.001 51 | if self.pitch < -pi/2.001: 52 | self.pitch = -pi/2.001 53 | self.camera.set_pos(sin(self.angle)*cos(abs(self.pitch))*20, 54 | -cos(self.angle)*cos(abs(self.pitch))*20, 55 | sin(self.pitch)*20) 56 | self.camera.look_at(0,0,0) 57 | return Task.cont 58 | 59 | app = MyApp() 60 | app.run() 61 | 62 | -------------------------------------------------------------------------------- /boilerplate/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/boilerplate/__init__.py -------------------------------------------------------------------------------- /boilerplate/base.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import random 5 | 6 | from panda3d.core import PStatClient 7 | from panda3d.core import KeyboardButton 8 | from panda3d.core import Point2 9 | 10 | from direct.showbase.ShowBase import ShowBase 11 | from direct.task import Task 12 | 13 | 14 | class Base(ShowBase): 15 | def __init__(self, create_model=True): 16 | # The basics 17 | ShowBase.__init__(self) 18 | base.disableMouse() 19 | base.setBackgroundColor(0, 0, 0) 20 | base.setFrameRateMeter(True) 21 | PStatClient.connect() 22 | self.accept("escape", sys.exit) 23 | # Camera 24 | self.camera_orbit = base.render.attach_new_node("Camera orbit") 25 | self.camera_pitch = self.camera_orbit.attach_new_node("Camera pitch") 26 | base.camera.reparent_to(self.camera_pitch) 27 | base.camera.set_pos(0, -10, 0) 28 | self.rotation_mode = False 29 | self.mouse_pos = None 30 | self.accept("mouse3", self.set_rotation_mode, [True]) 31 | self.accept("mouse3-up", self.set_rotation_mode, [False]) 32 | self.accept("wheel_up", self.move_camera_distance, [-1]) 33 | self.accept("wheel_down", self.move_camera_distance, [1]) 34 | base.taskMgr.add(self.move_camera, "Move camera") 35 | # Object 36 | if create_model: 37 | self.create_model() 38 | 39 | def create_model(self): 40 | self.model = base.loader.loadModel("models/smiley") 41 | self.model.reparent_to(base.render) 42 | 43 | def set_rotation_mode(self, mode): 44 | self.rotation_mode = mode 45 | if base.mouseWatcherNode.has_mouse(): 46 | self.mouse_pos = base.mouseWatcherNode.get_mouse() 47 | 48 | def move_camera_distance(self, direction): 49 | base.camera.set_pos(base.camera.get_pos() * (1 + 0.1 * direction)) 50 | 51 | def move_camera(self, task): 52 | rot = globalClock.get_dt() * 360.0 / 3.0 53 | up_down = 0 54 | left_right = 0 55 | if base.mouseWatcherNode.is_button_down(KeyboardButton.up()): 56 | up_down -= 1 57 | if base.mouseWatcherNode.is_button_down(KeyboardButton.down()): 58 | up_down += 1 59 | if base.mouseWatcherNode.is_button_down(KeyboardButton.left()): 60 | left_right -=1 61 | if base.mouseWatcherNode.is_button_down(KeyboardButton.right()): 62 | left_right +=1 63 | if self.rotation_mode and base.mouseWatcherNode.has_mouse(): 64 | mouse_pos = base.mouseWatcherNode.get_mouse() 65 | mouse_delta = mouse_pos - self.mouse_pos 66 | self.mouse_pos = Point2(mouse_pos) 67 | up_down += mouse_delta.get_y() * 50 68 | left_right += mouse_delta.get_x() * -50 69 | self.camera_orbit.set_h(self.camera_orbit, left_right * rot) 70 | new_pitch = self.camera_pitch.get_p() + up_down * rot 71 | self.camera_pitch.set_p(min(max(new_pitch, -89), 89)) 72 | return Task.cont 73 | 74 | 75 | if __name__ == '__main__': 76 | app = Base() 77 | app.run() 78 | -------------------------------------------------------------------------------- /boilerplate/shader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from base import Base 4 | from panda3d.core import NodePath 5 | from panda3d.core import Geom, GeomNode, GeomVertexFormat, \ 6 | GeomVertexData, GeomTriangles, GeomVertexWriter, GeomVertexReader 7 | from panda3d.core import Shader 8 | from direct.task import Task 9 | 10 | class ShaderBase(Base): 11 | def __init__(self): 12 | Base.__init__(self) 13 | taskMgr.add(self.refresh_shader_vars, "Refresh shaders variables", sort = 49) 14 | self.accept("r", self.reload_shader) 15 | 16 | def create_model(self): 17 | # Set up the vertex arrays 18 | vformat = GeomVertexFormat.get_v3c4() 19 | vdata = GeomVertexData("Data", vformat, Geom.UHStatic) 20 | vertex = GeomVertexWriter(vdata, 'vertex') 21 | color = GeomVertexWriter(vdata, 'color') 22 | geom = Geom(vdata) 23 | # Vertex data 24 | vertex.addData3f(1.5, 0, -1) 25 | color.addData4f(1, 0, 0, 1) 26 | vertex.addData3f(-1.5, 0, -1) 27 | color.addData4f(0, 1, 0, 1) 28 | vertex.addData3f(0, 0, 1) 29 | color.addData4f(0, 0, 1, 1) 30 | # Primitive 31 | tri = GeomTriangles(Geom.UHStatic) 32 | tri.add_vertex(2) 33 | tri.add_vertex(1) 34 | tri.add_vertex(0) 35 | tri.close_primitive() 36 | geom.addPrimitive(tri) 37 | # Create the actual node 38 | node = GeomNode('geom_node') 39 | node.addGeom(geom) 40 | np = NodePath(node) 41 | # Shader and initial shader vars 42 | np.set_shader(Shader.load(Shader.SL_GLSL, "shader/shader.vert", "shader/shader.frag")) 43 | np.set_shader_input("time", 0.0) 44 | # No instancing necessary 45 | #np.set_instance_count(27) 46 | # return np 47 | np.reparent_to(base.render) 48 | self.model = np 49 | 50 | def refresh_shader_vars(self, task): 51 | self.model.set_shader_input("time", task.time) 52 | return Task.cont 53 | 54 | def reload_shader(self): 55 | self.model.set_shader(Shader.load(Shader.SL_GLSL, "shader/shader.vert", "shader/shader.frag")) 56 | 57 | if __name__ == '__main__': 58 | demo = ShaderBase() 59 | demo.run() 60 | -------------------------------------------------------------------------------- /boilerplate/shader/shader.frag: -------------------------------------------------------------------------------- 1 | #version 130 2 | 3 | in vec4 vert_color; 4 | out vec4 frag_color; 5 | 6 | void main () { 7 | frag_color = vert_color; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /boilerplate/shader/shader.vert: -------------------------------------------------------------------------------- 1 | #version 130 2 | 3 | in vec4 p3d_Vertex; 4 | uniform mat4 p3d_ModelViewProjectionMatrix; 5 | 6 | in vec4 color; 7 | out vec4 vert_color; 8 | 9 | void main() { 10 | gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; 11 | vert_color = color; 12 | } 13 | 14 | 15 | -------------------------------------------------------------------------------- /bones/bones_model.py: -------------------------------------------------------------------------------- 1 | # Have you read the geometry example yet to learn how you generate a 2 | # static model? Do you want to give it a skeleton and make it move? You 3 | # have come to the right tutorial. 4 | 5 | from panda3d.core import Vec3 6 | from panda3d.core import Mat4 7 | from panda3d.core import NodePath 8 | from panda3d.core import Geom 9 | from panda3d.core import GeomNode 10 | from panda3d.core import InternalName 11 | from panda3d.core import GeomVertexArrayFormat 12 | from panda3d.core import GeomVertexFormat 13 | from panda3d.core import GeomVertexData 14 | from panda3d.core import GeomVertexWriter 15 | from panda3d.core import GeomTriangles 16 | from panda3d.core import GeomVertexAnimationSpec 17 | from panda3d.core import Character 18 | from panda3d.core import CharacterJoint 19 | from panda3d.core import PartGroup 20 | from panda3d.core import TransformBlendTable 21 | from panda3d.core import TransformBlend 22 | from panda3d.core import JointVertexTransform 23 | from panda3d.core import SparseArray 24 | 25 | from direct.actor.Actor import Actor 26 | 27 | # 28 | # Model 29 | # 30 | 31 | # We are going to build a tentacle. It will essentially be a cone split 32 | # into several segments. The cuts will be circles of vertices. We also 33 | # create an animation joint for each circle. 34 | 35 | # First, some user-provided values to fiddle with. 36 | vertices_per_circle = 30 37 | circle_radius = 0.25 38 | segments = 40 39 | length = 4.0 40 | 41 | # ...and data derived from it 42 | segment_height = length / segments 43 | num_rows = (vertices_per_circle + 1) * (segments + 1) 44 | num_tris = segments * (vertices_per_circle - 1) * 2 45 | 46 | # Setting up the columns for our vertex data. This time we have a column 47 | # to essentially point to entries in a table to; More on that later. 48 | v_array_format = GeomVertexArrayFormat() 49 | v_array_format.add_column(InternalName.get_vertex(), 3, Geom.NT_float32, Geom.C_point) 50 | v_array_format.add_column(InternalName.get_color(), 4, Geom.NT_float32, Geom.C_color) 51 | v_array_format.add_column(InternalName.get_transform_blend(), 1, Geom.NT_uint16, Geom.C_index) 52 | 53 | # Setting up the animation. 54 | anim_spec = GeomVertexAnimationSpec() 55 | anim_spec.set_panda() 56 | 57 | # Putting it all together. 58 | v_format = GeomVertexFormat() 59 | v_format.add_array(v_array_format) 60 | v_format.set_animation(anim_spec) 61 | v_format = GeomVertexFormat.register_format(v_format) 62 | 63 | # The actual data container and the writers. 64 | v_data = GeomVertexData('name', v_format, Geom.UHStatic) 65 | v_data.unclean_set_num_rows(num_rows) 66 | vertex = GeomVertexWriter(v_data, InternalName.get_vertex()) 67 | color = GeomVertexWriter(v_data, InternalName.get_color()) 68 | transform = GeomVertexWriter(v_data, InternalName.get_transform_blend()) 69 | 70 | tris = GeomTriangles(Geom.UHStatic) 71 | tris.reserve_num_vertices(num_tris * 3) 72 | 73 | # Okay, new containers! 74 | # A `Character` contains `CharacterJoint`s; These are the basic unit of 75 | # animation. Like nodes in a scene graph, they form a tree structure and 76 | # have a transformation that moves / rotates them relative to their 77 | # parent. These joints can be exposed to attach NodePaths to them, or 78 | # controlled to move them around like NodePaths. 79 | # There are further details to the `Character` which we will skip for 80 | # now. 81 | # At the other end of the animation system are the vertices that are 82 | # moved around when joints move. The vertex table contains a column 83 | # that tells what row of the `TransformBlendTable` to use for this 84 | # vertex row. Each row of that table contains a `TransformBlend`, which 85 | # is a weighted list of `JointVertexTransforms`, which each reference a 86 | # `CharacterJoint`. So each time that a model is animated, its joints 87 | # get moved around, these movements are weighted and averaged, and then 88 | # vertices get moved around accordingly. 89 | # Now, the details of `Character` that we skipped. 90 | # FIXME: Character, bundle, CharacterJoint (PartGroup, name, matrix) 91 | character = Character("model") 92 | bundle = character.get_bundle(0) 93 | blend_table = TransformBlendTable() 94 | # The vertex rows to be animated have to be explicitly "switched on". 95 | blend_table.set_rows(SparseArray.lower_on(num_rows)) 96 | # We need to tell our vertex data about that table that its 97 | # `transform_blend` column refers to. 98 | v_data.set_transform_blend_table(blend_table) 99 | 100 | # With just two NodePaths, a lot of math can be avoided. 101 | reference_space = NodePath('ref_space') 102 | turtle = reference_space.attach_new_node('turtle') 103 | 104 | # Now we actually dump the data into their containers. 105 | for circle in range(segments + 1): 106 | # Joints first. They sit in the center of the circles. 107 | if circle == 0: # Model root 108 | joint = CharacterJoint(character, bundle, PartGroup(bundle, ""), f"joint_{circle}", Mat4.ident_mat()) 109 | else: 110 | # The transformations are relative to the parent, so we will use 111 | # the `segment_height` here, but multiples of it for calculating 112 | # the vertex data. 113 | turtle.set_pos(0, 0, segment_height) 114 | turtle.set_hpr(0, 0, 0) 115 | mat = turtle.get_transform(reference_space).get_mat() 116 | joint = CharacterJoint(character, bundle, parent_joint, f"joint_{circle}", mat) 117 | parent_joint = joint # For the next time around. 118 | # Each joint controls a circle of vertices, and those vertices are 119 | # controlled only by the ring, so we need one `TransformBlend` per 120 | # circle. 121 | blend = TransformBlend() 122 | blend.add_transform(JointVertexTransform(joint), weight=1) 123 | bt_index = blend_table.add_blend(blend) 124 | 125 | # Vertex and triangle data 126 | for vertex_in_circle in range(vertices_per_circle + 1): 127 | vertex_id = circle * (vertices_per_circle + 1) + vertex_in_circle 128 | # Nudge the turtle into position, ... 129 | turtle.set_pos(0, 0, circle * segment_height) 130 | turtle.set_hpr(vertex_in_circle * 360.0 / vertices_per_circle, 0, 0) 131 | # ...get and write the data for position, color and the 132 | # applicable row of the blend table, ... 133 | local_radius = circle_radius * (1 - circle / segments) 134 | v_pos = reference_space.get_relative_point(turtle, Vec3(0, local_radius, 0)) 135 | vertex.set_data3f(v_pos) 136 | color.set_data4f(vertex_in_circle % 2, circle % 2, 0, 1) 137 | transform.set_data1i(bt_index) 138 | # ...and fill in the triangles (where applicable). 139 | if circle > 0 and vertex_in_circle > 0: 140 | vertex_id = circle * (vertices_per_circle + 1) + vertex_in_circle 141 | tl = vertex_id - 1 142 | tr = vertex_id 143 | bl = vertex_id - 1 - (vertices_per_circle + 1) 144 | br = vertex_id - (vertices_per_circle + 1) 145 | tris.add_vertices(tl, br, tr) 146 | tris.add_vertices(bl, br, tl) 147 | 148 | # Putting the GeomNode together... 149 | geom = Geom(v_data) 150 | geom.add_primitive(tris) 151 | node = GeomNode('geom_node') 152 | node.add_geom(geom) 153 | 154 | # ...and wrapping it up for the scene graph. 155 | node_np = NodePath(node) 156 | character_np = NodePath(character) 157 | node_np.reparent_to(character_np) 158 | actor = Actor(character_np) 159 | -------------------------------------------------------------------------------- /bones/main_animation.py: -------------------------------------------------------------------------------- 1 | # You know how most games have a bunch of artist-defined animations, and 2 | # blend them together at runtime, and the end result may not be great, 3 | # but it is totally good enough? So good in fact that when developers 4 | # revisit their project for improvements, animations are usually not 5 | # something they improve the code for, as it would yield too little 6 | # gains for too much effort? 7 | # That is the kind of animation that we are going to play back here. 8 | 9 | # I'll skip explaining the code except for the directly animation- 10 | # related bits. 11 | import sys 12 | from math import pi 13 | from math import sin 14 | from direct.showbase.ShowBase import ShowBase 15 | 16 | from bones_model import actor 17 | from bones_model import segments 18 | 19 | 20 | ShowBase() 21 | base.accept('escape', sys.exit) 22 | base.cam.set_pos(0, -10, 3) 23 | base.cam.look_at(0, 0, 2.5) 24 | 25 | actor.reparent_to(base.render) 26 | 27 | # By default, animations are played as they themselves specify. If the 28 | # animation specifies a frame rate slower than the application's frame 29 | # rate, that means that the model will jump from keyframe to keyframe at 30 | # the appropriate time, while freezing in the time in between. While 31 | # this *may* at times be desirable, in most cases we want the animation 32 | # to be interpolated, so it is smoothed and continuous. We could make 33 | # that the default befavior by setting the config string 34 | # 'interpolate-frames 1'. Or we can just activate it on the actor in 35 | # question. 36 | actor.set_blend(frameBlend=True) 37 | # By default, this actor does have an animation on it, because 38 | # generating that is the point of `main_mocap.py`. No matter, animations 39 | # can also be loaded into pre-existing actors, so we do just that, and 40 | # give the animation the name `undulate`. 41 | actor.load_anims({'undulate': 'undulate.bam'}) 42 | # Now we tell the actor to play that animation in a loop, and that's it. 43 | actor.loop('undulate') 44 | 45 | base.run() 46 | -------------------------------------------------------------------------------- /bones/main_control_joints.py: -------------------------------------------------------------------------------- 1 | # Now that we have the code to generate a tentacle model with joints, we 2 | # do of course still need to make it move. For this example, we will 3 | # take direct control of the joints and rotate them so as to make the 4 | # tentacle flail around wildly. 5 | 6 | import sys 7 | from math import pi 8 | from math import sin 9 | from direct.showbase.ShowBase import ShowBase 10 | 11 | from bones_model import actor 12 | from bones_model import segments 13 | 14 | 15 | ShowBase() 16 | base.accept('escape', sys.exit) 17 | base.cam.set_pos(0, -10, 3) 18 | base.cam.look_at(0, 0, 2.5) 19 | 20 | # This is all that we need to do to get our actor into the scene. 21 | actor.reparent_to(base.render) 22 | 23 | # Some parameters to control the undulation of the tentacle. 24 | max_angle = 120.0 25 | side_speed = 1.0 26 | side_phases = 0.7 27 | front_speed = 1.1 28 | front_phases = 0.0 29 | 30 | # We animate the model by controlling its joints directly. 31 | joints = [actor.control_joint(None, "modelRoot", f"joint_{i}") for i in range(1, segments)] 32 | 33 | def undulate_tentacle(task): 34 | for j_idx, j_node in enumerate(joints): 35 | side_phase_offset = side_phases / (segments - 1) * j_idx * 2 * pi 36 | front_phase_offset = front_phases / (segments - 1) * j_idx * 2 * pi 37 | j_node.set_r(sin(task.time * side_speed + side_phase_offset) * max_angle / (segments - 1)) 38 | j_node.set_p(sin(task.time * front_speed + front_phase_offset) * max_angle / (segments - 1)) 39 | return task.cont 40 | 41 | base.task_mgr.add(undulate_tentacle) 42 | base.run() 43 | -------------------------------------------------------------------------------- /bones/main_inverse_kinematics.py: -------------------------------------------------------------------------------- 1 | # So, inverse kinematics... You have a model with bones, and instead of 2 | # controlling the animation, you want to move some joint to some point, 3 | # and the bones leading to it should move on their own? Yup, inverse 4 | # kinematics. 5 | # For this example we use https://github.com/Germanunkol/CCD-IK-Panda3D 6 | 7 | import sys 8 | from math import pi 9 | from math import sin 10 | from math import cos 11 | from direct.showbase.ShowBase import ShowBase 12 | 13 | from CCDIK.ik_chain import IKChain 14 | from CCDIK.ik_actor import IKActor 15 | from CCDIK.utils import * 16 | 17 | from bones_model import actor 18 | from bones_model import segments 19 | 20 | 21 | ShowBase() 22 | base.accept('escape', sys.exit) 23 | base.cam.set_pos(0, -10, 3) 24 | base.cam.look_at(0, 0, 2.5) 25 | 26 | # Instead of using the Actor directly, we wrap it into an IKActor. 27 | ik_actor = IKActor(actor) 28 | ik_actor.reparent_to(base.render) 29 | # We create the IK chain consisting of all joints but the first. That 30 | # means that the tentacle's root will remain fixed, and all joints above 31 | # it will move. 32 | joint_names = [f"joint_{i}" for i in range(1, segments + 1)] 33 | ik_chain = ik_actor.create_ik_chain(joint_names) 34 | # Now we set constraints on the joints; This step is optional, by 35 | # each joint will be an unconstrained ball joint, able to rotate and 36 | # twist freely. 37 | # A ball constraint sets a minimum and maximum angle for rotations away 38 | # from the previous bone, forming a ring of valid positions. 39 | # Hinge joints have an axis around which they rotate. 40 | for idx, joint_name in enumerate(joint_names): 41 | if idx % 2 == 0: # Every second joint, we have a hinge along y, ... 42 | ik_chain.set_hinge_constraint(joint_name, Vec3(0, 1, 0), -pi*0.2, pi*0.2) 43 | else: # ...and along x for the others. 44 | ik_chain.set_hinge_constraint(joint_name, Vec3(1, 0, 0), -pi*0.2, pi*0.2) 45 | 46 | # Now we need a target that the tentacle can be reaching for. 47 | target = base.render.attach_new_node('target') 48 | ik_chain.set_target(target) 49 | 50 | # Every frame, we move the target and update the IK simulation. The 51 | # target moves up and down while orbiting around the tentacle. 52 | def undulate_tentacle(task): 53 | target.set_pos(sin(task.time), cos(task.time), 2 + sin(task.time*3)) 54 | ik_chain.update_ik() 55 | return task.cont 56 | 57 | base.task_mgr.add(undulate_tentacle) 58 | base.run() 59 | -------------------------------------------------------------------------------- /bones/main_mocap.py: -------------------------------------------------------------------------------- 1 | # Now you are in for a treat. We will procedurally pose the model like 2 | # in `main_control_joints.py`, but instead of displaying anything, we 3 | # will save the movement as an animation to disk! 4 | 5 | from math import pi 6 | from math import sin 7 | 8 | from panda3d.core import NodePath 9 | from panda3d.core import AnimBundleNode 10 | 11 | from mocap import MotionCapture 12 | 13 | from bones_model import actor 14 | from bones_model import segments 15 | 16 | 17 | # Animations are made from keyframes that encode the transformations of 18 | # all bones (that the animation applies to) at a point in time. 19 | # Keyframes are spaced out over time regularly, so the animation has 20 | # (key)frames per second as its playrate. 21 | fps = 2.0 22 | 23 | # We create a recorder for our animation. 24 | mocap = MotionCapture(actor) 25 | 26 | max_angle = 120.0 27 | side_speed = 1.0 28 | side_phases = 0.7 29 | front_speed = 1.1 30 | front_phases = 0.0 31 | 32 | joints = [actor.control_joint(None, "modelRoot", f"joint_{i}") for i in range(1, segments)] 33 | 34 | def undulate_tentacle(time): 35 | for j_idx, j_node in enumerate(joints): 36 | side_phase_offset = side_phases / (segments - 1) * j_idx * 2 * pi 37 | front_phase_offset = front_phases / (segments - 1) * j_idx * 2 * pi 38 | j_node.set_r(sin(time * side_speed + side_phase_offset) * max_angle / (segments - 1)) 39 | j_node.set_p(sin(time * front_speed + front_phase_offset) * max_angle / (segments - 1)) 40 | 41 | for idx in range(100): 42 | time = idx * 1. / fps 43 | undulate_tentacle(time) 44 | #Now that the tentacle is posed, let's capture it 45 | mocap.capture_frame() 46 | 47 | # As so many things, the animation is wrapped into a Node that gets 48 | # wrapped into a NodePath. NodePaths can easily be written to files, so 49 | # we do that, and once again, we are done. 50 | anim_node = AnimBundleNode("undulate", mocap.make_anim_bundle("undulate", fps=fps)) 51 | anim_nodepath = NodePath(anim_node) 52 | anim_nodepath.write_bam_file('undulate.bam') 53 | -------------------------------------------------------------------------------- /bones/main_physics.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from random import random 3 | from math import pi 4 | from math import sin 5 | 6 | from panda3d.core import Point3 7 | from panda3d.core import Vec3 8 | from panda3d.core import NodePath 9 | from panda3d.core import TransformState 10 | 11 | from panda3d.bullet import BulletWorld 12 | from panda3d.bullet import BulletRigidBodyNode 13 | from panda3d.bullet import BulletPlaneShape 14 | from panda3d.bullet import BulletSphereShape 15 | from panda3d.bullet import BulletConeTwistConstraint 16 | 17 | from direct.showbase.ShowBase import ShowBase 18 | 19 | from bones_model import actor 20 | from bones_model import segments 21 | from bones_model import segment_height 22 | from bones_model import circle_radius 23 | 24 | ShowBase() 25 | base.accept('escape', sys.exit) 26 | base.cam.set_pos(0, -10, 3) 27 | base.cam.look_at(0, 0, 2.5) 28 | 29 | actor.reparent_to(base.render) 30 | 31 | # 32 | bullet_world = BulletWorld() 33 | bullet_world.set_gravity(Vec3(0, 0, -9.81)) 34 | 35 | def update_physics(task): 36 | bullet_world.do_physics(globalClock.get_dt()) 37 | return task.cont 38 | 39 | base.task_mgr.add(update_physics, 'update_physics') 40 | 41 | # Plane 42 | plane_shape = BulletPlaneShape(Vec3(0, 0, 1), 0) 43 | plane_node = BulletRigidBodyNode('Ground') 44 | plane_node.add_shape(plane_shape) 45 | 46 | plane_np = render.attach_new_node(plane_node) 47 | bullet_world.attach_rigid_body(plane_node) 48 | 49 | # 50 | 51 | reference_space = NodePath('') 52 | turtle = reference_space.attach_new_node('') 53 | 54 | parent = actor 55 | parent_bullet_node = None 56 | for i in range(1, segments): 57 | joint_radius = circle_radius * (1 - i / segments) 58 | joint_height = segment_height / 2.0 * 0.9 59 | 60 | #bullet_shape = BulletSphereShape(Vec3(joint_radius, joint_radius, joint_height)) 61 | bullet_shape = BulletSphereShape(joint_height) 62 | bullet_node = BulletRigidBodyNode('joint') 63 | bullet_node.set_mass(1.0) 64 | bullet_node.add_shape(bullet_shape) 65 | bullet_world.attach(bullet_node) 66 | 67 | control_np = parent.attach_new_node(bullet_node) 68 | control_np.set_z(segment_height) 69 | 70 | actor.control_joint(control_np, "modelRoot", f"joint_{i}") 71 | 72 | if i == 1: 73 | local_frame = TransformState.makePosHpr(Point3(0, 0, 0), Vec3(0, -90, 0)) 74 | cs = BulletConeTwistConstraint(bullet_node, local_frame) 75 | swing1 = 0.01 76 | swing2 = 0.01 77 | twist = 0.01 78 | cs.setLimit(swing1, swing2, twist) 79 | bullet_world.attachConstraint(cs) 80 | else: 81 | local_frame = TransformState.makePosHpr(Point3(0, 0, 0), Vec3(0, -90, 0)) 82 | parent_frame = TransformState.makePosHpr(Point3(0, 0, segment_height), Vec3(0, -90, 0)) 83 | swing1 = 0.1 84 | swing2 = 0.1 85 | twist = 0.1 86 | cs = BulletConeTwistConstraint(bullet_node, parent_bullet_node, local_frame, parent_frame) 87 | #cs.setDebugDrawSize(2.0) 88 | cs.setLimit(swing1, swing2, twist) 89 | bullet_world.attachConstraint(cs) 90 | 91 | m = base.loader.load_model('models/smiley') 92 | m.set_scale(segment_height / 2.0 * 0.9) 93 | #m.reparent_to(control_np) 94 | parent = control_np 95 | parent_bullet_node = bullet_node 96 | 97 | bullet_node.apply_central_impulse( 98 | Vec3( 99 | random()*0.001, 100 | random()*0.001, 101 | random()*0.001, 102 | ) 103 | ) 104 | 105 | base.run() 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /bones/mocap.py: -------------------------------------------------------------------------------- 1 | # This piece of code was written by rdb. It is reproduced here with 2 | # permission, or rather, to quote him directly, "Consider it public 3 | # domain. Credit rdb if you like." 4 | # It is a bit too involved for me to grok quite yet. If you want to 5 | # adapt it to any use case, godspeed. 6 | 7 | from panda3d.core import CharacterJoint, CharacterSlider, CharacterVertexSlider 8 | from panda3d.core import AnimBundle, AnimGroup, AnimChannelScalarDynamic 9 | from panda3d.core import AnimChannelMatrixXfmTable, AnimChannelScalarTable 10 | from panda3d.core import PTA_float 11 | 12 | 13 | class MotionCapture: 14 | def __init__(self, actor, part_name="modelRoot", lod_name="lodRoot"): 15 | self.part_bundle = actor.get_part_bundle(part_name, lod_name) 16 | self.joint_tables = {} 17 | self.slider_tables = {} 18 | self.num_frames = 0 19 | 20 | for joint in actor.get_joints(partName=part_name, lodName=lod_name): 21 | if isinstance(joint, CharacterJoint): 22 | self.joint_tables[joint] = [PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float(), PTA_float()] 23 | elif isinstance(joint, CharacterSlider): 24 | self.slider_tables[joint] = PTA_float() 25 | 26 | def capture_frame(self): 27 | self.part_bundle.force_update() 28 | self.num_frames += 1 29 | 30 | for joint, tables in self.joint_tables.items(): 31 | transform = joint.get_transform_state() 32 | 33 | scale = transform.get_scale() 34 | tables[0].push_back(scale.x) 35 | tables[1].push_back(scale.y) 36 | tables[2].push_back(scale.z) 37 | 38 | shear = transform.get_shear() 39 | tables[3].push_back(shear.x) 40 | tables[4].push_back(shear.y) 41 | tables[5].push_back(shear.z) 42 | 43 | hpr = transform.get_hpr() 44 | tables[6].push_back(hpr.x) 45 | tables[7].push_back(hpr.y) 46 | tables[8].push_back(hpr.z) 47 | 48 | pos = transform.get_pos() 49 | tables[ 9].push_back(pos.x) 50 | tables[10].push_back(pos.y) 51 | tables[11].push_back(pos.z) 52 | 53 | for slider, table in self.slider_tables.items(): 54 | channel = slider.get_forced_channel() 55 | if channel and isinstance(channel, AnimChannelScalarDynamic): 56 | value_node = channel.value_node 57 | if value_node: 58 | value = value_node.get_transform().get_pos()[0] 59 | else: 60 | value = channel.value 61 | else: 62 | value = CharacterVertexSlider(slider).get_slider() 63 | table.push_back(value) 64 | 65 | def make_anim_bundle(self, name, fps): 66 | bundle = AnimBundle(name, fps, self.num_frames) 67 | 68 | for child in self.part_bundle.children: 69 | self._make_anim_group(bundle, child) 70 | 71 | return bundle 72 | 73 | def _make_anim_group(self, parent, part_group): 74 | joint_tables = self.joint_tables.get(part_group) 75 | slider_table = self.slider_tables.get(part_group) 76 | if joint_tables: 77 | group = AnimChannelMatrixXfmTable(parent, part_group.name) 78 | for i, table in enumerate(joint_tables): 79 | group.set_table('ijkabchprxyz'[i], table) 80 | 81 | elif slider_table: 82 | group = AnimChannelScalarTable(parent, part_group.name) 83 | group.set_table(slider_table) 84 | 85 | else: 86 | group = AnimGroup(parent, part_group.name) 87 | 88 | for child in part_group.children: 89 | self._make_anim_group(group, child) 90 | -------------------------------------------------------------------------------- /buffer_protocol/gizeh_to_panda3d.py: -------------------------------------------------------------------------------- 1 | from direct.showbase.ShowBase import ShowBase 2 | from panda3d.core import PNMImage, Texture 3 | from panda3d.core import PTAUchar 4 | from panda3d.core import CardMaker 5 | from panda3d.core import Point2 6 | 7 | import numpy as np 8 | import gizeh as gz 9 | import random 10 | 11 | s = ShowBase() 12 | 13 | image_x_size = 512 14 | image_y_size = 512 15 | 16 | # Quad in scene, to display the image on 17 | input_img = PNMImage(image_x_size, image_y_size) # It's RGB. 18 | input_tex = Texture() 19 | input_tex.load(input_img) 20 | card = CardMaker('in_scene_screen') 21 | card.setUvRange(Point2(0, 1), # ll 22 | Point2(1, 1), # lr 23 | Point2(1, 0), # ur 24 | Point2(0, 0)) # ul 25 | screen = render.attach_new_node(card.generate()) 26 | screen.set_pos(-0.5, 2, -0.5) 27 | screen.set_texture(input_tex) 28 | 29 | # Gizeh setup 30 | surface = gz.Surface(image_x_size, image_y_size) 31 | 32 | 33 | def update_gizeh_image(): 34 | star1 = gz.star(radius=70, ratio=.4, fill=(1,1,1), angle=-np.pi/2, 35 | stroke_width=2, stroke=(1,0,0)) 36 | star2 = gz.star(radius=55, ratio=.4, fill=(1,0,0), angle=-np.pi/2) 37 | # Gizeh coords are right-down. 38 | stars = gz.Group([star1, star2]).translate([random.randint(100,412), 39 | random.randint(100,412)]) 40 | stars.draw(surface) 41 | 42 | 43 | def gizeh_image(task): 44 | # Why does this need to be copied? That might be expensive! 45 | # Some manipulations apparently make an array non-viable for transfer, and 46 | # that includes even an out-of-the-box gizeh surface. 47 | input_tex.set_ram_image_as(PTAUchar(surface.get_npimage().copy()), 48 | 'RGB') 49 | return task.cont 50 | 51 | 52 | base.taskMgr.add(gizeh_image, "gizeh to screen", sort=45) 53 | 54 | s.run() 55 | 56 | -------------------------------------------------------------------------------- /buffer_protocol/matplotlib_to_panda3d.py: -------------------------------------------------------------------------------- 1 | from random import random 2 | 3 | from matplotlib.figure import Figure 4 | from matplotlib.backends.backend_agg import FigureCanvasAgg 5 | import numpy as np 6 | 7 | from direct.showbase.ShowBase import ShowBase 8 | from direct.task import Task 9 | from panda3d.core import ( 10 | CardMaker, 11 | PTAUchar, 12 | Texture, 13 | PNMImage, 14 | Point2, 15 | TransparencyAttrib, 16 | ) 17 | 18 | 19 | class Plot: 20 | def __init__(self, x_size=640, y_size=480): 21 | self.data_x = list(range(101)) 22 | self.data_y1 = [random()] 23 | self.data_y2 = [self.data_y1[0]] 24 | for _ in range(100): 25 | r = random() 26 | self.data_y1.append((self.data_y1[-1] + r) / 2.0) 27 | self.data_y2.append(self.data_y2[-1] + r) 28 | 29 | self.fig = Figure(figsize=(x_size / 100.0, y_size / 100.0), dpi=100.0) 30 | 31 | # The first graph 32 | self.ax1 = self.fig.add_subplot(1, 1, 1) 33 | [self.line1_data] = self.ax1.step( 34 | self.data_x, 35 | self.data_y1, 36 | 'g-', 37 | label="Nearly random data", 38 | ) 39 | 40 | # X axis decoration 41 | self.ax1.set_xlim([0, 100]) 42 | self.ax1.set_xlabel("A series of 100 values") 43 | 44 | # Y axis decoration 45 | self.ax1.set_ylabel("How much?") 46 | self.ax1.set_autoscaley_on(False) 47 | self.ax1.set_ylim([0.0, 1.0]) 48 | self.ax1.set_yticks((0.0, 0.33, 0.66, 1.00)) 49 | self.y_labels = self.ax1.set_yticklabels( 50 | ('None', 'Some', 'Much', 'Lots') 51 | ) 52 | for label in self.y_labels: 53 | label.set_rotation(45) 54 | 55 | # A second graph with a different axis on the same plot 56 | self.ax2 = self.ax1.twinx() 57 | self.ax2.set_autoscaley_on(True) 58 | [self.line2_data] = self.ax2.plot( 59 | self.data_x, 60 | self.data_y2, 61 | 'r-', 62 | label="Summed random data", 63 | ) 64 | 65 | # Title, legend and grid 66 | self.ax1.set_title("A matplotlib figure") 67 | self.ax1.legend(loc='lower right') 68 | self.ax1.grid() 69 | 70 | # FIXME: This doesn't work yet; see comments in draw(). 71 | # self.fig.patch.set_alpha(0.5) 72 | # self.ax1.patch.set_alpha(0.5) 73 | 74 | self.canvas = FigureCanvasAgg(self.fig) 75 | 76 | def draw(self): 77 | r = random() 78 | self.data_y1.append((self.data_y1[-1] + r) / 2.0) 79 | self.data_y1.pop(0) 80 | self.data_y2.append(self.data_y2[-1] + r) 81 | self.data_y2.pop(0) 82 | # If your y axis limits are going to change, you need: 83 | # self.line_data.set_ydata(self.data_y) 84 | # or set limits explicitly, as shown in __init__(). 85 | # set_data() doesn't realign them, but is cheaper. 86 | self.line1_data.set_data(self.data_x, self.data_y1) 87 | self.line2_data.set_ydata(self.data_y2) 88 | graph_bytes, _res = self.canvas.print_to_buffer() 89 | return graph_bytes 90 | 91 | 92 | class MatPlotLibDemo(ShowBase): 93 | def __init__(self): 94 | ShowBase.__init__(self) 95 | base.setFrameRateMeter(True) 96 | 97 | m = loader.loadModel("models/smiley") 98 | m.reparent_to(render) 99 | m.set_pos(0, 5, 0) 100 | 101 | x_size, y_size = 640, 480 102 | xy_ratio = float(y_size) / float(x_size) 103 | self.plot = Plot(x_size, y_size) 104 | 105 | self.input_img = PNMImage(x_size, y_size) 106 | self.input_tex = Texture() 107 | self.input_tex.load(self.input_img) 108 | 109 | self.card = CardMaker('pygame_card') 110 | self.card.setUvRange(Point2(0, 1), # ll 111 | Point2(1, 1), # lr 112 | Point2(1, 0), # ur 113 | Point2(0, 0)) # ul 114 | self.screen = render.attach_new_node(self.card.generate()) 115 | self.screen.set_scale(1, 1, xy_ratio) 116 | self.screen.set_pos(-0.5, 2, -0.5 * xy_ratio) 117 | self.screen.setTexture(self.input_tex) 118 | # FIXME: Apparently mpl's print_to_buffer() doesn't write 119 | # alpha values properly. 120 | self.screen.setTransparency(TransparencyAttrib.MAlpha) 121 | 122 | taskMgr.add(self.update, "update plot") 123 | 124 | def update(self, task): 125 | self.input_tex.set_ram_image_as(self.plot.draw(), "RGBA") 126 | return task.cont 127 | 128 | 129 | mpld = MatPlotLibDemo() 130 | mpld.run() 131 | -------------------------------------------------------------------------------- /buffer_protocol/pygame_to_panda3d.py: -------------------------------------------------------------------------------- 1 | from direct.showbase.ShowBase import * 2 | from direct.task import Task 3 | from panda3d.core import CardMaker, PTAUchar, Texture, PNMImage, Point2 4 | 5 | import pygame 6 | import random 7 | 8 | 9 | class Game: 10 | def __init__(self): 11 | pygame.init() 12 | self.res = (320, 320) 13 | self.surface = pygame.Surface(self.res) 14 | 15 | def update(self): 16 | r = [] 17 | for i in range(7): 18 | r.append(random.randint(0,255)) 19 | self.surface.fill((r[0],r[1],r[2]),(r[3], r[4], r[5], r[6])) 20 | 21 | 22 | class PygameCard(ShowBase): 23 | def __init__(self): 24 | ShowBase.__init__(self) 25 | 26 | self.game = Game() 27 | self.camLens.setFov(120) 28 | self.input_img = PNMImage(self.game.res[0], self.game.res[1]) 29 | self.input_tex = Texture() 30 | self.input_tex.load(self.input_img) 31 | 32 | self.card = CardMaker('pygame_card') 33 | self.screen = render.attach_new_node(self.card.generate()) 34 | self.screen.setPos(-0.5,2,-0.5) 35 | self.screen.setTexture(self.input_tex) 36 | 37 | self.game_ram_image = self.game.surface.get_view("0") 38 | taskMgr.add(self.update, "update pygame_card") 39 | 40 | def update(self, task): 41 | self.game.update() 42 | self.input_tex.set_ram_image_as(self.game_ram_image, "RGBA") 43 | return task.cont 44 | 45 | 46 | p = PygameCard() 47 | p.run() 48 | -------------------------------------------------------------------------------- /buffer_protocol/terse_description.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from panda3d.core import PTAUchar, Texture, LVecBase4f 3 | 4 | # numpy Panda3D (without transpose) 5 | a = [0, 1, 2] # 0,0 0,0 6 | b = [3, 4, 5] # 0,1 1,0 7 | c = [6, 7, 8] # 0,2 0,1 8 | d = [9, 10, 11] # 1,0 1,1 9 | e = [12, 13, 14] # 1,1 0,2 10 | f = [15, 16, 17] # 1,2 1,2 11 | 12 | a_np = np.array([[a, b, c], 13 | [d, e, f]], 14 | dtype=np.uint8) 15 | 16 | # When accessed through the buffer protocol, ndarrays are (apparently) 17 | # linearized in reverse order of axes. So as we have an [x, y, c] array here, 18 | # the data will be linearized in the order seen above, monotonically ascending. 19 | # This can be seen if you PTAUchar(a_np).get_subdata(0, 18). First all c-columns 20 | # in x=0 get written in order of y, then all in x=1. 21 | # However, Texture.set_ram_image will delinearize the data slightly different. 22 | # c-column order can be manipulated with an additional argument to 23 | # set_ram_image_as, but x/y-wise, it will first write along the x-axis in y=0, 24 | # then y=1 and so on. 25 | # So to preserve x/y coordinate equality between numpy and Panda3D, the array 26 | # first has to be transposed, swapping its x and y axes. That in turn seems to 27 | # somehow "dirty" the returned array, making it necessary to copy it first. 28 | # So basically you have the choice between one additional copy of the whole 29 | # array that you're transferring, or you think of numpy arrays as [y, x, c] 30 | # images. 31 | 32 | a_np_t = a_np.transpose(1, 0, 2).copy() 33 | a_p3d = PTAUchar(a_np_t) 34 | 35 | tex = Texture() 36 | # Note that if the Texture's dimentions and component type don't match that of 37 | # your numpy array, you're gonna have a bad time. 38 | tex.setup_2d_texture (2, 3, Texture.T_unsigned_byte, Texture.F_rgb) 39 | tex.set_ram_image_as(a_p3d, 'RGB') 40 | 41 | # Now let's see the data that we've just moved around. 42 | p = LVecBase4f() 43 | for x in range(2): 44 | for y in range(3): 45 | tex.peek().fetch_pixel(p, x, y); print(x,y,p*256, a_np[x,y]) 46 | 47 | -------------------------------------------------------------------------------- /bullet_physics/bullet_physics.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | from direct.showbase.ShowBase import ShowBase 6 | from panda3d.core import Vec3, Point3 7 | from panda3d.bullet import BulletWorld, BulletRigidBodyNode, BulletSphereShape 8 | 9 | s = ShowBase() 10 | s.disable_mouse() 11 | s.accept('escape', sys.exit) 12 | 13 | # The physics simulation itself 14 | physics = BulletWorld() 15 | physics.setGravity(Vec3(0, 0, 0)) 16 | def step_physics(task): 17 | dt = globalClock.getDt() 18 | physics.doPhysics(dt) 19 | return task.cont 20 | s.taskMgr.add(step_physics, 'physics simulation') 21 | 22 | # A physical object in the simulation 23 | node = BulletRigidBodyNode('Box') 24 | node.setMass(1.0) 25 | node.addShape(BulletSphereShape(1)) 26 | # Attaching the physical object in the scene graph and adding 27 | # a visible model to it 28 | np = s.render.attachNewNode(node) 29 | np.set_pos(0, 0, 0) 30 | np.set_hpr(45, 0, 45) 31 | m = loader.loadModel("models/smiley") 32 | m.reparentTo(np) 33 | physics.attachRigidBody(node) 34 | 35 | 36 | # Let's actually see what's happening 37 | base.cam.setPos(0, -10, 0) 38 | base.cam.lookAt(0, 0, 0) 39 | 40 | # Give the object a nudge and run the program 41 | # the impulse vector is in world space, the position at which it is 42 | # applied is in object space. 43 | node.apply_impulse(Vec3(0, 0, 1), Point3(1, 0, 0)) 44 | node.apply_impulse(Vec3(0, 0, -1), Point3(-1, 0, 0)) 45 | s.run() 46 | -------------------------------------------------------------------------------- /collision/collision.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from math import sin, cos, pi 4 | 5 | from panda3d.core import Vec3, BitMask32 6 | from panda3d.core import CollisionNode, CollisionSphere, CollisionRay 7 | from panda3d.core import CollisionHandlerQueue, CollisionTraverser 8 | 9 | from boilerplate.base import Base 10 | 11 | 12 | BM_LEFT = BitMask32(0x1) 13 | BM_RIGHT = BitMask32(0x10) 14 | 15 | 16 | class SphereThatGetsCollidedWith: 17 | def __init__(self): 18 | # Target; a sphere. You should attach it to your actual model, 19 | # not .show() it directly. Also, the sphere you see is a low- 20 | # poly model, but the collisions will be calculated against a 21 | # mathematically perfect sphere. 22 | self.targets = [self.create_sphere(model_name, bitmask) 23 | for model_name, bitmask in [ 24 | ("smiley", BM_LEFT), 25 | ("frowney", BM_RIGHT), 26 | ("jack", BM_LEFT | BM_RIGHT), 27 | ]] 28 | base.taskMgr.add(self.move_collidee, "Moving Target Task") 29 | 30 | def create_sphere(self, model_name, bitmask): 31 | model = loader.loadModel("models/" + model_name) 32 | model.reparent_to(base.render) 33 | target = CollisionSphere(0, 0, 0, 1) 34 | target_node = CollisionNode('collision_target') 35 | target_node.setIntoCollideMask(bitmask) 36 | target_nodepath = model.attach_new_node(target_node) 37 | target_nodepath.node().addSolid(target) 38 | target_nodepath.show() 39 | return model 40 | 41 | def move_collidee(self, task): 42 | def circle_pos(t): 43 | return Vec3(cos(t) * 3, 0, sin(t) * 3) 44 | t_task = task.time 45 | [target.set_pos(circle_pos(t_task + idx * pi*2./3.)) 46 | for idx, target in enumerate(self.targets)] 47 | return task.cont 48 | 49 | 50 | class RayThatCollidesWithScene: 51 | def __init__(self): 52 | self.hitters = [self.setup_collision_ray(offset, bitmask) 53 | for offset, bitmask in [ 54 | (-3, BM_LEFT), 55 | (3, BM_RIGHT), 56 | ]] 57 | self.queue = CollisionHandlerQueue() 58 | self.traverser = CollisionTraverser('Collision Traverser') 59 | self.traverser.showCollisions(base.render) 60 | for ray in self.hitters: 61 | self.traverser.add_collider(ray, self.queue) 62 | base.taskMgr.add(self.collide, "Collision Task") 63 | 64 | def setup_collision_ray(self, offset, bitmask): 65 | # Hitter. Do note that not every combination of object works, 66 | # there is a table for that in the manual. 67 | hitter = CollisionRay(0, 0, 0, 0, 1, 0) 68 | hitter_node = CollisionNode('collision_hitter') 69 | hitter_node.setFromCollideMask(bitmask) 70 | hitter_nodepath = base.render.attach_new_node(hitter_node) 71 | hitter_nodepath.node().addSolid(hitter) 72 | hitter_nodepath.set_pos(offset, -2, 0) 73 | hitter_nodepath.show() 74 | return hitter_nodepath 75 | 76 | def collide(self, task): 77 | self.traverser.traverse(render) 78 | for entry in self.queue.get_entries(): 79 | print(entry) 80 | return task.cont 81 | 82 | 83 | if __name__ == '__main__': 84 | app = Base(create_model=False) 85 | from_object = RayThatCollidesWithScene() 86 | into_object = SphereThatGetsCollidedWith() 87 | app.run() 88 | 89 | -------------------------------------------------------------------------------- /cubemap/create_cubemap.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pandac.PandaModules import loadPrcFileData 4 | loadPrcFileData("", "window-type offscreen") 5 | 6 | from direct.showbase.ShowBase import ShowBase 7 | import sys 8 | 9 | class MyApp(ShowBase): 10 | def __init__(self): 11 | # The basics 12 | ShowBase.__init__(self) 13 | base.disableMouse() 14 | # The scene 15 | scene = self.loader.loadModel("models/environment") 16 | scene.reparent_to(self.render) 17 | scene.set_scale(0.25, 0.25, 0.25) 18 | scene.set_pos(-8, 42, 0) 19 | self.camera.set_pos(0,0,3) 20 | # Render/save the cubemap and exit 21 | base.saveCubeMap('grassy_#.jpg', size = 512) 22 | sys.exit() 23 | 24 | app = MyApp() 25 | # Actually running the program is not required 26 | # app.run() 27 | 28 | -------------------------------------------------------------------------------- /extrawindows/extrawindows.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import random 5 | 6 | from direct.showbase.ShowBase import ShowBase 7 | from panda3d.core import NodePath, PandaNode 8 | 9 | class Base(ShowBase): 10 | def __init__(self): 11 | ShowBase.__init__(self) 12 | base.disableMouse() 13 | self.accept("escape", sys.exit) 14 | # Set up main window 15 | base.camera.set_pos(10, -10, 0) 16 | base.camera.look_at(0, 0, 0) 17 | self.model = loader.loadModel("models/smiley") 18 | self.model.reparent_to(base.render) 19 | # Set up secondary scene graph 20 | self.secondary_scene_graph = NodePath("foo") 21 | self.model2 = loader.loadModel("models/frowney") 22 | self.model2.reparent_to(self.secondary_scene_graph) 23 | # Prepare for second window 24 | self.second_window = False 25 | self.accept("t", self.toggle_second_window) 26 | 27 | def toggle_second_window(self): 28 | if not self.second_window: 29 | self.win2 = base.openWindow(makeCamera = False, scene = self.secondary_scene_graph) 30 | self.cam2 = base.makeCamera(self.win2) 31 | self.cam2.reparent_to(self.model2) 32 | self.cam2.set_pos(-10, -10, 0) 33 | self.cam2.look_at(0, 0, 0) 34 | self.second_window = True 35 | else: 36 | base.closeWindow(self.win2) 37 | self.second_window = False 38 | 39 | if __name__ == '__main__': 40 | app = Base() 41 | app.run() 42 | 43 | -------------------------------------------------------------------------------- /filter/app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from math import pi, sin, cos 4 | import sys 5 | 6 | from direct.showbase.ShowBase import ShowBase 7 | from direct.task import Task 8 | from direct.actor.Actor import Actor 9 | from direct.interval.IntervalGlobal import Sequence 10 | from panda3d.core import Point3, Texture, Shader 11 | from direct.filter.FilterManager import FilterManager 12 | 13 | class PostEffect(ShowBase): 14 | def __init__(self): 15 | ShowBase.__init__(self) 16 | self.disableMouse() 17 | base.setFrameRateMeter(True) 18 | self.accept("escape", sys.exit) 19 | self.setup_scene() 20 | self.setup_post_effect() 21 | 22 | def setup_scene(self): 23 | # Environment 24 | self.environ = self.loader.loadModel("models/environment") 25 | self.environ.reparentTo(self.render) 26 | self.environ.set_scale(0.25) 27 | self.environ.set_pos(-8, 42, 0) 28 | # Camera 29 | self.taskMgr.add(self.spinCameraTask, "SpinCameraTask") 30 | # Panda 31 | self.pandaActor = Actor("models/panda-model", 32 | {"walk": "models/panda-walk4"}) 33 | self.pandaActor.setScale(0.005, 0.005, 0.005) 34 | self.pandaActor.reparentTo(self.render) 35 | self.pandaActor.loop("walk") 36 | pandaPosInterval1 = self.pandaActor.posInterval(13, 37 | Point3(0, -10, 0), 38 | startPos=Point3(0, 10, 0)) 39 | pandaPosInterval2 = self.pandaActor.posInterval(13, 40 | Point3(0, 10, 0), 41 | startPos=Point3(0, -10, 0)) 42 | pandaHprInterval1 = self.pandaActor.hprInterval(3, 43 | Point3(180, 0, 0), 44 | startHpr=Point3(0, 0, 0)) 45 | pandaHprInterval2 = self.pandaActor.hprInterval(3, 46 | Point3(0, 0, 0), 47 | startHpr=Point3(180, 0, 0)) 48 | self.panda_pace = Sequence(pandaPosInterval1, 49 | pandaHprInterval1, 50 | pandaPosInterval2, 51 | pandaHprInterval2, 52 | name="pandaPace") 53 | self.panda_pace.loop() 54 | 55 | # The post-processing effect is set up here. 56 | def setup_post_effect(self): 57 | self.manager = FilterManager(base.win, base.cam) 58 | tex = Texture() 59 | dtex = Texture() 60 | quad = self.manager.renderSceneInto(colortex=tex, depthtex=dtex) 61 | quad.setShader(Shader.load(Shader.SL_GLSL, "vertex.glsl", "fragment.glsl")) 62 | quad.setShaderInput("tex", tex) 63 | quad.setShaderInput("dtex", dtex) 64 | 65 | # Define a procedure to move the camera. 66 | def spinCameraTask(self, task): 67 | angleDegrees = task.time * 6.0 68 | angleRadians = angleDegrees * (pi / 180.0) 69 | self.camera.setPos(20 * sin(angleRadians), -20.0 * cos(angleRadians), 3) 70 | self.camera.setHpr(angleDegrees, 0, 0) 71 | return Task.cont 72 | 73 | app = PostEffect() 74 | app.run() 75 | 76 | -------------------------------------------------------------------------------- /filter/fragment.glsl: -------------------------------------------------------------------------------- 1 | #version 130 2 | uniform sampler2D tex; 3 | uniform sampler2D dtex; 4 | out vec4 color; 5 | 6 | void main () { 7 | /* For each of the eight pixels around the current one, calculate 8 | the difference in depth between it and the current pixel. Take 9 | the absolute of each of these differences, then add those up. 10 | Multiply that with a vector that will, after shadig, be clamped 11 | to a color. 12 | */ 13 | vec4 color_base = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2(0, 0), 0); 14 | vec4 color_1 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2(-1, -1), 0); 15 | vec4 color_2 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2(-1, 0), 0); 16 | vec4 color_3 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2(-1, 1), 0); 17 | vec4 color_4 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2( 0, -1), 0); 18 | vec4 color_5 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2( 0, 1), 0); 19 | vec4 color_6 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2( 1, -1), 0); 20 | vec4 color_7 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2( 1, 0), 0); 21 | vec4 color_8 = texelFetch(dtex, ivec2(gl_FragCoord.xy) + ivec2( 1, 1), 0); 22 | color = (abs(color_base - color_1) + 23 | abs(color_base - color_2) + 24 | abs(color_base - color_3) + 25 | abs(color_base - color_4) + 26 | abs(color_base - color_5) + 27 | abs(color_base - color_6) + 28 | abs(color_base - color_7) + 29 | abs(color_base - color_8)) * vec4(100, 10, 10, 0); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /filter/vertex.glsl: -------------------------------------------------------------------------------- 1 | #version 130 2 | // Exactly nothing happens in vertex shading. 3 | 4 | in vec4 p3d_Vertex; 5 | uniform mat4 p3d_ModelViewProjectionMatrix; 6 | 7 | void main() { 8 | gl_Position = p3d_ModelViewProjectionMatrix * p3d_Vertex; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /geometry/basic_model.py: -------------------------------------------------------------------------------- 1 | # Hi! Let's quickly generate a mesh, or as we call it in Panda3D, a 2 | # Geom. To keep things simple, we'll do a square. 3 | 4 | from panda3d.core import InternalName 5 | from panda3d.core import NodePath 6 | from panda3d.core import Geom 7 | from panda3d.core import GeomNode 8 | from panda3d.core import GeomVertexArrayFormat 9 | from panda3d.core import GeomVertexFormat 10 | from panda3d.core import GeomVertexData 11 | from panda3d.core import GeomVertexWriter 12 | from panda3d.core import GeomTriangles 13 | 14 | ### 15 | ### MODEL 16 | ### 17 | 18 | # So, what is a Geom? First, a bunch of data about vertices (points in 19 | # space), which is why we call is GeomVertexData. Each row in the table 20 | # represents a vertex, and each column an information about it, for 21 | # example its position in space, its color, what position on a texture 22 | # it corresponds to, and so on. Each column has a name, a number of 23 | # elements of each entry in that column, a type that those elements are 24 | # of, and information about the semantic of the entries (which affect 25 | # how the data is processed). This column definition is provided by a 26 | # GeomVertexFormat. 27 | 28 | # There are a number of predefined formats, which can be found on 29 | # https://docs.panda3d.org/1.10/python/programming/internal-structures/procedural-generation/predefined-vertex-formats 30 | # Here we will reinvent the wheel; This codeblock does the same as 31 | # `v_format = GeomVertexFormat.get_v3c4()`. 32 | 33 | v_array_format = GeomVertexArrayFormat() 34 | # We add each column individually. As mentioned above, each column has 35 | # for properties, 36 | # * the first of which is the name. Panda3D provides `InternalName` as a 37 | # set of canonical names for each columns, so that e.g. shaders have a 38 | # standard by which they can adhere. The name for the position is 39 | # (unfortunately) `vertex`, 40 | # * the number of elements, here 3 (x/y/z values), 41 | # * the type of the column, here 32 bit floating point numbers, 42 | # * and the semantic, a point in space, as opposed to e.g. a vector. 43 | # 44 | # For the full list of types and semantics, please refer to 45 | # https://docs.panda3d.org/1.10/python/reference/panda3d.core.GeomEnums 46 | v_array_format.add_column(InternalName.get_vertex(), 3, Geom.NT_float32, Geom.C_point) 47 | # For the color, we will have an RGBA one in 32 bit floating points. 48 | v_array_format.add_column(InternalName.get_color(), 4, Geom.NT_float32, Geom.C_color) 49 | # Now we create a format to add this array to, and register the former 50 | # to create an actually usable one (which is why we overwrite `v_format` 51 | # with it). 52 | v_format = GeomVertexFormat() 53 | v_format.add_array(v_array_format) 54 | v_format = GeomVertexFormat.register_format(v_format) 55 | 56 | # So far, we have a description of what our data table looks like. Now 57 | # we actually create it, reserve the memory needed for our model's data, 58 | # and create a writer for each column. 59 | # Besides a name and the format, we also specify a usage hint, namely 60 | # that the mesh will be static. For a full list of options, see the link 61 | # above. 62 | v_data = GeomVertexData("Data", v_format, Geom.UH_static) 63 | # A square needs four vertices. There are four ways to indicate that: 64 | # * `reserve_num_rows` allocates a number of rows. Each time that you 65 | # use `add_data` to write beyond the allocated limit, the data table 66 | # will be copied into a new one which has one more row available. 67 | # * Not at all. Same as above, starting with 0 reserved rows. 68 | # * `set_num_rows` allocates and zeroes the memory. You use `set_data` 69 | # to write the data, and you can't increase the table's size, but 70 | # writing the data will be significantly faster. 71 | # * `unclean_set_num_rows` allocates memory, but does not zero it. But 72 | # since we'll be writing over it anyway, why bother? 73 | v_data.unclean_set_num_rows(4) 74 | vertex = GeomVertexWriter(v_data, InternalName.get_vertex()) 75 | color = GeomVertexWriter(v_data, InternalName.get_color()) 76 | 77 | # And here we go... Top Left will be red. 78 | vertex.set_data3f(-1, 0, 1) 79 | color.set_data4f(1, 0, 0, 1) 80 | 81 | # Top Right is green. 82 | vertex.set_data3f(1, 0, 1) 83 | color.set_data4f(0, 1, 0, 1) 84 | 85 | # Bottom Left is blue. 86 | vertex.set_data3f(-1, 0, -1) 87 | color.set_data4f(0, 0, 1, 1) 88 | 89 | # And Bottom Right is grey. 90 | vertex.set_data3f(1, 0, -1) 91 | color.set_data4f(0.5, 0.5, 0.5, 1) 92 | 93 | # At this point, we have specifieed all points of the Geom. What we have 94 | # not spent a single thought on is that those points will also have to 95 | # be connected. As it is currently 2023, the answer is "triangles"; 96 | # Points and Lines/Linestrips may or may not still work for a given GPU 97 | # and driver with or without their full capabilities, while 98 | # TriangleStrips and TriangleFans are hard-to-work-with optimizations 99 | # that usually get converted back to triangles by the driver anyway, as 100 | # the GPU's bandwidth has increased significantly since The Old Days. 101 | # The order in which we have written the vertices determines the indices 102 | # that we use to refer to them; Top Left is 0, Top Right 1, etc. 103 | # The order in which we specify them (winding order) determines in which 104 | # direction the triangle faces; If you can see a triangle, you can go 105 | # counter-clockwise from vertex to vertex. 106 | tris = GeomTriangles(Geom.UHStatic) 107 | tris.add_vertices(2, 1, 0) # Bottom Left, Top Right, Top Left 108 | tris.add_vertices(2, 3, 1) # Bottom Left, Bottom Right, Top Right 109 | # If we used Strips or Fans, we would now have to call 110 | # `tris.close_primitive()` to indicate that a new Strip/Fan is about to 111 | # start. In our case, that call would do literally nothing. 112 | 113 | # Now we store the vertex data and primitives in a Geom, store that in 114 | # a GeomNode, and store that in turn in a NodePath, so it can be readily 115 | # attached to the scene graph. 116 | geom = Geom(v_data) 117 | geom.add_primitive(tris) 118 | node = GeomNode('geom_node') 119 | node.add_geom(geom) 120 | surface = NodePath(node) 121 | 122 | # Done. Now run `main.py` to see our square wobble around. 123 | -------------------------------------------------------------------------------- /geometry/main.py: -------------------------------------------------------------------------------- 1 | # This program places the model created in `basic_model.py` into a scene 2 | # so that we can actually see it, and animates it a little by moving it 3 | # around. Not much of interest can be learned here, but it serves as a 4 | # basis for later elaborations. 5 | 6 | import sys 7 | from math import pi 8 | from math import sin 9 | 10 | from panda3d.core import load_prc_file_data 11 | from panda3d.core import AmbientLight 12 | from panda3d.core import PointLight 13 | 14 | from direct.showbase.ShowBase import ShowBase 15 | 16 | import basic_model 17 | 18 | # First, let's get a ShowBase instance with an sRGB framebuffer in the 19 | # main window going, the background black, and with 'escape' as a quick 20 | # and easy exit. 21 | load_prc_file_data( 22 | '', 23 | 'framebuffer-srgb #t', 24 | ) 25 | ShowBase() 26 | base.win.set_clear_color((0, 0, 0, 1)) 27 | base.accept('escape', sys.exit) 28 | 29 | # There will be three things in our scene: A light source, a surface, 30 | # and a camera. There will also be an ambient light, because ambient 31 | # occlusion affects only that. 32 | ambient_light = base.render.attach_new_node(AmbientLight('ambient_light')) 33 | base.render.set_light(ambient_light) 34 | ambient_light.node().color = (0.2, 0.2, 0.2, 1) 35 | 36 | point_light = base.render.attach_new_node(PointLight('point_light')) 37 | base.render.set_light(point_light) 38 | point_light.node().color = (1, 1, 1, 1) 39 | point_light.set_z(5) 40 | 41 | surface = basic_model.surface 42 | surface.reparent_to(base.render) 43 | 44 | base.cam.set_y(-5) 45 | 46 | # Finally, just so we can see some action, let us make the surface move 47 | # a little. Also the camera is at the level of it, while the light 48 | # source is above it, so we need to tilt it by 45 degrees anyway to see 49 | # it at all. 50 | def do_the_lissajous_twist(task): 51 | time = task.time 52 | surface.set_hpr(0,0,0) 53 | surface.set_p(-45 + sin(time) * 7) 54 | surface.set_hpr(surface, sin(time * 3 + pi * 0.5) * 10, 0, 0) 55 | return task.cont 56 | 57 | base.task_mgr.add(do_the_lissajous_twist) 58 | 59 | # And now we are done, and let Panda3D do its magic. 60 | base.run() 61 | -------------------------------------------------------------------------------- /headless.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from panda3d.core import loadPrcFileData 4 | from direct.showbase.ShowBase import ShowBase 5 | 6 | 7 | loadPrcFileData("", "window-type none") 8 | s = ShowBase() 9 | base.task_mgr.do_method_later(3, print, "Panda3D is still running", ["foo"]) 10 | 11 | 12 | s.run() 13 | 14 | -------------------------------------------------------------------------------- /libRocket/demo.rml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | libRocket GUI 5 | 6 | 7 |
Your menus here
8 | 9 |
10 | 11 | -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/AUTHORS: -------------------------------------------------------------------------------- 1 | -*- mode:text; coding:utf-8; -*- 2 | GNU FreeFont Authors 3 | ==================== 4 | 5 | The FreeFont collection is being maintained by 6 | Steve White 7 | The folowing list cites the other contributors that contributed to 8 | particular ISO 10646 blocks. 9 | 10 | * URW++ Design & Development GmbH 11 | 12 | Basic Latin (U+0041-U+007A) 13 | Latin-1 Supplement (U+00C0-U+00FF) (most) 14 | Latin Extended-A (U+0100-U+017F) 15 | Spacing Modifier Letters (U+02B0-U+02FF) 16 | Mathematical Operators (U+2200-U+22FF) (parts) 17 | Block Elements (U+2580-U+259F) 18 | Dingbats (U+2700-U+27BF) 19 | 20 | * Yannis Haralambous and John 21 | Plaice 22 | 23 | Latin Extended-B (U+0180-U+024F) 24 | IPA Extensions (U+0250-U+02AF) 25 | Greek (U+0370-U+03FF) 26 | Armenian (U+0530-U+058F) 27 | Hebrew (U+0590-U+05FF) 28 | Arabic (U+0600-U+06FF) 29 | Currency Symbols (U+20A0-U+20CF) 30 | Arabic Presentation Forms-A (U+FB50-U+FDFF) 31 | Arabic Presentation Forms-B (U+FE70-U+FEFF) 32 | 33 | * Yannis Haralambous and Wellcome Institute 34 | 35 | Sinhala (U+0D80-U+0DFF) 36 | 37 | * Young U. Ryu 38 | 39 | Arrows (U+2190-U+21FF) 40 | Mathematical Symbols (U+2200-U+22FF) 41 | Mathematical Alphanumeric Symbols (U+1D400-U+1D7FF) 42 | 43 | * Valek Filippov 44 | 45 | Cyrillic (U+0400-U+04FF) 46 | 47 | * Wadalab Kanji Comittee 48 | 49 | Hiragana (U+3040-U+309F) 50 | Katakana (U+30A0-U+30FF) 51 | 52 | * Angelo Haritsis 53 | 54 | Greek (U+0370-U+03FF) 55 | 56 | * Yannis Haralambous and Virach Sornlertlamvanich 57 | 58 | Thai (U+0E00-U+0E7F) 59 | 60 | * Shaheed R. Haque 61 | 62 | Bengali (U+0980-U+09FF) 63 | 64 | * Sam Stepanyan 65 | 66 | Armenian (U+0530-U+058F) 67 | 68 | * Mohamed Ishan 69 | 70 | Thaana (U+0780-U+07BF) 71 | 72 | * Sushant Kumar Dash 73 | 74 | Oriya (U+0B00-U+0B7F) 75 | 76 | * Harsh Kumar 77 | 78 | Devanagari (U+0900-U+097F) 79 | Bengali (U+0980-U+09FF) 80 | Gurmukhi (U+0A00-U+0A7F) 81 | Gujarati (U+0A80-U+0AFF) 82 | 83 | * Prasad A. Chodavarapu 84 | 85 | Telugu (U+0C00-U+0C7F) 86 | 87 | * Frans Velthuis and Anshuman Pandey 88 | 89 | 90 | Devanagari (U+0900-U+097F) 91 | 92 | * Hardip Singh Pannu 93 | 94 | Gurmukhi (U+0A00-U+0A7F) 95 | 96 | * Jeroen Hellingman 97 | 98 | Oriya (U+0B00-U+0B7F) 99 | Malayalam (U+0D00-U+0D7F) 100 | 101 | * Thomas Ridgeway 102 | 103 | Tamil (U+0B80-U+0BFF) 104 | 105 | * Berhanu Beyene <1beyene AT informatik.uni-hamburg.de>, 106 | Prof. Dr. Manfred Kudlek , Olaf 107 | Kummer , and Jochen Metzinger 108 | 109 | Ethiopic (U+1200-U+137F) 110 | 111 | * Maxim Iorsh 112 | 113 | Hebrew (U+0590-U+05FF) 114 | 115 | * Vyacheslav Dikonov 116 | 117 | Syriac (U+0700-U+074A) 118 | Braille (U+2800-U+28FF) 119 | 120 | * Panayotis Katsaloulis 121 | 122 | Greek Extended (U+1F00-U+1FFF) 123 | 124 | * M.S. Sridhar 125 | 126 | Devanagari (U+0900-U+097F) 127 | Bengali (U+0980-U+09FF) 128 | Gurmukhi (U+0A00-U+0A7F) 129 | Gujarati (U+0A80-U+0AFF) 130 | Oriya (U+0B00-U+0B7F) 131 | Tamil (U+0B80-U+0BFF) 132 | Telugu (U+0C00-U+0C7F) 133 | Kannada (U+0C80-U+0CFF) 134 | Malayalam (U+0D00-U+0D7F) 135 | 136 | * DMS Electronics, The Sri Lanka Tipitaka Project, and Noah Levitt 137 | 138 | 139 | Sinhala (U+0D80-U+0DFF) 140 | 141 | * Dan Shurovich Chirkov 142 | 143 | Cyrillic (U+0400-U+04FF) 144 | 145 | * Abbas Izad 146 | 147 | Arabic (U+0600-U+06FF) 148 | Arabic Presentation Forms-A (U+FB50-U+FDFF) 149 | Arabic Presentation Forms-B (U+FE70-U+FEFF) 150 | 151 | * Denis Jacquerye 152 | 153 | Latin Extended-B (U+0180-U+024F) 154 | IPA Extensions (U+0250-U+02AF) 155 | 156 | * K.H. Hussain and R. Chitrajan 157 | 158 | Malayalam (U+0D00-U+0D7F) 159 | 160 | * Solaiman Karim and Omi Azad 161 | 162 | Bengali (U+0980-U+09FF) 163 | 164 | * Sonali Sonania and Monika Shah 165 | 166 | 167 | Devanagari (U+0900-U+097F) 168 | Gujarati (U+0A80-U+0AFF) 169 | 170 | * Pravin Satpute , Bageshri Salvi 171 | , Rahul Bhalerao and Sandeep Shedmake 173 | 174 | Devanagari (U+0900-U+097F) 175 | Gujarati (U+0A80-U+0AFF) 176 | Oriya (U+0B00-U+0B7F) 177 | Malayalam (U+0D00-U+0D7F) 178 | Tamil (U+0B80-U+0BFF) 179 | 180 | * Kulbir Singh Thind 181 | 182 | Gurmukhi (U+0A00-U+0A7F) 183 | 184 | * Gia Shervashidze 185 | 186 | Georgian (U+10A0-U+10FF) 187 | 188 | * Daniel Johnson 189 | 190 | Armenian (serif) (U+0530-U+058F) 191 | Cherokee (U+13A0-U+13FF) 192 | Unified Canadian Aboriginal Syllabics (U+1400-U+167F) 193 | UCAS Extended (U+18B0-U+18F5) 194 | Tifinagh (U+2D30-U+2D7F) 195 | Vai (U+A500-U+A62B) 196 | Latin Extended-D (Mayanist letters) (U+A720-U+A7FF) 197 | Kayah Li (U+A900-U+A92F) 198 | Osmanya (U+10480-U+104a7) 199 | 200 | * George Douros 201 | 202 | Gothic (U+10330-U+1034F) 203 | Phoenecian (U+10900-U+1091F) 204 | Byzantine Musical Symbols (U+1D000-U+1D0FF) 205 | Western Musical Symbols (U+1D100-U+1D1DF) 206 | Mathematical Alphanumeric Symbols (U+1D400-U+1D7FF) 207 | Mah Jong Tiles (U+1F000-U+1F02B) 208 | Dominoes (U+1F030-U+1F093) 209 | 210 | * Steve White 211 | Glagolitic (U+2C00-U+2C5F) 212 | Coptic (U+2C80-U+2CFF) 213 | 214 | * Pavel Skrylev is responsible for 215 | Cyrillic Extended-A (U+2DEO-U+2DFF) 216 | as well as many of the additions to 217 | Cyrillic Extended-B (U+A640-U+A65F) 218 | 219 | * Mark Williamson 220 | Made the MPH 2 Damase font, from which 221 | Hanunóo (U+1720-U+173F) 222 | Buginese (U+1A00-U+1A1F) 223 | Tai Le (U+1950-U+197F) 224 | Ugaritic (U+10380-U+1039F) 225 | Old Persian (U+103A0-U+103DF) 226 | 227 | 228 | * Primož Peterlin 229 | maintained FreeFont for several years, and is thanked for all his work. 230 | 231 | Please see the CREDITS file for details on who contributed particular 232 | subsets of the glyphs in font files. 233 | 234 | -------------------------------------------------------------------------- 235 | $Id: AUTHORS,v 1.23 2010/09/11 13:24:11 Stevan_White Exp $ 236 | -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/CREDITS: -------------------------------------------------------------------------------- 1 | -*- mode:text; coding:utf-8; -*- 2 | GNU FreeFont Credits 3 | ==================== 4 | 5 | This file lists contributors and contributions to the GNU FreeFont project. 6 | 7 | 8 | * URW++ Design & Development GmbH 9 | 10 | URW++ donated a set of 35 core PostScript Type 1 fonts to the 11 | Ghostscript project , to be available 12 | under the terms of GNU General Public License (GPL). 13 | 14 | Basic Latin (U+0041-U+007A) 15 | Latin-1 Supplement (U+00C0-U+00FF) 16 | Latin Extended-A (U+0100-U+017F) 17 | Spacing Modifier Letters (U+02B0-U+02FF) 18 | Mathematical Operators (U+2200-U+22FF) 19 | Block Elements (U+2580-U+259F) 20 | Dingbats (U+2700-U+27BF) 21 | 22 | 23 | * Yannis Haralambous and John 24 | Plaice 25 | 26 | Yannis Haralambous and John Plaice are the authors of Omega typesetting 27 | system, . Omega is an extension of TeX. 28 | Its first release, aims primarily at improving TeX's multilingual abilities. 29 | In Omega all characters and pointers into data-structures are 16-bit wide, 30 | instead of 8-bit, thereby eliminating many of the trivial limitations of TeX. 31 | Omega also allows multiple input and output character sets, and uses 32 | programmable filters to translate from one encoding to another, to perform 33 | contextual analysis, etc. Internally, Omega uses the universal 16-bit Unicode 34 | standard character set, based on ISO-10646. These improvements not only make 35 | it a lot easier for TeX users to cope with multiple or complex languages, 36 | like Arabic, Indic, Khmer, Chinese, Japanese or Korean, in one document, but 37 | will also form the basis for future developments in other areas, such as 38 | native color support and hypertext features. ... Fonts for UT1 (omlgc family) 39 | and UT2 (omah family) are under development: these fonts are in PostScript 40 | format and visually close to Times and Helvetica font families. 41 | Omega fonts are available subject to GPL 42 | 43 | Latin Extended-B (U+0180-U+024F) 44 | IPA Extensions (U+0250-U+02AF) 45 | Greek (U+0370-U+03FF) 46 | Armenian (U+0530-U+058F) 47 | Hebrew (U+0590-U+05FF) 48 | Arabic (U+0600-U+06FF) 49 | Currency Symbols (U+20A0-U+20CF) 50 | Arabic Presentation Forms-A (U+FB50-U+FDFF) 51 | Arabic Presentation Forms-B (U+FE70-U+FEFF) 52 | 53 | Current info: 54 | 55 | * Valek Filippov 56 | 57 | Valek Filippov added Cyrillic glyphs and composite Latin Extended A to 58 | the whole set of the abovementioned URW set of 35 PostScript core fonts, 59 | . The fonts are available under GPL. 60 | 61 | Latin Extended-A (U+0100-U+017F) 62 | Cyrillic (U+0400-U+04FF) 63 | 64 | 65 | * Wadalab Kanji Comittee 66 | 67 | Between April 1990 and March 1992, Wadalab Kanji Comittee put together 68 | a series of scalable font files with Japanese scripts, in four forms: 69 | Sai Micho, Chu Mincho, Cho Kaku and Saimaru. The font files are 70 | written in custom file format, while tools for conversion into 71 | Metafont and PostScript Type 1 are also supplied. The Wadalab Kanji 72 | Comittee has later been dismissed, and the resulting files can be now 73 | found on the FTP server of the Depertment of Mathematical Engineering 74 | and Information Physics, Faculty of Engineering, University of Tokyo 75 | . 76 | 77 | Hiragana (U+3040-U+309F) 78 | Katakana (U+30A0-U+30FF) 79 | 80 | 81 | * Young U. Ryu 82 | 83 | Young Ryu is the author of Txfonts, a set of mathematical symbols 84 | designed to accompany text typeset in Times or its variants. In the 85 | documentation, Young adresses the design of mathematical symbols: "The 86 | Adobe Times fonts are thicker than the CM fonts. Designing math fonts 87 | for Times based on the rule thickness of Times = , , + , / , < , 88 | etc. would result in too thick math symbols, in my opinion. In the TX 89 | fonts, these glyphs are thinner than those of original Times 90 | fonts. That is, the rule thickness of these glyphs is around 85% of 91 | that of the Times fonts, but still thicker than that of the CM fonts." 92 | TX fonts are are distributed under the GNU public license (GPL). 93 | . 94 | 95 | Arrows (U+2190-U+21FF) 96 | Mathematical Symbols (U+2200-U+22FF) 97 | 98 | 99 | * Angelo Haritsis 100 | 101 | Angelo Haritsis has compiled a set of Greek Type 1 fonts, available on 102 | . 103 | The glyphs from this source has been used to compose Greek glyphs in 104 | FreeSans and FreeMono. 105 | 106 | Angelo's licence says: "You can enjoy free use of these fonts for 107 | educational or commercial purposes. All derived works should include 108 | this paragraph. If you want to change something please let me have 109 | your changes (via email) so that they can go into the next 110 | version. You can also send comments etc to the above address." 111 | 112 | Greek (U+0370-U+03FF) 113 | 114 | 115 | * Yannis Haralambous and Virach Sornlertlamvanich 116 | 117 | In 1999, Yannis Haralambous and Virach Sornlertlamvanich made a set of 118 | glyphs covering the Thai national standard Nf3, in both upright and 119 | slanted shape. The collection of glyphs have been made part of GNU 120 | intlfonts 1.2 package and is available under the GPL at 121 | . 122 | 123 | Thai (U+0E00-U+0E7F) 124 | 125 | * Shaheed R. Haque 126 | 127 | Shaheed Haque has developed a basic set of basic Bengali glyphs 128 | (without ligatures), using ISO10646 encoding. They are available under 129 | the XFree86 license at . 130 | 131 | Copyright (C) 2001 S.R.Haque . All Rights Reserved. 132 | 133 | Permission is hereby granted, free of charge, to any person obtaining 134 | a copy of this software and associated documentation files (the 135 | "Software"), to deal in the Software without restriction, including 136 | without limitation the rights to use, copy, modify, merge, publish, 137 | distribute, sublicense, and/or sell copies of the Software, and to 138 | permit persons to whom the Software is furnished to do so, subject to 139 | the following conditions: 140 | 141 | The above copyright notice and this permission notice shall be 142 | included in all copies or substantial portions of the Software. 143 | 144 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 145 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 146 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 147 | IN NO EVENT SHALL S.R.HAQUE BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 148 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 149 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 150 | OTHER DEALINGS IN THE SOFTWARE. 151 | 152 | Except as contained in this notice, the name of S.R.Haque shall not be 153 | used in advertising or otherwise to promote the sale, use or other 154 | dealings in this Software without prior written authorization from 155 | S.R.Haque. 156 | 157 | Bengali (U+0980-U+09FF) 158 | 159 | 160 | * Sam Stepanyan 161 | 162 | Sam Stepanyan created a set of Armenian sans serif glyphs visually 163 | compatible with Helvetica or Arial. Available on 164 | . On 165 | 2002-01-24, Sam writes: "Arial Armenian font is free for 166 | non-commercial use, so it is OK to use under GPL license." 167 | 168 | Armenian (U+0530-U+058F) 169 | 170 | 171 | * Mohamed Ishan 172 | 173 | Mohamed Ishan has started a Thaana Unicode Project 174 | and among other things created a 175 | couple of Thaana fonts, available under FDL or BDF license. 176 | 177 | Thaana (U+0780-U+07BF) 178 | 179 | 180 | * Sushant Kumar Dash (*) 181 | 182 | Sushant Dash has created a font in his mother tongue, Oriya. As he 183 | states on his web page : 184 | "Please feel free to foreword this mail to your Oriya friends. No 185 | copyright law is applied for this font. It is totally free!!! Feel 186 | free to modify this using any font editing tools. This is designed for 187 | people like me, who are away from Orissa and want to write letters 188 | home using Computers, but suffer due to unavailability of Oriya 189 | fonts.(Or the cost of the available packages are too much)." 190 | 191 | Oriya (U+0B00-U+0B7F) 192 | 193 | 194 | * Harsh Kumar 195 | 196 | Harsh Kumar has started BharatBhasha - 197 | an effort to provide "FREE software, Tutorial, Source Codes 198 | etc. available for working in Hindi, Marathi, Gujarati, Gurmukhi and 199 | Bangla. You can type text, write Web pages or develop Indian Languages 200 | Applications on Windows and on Linux. We also offer FREE help to 201 | users, enthusiasts and software developers for their work in Indian 202 | languages." 203 | 204 | Devanagari (U+0900-U+097F) 205 | Bengali (U+0980-U+09FF) 206 | Gurmukhi (U+0A00-U+0A7F) 207 | Gujarati (U+0A80-U+0AFF) 208 | 209 | 210 | * Prasad A. Chodavarapu 211 | 212 | Prasad A. Chodavarapu created Tikkana, a Telugu font available in Type 213 | 1 and TrueType format on . 214 | Tikkana exceeds the Unicode Telugu range with some composite glyphs. 215 | Available under the GNU General Public License. 216 | 217 | Telugu (U+0C00-U+0C7F) 218 | 219 | 220 | * Frans Velthuis and Anshuman Pandey 221 | 222 | 223 | In 1991, Frans Velthuis from the Groningen University, The 224 | Netherlands, released a Devanagari font as Metafont source, available 225 | under the terms of GNU GPL. Later, Anshuman Pandey from the Washington 226 | University, Seattle, USA, took over the maintenance of font. Fonts can 227 | be found on CTAN, . I 228 | converted the font to Type 1 format using Péter Szabó's TeXtrace 229 | program and removed some 230 | redundant control points with PfaEdit. 231 | 232 | Devanagari (U+0900-U+097F) 233 | 234 | 235 | * Hardip Singh Pannu 236 | 237 | In 1991, Hardip Singh Pannu has created a free Gurmukhi TrueType font, 238 | available as regular, bold, oblique and bold oblique form. Its license 239 | says "Please remember that these fonts are copyrighted (by me) and are 240 | for non-profit use only." 241 | 242 | Gurmukhi (U+0A00-U+0A7F) 243 | 244 | 245 | * Jeroen Hellingman 246 | 247 | Jeroen Hellingman created a set of Malayalam metafonts in 1994, and a 248 | set of Oriya metafonts in 1996. Malayalam fonts were created as 249 | uniform stroke only, while Oriya metafonts exist in both uniform and 250 | modulated stroke. From private communication: "It is my intention to 251 | release the fonts under GPL, but not all copies around have this 252 | notice on them." Metafonts can be found on CTAN, 253 | and 254 | . 255 | 256 | Oriya (U+0B00-U+0B7F) 257 | Malayalam (U+0D00-U+0D7F) 258 | 259 | 260 | * Thomas Ridgeway <> (*) 261 | 262 | Thomas Ridgeway, then at the Humanities And Arts Computing Center, 263 | Washington University, Seattle, USA, (now defunct), created a Tamil 264 | metafont in 1990. Anshuman Pandey from the same university took over 265 | the maintenance of font. Fonts can be found at CTAN, 266 | . 267 | 268 | Tamil (U+0B80-U+0BFF) 269 | 270 | 271 | * Berhanu Beyene <1beyene AT informatik.uni-hamburg.de>, 272 | Prof. Dr. Manfred Kudlek , Olaf 273 | Kummer , and Jochen Metzinger 274 | 275 | Beyene, Kudlek, Kummer and Metzinger from the Theoretical Foundations 276 | of Computer Science, University of Hamburg, prepared a set of Ethiopic 277 | metafonts, found on 278 | . They also 279 | maintain home page on the Ethiopic font project, 280 | , 281 | and can be reached at . The current 282 | version of fonts is 0.7 (1998), and they are released under GNU GPL. I 283 | converted the fonts to Type 1 format using Péter Szabó's TeXtrace-A 284 | program and removed some 285 | redundant control points with PfaEdit. 286 | 287 | Ethiopic (U+1200-U+137F) 288 | 289 | 290 | * Maxim Iorsh 291 | 292 | In 2002, Maxim Iorsh started the Culmus project, aiming at providing 293 | Hebrew-speaking Linux and Unix community with a basic collection of 294 | Hebrew fonts for X Windows. The fonts are visually compatible with 295 | URW++ Century Schoolbook L, URW++ Nimbus Sans L and URW++ Nimbus Mono 296 | L families, respectively, and are released under GNU GPL license. See 297 | also . 298 | 299 | Hebrew (U+0590-U+05FF) 300 | 301 | 302 | * Panayotis Katsaloulis 303 | 304 | Panayotis Katsaloulis helped fixing Greek accents in the Greek 305 | Extended area. 306 | 307 | Greek Extended (U+1F00-U+1FFF) 308 | 309 | 310 | * Vyacheslav Dikonov 311 | 312 | Vyacheslav Dikonov made a Braille unicode font that could be merged 313 | with the UCS fonts to fill the 2800-28FF range completely. (uniform 314 | scaling is possible to adapt it to any cell size). He also contributed 315 | a free syriac font, whose glyphs (about half of them) are borrowed 316 | from the "Carlo Ator" font freely downloadable from 317 | . Vyacheslav also filled in a few missing 318 | spots in the U+2000-U+27FF area, e.g. the box drawing section, sets of 319 | subscript and superscript digits and capital Roman numbers. 320 | 321 | Syriac (U+0700-U+074A) 322 | Box Drawing (U+2500-U+257F) 323 | Braille (U+2800-U+28FF) 324 | 325 | 326 | * M.S. Sridhar 327 | 328 | M/S Cyberscape Multimedia Limited, Mumbai, developers of Akruti 329 | Software for Indian Languages (http://www.akruti.com/), have released 330 | a set of TTF fonts for nine Indian scripts (Devanagari, Gujarati, 331 | Telugu, Tamil, Malayalam, Kannada, Bengali, Oriya, and Gurumukhi) 332 | under the GNU General Public License (GPL). You can download the fonts 333 | from the Free Software Foundation of India WWW site 334 | (http://www.gnu.org.in/akruti-fonts/) or from the Akruti website. 335 | 336 | For any further information or assistance regarding these fonts, 337 | please contact mssridhar AT vsnl.com. 338 | 339 | Devanagari (U+0900-U+097F) 340 | Bengali (U+0980-U+09FF) 341 | Gurmukhi (U+0A00-U+0A7F) 342 | Gujarati (U+0A80-U+0AFF) 343 | Oriya (U+0B00-U+0B7F) 344 | Tamil (U+0B80-U+0BFF) 345 | Telugu (U+0C00-U+0C7F) 346 | Kannada (U+0C80-U+0CFF) 347 | Malayalam (U+0D00-U+0D7F) 348 | 349 | 350 | * DMS Electronics, The Sri Lanka Tipitaka Project, and Noah Levitt 351 | 352 | 353 | Noah Levitt found out that the Sinhalese fonts available on the site 354 | are released under GNU GPL, or, 355 | precisely, "Public Domain under GNU Licence Produced by DMS 356 | Electronics for The Sri Lanka Tipitaka Project" (taken from the font 357 | comment), and took the effort of recoding the font to Unicode. 358 | 359 | These glyphs were later replaced by those from the LKLUG font 360 | 361 | 362 | Finally the range was completely replaced by glyphs from the sinh TeX 363 | font, with much help and advice from Harshula Jayasuriya. 364 | 365 | Sinhala (U+0D80-U+0DFF) 366 | 367 | 368 | * Daniel Shurovich Chirkov 369 | 370 | Dan Chirkov updated the FreeSerif font with the missing Cyrillic 371 | glyphs needed for conformance to Unicode 3.2. The effort is part of 372 | the Slavjanskij package for Mac OS X, 373 | . 374 | 375 | Cyrillic (U+0400-U+04FF) 376 | 377 | 378 | * Denis Jacquerye 379 | 380 | Denis Jacquerye added new glyphs and corrected existing ones in the 381 | Latin Extended-B and IPA Extensions ranges. 382 | 383 | Latin Extended-B (U+0180-U+024F) 384 | IPA Extensions (U+0250-U+02AF) 385 | 386 | 387 | * K.H. Hussain and R. Chitrajan 388 | 389 | `Rachana' in Malayalam means `to write', `to create'. Rachana Akshara Vedi, 390 | a team of socially committed information technology professionals and 391 | philologists, has applied developments in computer technology and desktop 392 | publishing to resurrect the Malayalam language from the disorder, 393 | fragmentation and degeneration it had suffered since the attempt to adapt 394 | the Malayalam script for using with a regular mechanical typewriter, which 395 | took place in 1967-69. K.H. Hussein at the Kerala Forest Research Institute 396 | has released "Rachana Normal" fonts with approximately 900 glyphs required 397 | to typeset traditional Malayalam. R. Chitrajan apparently encoded the 398 | glyphs in the OpenType table. 399 | 400 | In 2008, the Malayalam ranges in FreeSerif were updated under the advise 401 | and supervision of Hiran Venugopalan of Swathanthra Malayalam Computing, 402 | to reflect the revised edition Rachana_04. 403 | 404 | Malayalam (U+0D00-U+0D7F) 405 | 406 | 407 | * Solaiman Karim 408 | 409 | Bengali (U+0980-U+09FF) 410 | 411 | Solaiman Karim has developed several OpenType Bangla fonts and 412 | released them under GNU GPL on . 413 | 414 | 415 | * Sonali Sonania and Monika Shah 416 | 417 | 418 | Devanagari (U+0900-U+097F) 419 | Gujarati (U+0A80-U+0AFF) 420 | 421 | Glyphs were drawn by Cyberscape Multimedia Ltd., #101,Mahalakshmi 422 | Mansion 21st Main 22nd "A" Cross Banashankari 2nd stage Banglore 423 | 560070, India. Converted to OTF by IndicTrans Team, Powai, Mumbai, 424 | lead by Prof. Jitendra Shah. Maintained by Monika Shah and Sonali 425 | Sonania of janabhaaratii Team, C-DAC, Mumbai. This font is released 426 | under GPL by Dr. Alka Irani and Prof Jitendra Shah, janabhaaratii 427 | Team, C-DAC, Mumabi. janabhaaratii is localisation project at C-DAC 428 | Mumbai (formerly National Centre for Software Technology); funded by 429 | TDIL, Govt. of India. Contact:monika_shah AT lycos.com, 430 | sonalisonania AT yahoo.com, jitendras AT vsnl.com, alka AT ncst.ernet.in. 431 | website: www.janabhaaratii.org.in. 432 | 433 | 434 | * Pravin Satpute , Bageshri Salvi 435 | , Rahul Bhalerao and Sandeep Shedmake 437 | 438 | Devanagari (U+0900-U+097F) 439 | Gujarati (U+0A80-U+0AFF) 440 | Oriya (U+0B00-U+0B7F) 441 | Malayalam (U+0D00-U+0D7F) 442 | Tamil (U+0B80-U+0BFF) 443 | 444 | In December 2005 the team at www.gnowledge.org released a set of two 445 | Unicode pan-Indic fonts: "Samyak" and "Samyak Sans". "Samyak" font 446 | belongs to serif style and is an original work of the team; "Samyak 447 | Sans" font belongs to sans serif style and is actually a compilation 448 | of already released Indic fonts (Gargi, Padma, Mukti, Utkal, Akruti 449 | and ThendralUni). Both fonts are based on Unicode standard. You can 450 | download the font files (released under GNU/GPL License) from 451 | http://www.gnowledge.org/Gnoware/localization/font.htm 452 | 453 | 454 | * Kulbir Singh Thind 455 | 456 | Gurmukhi (U+0A00-U+0A7F) 457 | 458 | Dr. Kulbir Singh Thind designed a set of Gurmukhi Unicode fonts, 459 | AnmolUni and AnmolUni-Bold, which are available under the terms of GNU 460 | Generel Public Licens from the Punjabu Computing Resource Center, 461 | http://guca.sourceforge.net/typography/fonts/anmoluni/. 462 | 463 | 464 | * Gia Shervashidze 465 | 466 | Georgian (U+10A0-U+10FF) 467 | 468 | Starting in mid-1990s, Gia Shervashidze designed many 469 | Unicode-compliant Georgian fonts: Times New Roman Georgian, Arial 470 | Georgian, Courier New Georgian. His work on Georgian localization can 471 | be reached at http://www.gia.ge/. 472 | 473 | 474 | * Primož Peterlin 475 | 476 | Primož Peterlin filled in missing glyphs here and there (e.g. Latin 477 | Extended-B and IPA Extensions ranges in the FreeMono familiy), and 478 | created the following UCS blocks: 479 | 480 | Latin Extended-B (U+0180-U+024F) 481 | IPA Extensions (U+0250-U+02AF) 482 | Arrows (U+2190-U+21FF) 483 | Box Drawing (U+2500-U+257F) 484 | Block Elements (U+2580-U+259F) 485 | Geometrical Shapes (U+25A0-U+25FF) 486 | 487 | * Mark Williamson 488 | 489 | Made the MPH 2 Damase font, from which 490 | Hanunóo (U+1720-U+173F) 491 | Buginese (U+1A00-U+1A1F) 492 | Tai Le (U+1950-U+197F) 493 | Ugaritic (U+10380-U+1039F) 494 | Old Persian (U+103A0-U+103DF) 495 | 496 | * Jacob Poon 497 | 498 | Submitted a very thorough survey of glyph problems and other suggestions. 499 | 500 | * Alexey Kryukov 501 | 502 | Made the TemporaLCGUni fonts, based on the URW++ fonts, from which at one 503 | point FreeSerif Cyrillic, and some of the Greek, was drawn. He also provided 504 | valuable direction about Cyrillic and Greek typesetting. 505 | 506 | * George Douros 507 | 508 | The creator of several fonts focusing on ancient scripts and symbols. 509 | Many of the glyphs are created by making outlines from scanned images 510 | of ancient sources. 511 | 512 | Aegean: Phoenecian 513 | Analecta: Gothic (U+10330-U+1034F) 514 | Musical: Byzantine & Western 515 | Unicode: many Miscellaneous Symbols, Miscellaneous Technical, 516 | supplemental Symbols, and Mathematical Alphanumeric symbols, 517 | Mah Jong, and the outline of the Domino. 518 | 519 | * Daniel Johnson 520 | 521 | Created by hand a Cherokee range specially for FreeFont to be "in line with 522 | the classic Cherokee typefaces used in 19th century printing", but also to 523 | fit well with ranges previously in FreeFont. Then he made Unified Canadian 524 | Syllabics in Sans, and a Cherokee and Kayah Li in Mono! And never to be 525 | outdone by himself, then did UCAS Extended and Osmanya.... What next? 526 | 527 | Armenian (serif) (U+0530-U+058F) 528 | Cherokee (U+13A0-U+13FF) 529 | Unified Canadian Aboriginal Syllabics (U+1400-U+167F) 530 | UCAS Extended (U+18B0-U+18F5) 531 | Kayah Li (U+A900-U+A92F) 532 | Tifinagh (U+2D30-U+2D7F) 533 | Vai (U+A500-U+A62B) 534 | Latin Extended-D (Mayanist letters) (U+A720-U+A7FF) 535 | Osmanya (U+10480-U+104a7) 536 | 537 | * Yannis Haralambous and Wellcome Institute 538 | 539 | In 1994, The Wellcome Library 540 | The Wellcome Institute for the History of Medicine 541 | 183 Euston Road, London NW1 2BE, England. 542 | commissioned Mr. Haralambous to produce a Sinhalese font for them. 543 | 544 | We have received 03/09 official notice from Robert Kiley, Head of e-Strategy 545 | for the Wellcome Library, that Yannis' font could be included in GNU 546 | FreeFont under its GNU license. 547 | 548 | Thanks to Dominik Wujastyk, for providing us with feedback and contacts 549 | to repsonsible people at the Trust. 550 | 551 | Sinhala (U+0D80-U+0DFF) 552 | 553 | * The Sinhala font project http://sinhala.sourceforge.net/ 554 | 555 | The Sinhala font project has taken the glyphs from Yannis Haralambous' 556 | Sinhala font, to produce a Unicode TrueType font, LKLUG. These glyphs 557 | were for a while included in FreeFont. 558 | 559 | Sinhala (U+0D80-U+0DFF) 560 | 561 | * Steve White 562 | 563 | Filled in a lot of missing characters, got some font features working, 564 | left fingerprints almost everywhere, and is responsible for these blocks: 565 | 566 | Glagolitic (U+2C00-U+2C5F) 567 | Coptic (U+2C80-U+2CFF) 568 | 569 | * Pavel Skrylev is responsible for 570 | Cyrillic Extended-A (U+2DEO-U+2DFF) 571 | as well as many of the additions to 572 | Cyrillic Extended-B (U+A640-U+A65F) 573 | 574 | Notes: 575 | 576 | *: The glyph collection looks license-compatible, but its author has 577 | not yet replied and agreed on their work being used in part of 578 | this glyph collection. 579 | 580 | -------------------------------------------------------------------------- 581 | $Id: CREDITS,v 1.28 2010/09/11 13:24:11 Stevan_White Exp $ 582 | -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSans.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSans.otf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSans.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSans.ttf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSansBold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSansBold.otf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSansBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSansBold.ttf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSansBoldOblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSansBoldOblique.otf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSansBoldOblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSansBoldOblique.ttf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSansOblique.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSansOblique.otf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/FreeSansOblique.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/libRocket/gnu-freefont_freesans/FreeSansOblique.ttf -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/INSTALL: -------------------------------------------------------------------------------- 1 | Installing GNU FreeFont 2 | ======================= 3 | 4 | GNU FreeFont can be used in any modern operating system. 5 | 6 | This document explains how to install FreeFont on some common systems. 7 | 8 | UNIX/GNU/Linux/BSD Systems 9 | -------------------------- 10 | 11 | FreeFont works with any system using the free font rasterizer FreeType 12 | . 13 | 14 | * Debian GNU/Linux 15 | 16 | Users of Debian GNU/Linux system will probably want to use the Debian package, 17 | available from the Debian site, 18 | 19 | , 20 | 21 | or any of its mirrors. 22 | 23 | Install them by issuing the command 24 | apt-get install ttf-freefont 25 | 26 | 27 | * KDE local installation 28 | 29 | Users of KDE can install .ttf files on a per-user basis using the KDE 30 | Control Center module "kcmfontinst", which may appear in the menu as 31 | 32 | Settings -> System Administration -> Font Installer 33 | 34 | This is especially helpful for developers and testers. 35 | 36 | 37 | * Generic X-windows 38 | 39 | 1) Fetch the freefont-ttf.tar.gz package with Free UCS outline fonts 40 | in the TrueType format. 41 | 42 | 2) Unpack TrueType fonts into a suitable directory, 43 | e.g. /usr/local/share/fonts/default/TrueType/ 44 | 45 | 3) If you have chosen any other directory, make sure the directory you 46 | used to install the fonts is listed in the path searched by the X 47 | Font Server by editing the config file in /etc/X11/. 48 | 49 | In some systems, you list the directory in the item "catalogue=" 50 | in the file /etc/X11/fs/config. 51 | 52 | 4) Run ttmkfdir in the directory where you unpacked the fonts. 53 | 54 | 55 | Windows 95/98/NT/2000/XP; Vista 56 | ------------------------------- 57 | 58 | Note that in at least Vista, XP and 2000, the OpenType versions perform much 59 | better than, and are recommended over, the TrueType ones. 60 | 61 | * Vista: 62 | 1) From the Start menu, open Control Panels 63 | 2) Drag-n-drop font files onto Fonts control panel 64 | You may get a dialog saying 65 | "Windows needs your permission to continue" 66 | a) Click Continue 67 | 68 | * 95/98/NT: 69 | The font installation is similar to Vista. 70 | 71 | In order to use OpenType, users of Windows 95, 98 and NT 4.0 can 72 | install Adobe's 'Type Manager Light'. It is available for download 73 | without cost from Adobe's web site. 74 | 75 | Otherwise, use the TrueType versions. 76 | 77 | Mac OS X 78 | -------- 79 | 80 | Installing on Mac OS X consists of moving the .ttf files to either 81 | /Library/Fonts/ or ~/Library/Fonts/ 82 | depending on whether they should be available to all users on your system 83 | or just to yourself. 84 | 85 | -------------------------------------------------------------------------- 86 | $Id: INSTALL,v 1.7 2008/12/26 12:33:31 Stevan_White Exp $ 87 | -------------------------------------------------------------------------------- /libRocket/gnu-freefont_freesans/README: -------------------------------------------------------------------------------- 1 | -*-text-*- 2 | GNU FreeFont 3 | 4 | The GNU FreeFont project aims to provide a useful set of free scalable 5 | (i.e., OpenType) fonts covering as much as possible of the ISO 10646/Unicode 6 | UCS (Universal Character Set). 7 | 8 | Statement of Purpose 9 | -------------------- 10 | 11 | The practical reason for putting glyphs together in a single font face is 12 | to conveniently mix symbols and characters from different writing systems, 13 | without having to switch fonts. 14 | 15 | Coverage 16 | -------- 17 | 18 | FreeFont covers the following character sets 19 | 20 | * ISO 8859 parts 1-15 21 | * CEN MES-3 European Unicode Subset 22 | http://www.evertype.com/standards/iso10646/pdf/cwa13873.pdf 23 | * IBM/Microsoft code pages 437, 850, 852, 1250, 1252 and more 24 | * Microsoft/Adobe Windows Glyph List 4 (WGL4) 25 | http://www.microsoft.com/typography/otspec/WGL4.htm 26 | * KOI8-R and KOI8-RU 27 | * DEC VT100 graphics symbols 28 | * International Phonetic Alphabet 29 | * Arabic, Hebrew, Armenian, Georgian, Ethiopian and Thai alphabets, 30 | including Arabic presentation forms A/B 31 | * mathematical symbols, including the whole TeX repertoire of symbols 32 | * APL symbols 33 | etc. 34 | 35 | Editing 36 | ------- 37 | 38 | The free outline font editor, George Williams's FontForge 39 | is used for editing the fonts. 40 | 41 | Design Issues 42 | ------------- 43 | 44 | Which font shapes should be made? Historical style terms like Renaissance 45 | or Baroque letterforms cannot be applied beyond Latin/Cyrillic/Greek 46 | scripts to any greater extent than Kufi or Nashki can be applied beyond 47 | Arabic script; "italic" is really only meaningful for Latin letters. 48 | 49 | However, most modern writing systems have typographic formulations for 50 | contrasting uniform and modulated character stroke widths, and have some 51 | history with "oblique", faces. Since the advent of the typewriter, most 52 | have developed a typographic style with uniform-width characters. 53 | 54 | Accordingly, the FreeFont family has one monospaced - FreeMono - and two 55 | proportional faces (one with uniform stroke - FreeSans - and one with 56 | modulated stroke - FreeSerif). 57 | 58 | To make text from different writing systems look good side-by-side, each 59 | FreeFont face is meant to contain characters of similar style and weight. 60 | 61 | Licensing 62 | --------- 63 | 64 | Free UCS scalable fonts is free software; you can redistribute it and/or 65 | modify it under the terms of the GNU General Public License as published 66 | by the Free Software Foundation; either version 3 of the License, or 67 | (at your option) any later version. 68 | 69 | The fonts are distributed in the hope that they will be useful, but 70 | WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 71 | or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 72 | for more details. 73 | 74 | You should have received a copy of the GNU General Public License along 75 | with this program; if not, write to the Free Software Foundation, Inc., 76 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 77 | 78 | As a special exception, if you create a document which uses this font, and 79 | embed this font or unaltered portions of this font into the document, this 80 | font does not by itself cause the resulting document to be covered by the 81 | GNU General Public License. This exception does not however invalidate any 82 | other reasons why the document might be covered by the GNU General Public 83 | License. If you modify this font, you may extend this exception to your 84 | version of the font, but you are not obligated to do so. If you do not 85 | wish to do so, delete this exception statement from your version. 86 | 87 | 88 | Files and their suffixes 89 | ------------------------ 90 | 91 | The files with .sfd (Spline Font Database) are in FontForge's native format. 92 | Please use these if you plan to modify the font files. 93 | 94 | TrueType fonts for immediate consumption are the files with the .ttf 95 | (TrueType Font) suffix. These are ready to use in Xwindows based 96 | systems using FreeType, on Mac OS, and on older Windows systems. 97 | 98 | OpenType fonts (with suffix .otf) are for use in Windows Vista. 99 | Note that although they can be installed on Linux, but many applications 100 | in Linux still don't support them. 101 | 102 | 103 | -------------------------------------------------------------------------- 104 | Primoz Peterlin, 105 | Steve White 106 | 107 | Free UCS scalable fonts: http://savannah.gnu.org/projects/freefont/ 108 | $Id: README,v 1.7 2009/01/13 08:43:23 Stevan_White Exp $ 109 | -------------------------------------------------------------------------------- /libRocket/libRocket.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from panda3d.rocket import RocketRegion, RocketInputHandler, LoadFontFace 5 | import sys 6 | 7 | class GUI(ShowBase): 8 | def __init__(self): 9 | # Basics 10 | ShowBase.__init__(self) 11 | base.disableMouse() 12 | self.accept("escape", sys.exit) 13 | 14 | # Loading a font 15 | LoadFontFace("./gnu-freefont_freesans/FreeSans.ttf") 16 | 17 | # Creating all necessary context 18 | self.region = RocketRegion.make('pandaRocket', base.win) 19 | self.region.setActive(1) 20 | self.region.initDebugger() 21 | self.region.setDebuggerVisible(False) 22 | self.context = self.region.getContext() 23 | 24 | # Loading documents 25 | self.documents = {} 26 | self.documents['main'] = self.context.LoadDocument('./demo.rml') 27 | # .Show() to display document, .Hide() to hide. 28 | self.documents['main'].Show() 29 | 30 | self.elementEffects = [] 31 | 32 | # setup the mouse 33 | self.inputHandler = RocketInputHandler() 34 | base.mouseWatcher.attachNewNode(self.inputHandler) 35 | self.region.setInputHandler(self.inputHandler) 36 | 37 | # taskMgr.doMethodLater(5, self.loadingFinished, 'HUD Loading Finished') 38 | 39 | # Setup the messages we accept. 40 | #self.accept('update', self.updateMessage) 41 | 42 | def update(self, dt): 43 | BaseDirectObject.update(self, dt) 44 | self.updateElementEffects(dt) 45 | 46 | def destroy(self): 47 | BaseDirectObject.destroy(self) 48 | 49 | for key in self.documents: 50 | self.documents[key].Close() 51 | self.context.UnloadDocument(self.documents[key]) 52 | del self.documents 53 | 54 | del self.context 55 | base.win.removeDisplayRegion(self.region) 56 | del self.region 57 | 58 | #def updateElementEffects(self, dt): 59 | # for elementEffect in self.elementEffects: 60 | # elementEffect.update(dt) 61 | # element = self.documents['main'].GetElementById(elementEffect.element) 62 | # if elementEffect.property == 'opacity': 63 | # element.style.opacity = elementEffect.currentValue() 64 | # elif elementEffect.property == 'top': 65 | # element.style.top = elementEffect.currentValue() 66 | # elif elementEffect.property == 'bottom': 67 | # element.style.bottom = elementEffect.currentValue() 68 | # elif elementEffect.property == 'left': 69 | # element.style.left = elementEffect.currentValue() 70 | # elif elementEffect.property == 'right': 71 | # element.style.right = elementEffect.currentValue() 72 | # if elementEffect.finished(): 73 | # self.elementEffects.remove(elementEffect) 74 | 75 | #def updateMessage(self, updateType, *args): 76 | # if updateType == "debug": 77 | # debugInfo1 = self.documents['main'].GetElementById('debugInfo1') 78 | # debugInfo2 = self.documents['main'].GetElementById('debugInfo2') 79 | # text = "" 80 | # for arg in args: 81 | # if text == "": 82 | # text = arg 83 | # else: 84 | # text = strcat(text, "
\n", arg) 85 | # debugInfo1.inner_rml = text 86 | # if updateType == "debug2": 87 | # debugInfo1 = self.documents['main'].GetElementById('debugInfo1') 88 | # debugInfo2 = self.documents['main'].GetElementById('debugInfo2') 89 | # text = "" 90 | # for arg in args: 91 | # if text == "": 92 | # text = arg 93 | # else: 94 | # text = strcat(text, "
\n", arg) 95 | # debugInfo2.inner_rml = text 96 | # elif updateType == "messageBox": 97 | # messageBox = self.documents['main'].GetElementById('messageBox') 98 | # text = "" 99 | # for arg in args: 100 | # if text == "": 101 | # text = arg 102 | # else: 103 | # text = strcat(text, "
\n", arg) 104 | # messageBox.inner_rml = text 105 | 106 | #def toggleGuiMode(self): 107 | # # no switching while things are moving 108 | # if self.elementEffects: 109 | # return self.guiMode 110 | 111 | # self.guiMode = not self.guiMode 112 | # self.setMessageBoxVisible(self.guiMode) 113 | # self.setHudVisible(not self.guiMode) 114 | 115 | # return self.guiMode 116 | 117 | #def setMessageBoxVisible(self, visible): 118 | # if visible: 119 | # self.documents['main'].GetElementById('messageBox').style.visibility = "visible" 120 | # else: 121 | # self.documents['main'].GetElementById('messageBox').style.visibility = "hidden" 122 | 123 | #def setHudVisible(self, visible, duration=0.1): 124 | # topContainer = self.documents['main'].GetElementById('topContainer') 125 | # bottomContainer = self.documents['main'].GetElementById('bottomContainer') 126 | # if not visible: 127 | # self.elementEffects.append(ElementMoveEffect('topContainer', 0, 0 - topContainer.offset_height, duration, 'top')) 128 | # self.elementEffects.append(ElementMoveEffect('bottomContainer', 0, 0 - bottomContainer.offset_height, duration, 'bottom')) 129 | # else: 130 | # self.elementEffects.append(ElementMoveEffect('topContainer', 0 - topContainer.offset_height, 0, duration, 'top')) 131 | # self.elementEffects.append(ElementMoveEffect('bottomContainer', 0 - bottomContainer.offset_height, 0, duration, 'bottom')) 132 | # self.updateElementEffects(0) 133 | 134 | #def toggleDebugger(self): 135 | # self.region.setDebuggerVisible(not self.region.isDebuggerVisible()) 136 | 137 | #def debuggerPrint(self, message, type="always"): 138 | # options = { 139 | # "always": logtype.always, 140 | # "error": logtype.error, 141 | # "warning": logtype.warning, 142 | # "info": logtype.info, 143 | # "debug": logtype.debug, 144 | # } 145 | # Log(options[type], message) 146 | 147 | #def loadingFinished(self, task=None): 148 | # self.setMessageBoxVisible(self.guiMode) 149 | # self.setHudVisible(not self.guiMode, 0) 150 | # self.docLoading.Hide() 151 | # self.documents['main'].Show() 152 | 153 | # if task: 154 | # return task.done 155 | 156 | #def removeCoordType(self, coord): 157 | # return float(str(coord).rstrip("%px")) 158 | 159 | gui = GUI() 160 | gui.run() 161 | 162 | -------------------------------------------------------------------------------- /libRocket/main.rcss: -------------------------------------------------------------------------------- 1 | .main { 2 | font-family: FreeSans; 3 | font-size: 200%; 4 | position: absolute; 5 | background-color: #333333; 6 | border-width: 3px; 7 | border-color: #999999; 8 | margin-left: 20%; 9 | margin-right: 20%; 10 | text-align: center; 11 | } 12 | 13 | .buttonDemo { 14 | font-family: FreeSans; 15 | } 16 | -------------------------------------------------------------------------------- /lod/lod.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from direct.task import Task 5 | from panda3d.core import NodePath, LODNode 6 | from math import sin, cos, pi 7 | import sys 8 | 9 | class LOD(ShowBase): 10 | def __init__(self): 11 | # The basics 12 | ShowBase.__init__(self) 13 | base.disableMouse() 14 | # Add the model 15 | 16 | lod = LODNode('Tree LOD node') 17 | tree = NodePath(lod) 18 | tree.reparentTo(render) 19 | 20 | tree2 = self.loader.loadModel("tree2") 21 | lod.addSwitch(50.0, 0.0) 22 | tree2.reparentTo(tree) 23 | 24 | tree1 = self.loader.loadModel("tree1") 25 | lod.addSwitch(100.0, 50.0) 26 | tree1.reparentTo(tree) 27 | 28 | tree0 = self.loader.loadModel("tree0") 29 | lod.addSwitch(999999.0, 100.0) 30 | tree0.reparentTo(tree) 31 | 32 | # Bookkeeping for the rotation around the model 33 | self.direction = 0.0 34 | self.distance = 20.0 35 | self.speed = 20.0 36 | self.last_time = 0.0 37 | 38 | # Initial camera setup 39 | self.camera.set_pos(0, -self.distance, self.distance) 40 | self.camera.look_at(0,0,5) 41 | 42 | # Key events and camera movement task 43 | self.accept("arrow_up", self.adjust_distance, [-1.0]) 44 | self.accept("arrow_up-up", self.adjust_distance, [1.0]) 45 | self.accept("arrow_down", self.adjust_distance, [1.0]) 46 | self.accept("arrow_down-up", self.adjust_distance, [-1.0]) 47 | self.accept("escape", sys.exit) 48 | self.taskMgr.add(self.update_camera, 'adjust camera', sort = 10) 49 | def adjust_distance(self, direction): 50 | self.direction += direction 51 | def update_camera(self, task): 52 | if task.time != 0.0: 53 | dt = task.time - self.last_time 54 | self.last_time = task.time 55 | self.distance += self.direction * self.speed * dt 56 | self.camera.set_pos(0, 57 | -self.distance, 58 | self.distance) 59 | self.camera.look_at(0,0,5) 60 | return Task.cont 61 | 62 | demo = LOD() 63 | demo.run() 64 | 65 | -------------------------------------------------------------------------------- /lod/tree0.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/lod/tree0.blend -------------------------------------------------------------------------------- /lod/tree0.egg: -------------------------------------------------------------------------------- 1 | { Z-up } 2 | Cube { 3 | { 4 | { 5 | 1.000000 0.000000 0.000000 0.000000 6 | 0.000000 1.000000 0.000000 0.000000 7 | 0.000000 0.000000 1.000000 0.000000 8 | 0.000000 0.000000 0.000000 1.000000 9 | } 10 | } 11 | 12 | Cube { 13 | 14 | 0 { 15 | -3.0 -3.0 1.0 16 | } 17 | 1 { 18 | -3.0 3.0 1.0 19 | } 20 | 2 { 21 | -1.0 1.0 0.0 22 | } 23 | 3 { 24 | -1.0 -1.0 0.0 25 | } 26 | 4 { 27 | -3.0 3.0 1.0 28 | } 29 | 5 { 30 | 3.0 3.0 1.0 31 | } 32 | 6 { 33 | 1.0 1.0 0.0 34 | } 35 | 7 { 36 | -1.0 1.0 0.0 37 | } 38 | 8 { 39 | 3.0 3.0 1.0 40 | } 41 | 9 { 42 | 3.0 -3.0 1.0 43 | } 44 | 10 { 45 | 1.0 -1.0 0.0 46 | } 47 | 11 { 48 | 1.0 1.0 0.0 49 | } 50 | 12 { 51 | 3.0 -3.0 1.0 52 | } 53 | 13 { 54 | -3.0 -3.0 1.0 55 | } 56 | 14 { 57 | -1.0 -1.0 0.0 58 | } 59 | 15 { 60 | 1.0 -1.0 0.0 61 | } 62 | 16 { 63 | -1.0 -1.0 0.0 64 | } 65 | 17 { 66 | -1.0 1.0 0.0 67 | } 68 | 18 { 69 | 1.0 1.0 0.0 70 | } 71 | 19 { 72 | 1.0 -1.0 0.0 73 | } 74 | 20 { 75 | -3.0 -3.0 1.0 76 | } 77 | 21 { 78 | 3.0 -3.0 1.0 79 | } 80 | 22 { 81 | 0.0 0.0 10.0 82 | } 83 | 23 { 84 | 0.0 0.0 10.0 85 | } 86 | 24 { 87 | 0.0 0.0 10.0 88 | } 89 | 25 { 90 | 0.0 0.0 10.0 91 | } 92 | 26 { 93 | 0.0 0.0 10.0 94 | } 95 | 27 { 96 | 0.0 0.0 10.0 97 | } 98 | 28 { 99 | 3.0 -3.0 1.0 100 | } 101 | 29 { 102 | 3.0 3.0 1.0 103 | } 104 | 30 { 105 | 0.0 0.0 10.0 106 | } 107 | 31 { 108 | 0.0 0.0 10.0 109 | } 110 | 32 { 111 | 3.0 3.0 1.0 112 | } 113 | 33 { 114 | -3.0 3.0 1.0 115 | } 116 | 34 { 117 | 0.0 0.0 10.0 118 | } 119 | 35 { 120 | 0.0 0.0 10.0 121 | } 122 | 36 { 123 | -3.0 3.0 1.0 124 | } 125 | 37 { 126 | -3.0 -3.0 1.0 127 | } 128 | 38 { 129 | 0.0 0.0 10.0 130 | } 131 | 39 { 132 | 0.0 0.0 10.0 133 | }} 134 | 135 | 136 | { 137 | {-0.447214 0.000000 -0.894427} 138 | { 0 1 2 3 { Cube }} 139 | } 140 | { 141 | {0.000000 0.447214 -0.894427} 142 | { 4 5 6 7 { Cube }} 143 | } 144 | { 145 | {0.447214 0.000000 -0.894427} 146 | { 8 9 10 11 { Cube }} 147 | } 148 | { 149 | {0.000000 -0.447214 -0.894427} 150 | { 12 13 14 15 { Cube }} 151 | } 152 | { 153 | {0.000000 0.000000 -1.000000} 154 | { 16 17 18 19 { Cube }} 155 | } 156 | { 157 | {0.000000 -0.948683 0.316228} 158 | { 20 21 22 23 { Cube }} 159 | } 160 | { 161 | {0.000000 0.000000 0.000000} 162 | { 24 25 26 27 { Cube }} 163 | } 164 | { 165 | {0.948683 0.000000 0.316228} 166 | { 28 29 30 31 { Cube }} 167 | } 168 | { 169 | {0.000000 0.948683 0.316228} 170 | { 32 33 34 35 { Cube }} 171 | } 172 | { 173 | {-0.948683 0.000000 0.316228} 174 | { 36 37 38 39 { Cube }} 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /lod/tree1.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/lod/tree1.blend -------------------------------------------------------------------------------- /lod/tree1.egg: -------------------------------------------------------------------------------- 1 | { Z-up } 2 | Cube { 3 | { 4 | { 5 | 1.000000 0.000000 0.000000 0.000000 6 | 0.000000 1.000000 0.000000 0.000000 7 | 0.000000 0.000000 1.000000 0.000000 8 | 0.000000 0.000000 0.000000 1.000000 9 | } 10 | } 11 | 12 | Cube { 13 | 14 | 0 { 15 | -4.0 0.0 1.0 16 | } 17 | 1 { 18 | -3.0 3.0 1.0 19 | } 20 | 2 { 21 | -1.0 1.0 0.0 22 | } 23 | 3 { 24 | -1.0 0.0 0.0 25 | } 26 | 4 { 27 | 0.0 4.0 1.0 28 | } 29 | 5 { 30 | 3.0 3.0 1.0 31 | } 32 | 6 { 33 | 1.0 1.0 0.0 34 | } 35 | 7 { 36 | 0.0 1.0 0.0 37 | } 38 | 8 { 39 | 4.0 0.0 1.0 40 | } 41 | 9 { 42 | 3.0 -3.0 1.0 43 | } 44 | 10 { 45 | 1.0 -1.0 0.0 46 | } 47 | 11 { 48 | 1.0 0.0 0.0 49 | } 50 | 12 { 51 | 0.0 -4.0 1.0 52 | } 53 | 13 { 54 | -3.0 -3.0 1.0 55 | } 56 | 14 { 57 | -1.0 -1.0 0.0 58 | } 59 | 15 { 60 | 0.0 -1.0 0.0 61 | } 62 | 16 { 63 | 0.0 0.0 0.0 64 | } 65 | 17 { 66 | 0.0 1.0 0.0 67 | } 68 | 18 { 69 | 1.0 1.0 0.0 70 | } 71 | 19 { 72 | 1.0 0.0 0.0 73 | } 74 | 20 { 75 | 0.0 -4.0 1.0 76 | } 77 | 21 { 78 | 3.0 -3.0 1.0 79 | } 80 | 22 { 81 | 0.0 0.0 10.0 82 | } 83 | 23 { 84 | 0.0 0.0 10.0 85 | } 86 | 24 { 87 | 0.0 0.0 10.0 88 | } 89 | 25 { 90 | 0.0 0.0 10.0 91 | } 92 | 26 { 93 | 0.0 0.0 10.0 94 | } 95 | 27 { 96 | 0.0 0.0 10.0 97 | } 98 | 28 { 99 | 4.0 0.0 1.0 100 | } 101 | 29 { 102 | 3.0 3.0 1.0 103 | } 104 | 30 { 105 | 0.0 0.0 10.0 106 | } 107 | 31 { 108 | 0.0 0.0 10.0 109 | } 110 | 32 { 111 | 0.0 4.0 1.0 112 | } 113 | 33 { 114 | -3.0 3.0 1.0 115 | } 116 | 34 { 117 | 0.0 0.0 10.0 118 | } 119 | 35 { 120 | 0.0 0.0 10.0 121 | } 122 | 36 { 123 | -4.0 0.0 1.0 124 | } 125 | 37 { 126 | -3.0 -3.0 1.0 127 | } 128 | 38 { 129 | 0.0 0.0 10.0 130 | } 131 | 39 { 132 | 0.0 0.0 10.0 133 | } 134 | 40 { 135 | -3.0 3.0 1.0 136 | } 137 | 41 { 138 | 0.0 4.0 1.0 139 | } 140 | 42 { 141 | 0.0 1.0 0.0 142 | } 143 | 43 { 144 | -1.0 1.0 0.0 145 | } 146 | 44 { 147 | 3.0 -3.0 1.0 148 | } 149 | 45 { 150 | 0.0 -4.0 1.0 151 | } 152 | 46 { 153 | 0.0 -1.0 0.0 154 | } 155 | 47 { 156 | 1.0 -1.0 0.0 157 | } 158 | 48 { 159 | -1.0 0.0 0.0 160 | } 161 | 49 { 162 | -1.0 1.0 0.0 163 | } 164 | 50 { 165 | 0.0 1.0 0.0 166 | } 167 | 51 { 168 | 0.0 0.0 0.0 169 | } 170 | 52 { 171 | -3.0 -3.0 1.0 172 | } 173 | 53 { 174 | 0.0 -4.0 1.0 175 | } 176 | 54 { 177 | 0.0 0.0 10.0 178 | } 179 | 55 { 180 | 0.0 0.0 10.0 181 | } 182 | 56 { 183 | 0.0 0.0 10.0 184 | } 185 | 57 { 186 | 0.0 0.0 10.0 187 | } 188 | 58 { 189 | 0.0 0.0 10.0 190 | } 191 | 59 { 192 | 0.0 0.0 10.0 193 | } 194 | 60 { 195 | 3.0 3.0 1.0 196 | } 197 | 61 { 198 | 0.0 4.0 1.0 199 | } 200 | 62 { 201 | 0.0 0.0 10.0 202 | } 203 | 63 { 204 | 0.0 0.0 10.0 205 | } 206 | 64 { 207 | -3.0 -3.0 1.0 208 | } 209 | 65 { 210 | -4.0 0.0 1.0 211 | } 212 | 66 { 213 | -1.0 0.0 0.0 214 | } 215 | 67 { 216 | -1.0 -1.0 0.0 217 | } 218 | 68 { 219 | 3.0 3.0 1.0 220 | } 221 | 69 { 222 | 4.0 0.0 1.0 223 | } 224 | 70 { 225 | 1.0 0.0 0.0 226 | } 227 | 71 { 228 | 1.0 1.0 0.0 229 | } 230 | 72 { 231 | 0.0 -1.0 0.0 232 | } 233 | 73 { 234 | 0.0 0.0 0.0 235 | } 236 | 74 { 237 | 1.0 0.0 0.0 238 | } 239 | 75 { 240 | 1.0 -1.0 0.0 241 | } 242 | 76 { 243 | 0.0 0.0 10.0 244 | } 245 | 77 { 246 | 0.0 0.0 10.0 247 | } 248 | 78 { 249 | 0.0 0.0 10.0 250 | } 251 | 79 { 252 | 0.0 0.0 10.0 253 | } 254 | 80 { 255 | 3.0 -3.0 1.0 256 | } 257 | 81 { 258 | 4.0 0.0 1.0 259 | } 260 | 82 { 261 | 0.0 0.0 10.0 262 | } 263 | 83 { 264 | 0.0 0.0 10.0 265 | } 266 | 84 { 267 | -3.0 3.0 1.0 268 | } 269 | 85 { 270 | -4.0 0.0 1.0 271 | } 272 | 86 { 273 | 0.0 0.0 10.0 274 | } 275 | 87 { 276 | 0.0 0.0 10.0 277 | } 278 | 88 { 279 | -1.0 -1.0 0.0 280 | } 281 | 89 { 282 | -1.0 0.0 0.0 283 | } 284 | 90 { 285 | 0.0 0.0 0.0 286 | } 287 | 91 { 288 | 0.0 -1.0 0.0 289 | } 290 | 92 { 291 | 0.0 0.0 10.0 292 | } 293 | 93 { 294 | 0.0 0.0 10.0 295 | } 296 | 94 { 297 | 0.0 0.0 10.0 298 | } 299 | 95 { 300 | 0.0 0.0 10.0 301 | }} 302 | 303 | 304 | { 305 | {-0.340503 0.085126 -0.936382} 306 | { 0 1 2 3 { Cube }} 307 | } 308 | { 309 | {0.085126 0.340503 -0.936382} 310 | { 4 5 6 7 { Cube }} 311 | } 312 | { 313 | {0.340503 -0.085126 -0.936382} 314 | { 8 9 10 11 { Cube }} 315 | } 316 | { 317 | {-0.085126 -0.340503 -0.936382} 318 | { 12 13 14 15 { Cube }} 319 | } 320 | { 321 | {0.000000 0.000000 -1.000000} 322 | { 16 17 18 19 { Cube }} 323 | } 324 | { 325 | {0.291386 -0.874157 0.388514} 326 | { 20 21 22 23 { Cube }} 327 | } 328 | { 329 | {0.000000 0.000000 0.000000} 330 | { 24 25 26 27 { Cube }} 331 | } 332 | { 333 | {0.874157 0.291386 0.388514} 334 | { 28 29 30 31 { Cube }} 335 | } 336 | { 337 | {-0.291386 0.874157 0.388514} 338 | { 32 33 34 35 { Cube }} 339 | } 340 | { 341 | {-0.874157 -0.291386 0.388514} 342 | { 36 37 38 39 { Cube }} 343 | } 344 | { 345 | {-0.085126 0.340503 -0.936382} 346 | { 40 41 42 43 { Cube }} 347 | } 348 | { 349 | {0.085126 -0.340503 -0.936382} 350 | { 44 45 46 47 { Cube }} 351 | } 352 | { 353 | {0.000000 0.000000 -1.000000} 354 | { 48 49 50 51 { Cube }} 355 | } 356 | { 357 | {-0.291386 -0.874157 0.388514} 358 | { 52 53 54 55 { Cube }} 359 | } 360 | { 361 | {0.000000 0.000000 0.000000} 362 | { 56 57 58 59 { Cube }} 363 | } 364 | { 365 | {0.291386 0.874157 0.388514} 366 | { 60 61 62 63 { Cube }} 367 | } 368 | { 369 | {-0.340503 -0.085126 -0.936382} 370 | { 64 65 66 67 { Cube }} 371 | } 372 | { 373 | {0.340503 0.085126 -0.936382} 374 | { 68 69 70 71 { Cube }} 375 | } 376 | { 377 | {0.000000 0.000000 -1.000000} 378 | { 72 73 74 75 { Cube }} 379 | } 380 | { 381 | {0.000000 0.000000 0.000000} 382 | { 76 77 78 79 { Cube }} 383 | } 384 | { 385 | {0.874157 -0.291386 0.388514} 386 | { 80 81 82 83 { Cube }} 387 | } 388 | { 389 | {-0.874157 0.291386 0.388514} 390 | { 84 85 86 87 { Cube }} 391 | } 392 | { 393 | {0.000000 0.000000 -1.000000} 394 | { 88 89 90 91 { Cube }} 395 | } 396 | { 397 | {0.000000 0.000000 0.000000} 398 | { 92 93 94 95 { Cube }} 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /lod/tree2.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/lod/tree2.blend -------------------------------------------------------------------------------- /offscreen_filming/offscreen_filming.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | from panda3d.core import loadPrcFileData 4 | from direct.showbase.ShowBase import ShowBase 5 | 6 | 7 | loadPrcFileData('', 'window-type offscreen') 8 | ShowBase() 9 | model = base.loader.load_model('models/panda-model') 10 | model.reparent_to(base.render) 11 | camera_gimbal = base.render.attach_new_node('gimbal') 12 | base.cam.reparent_to(camera_gimbal) 13 | base.cam.set_pos(0, -5000, 1000) 14 | base.cam.look_at(0, 0, 0) 15 | image_count = 0 16 | def rotate_gimbal(task): 17 | global image_count 18 | camera_gimbal.set_h(image_count * 12.0) 19 | return task.cont 20 | def screenshot_and_possibly_abort(task): 21 | global image_count 22 | base.screenshot(f"screenshot-{image_count:03d}.png", False) 23 | image_count += 1 24 | if image_count == 30: 25 | sys.exit() 26 | return task.cont 27 | base.task_mgr.add(rotate_gimbal, sort=49) 28 | base.task_mgr.add(screenshot_and_possibly_abort, sort=51) 29 | base.run() 30 | -------------------------------------------------------------------------------- /pbr/main_complexpbr.py: -------------------------------------------------------------------------------- 1 | # This file is about using `panda3d-complexpbr`, using the model created 2 | # in `pbr_model.py`. 3 | # 4 | # Interesting details: 5 | # * The model's `binormal` column is ignored, and the value is inferred 6 | # from normal and tangent by calculating the cross-product of the 7 | # `xyz` elements. The result is then multiplied with `tangent.w` 8 | # (which typically has a value of 1 or -1), so that the vector can be 9 | # reversed. 10 | 11 | import sys 12 | from math import pi 13 | from math import sin 14 | 15 | from panda3d.core import load_prc_file_data 16 | from panda3d.core import AmbientLight 17 | from panda3d.core import PointLight 18 | 19 | from direct.showbase.ShowBase import ShowBase 20 | 21 | import complexpbr 22 | 23 | import pbr_model 24 | 25 | # First, let's get a ShowBase instance with an sRGB framebuffer in the 26 | # main window going, the background black, and with 'escape' as a quick 27 | # and easy exit. 28 | load_prc_file_data( 29 | '', 30 | 'framebuffer-srgb #t', 31 | ) 32 | ShowBase() 33 | base.win.set_clear_color((0, 0, 0, 1)) 34 | base.accept('escape', sys.exit) 35 | 36 | # FIXME: Here's the complexpbr-specific stuff 37 | complexpbr.apply_shader(base.render) 38 | complexpbr.screenspace_init() 39 | 40 | # There will be three things in our scene: A light source, a surface, 41 | # and a camera. There will also be an ambient light, because ambient 42 | # occlusion affects only that. 43 | ambient_light = base.render.attach_new_node(AmbientLight('ambient_light')) 44 | base.render.set_light(ambient_light) 45 | ambient_light.node().color = (0.2, 0.2, 0.2, 1) 46 | 47 | point_light = base.render.attach_new_node(PointLight('point_light')) 48 | base.render.set_light(point_light) 49 | point_light.node().color = (1, 1, 1, 1) 50 | point_light.set_z(5) 51 | 52 | surface = pbr_model.surface 53 | surface.reparent_to(base.render) 54 | 55 | base.cam.set_y(-5) 56 | 57 | # Finally, just so we can see some action, let us make the surface move 58 | # a little. Also the camera is at the level of it, while the light 59 | # source is above it, so we need to tilt it by 45 degrees anyway to see 60 | # it at all. 61 | def do_the_lissajous_twist(task): 62 | time = task.time 63 | surface.set_hpr(0,0,0) 64 | surface.set_p(-45 + sin(time) * 7) 65 | surface.set_hpr(surface, sin(time * 3 + pi * 0.5) * 10, 0, 0) 66 | return task.cont 67 | 68 | base.task_mgr.add(do_the_lissajous_twist) 69 | 70 | # And now we are done, and let Panda3D do its magic. 71 | base.run() 72 | -------------------------------------------------------------------------------- /pbr/main_no_pbr.py: -------------------------------------------------------------------------------- 1 | # A PBR model has vertex columns with vertex position and texture 2 | # coordinates, so we can also just render it without PBR shaders at all. 3 | 4 | import sys 5 | from math import pi 6 | from math import sin 7 | 8 | from panda3d.core import load_prc_file_data 9 | from panda3d.core import AmbientLight 10 | from panda3d.core import PointLight 11 | 12 | from direct.showbase.ShowBase import ShowBase 13 | 14 | import pbr_model 15 | 16 | # First, let's get a ShowBase instance with an sRGB framebuffer in the 17 | # main window going, the background black, and with 'escape' as a quick 18 | # and easy exit. 19 | load_prc_file_data( 20 | '', 21 | 'framebuffer-srgb #t', 22 | ) 23 | ShowBase() 24 | base.win.set_clear_color((0, 0, 0, 1)) 25 | base.accept('escape', sys.exit) 26 | 27 | # There will be three things in our scene: A light source, a surface, 28 | # and a camera. There will also be an ambient light, because ambient 29 | # occlusion affects only that. 30 | ambient_light = base.render.attach_new_node(AmbientLight('ambient_light')) 31 | base.render.set_light(ambient_light) 32 | ambient_light.node().color = (0.2, 0.2, 0.2, 1) 33 | 34 | point_light = base.render.attach_new_node(PointLight('point_light')) 35 | base.render.set_light(point_light) 36 | point_light.node().color = (1, 1, 1, 1) 37 | point_light.set_z(5) 38 | 39 | surface = pbr_model.surface 40 | surface.reparent_to(base.render) 41 | 42 | base.cam.set_y(-5) 43 | 44 | # Finally, just so we can see some action, let us make the surface move 45 | # a little. Also the camera is at the level of it, while the light 46 | # source is above it, so we need to tilt it by 45 degrees anyway to see 47 | # it at all. 48 | def do_the_lissajous_twist(task): 49 | time = task.time 50 | surface.set_hpr(0,0,0) 51 | surface.set_p(-45 + sin(time) * 7) 52 | surface.set_hpr(surface, sin(time * 3 + pi * 0.5) * 10, 0, 0) 53 | return task.cont 54 | 55 | base.task_mgr.add(do_the_lissajous_twist) 56 | 57 | # And now we are done, and let Panda3D do its magic. 58 | base.run() 59 | -------------------------------------------------------------------------------- /pbr/main_simplepbr.py: -------------------------------------------------------------------------------- 1 | # This file is about using `panda3d-simplepbr`, using the model created 2 | # in `pbr_model.py`. 3 | # 4 | # Interesting details: 5 | # * The model's `binormal` column is ignored, and the value is inferred 6 | # from normal and tangent by calculating the cross-product of the 7 | # `xyz` elements. The result is then multiplied with `tangent.w` 8 | # (which typically has a value of 1 or -1), so that the vector can be 9 | # reversed. 10 | 11 | import sys 12 | from math import pi 13 | from math import sin 14 | 15 | from panda3d.core import load_prc_file_data 16 | from panda3d.core import AmbientLight 17 | from panda3d.core import PointLight 18 | 19 | from direct.showbase.ShowBase import ShowBase 20 | 21 | import simplepbr 22 | 23 | import pbr_model 24 | 25 | # First, let's get a ShowBase instance with an sRGB framebuffer in the 26 | # main window going, the background black, and with 'escape' as a quick 27 | # and easy exit. 28 | load_prc_file_data( 29 | '', 30 | 'framebuffer-srgb #t', 31 | ) 32 | ShowBase() 33 | base.win.set_clear_color((0, 0, 0, 1)) 34 | base.accept('escape', sys.exit) 35 | 36 | # Now we initialize simplepbr; By default, it will apply its shader to 37 | # `base.render` (and below, as per the usual rules of render attributes; 38 | # For details see https://docs.panda3d.org/1.10/python/programming/shaders/shader-basics#applying-the-shader 39 | # and https://docs.panda3d.org/1.10/python/programming/render-attributes/index 40 | # 41 | # Also by default, it will ignore normal maps. There are two ways to 42 | # activate them: 43 | # * Set an environment variable: `simplepbr-use-normal-maps t` 44 | # This (currently, 2023-10-01) only works on development builds. 45 | # * Set `simplepbr.use_normal_maps = True` 46 | # * Pass it as an argument to `init`, as shown below. 47 | simplepbr.init(use_normal_maps=True, use_occlusion_maps=True) 48 | 49 | # There will be three things in our scene: A light source, a surface, 50 | # and a camera. There will also be an ambient light, because ambient 51 | # occlusion affects only that. 52 | ambient_light = base.render.attach_new_node(AmbientLight('ambient_light')) 53 | base.render.set_light(ambient_light) 54 | ambient_light.node().color = (0.2, 0.2, 0.2, 1) 55 | 56 | point_light = base.render.attach_new_node(PointLight('point_light')) 57 | base.render.set_light(point_light) 58 | point_light.node().color = (1, 1, 1, 1) 59 | point_light.set_z(5) 60 | 61 | surface = pbr_model.surface 62 | surface.reparent_to(base.render) 63 | 64 | base.cam.set_y(-5) 65 | 66 | # Finally, just so we can see some action, let us make the surface move 67 | # a little. Also the camera is at the level of it, while the light 68 | # source is above it, so we need to tilt it by 45 degrees anyway to see 69 | # it at all. 70 | def do_the_lissajous_twist(task): 71 | time = task.time 72 | surface.set_hpr(0,0,0) 73 | surface.set_p(-45 + sin(time) * 7) 74 | surface.set_hpr(surface, sin(time * 3 + pi * 0.5) * 10, 0, 0) 75 | return task.cont 76 | 77 | base.task_mgr.add(do_the_lissajous_twist) 78 | 79 | # And now we are done, and let Panda3D do its magic. 80 | base.run() 81 | -------------------------------------------------------------------------------- /pbr/pbr_model.py: -------------------------------------------------------------------------------- 1 | # Hi! Wanna learn about the nitty-gritty of PBR models and textures, or 2 | # at least with textures in principle? Have you read 3 | # `geometry/basic_model.py`, so that you know how a model is generated 4 | # in general? 5 | # Nice, let's go! 6 | 7 | from math import sin, cos, pi, sqrt, ceil 8 | from colorsys import hls_to_rgb 9 | import random 10 | 11 | from panda3d.core import NodePath 12 | from panda3d.core import Geom 13 | from panda3d.core import GeomNode 14 | from panda3d.core import InternalName 15 | from panda3d.core import GeomVertexArrayFormat 16 | from panda3d.core import GeomVertexFormat 17 | from panda3d.core import GeomVertexData 18 | from panda3d.core import GeomVertexWriter 19 | from panda3d.core import GeomTriangles 20 | from panda3d.core import PNMImage 21 | from panda3d.core import Texture 22 | from panda3d.core import TextureStage 23 | from panda3d.core import Material 24 | from panda3d.core import SamplerState 25 | 26 | ### 27 | ### MODEL 28 | ### 29 | 30 | # A PBR-using model is no different from any other kind of model, it 31 | # just uses a specific vertex format, textures, and materials so as to 32 | # satisfy the needs of PBR shaders. 33 | # 34 | # First we have to specify the format, this time because Panda3D doesn't 35 | # come with a stock one for our needs. 36 | # There will be five columns: 37 | # * `vertex`: The position of the vertex in model space. 38 | # * `texcoord`: The UV coordinate of the texture at this vertex. 39 | # * `normal`: The normal vector is the one standing perpendicular to the 40 | # surface; Not the actual geometric surface, but the implied optical 41 | # one. This alone is enough to simulate a smoothly bent surface, as 42 | # the normals at the corners at a face get interpolated. The normal 43 | # map is a transformation on top of that. 44 | # * `tangent`: Orthogonal to the normal, this vector points into the 45 | # direction of the texture's `u` vector. 46 | # * `binormal`: Orthogonal to normal and tangent, this vector completes 47 | # the orthonormal basis for the vertices' surface. It points in the 48 | # direction of the texture's `v` vector. 49 | v_array_format = GeomVertexArrayFormat() 50 | v_array_format.add_column(InternalName.get_vertex(), 3, Geom.NT_float32, Geom.C_point) 51 | v_array_format.add_column(InternalName.get_texcoord(), 2, Geom.NT_float32, Geom.C_texcoord) 52 | v_array_format.add_column(InternalName.get_normal(), 3, Geom.NT_float32, Geom.C_normal) 53 | v_array_format.add_column(InternalName.get_tangent(), 4, Geom.NT_float32, Geom.C_other) 54 | v_array_format.add_column(InternalName.get_binormal(), 4, Geom.NT_float32, Geom.C_other) 55 | v_format = GeomVertexFormat() 56 | v_format.add_array(v_array_format) 57 | v_format = GeomVertexFormat.register_format(v_format) 58 | 59 | # And again, the data table and the writers. 60 | v_data = GeomVertexData("Data", v_format, Geom.UHStatic) 61 | v_data.unclean_set_num_rows(4) 62 | vertex = GeomVertexWriter(v_data, InternalName.get_vertex()) 63 | normal = GeomVertexWriter(v_data, InternalName.get_normal()) 64 | texcoord = GeomVertexWriter(v_data, InternalName.get_texcoord()) 65 | tangent = GeomVertexWriter(v_data, InternalName.get_tangent()) 66 | binormal = GeomVertexWriter(v_data, InternalName.get_binormal()) 67 | 68 | # And the same four vertices, just with another data set. 69 | # Top Left 70 | vertex.set_data3f(-1, 0, 1) 71 | texcoord.set_data2f(0, 1) 72 | normal.set_data3f(0, -1, 0) 73 | binormal.set_data4f(0, 0, 1, 1) 74 | tangent.set_data4f(1, 0, 0, 1) 75 | 76 | # Top Right 77 | vertex.set_data3f(1, 0, 1) 78 | texcoord.set_data2f(1, 1) 79 | normal.set_data3f(0, -1, 0) 80 | binormal.set_data4f(0, 0, 1, 1) 81 | tangent.set_data4f(1, 0, 0, 1) 82 | 83 | # Bottom Left 84 | vertex.set_data3f(-1, 0, -1) 85 | texcoord.set_data2f(0, 0) 86 | normal.set_data3f(0, -1, 0) 87 | binormal.set_data4f(0, 0, 1, 1) 88 | tangent.set_data4f(1, 0, 0, 1) 89 | 90 | # Bottom Right 91 | vertex.set_data3f(1, 0, -1) 92 | texcoord.set_data2f(1, 0) 93 | normal.set_data3f(0, -1, 0) 94 | binormal.set_data4f(0, 0, 1, 1) 95 | tangent.set_data4f(1, 0, 0, 1) 96 | 97 | # And the same two triangles again. 98 | tris = GeomTriangles(Geom.UHStatic) 99 | tris.add_vertices(2, 1, 0) 100 | tris.add_vertices(2, 3, 1) 101 | tris.close_primitive() 102 | 103 | # ...and putting it all together... again. 104 | geom = Geom(v_data) 105 | geom.add_primitive(tris) 106 | node = GeomNode('geom_node') 107 | node.add_geom(geom) 108 | surface = NodePath(node) 109 | 110 | ### 111 | ### MATERIAL 112 | ### 113 | 114 | # Materials are values affecting how a pack of textures is rendered. 115 | # Specifically, we can multiply the color texture 116 | # (`TextureStage.Modulate`) with an arbitrary RGBA value; Typically 117 | # we'll just multiply it with 1.0 though. The same goes for the emission 118 | # texture. For details on what those are, see below. 119 | surface_material = Material() 120 | surface_material.set_diffuse((1,1,1,1)) 121 | surface_material.set_emission((1,1,1,1)) 122 | surface.set_material(surface_material) 123 | 124 | ### 125 | ### TEXTURES 126 | ### 127 | 128 | # Textures are images that get mapped onto meshes; Each vertex has a u/v 129 | # coordinate, where u is the to-the-right axis and v the upwards one. 130 | # They also have properties like the TextureStage, which specifies how 131 | # the texture is to be used. The trivial case is to use a simple RGB 132 | # color image and be done with it. In our case, we have four types of 133 | # texture: 134 | # * "Base color" is an easy one, that is just the basic color image. 135 | # * "Occlusion roughness metallicity" is obviously three values in one 136 | # map. 137 | # * Occlusion refers to ambient occlusion, which darkens areas for 138 | # ambient light. 0.0 is fully shadowed, 1.0 not shadowed at all. 139 | # * Roughness measures how well microscopic facets of the surface 140 | # would be aligned with each other. Low roughness (~0.01) will 141 | # create highly polished surface, higher roughness will reflect 142 | # light more diffusely. 143 | # * Metallicity deals with the material's electric properties 144 | # influencing reflection. Realistic values will be close to either 145 | # 0.0 (non-metallic) or 1.0 (metallic). 146 | # * "Normal" is obviously the normal map. Its RGB values are 147 | # representing a vector of unit length in tangent/binormal/normal 148 | # space. The vector's values along these axes are in the range 149 | # [-1, 1], while the values in the map are [0.0, 1.0]; This can be 150 | # easily converted by `map_value = vector_value / 2 + 0.5`. 151 | # As you can see, the trivial case of the normal vector standing 152 | # perpendicular on the surface has the value of <0, 0, 1>, and its 153 | # color value therefore is <0.5, 0.5, 1.0>. We will go through a 154 | # non-trivial case later, when we create some values for visual 155 | # appeal; Also to have done it, because the trivial case is boring. 156 | # * "Emissions" covers glowing parts. This is an RGB map. 157 | 158 | # Before we do anything else, let's decide on the size of the textures. 159 | resolution = 256 160 | 161 | # First then, we create a list of default values for each image that we 162 | # create. 163 | stages = [ 164 | # Base color; Near black. 165 | (TextureStage.MModulate, (0.01, 0.01, 0.01)), 166 | # Occlusion Roughness Metallicity; Lit polished non-metal 167 | (TextureStage.MSelector, (1.0, 0.01, 0.01)), 168 | # Normals; They're all standing straight up. 169 | (TextureStage.MNormal, (0.5, 0.5, 1.0)), 170 | # Emission; Nothing for now. 171 | (TextureStage.MEmission, (0.0, 0.0, 0.0)), 172 | ] 173 | 174 | # Then we... 175 | image_to_texture_mapping = [] 176 | for mode, values in stages: 177 | # ...create the texture stage, ... 178 | stage = TextureStage('') 179 | stage.set_mode(mode) 180 | # ...create and fill the image, ... 181 | image = PNMImage(resolution, resolution) 182 | image.fill(*values) 183 | # ... create the texture, and apply it to the surface. 184 | texture = Texture('pbr_color_texture') 185 | surface.set_texture(stage, texture) 186 | # FIXME: Why do these have no effect? 187 | #texture.set_magfilter(SamplerState.FT_nearest) 188 | #texture.set_minfilter(SamplerState.FT_nearest) 189 | #texture.set_wrap_u(Texture.WM_clamp) 190 | #texture.set_wrap_v(Texture.WM_clamp) 191 | #texture.set_anisotropic_degree(4) 192 | # We do NOT load the images into the textures yet, as we first want 193 | # to play around with them a bit more. We're here for the cool 194 | # graphics after all, right? But we *do* remember which image goes 195 | # into which texture. 196 | image_to_texture_mapping.append((image, texture)) 197 | 198 | # On to the fun part, raising our procedural generation level above a 199 | # featureless square! Except for the step of loading the images into the 200 | # textures, which is the very last two lines of code, the whole rest of 201 | # the file will be about fun with pixel values, so if you're just 202 | # interested in the basics of PBR, you are now done; Congratulations. 203 | 204 | # Now let us consider our black square as a set of tiles. We will color 205 | # every second tile in spectral-colored glitter. 206 | 207 | tile_size = 16 # Size of tile in texels 208 | max_glitter_angle = 8.0 / 360.0 * 2.0 * pi # 8.0 degree in radians 209 | 210 | # A source for normal map values; They are random within the angle 211 | # specified above. 212 | # FIXME: This needs to change to something where we can see the normals 213 | # explicitly pointing towards the intended directions. 214 | def glitter_normal(): 215 | # First, we need a random value between -1 and 1, and scale it by 216 | # the maximum angle for the glitter. 217 | tangent_turn = (random.random() * 2.0 - 1.0) * max_glitter_angle 218 | binormal_turn = (random.random() * 2.0 - 1.0) * max_glitter_angle 219 | # Now let's see what the vector's values along those axes are. 220 | tangent_value = sin(tangent_turn) 221 | binormal_value = sin(binormal_turn) 222 | # We can infer the third component via the Pythagorean theorem: 223 | # `1.0 = sqrt(tangent_value ** 2 + binormal_value ** 2 + normal_value**2) 224 | normal_value = sqrt(1.0 - tangent_value ** 2 - binormal_value ** 2) 225 | return (tangent_value / 2.0 + 0.5, binormal_value / 2.0 + 0.5, normal_value / 2.0 + 0.5) 226 | 227 | # And a similar, simpler helper for the color. 228 | # FIXME: The latest in perceptually smooth color models is OKLab. 229 | def glitter_color(): 230 | return hls_to_rgb(random.random(), 0.5, 1.0) 231 | 232 | # Let's quickly precompute some values and create the palette to use. 233 | num_tiles = ceil(resolution / tile_size) 234 | tile_data = {(x, y): (glitter_color(), glitter_normal()) 235 | for x in range(0, num_tiles) 236 | for y in range(0, num_tiles) 237 | } 238 | 239 | # For ease of use, let's put the images in well-named variables. 240 | color = image_to_texture_mapping[0][0] 241 | orm = image_to_texture_mapping[1][0] 242 | normal = image_to_texture_mapping[2][0] 243 | emit = image_to_texture_mapping[3][0] 244 | 245 | # Now we iterate over all coordinates in our images. 246 | for x in range(0, resolution): 247 | for y in range(0, resolution): 248 | tile_id = (x // tile_size, y // tile_size) 249 | tile_x, tile_y = tile_id 250 | # If this texel is tile is to be colored, ... 251 | if (tile_x + tile_y) % 2 == 0: 252 | # ...we modify it to the precomputed values. 253 | texel_color, texel_normal = tile_data[tile_id] 254 | #texel_normal = (0.5, 0.5, 1.0) 255 | color.set_xel(x, y, *texel_color) 256 | normal.set_xel(x, y, *texel_normal) 257 | # ...and make it a bit less polished, and non-metallic. 258 | orm.set_xel(x, y, 1.0, 0.1, 0.01) 259 | 260 | # Now we load the manipulated images into the textures, and that is it 261 | # for the surface. 262 | for image, texture in image_to_texture_mapping: 263 | texture.load(image) 264 | -------------------------------------------------------------------------------- /quaternions/toy_problem.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from math import pi 3 | 4 | from direct.showbase.ShowBase import ShowBase 5 | 6 | from panda3d.core import VBase3 7 | from panda3d.core import Vec3 8 | from panda3d.core import Quat 9 | from panda3d.core import invert 10 | from panda3d.bullet import BulletWorld 11 | from panda3d.bullet import BulletRigidBodyNode 12 | from panda3d.bullet import BulletSphereShape 13 | from panda3d.bullet import BulletDebugNode 14 | 15 | 16 | # Basic setup 17 | s = ShowBase() 18 | s.disable_mouse() 19 | s.accept('escape', sys.exit) 20 | s.cam.set_pos(0, -10, 0) 21 | 22 | 23 | # Physics 24 | bullet_world = BulletWorld() 25 | def run_physics(task): 26 | bullet_world.do_physics(globalClock.getDt()) 27 | return task.cont 28 | s.task_mgr.add(run_physics, sort=1) 29 | # Debug visualization 30 | debug_node = BulletDebugNode('Debug') 31 | debug_node.showWireframe(True) 32 | debug_node.showConstraints(True) 33 | debug_node.showBoundingBoxes(False) 34 | debug_node.showNormals(False) 35 | debug_np = s.render.attach_new_node(debug_node) 36 | bullet_world.set_debug_node(debug_node) 37 | debug_np.show() 38 | 39 | 40 | # The object in question 41 | mass = BulletRigidBodyNode() 42 | mass.set_mass(1) 43 | mass.setLinearSleepThreshold(0) 44 | mass.setAngularSleepThreshold(0) 45 | shape = BulletSphereShape(1) 46 | mass.add_shape(shape) 47 | mass_node = s.render.attach_new_node(mass) 48 | mass_node.set_hpr(0, 0, 0) 49 | bullet_world.attach_rigid_body(mass) 50 | model = s.loader.load_model('models/smiley') 51 | model.reparent_to(mass_node) 52 | model_axis = loader.load_model('models/zup-axis') 53 | model_axis.reparent_to(model) 54 | model_axis.set_pos(-1, -1, -1) 55 | model_axis.set_scale(0.2) 56 | 57 | 58 | target_node = s.loader.load_model('models/smiley') 59 | target_node.reparent_to(s.render) 60 | target_node.set_hpr(179, 89, 0) 61 | target_node.set_scale(1.5) 62 | target_node.set_render_mode_wireframe() 63 | 64 | 65 | delta_node = s.render.attach_new_node('delta') 66 | delta_node.set_pos(2, -1, -1) 67 | delta_node_model = s.loader.load_model('models/smiley') 68 | delta_node_model.reparent_to(delta_node) 69 | delta_node_model.set_h(180) 70 | delta_node_model.set_render_mode_wireframe() 71 | delta_node_model.set_two_sided(True) 72 | delta_node = s.render.attach_new_node('delta') 73 | delta_node.set_pos(2, -1, -1) 74 | delta_node_model = s.loader.load_model('models/smiley') 75 | delta_node_model.reparent_to(delta_node) 76 | delta_node_model.set_h(180) 77 | delta_node_model.set_render_mode_wireframe() 78 | delta_node_model.set_two_sided(True) 79 | 80 | 81 | speed_node = s.loader.load_model('models/zup-axis') 82 | speed_node.reparent_to(s.render) 83 | speed_node.set_pos(2, -1, 1) 84 | speed_node.set_hpr(10, 0, 0) 85 | 86 | 87 | def stabilize(task): 88 | # inertia: kg * m^2 89 | # torque: N*m 90 | # angular_impulse: N*m*sec 91 | # angular_velocity: radians/sec 92 | # force = mass * acceleration 93 | # acceleration = delta_velocity / delta_time 94 | # impulse = force * time 95 | 96 | dt = globalClock.dt 97 | 98 | tau = 0.2 99 | inertia = 1.0 100 | 101 | orientation = mass_node.get_quat(s.render) 102 | angular_velocity = mass.get_angular_velocity() # radians / sec 103 | # speed_node.set_scale(angular_velocity / (2*pi) * 0.05 + VBase3(0.01, 0.01, 0.01)) 104 | 105 | delta_hpr = target_node.get_hpr(mass_node) 106 | delta_hpr.componentwise_mult(VBase3(1, -1, 1)) 107 | delta_node.set_hpr(delta_hpr) 108 | delta_angle = delta_node.get_quat().get_angle_rad() 109 | 110 | axis_of_torque = Vec3( 111 | -delta_node.get_p(s.render), 112 | delta_node.get_r(s.render), 113 | delta_node.get_h(s.render), 114 | ) 115 | axis_of_torque.normalize() 116 | target_angular_velocity = axis_of_torque * delta_angle / tau / pi 117 | steering_impulse = target_angular_velocity * inertia 118 | 119 | # We calculate the impulse that cancels out all current rotation, 120 | # which is noise with regard to the intended rotation. 121 | countering_impulse = -angular_velocity / 2.5 122 | 123 | max_impulse = 0.4 124 | 125 | impulse = countering_impulse + steering_impulse 126 | if impulse.length() > max_impulse: 127 | impulse = impulse / impulse.length() * max_impulse 128 | 129 | mass.apply_torque_impulse(impulse) 130 | return task.cont 131 | s.task_mgr.add(stabilize, sort=0) 132 | 133 | 134 | # Apply torque with 't' 135 | def add_torque(x=0, y=0, z=0): 136 | # This happens in world space 137 | mass.apply_torque_impulse(VBase3(x, y, z)) 138 | s.accept('x', add_torque, [10, 0, 0]) 139 | s.accept('y', add_torque, [0, 10, 0]) 140 | s.accept('z', add_torque, [0, 0, 10]) 141 | 142 | 143 | s.run() 144 | -------------------------------------------------------------------------------- /quaternions/toy_problem_quat.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from math import pi 3 | from random import random 4 | 5 | from direct.showbase.ShowBase import ShowBase 6 | 7 | from panda3d.core import VBase3 8 | from panda3d.core import Vec3 9 | from panda3d.core import Quat 10 | from panda3d.core import invert 11 | from panda3d.bullet import BulletWorld 12 | from panda3d.bullet import BulletRigidBodyNode 13 | from panda3d.bullet import BulletSphereShape 14 | from panda3d.bullet import BulletDebugNode 15 | 16 | 17 | # Basic setup 18 | s = ShowBase() 19 | s.disable_mouse() 20 | s.accept('escape', sys.exit) 21 | s.cam.set_pos(0, -10, 0) 22 | 23 | 24 | # Physics 25 | bullet_world = BulletWorld() 26 | def run_physics(task): 27 | bullet_world.do_physics(globalClock.getDt()) 28 | return task.cont 29 | s.task_mgr.add(run_physics, sort=1) 30 | 31 | 32 | # Debug visualization 33 | debug_node = BulletDebugNode('Debug') 34 | debug_node.showWireframe(True) 35 | debug_node.showConstraints(True) 36 | debug_node.showBoundingBoxes(False) 37 | debug_node.showNormals(False) 38 | debug_np = s.render.attach_new_node(debug_node) 39 | bullet_world.set_debug_node(debug_node) 40 | debug_np.show() 41 | 42 | 43 | # The object in question 44 | mass = BulletRigidBodyNode() 45 | mass.set_mass(1) 46 | mass.setLinearSleepThreshold(0) 47 | mass.setAngularSleepThreshold(0) 48 | shape = BulletSphereShape(1) 49 | mass.add_shape(shape) 50 | mass_node = s.render.attach_new_node(mass) 51 | mass_node.set_hpr(1, 1, 1) 52 | bullet_world.attach_rigid_body(mass) 53 | model = s.loader.load_model('models/smiley') 54 | model.reparent_to(mass_node) 55 | model_axis = loader.load_model('models/zup-axis') 56 | model_axis.reparent_to(model) 57 | model_axis.set_pos(0, 0, 0) 58 | model_axis.set_scale(0.2) 59 | 60 | 61 | # The orientation to reach 62 | target_node = s.loader.load_model('models/smiley') 63 | target_node.reparent_to(s.render) 64 | target_node.set_hpr(0,0,0) 65 | target_node.set_scale(1.5) 66 | target_node.set_render_mode_wireframe() 67 | target_node.set_two_sided(True) 68 | 69 | 70 | # The difference, as seen from the mass 71 | delta_node = s.render.attach_new_node('delta') 72 | delta_node.set_pos(2, -1, -1) 73 | delta_node_model = s.loader.load_model('models/smiley') 74 | delta_node_model.reparent_to(delta_node) 75 | delta_node_model.set_h(180) 76 | delta_node_model.set_render_mode_wireframe() 77 | delta_node_model.set_two_sided(True) 78 | 79 | 80 | # A visualization of the axis being used 81 | axis_node = s.loader.load_model('models/zup-axis') 82 | axis_node.reparent_to(s.render) 83 | axis_node.set_pos(2, -1, 1) 84 | axis_node.set_hpr(10, 0, 0) 85 | axis_node.set_scale(0.1) 86 | 87 | 88 | def stabilize(task): 89 | tau = 0.1 90 | inertia = 1.0 91 | 92 | orientation = mass_node.get_quat() 93 | delta_orientation = target_node.get_quat() * invert(orientation) 94 | delta_node.set_quat(invert(delta_orientation)) 95 | 96 | delta_angle = delta_orientation.get_angle_rad() 97 | if abs(delta_angle) < (pi/360*0.1): 98 | delta_angle = 0 99 | axis_of_torque = VBase3(0, 0, 0) 100 | else: 101 | axis_of_torque = delta_orientation.get_axis() 102 | axis_of_torque.normalize() 103 | axis_of_torque = s.render.get_relative_vector( 104 | mass_node, 105 | axis_of_torque, 106 | ) 107 | if delta_angle > pi: 108 | delta_angle -= 2*pi 109 | axis_node.set_scale(axis_of_torque * 0.2) 110 | 111 | # If the mass was standing still, this would be the velocity that has to be 112 | # reached to achieve the targeted orientation in tau seconds. 113 | target_angular_velocity = axis_of_torque * delta_angle / tau 114 | # But we also have to cancel out the current velocity for that. 115 | angular_velocity = mass.get_angular_velocity() # radians / sec 116 | countering_velocity = -angular_velocity 117 | 118 | # An impulse of 1 causes an angular velocity of 2.5 rad on a unit mass, so 119 | # we have to adjust accordingly. 120 | target_impulse = target_angular_velocity / 2.5 * inertia 121 | countering_impulse = countering_velocity / 2.5 * inertia 122 | 123 | # Now just sum those up, and we have the impulse that needs to be applied to 124 | # steer towards target. 125 | impulse = target_impulse + countering_impulse 126 | 127 | # Clamp the impulse to what the "motor" can produce. 128 | max_impulse = 0.4 129 | if impulse.length() > max_impulse: 130 | clamped_impulse = impulse / impulse.length() * max_impulse 131 | else: 132 | clamped_impulse = impulse 133 | 134 | print("{:4.0f} deg, " 135 | "{:2.5f} of {:2.5f} impulse, " 136 | "{:3.2f}% power of {:4.2f}% " 137 | "({:5.2f}% steering, {:5.2f}% countering) requested".format( 138 | delta_angle / (2*pi) * 360, 139 | clamped_impulse.length(), impulse.length(), 140 | clamped_impulse.length() / max_impulse * 100, 141 | impulse.length() / max_impulse * 100, 142 | target_angular_velocity.length() / pi * 100, 143 | countering_velocity.length() / pi * 100, 144 | )) 145 | mass.apply_torque_impulse(clamped_impulse) 146 | return task.cont 147 | s.task_mgr.add(stabilize, sort=0) 148 | 149 | 150 | # Apply torque with 't' 151 | def add_torque(x=0, y=0, z=0): 152 | # This happens in world space 153 | mass.apply_torque_impulse(VBase3(x, y, z)*5) 154 | s.accept('x', add_torque, [1, 0, 0]) 155 | s.accept('y', add_torque, [0, 1, 0]) 156 | s.accept('z', add_torque, [0, 0, 1]) 157 | s.accept('shift-x', add_torque, [-1, 0, 0]) 158 | s.accept('shift-y', add_torque, [0, -1, 0]) 159 | s.accept('shift-z', add_torque, [0, 0, -1]) 160 | def retarget(): 161 | target_node.set_hpr( 162 | (random() - 0.5) * 2 * 180, 163 | (random() - 0.5) * 2 * 90, 164 | (random() - 0.5) * 2 * 180, 165 | ) 166 | s.accept('r', retarget) 167 | s.run() 168 | -------------------------------------------------------------------------------- /render_to_texture/plane.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/render_to_texture/plane.blend -------------------------------------------------------------------------------- /render_to_texture/plane.egg: -------------------------------------------------------------------------------- 1 | { Z-up } 2 | Material.001 { 3 | diffr { 0.640000 } 4 | diffg { 0.640000 } 5 | diffb { 0.640000 } 6 | specr { 0.500000 } 7 | specg { 0.500000 } 8 | specb { 0.500000 } 9 | shininess { 12.5 } 10 | emitr { 0.000000 } 11 | emitg { 0.000000 } 12 | emitb { 0.000000 } 13 | } 14 | 15 | Texture { 16 | "./test.png" 17 | envtype { MODULATE } 18 | minfilter { LINEAR_MIPMAP_LINEAR } 19 | magfilter { LINEAR_MIPMAP_LINEAR } 20 | wrap { REPEAT } 21 | } 22 | 23 | Plane { 24 | { 25 | { 26 | 1.000000 -0.000000 0.000000 0.000000 27 | -0.000000 0.000000 1.000000 0.000000 28 | -0.000000 -1.000000 0.000000 0.000000 29 | 0.000000 0.000000 0.000000 1.000000 30 | } 31 | } 32 | 33 | Plane { 34 | 35 | 0 { 36 | -1.0 -7.549790126404332e-08 -1.0 37 | 38 | { 39 | 0.000100 0.000100 40 | } 41 | } 42 | 1 { 43 | 1.0 -7.549790126404332e-08 -1.0 44 | 45 | { 46 | 0.999900 0.000100 47 | } 48 | } 49 | 2 { 50 | 1.0 7.549790126404332e-08 1.0 51 | 52 | { 53 | 0.999900 0.999900 54 | } 55 | } 56 | 3 { 57 | -1.0 7.549790126404332e-08 1.0 58 | 59 | { 60 | 0.000100 0.999900 61 | } 62 | }} 63 | 64 | 65 | { 66 | { Texture } 67 | { Material.001 } 68 | {-0.000000 -1.000000 0.000000} 69 | { 0 1 2 3 { Plane }} 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /render_to_texture/rtt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from panda3d.core import NodePath 5 | import sys 6 | from panda3d.core import VBase4 7 | 8 | #from panda3d.core import loadPrcFileData 9 | #loadPrcFileData('init', 'basic-shaders-only #t') 10 | 11 | class RTT(ShowBase): 12 | def __init__(self): 13 | ShowBase.__init__(self) 14 | base.disableMouse() 15 | self.accept("escape", sys.exit) 16 | #base.setBackgroundColor(0, 0, 0) 17 | # The scene to be rendered to texture 18 | myscene = NodePath("My Scene") 19 | twisted = loader.loadModel("twisted") 20 | twisted.reparent_to(myscene) 21 | # A model that the twisted-scene is rendered to 22 | model = loader.loadModel("plane") 23 | model.reparent_to(self.render) 24 | model.set_pos(0,0,0) 25 | t1 = self.add_texcam(myscene, 0, -30, 10) 26 | model.setTexture(t1, 1) 27 | # A second model to the left 28 | model = loader.loadModel("plane") 29 | model.reparent_to(self.render) 30 | model.set_pos(-2.5,0,0) 31 | t2 = self.add_texcam(myscene, -20, -30, 10) 32 | model.setTexture(t2, 1) 33 | # A third one to the right 34 | model = loader.loadModel("plane") 35 | model.reparent_to(self.render) 36 | model.set_pos(2.5,0,0) 37 | t3 = self.add_texcam(myscene, 20, -30, 10) 38 | model.setTexture(t3, 1) 39 | # Main camera 40 | camera.set_pos(0,-10,0) 41 | camera.look_at(0,0,0) 42 | def add_texcam(self, scene, x, y, z): 43 | # Create a buffer and a camera that renders into it. 44 | # Set the sort to render the buffer before the main scene. 45 | # Return a texture derived from the buffer to be applied to an object. 46 | t_buffer = base.win.makeTextureBuffer("My Buffer", 512, 512) 47 | t_buffer.setClearColorActive(True) 48 | t_buffer.setClearColor(VBase4(0, 0, 0, 1)) 49 | t_texture = t_buffer.getTexture() 50 | t_buffer.setSort(-100) 51 | t_camera = base.makeCamera(t_buffer) 52 | t_camera.reparentTo(scene) 53 | t_camera.set_pos(x, y, z) 54 | t_camera.look_at(0,0,0) 55 | return t_texture 56 | 57 | rtt = RTT() 58 | rtt.run() 59 | 60 | -------------------------------------------------------------------------------- /render_to_texture/test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/render_to_texture/test.png -------------------------------------------------------------------------------- /render_to_texture/twisted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/render_to_texture/twisted.png -------------------------------------------------------------------------------- /reprojection/reproject.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | 5 | from panda3d.core import Point3 6 | 7 | from direct.showbase.ShowBase import ShowBase 8 | 9 | 10 | s = ShowBase() 11 | base.disable_mouse() 12 | s.accept("escape", sys.exit) 13 | 14 | 15 | model = base.loader.load_model("models/smiley") 16 | model.reparent_to(base.render) 17 | base.cam.set_pos(0, -20, 0) 18 | base.cam.look_at(0, 0, 0) 19 | 20 | 21 | def adjust_pos(task): 22 | if base.mouseWatcherNode.has_mouse(): 23 | model_pos = model.get_pos(base.cam) 24 | frustum_pos = Point3() 25 | base.cam.node().get_lens().project(model_pos, frustum_pos) 26 | mouse_x = base.mouseWatcherNode.get_mouse_x() 27 | mouse_y = base.mouseWatcherNode.get_mouse_y() 28 | model_depth = frustum_pos[2] 29 | new_frustum_pos = Point3(mouse_x, mouse_y, model_depth) 30 | new_model_pos = Point3() 31 | base.cam.node().get_lens().extrude_depth(new_frustum_pos, new_model_pos) 32 | model.set_pos(base.cam, new_model_pos) 33 | return task.cont 34 | 35 | 36 | base.task_mgr.add(adjust_pos) 37 | s.run() 38 | -------------------------------------------------------------------------------- /selfshadow/selfshadow.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from panda3d.core import Spotlight 5 | from direct.task import Task 6 | import sys 7 | 8 | # This is only needed on Linux with AMD cards, as they don't deal 9 | # well with Cg shaders currently. 10 | from panda3d.core import loadPrcFileData 11 | loadPrcFileData('init', 'basic-shaders-only #t') 12 | 13 | class MyApp(ShowBase): 14 | def __init__(self): 15 | # Basics 16 | ShowBase.__init__(self) 17 | base.disableMouse() 18 | self.render.setShaderAuto() 19 | self.accept("escape", sys.exit) 20 | # Model 21 | model = self.loader.loadModel("twisted") 22 | model.reparent_to(self.render) 23 | self.model = model # For reference in the rotation task 24 | # Light 25 | light = Spotlight('light') 26 | light_np = self.render.attachNewNode(light) 27 | light_np.set_pos(50, 50, 50) 28 | light_np.look_at(0, 0, 0) 29 | # Model-light interaction 30 | light.setShadowCaster(True) 31 | light.getLens().setNearFar(1, 100) 32 | self.render.setLight(light_np) 33 | # Camera 34 | self.camera.set_pos(0, -60, 30) 35 | self.camera.look_at(0, 0, 0) 36 | # Rotating the object 37 | self.taskMgr.add(self.rotate_object, 'rotate object') 38 | def rotate_object(self, task): 39 | self.model.set_h(task.getElapsedTime() * 60) 40 | return Task.cont 41 | 42 | app = MyApp() 43 | app.run() 44 | 45 | -------------------------------------------------------------------------------- /selfshadow/tex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/selfshadow/tex.png -------------------------------------------------------------------------------- /selfshadow/twisted.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/selfshadow/twisted.blend -------------------------------------------------------------------------------- /shaders/all_stages/shader.data: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct demoVertex { 4 | vec4 position; 5 | vec4 color; 6 | float instance; 7 | }; 8 | -------------------------------------------------------------------------------- /shaders/all_stages/shader.frag: -------------------------------------------------------------------------------- 1 | #version 410 2 | #pragma include "shader.data" 3 | 4 | // Invert the colors of the non-first instance. 5 | 6 | layout(location = 3) in demoVertex vertexGeom; 7 | 8 | out vec4 frag_color; 9 | 10 | void main () { 11 | if (vertexGeom.instance == 0) { 12 | frag_color = vertexGeom.color; 13 | } else { 14 | frag_color = mix(vertexGeom.color, vec4(1, 1, 1, 2) - vertexGeom.color, vertexGeom.instance); 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /shaders/all_stages/shader.geom: -------------------------------------------------------------------------------- 1 | #version 330 2 | #pragma include "shader.data" 3 | 4 | // Add backsides to the triangles. 5 | 6 | layout(triangles) in; 7 | layout(location = 2) in demoVertex[3] vertexTese; 8 | 9 | layout(triangle_strip, max_vertices = 6) out; 10 | layout(location = 3) out demoVertex vertexGeom; 11 | 12 | void main(void) { 13 | for (int i = 0; i < gl_in.length(); ++i) { 14 | gl_Position = gl_in[i].gl_Position; 15 | 16 | vertexGeom.position = vertexTese[i].position; 17 | vertexGeom.color = vertexTese[i].color; 18 | vertexGeom.instance = vertexTese[i].instance; 19 | 20 | EmitVertex(); 21 | } 22 | EndPrimitive(); 23 | for (int i = 0; i < gl_in.length(); ++i) { 24 | gl_Position = gl_in[2-i].gl_Position; 25 | 26 | vertexGeom.position = vertexTese[2-i].position; 27 | vertexGeom.color = vertexTese[2-i].color; 28 | vertexGeom.instance = vertexTese[2-i].instance; 29 | 30 | EmitVertex(); 31 | } 32 | EndPrimitive(); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /shaders/all_stages/shader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import random 5 | 6 | from direct.showbase.ShowBase import ShowBase 7 | from panda3d.core import NodePath 8 | from panda3d.core import Geom, GeomNode, GeomPatches, \ 9 | GeomVertexFormat, GeomVertexData, GeomVertexArrayFormat, \ 10 | GeomVertexWriter, GeomVertexReader, \ 11 | InternalName 12 | from panda3d.core import Shader 13 | from direct.task import Task 14 | from panda3d.core import PStatClient 15 | 16 | num_instances = 5 17 | 18 | class Base(ShowBase): 19 | def __init__(self): 20 | # The basics 21 | ShowBase.__init__(self) 22 | base.disableMouse() 23 | base.setBackgroundColor(0.1, 0.1, 0.1) 24 | base.setFrameRateMeter(True) 25 | PStatClient.connect() 26 | self.accept("escape", sys.exit) 27 | # Camera 28 | self.camera_orbit = base.render.attach_new_node("Camera orbit") 29 | self.camera_pitch = self.camera_orbit.attach_new_node("Camera pitch") 30 | base.camera.reparent_to(self.camera_pitch) 31 | base.camera.set_pos(0, -30, 0) 32 | # Camera control 33 | self.camera_movement = (0, 0) 34 | self.accept("arrow_up", self.change_camera_movement, [ 0, -1]) 35 | self.accept("arrow_up-up", self.change_camera_movement, [ 0, 1]) 36 | self.accept("arrow_down", self.change_camera_movement, [ 0, 1]) 37 | self.accept("arrow_down-up", self.change_camera_movement, [ 0, -1]) 38 | self.accept("arrow_left", self.change_camera_movement, [-1, 0]) 39 | self.accept("arrow_left-up", self.change_camera_movement, [ 1, 0]) 40 | self.accept("arrow_right", self.change_camera_movement, [ 1, 0]) 41 | self.accept("arrow_right-up", self.change_camera_movement, [-1, 0]) 42 | base.taskMgr.add(self.move_camera, "Move camera") 43 | # Object 44 | self.model = self.create_model() 45 | self.model.reparent_to(base.render) 46 | taskMgr.add(self.refresh_shader_vars, "Refresh shaders variables", sort = 49) 47 | self.accept("r", self.reload_shader) 48 | 49 | def create_model(self): 50 | # Set up the vertex arrays 51 | vformatArray = GeomVertexArrayFormat() 52 | # Panda3D implicitly generates a bounding volume from a 53 | # column named "vertex", so you either 54 | # * have a column of that name, or 55 | # * add a bounding volume yourself. 56 | vformatArray.addColumn(InternalName.make("vertex"), 3, Geom.NTFloat32, Geom.CPoint) 57 | vformatArray.addColumn(InternalName.make("color"), 4, Geom.NTFloat32, Geom.CColor) 58 | 59 | vformat = GeomVertexFormat() 60 | vformat.addArray(vformatArray) 61 | vformat = GeomVertexFormat.registerFormat(vformat) 62 | 63 | vdata = GeomVertexData("Data", vformat, Geom.UHStatic) 64 | vertex = GeomVertexWriter(vdata, 'vertex') 65 | color = GeomVertexWriter(vdata, 'color') 66 | 67 | geom = Geom(vdata) 68 | 69 | # Vertex data 70 | vertex.addData3f(1.5, 0, -1) 71 | color.addData4f(1, 0, 0, 1) 72 | vertex.addData3f(-1.5, 0, -1) 73 | color.addData4f(0, 1, 0, 1) 74 | vertex.addData3f(0, 0, 1) 75 | color.addData4f(0, 0, 1, 1) 76 | 77 | # Primitive 78 | tri = GeomPatches(3, Geom.UHStatic) 79 | tri.add_vertex(2) 80 | tri.add_vertex(1) 81 | tri.add_vertex(0) 82 | tri.close_primitive() 83 | geom.addPrimitive(tri) 84 | 85 | # Create the actual node 86 | node = GeomNode('geom_node') 87 | node.addGeom(geom) 88 | np = NodePath(node) 89 | 90 | # Shader, initial shader vars, number of instances 91 | np.set_shader(Shader.load(Shader.SL_GLSL, 92 | vertex = "shader.vert", 93 | tess_control = "shader.tesc", 94 | tess_evaluation = "shader.tese", 95 | geometry = "shader.geom", 96 | fragment = "shader.frag")) 97 | np.set_shader_input("time", 0.0) 98 | np.set_shader_input("tess_level", 32.0) 99 | np.set_instance_count(num_instances) 100 | np.set_shader_input("numInstances", num_instances) 101 | return np 102 | 103 | def change_camera_movement(self, turn, pitch): 104 | self.camera_movement = (self.camera_movement[0] + turn, 105 | self.camera_movement[1] + pitch) 106 | 107 | def move_camera(self, task): 108 | self.camera_orbit.set_h(self.camera_orbit, self.camera_movement[0] * globalClock.get_dt() * 360.0 / 3.0) 109 | new_pitch = self.camera_pitch.get_p() + self.camera_movement[1] * globalClock.get_dt() * 360.0 / 3.0 110 | self.camera_pitch.set_p(min(max(new_pitch, -89), 89)) 111 | return Task.cont 112 | 113 | def refresh_shader_vars(self, task): 114 | self.model.set_shader_input("time", task.time) 115 | return Task.cont 116 | 117 | def reload_shader(self): 118 | self.model.set_shader(Shader.load(Shader.SL_GLSL, 119 | vertex = "shader.vert", 120 | tess_control = "shader.tesc", 121 | tess_evaluation = "shader.tese", 122 | geometry = "shader.geom", 123 | fragment = "shader.frag")) 124 | 125 | if __name__ == '__main__': 126 | app = Base() 127 | app.run() 128 | 129 | -------------------------------------------------------------------------------- /shaders/all_stages/shader.tesc: -------------------------------------------------------------------------------- 1 | #version 430 2 | #pragma include "shader.data" 3 | 4 | // Set the tessellation level. 5 | 6 | uniform float tess_level; 7 | layout(location = 0) in demoVertex vertexRaw[]; 8 | 9 | layout(vertices = 3) out; 10 | layout(location = 1) out demoVertex vertexTesc[]; 11 | 12 | void main() { 13 | if (gl_InvocationID == 0) { 14 | gl_TessLevelInner[0] = tess_level; 15 | gl_TessLevelInner[1] = tess_level; 16 | gl_TessLevelOuter[0] = tess_level; 17 | gl_TessLevelOuter[1] = tess_level; 18 | gl_TessLevelOuter[2] = tess_level; 19 | gl_TessLevelOuter[3] = tess_level; 20 | } 21 | gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position; 22 | vertexTesc[gl_InvocationID] = vertexRaw[gl_InvocationID]; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /shaders/all_stages/shader.tese: -------------------------------------------------------------------------------- 1 | #version 430 2 | #pragma include "shader.data" 3 | 4 | // Calculate the position and other attributes of the vertices of the now 5 | // tessellated patches. 6 | 7 | uniform mat4 p3d_ModelViewProjectionMatrix; 8 | uniform float time; 9 | layout(triangles, equal_spacing, ccw) in; 10 | layout(location = 1) in demoVertex vertexTesc[]; 11 | 12 | layout(location = 2) out demoVertex vertexTese; 13 | 14 | void main() { 15 | gl_Position = p3d_ModelViewProjectionMatrix * 16 | (gl_TessCoord.x * gl_in[0].gl_Position + 17 | gl_TessCoord.y * gl_in[1].gl_Position + 18 | gl_TessCoord.z * gl_in[2].gl_Position + 19 | vec4(0, -0.3, 0, 0) * 20 | sin(time * 5.0 + gl_TessCoord.x * 3.1415 * 2.0 * 3.0)); 21 | 22 | 23 | vertexTese.position = gl_Position; 24 | vertexTese.color = gl_TessCoord.x * vertexTesc[0].color + 25 | gl_TessCoord.y * vertexTesc[1].color + 26 | gl_TessCoord.z * vertexTesc[2].color; 27 | vertexTese.instance = vertexTesc[0].instance; 28 | 29 | } 30 | 31 | -------------------------------------------------------------------------------- /shaders/all_stages/shader.vert: -------------------------------------------------------------------------------- 1 | #version 410 2 | #pragma include "shader.data" 3 | 4 | // Displace the patch vertices based on the patches instance. One goes left, one 5 | // goes right. 6 | 7 | uniform mat4 p3d_ModelViewProjectionMatrix; 8 | uniform float numInstances; 9 | in vec4 vertex; 10 | in vec4 color; 11 | 12 | layout(location = 0) out demoVertex vertexRaw; 13 | 14 | void main() { 15 | vertexRaw.color = color; 16 | vertexRaw.instance = float(gl_InstanceID) / (numInstances - 1); 17 | vertexRaw.position = vertex; 18 | 19 | float offset = (vertexRaw.instance - 0.5) * 2.0; 20 | offset = offset * numInstances * 1.5; 21 | vertexRaw.position.x = vertexRaw.position.x + offset; 22 | 23 | gl_Position = vertexRaw.position; 24 | } 25 | 26 | -------------------------------------------------------------------------------- /shaders/compute/main_basic_compute.py: -------------------------------------------------------------------------------- 1 | # Ummmmmhi. Just one minute please, before we start I wanna take a look 2 | # at what kind of computing power I'm working with here on this 3 | # notebook. 4 | # Okay, `lscpu` tells me that my computer has four CPU cores (on two 5 | # physical CPUs) with 3GHz maximum clock frequency. `lspci` and 6 | # Wikipedia reveal that my GPU has 24 execution units with 8 shading 7 | # units each, operating at up to 1GHz clock frequency. So each "core" 8 | # of a GPU is slower (and less flexible, as we will see) than those of 9 | # the CPU, but there are simply *many* more of them. Okay, back to our 10 | # scheduled program. 11 | # 12 | # Welcome back; We're gonna make compute shaders work today! So, what 13 | # *is* a compute shader? Well, I'm glad I asked! As with other shaders, 14 | # they are programs that run on the GPU, and transform incoming data 15 | # into outgoing data. They are run in parallel, usually in large 16 | # numbers, and you have no control over the timing at which they are 17 | # individually invoked, and there are (next to) no constructs for making 18 | # this parallelism thread-safe. Despite these limitations, when a 19 | # problem can be turned into a compute shader, the raw computing power 20 | # we can throw at it makes the initial investment of cleverness worth 21 | # it. 22 | 23 | 24 | from panda3d.core import NodePath 25 | from panda3d.core import Vec3 26 | from panda3d.core import Vec4 27 | from panda3d.core import Shader 28 | from panda3d.core import ShaderAttrib 29 | from panda3d.core import CardMaker 30 | from panda3d.core import PNMImage 31 | from panda3d.core import PfmFile 32 | from panda3d.core import TextureStage 33 | from panda3d.core import Texture 34 | from panda3d.core import LColor 35 | 36 | from direct.showbase.ShowBase import ShowBase 37 | 38 | 39 | # As usual, we will keep it simple. Specifically, we will use the 40 | # example shader from Panda3D's manual, and get to actually run. 41 | # The shader requires two textures, one to get data into the shader, and 42 | # one to get data out of it again without overwriting the original. The 43 | # shader itself will be run once per texel, will load "its" texel's 44 | # value, swap the red and green channel, and write it to the output 45 | # texture. 46 | resolution = 256 47 | 48 | # We'll work with floats, and we want them to be stored in a way that 49 | # allows for efficient upload to the GPU; Since neither is the case for 50 | # PNMImage, we will use a PfmFile today. 51 | image_in = PfmFile() 52 | image_in.clear( 53 | x_size=resolution, 54 | y_size=resolution, 55 | num_channels=4, # RGBA means four channels 56 | ) 57 | image_in.fill(Vec4(1, 0, 0, 1)) # We fill the image red. 58 | texture_in = Texture('') 59 | texture_in.setup_2d_texture( 60 | image_in.get_x_size(), 61 | image_in.get_y_size(), 62 | Texture.T_float, # Components will be 32 bit floats. 63 | Texture.F_rgba32, # Four 32 bit floats make one texel. 64 | ) 65 | texture_in.load(image_in) 66 | 67 | # ...and the second texture, nearly identical. 68 | image_out = PfmFile() 69 | image_out.clear( 70 | x_size=resolution, 71 | y_size=resolution, 72 | num_channels=4, 73 | ) 74 | image_out.fill(Vec4(0, 0, 0, 1)) # Right now, this texture is black. 75 | texture_out = Texture('') 76 | texture_out.setup_2d_texture( 77 | image_out.get_x_size(), 78 | image_out.get_y_size(), 79 | Texture.T_float, 80 | Texture.F_rgba32, 81 | ) 82 | texture_out.load(image_out) 83 | 84 | # Now for the shader, which we'll write in GLSL. 85 | shader_source = """#version 430 86 | 87 | // Here we define the workgroup size. When we later dispatch the shader 88 | // (send it off to be run), it will be done so in groups of 16x16. 89 | layout (local_size_x = 16, local_size_y = 16) in; 90 | 91 | // The shader needs to know about the textures' formats to process the 92 | // raw data correctly. 93 | layout(rgba32f) uniform readonly image2D fromTex; 94 | layout(rgba32f) uniform writeonly image2D toTex; 95 | 96 | void main() { 97 | ivec2 texelCoords = ivec2(gl_GlobalInvocationID.xy); // Where are we? 98 | vec4 pixel = imageLoad(fromTex, texelCoords); // Load the texel. 99 | pixel.gr = pixel.rg; // Swap the red and green channels. 100 | imageStore(toTex, texelCoords, pixel); // Store the texel. 101 | } 102 | """ 103 | 104 | # Technically, we could now dispatch the shader (if we had a graphics 105 | # engine set up already) and be done with it; We could now generalize 106 | # the code as we want to, and do all kinds of math with it. However, we 107 | # also want to see some outout (and haven't set up the graphics engine 108 | # yet anyway), so let's fire up Panda3D. 109 | ShowBase() 110 | base.cam.set_pos(0.5, -2.0, 0.5) 111 | base.accept('escape', base.task_mgr.stop) 112 | 113 | # The easiest way to look at a texture is still the humble quad. 114 | cm = CardMaker('card') 115 | card = render.attach_new_node(cm.generate()) 116 | card.set_texture(texture_out) 117 | 118 | # For management purposes, we create a NodePath to contain our shader 119 | # and associated data. 120 | shader = Shader.make_compute(Shader.SL_GLSL, shader_source) 121 | dummy = NodePath("dummy") 122 | dummy.set_shader(shader) 123 | dummy.set_shader_input("fromTex", texture_in) 124 | dummy.set_shader_input("toTex", texture_out) 125 | 126 | # Now we retrieve the relevant data from the node... 127 | sattr = dummy.get_attrib(ShaderAttrib) 128 | 129 | # ...and dispatch the shader. Each workgroup consists of 16x16x1 130 | # invocations, and we need `resolution`x`resolution`x1 invocations in 131 | # total, so it's trivial to math out how many workgroups we need. 132 | workgroups = (resolution // 16, resolution // 16, 1) 133 | # Here... we... GO! ...in a moment. When we dispatch a shader like this, 134 | # Panda3D will wait with its execution until the current frame is 135 | # rendered. 136 | base.graphicsEngine.dispatch_compute( 137 | workgroups, 138 | sattr, 139 | base.win.get_gsg(), 140 | ) 141 | 142 | # Since right here and now we *aren't* rendering a frame at all, I am 143 | # utterly clueless on whether the shader will be run before or after the 144 | # first frame, and I also don't know how to get the data from the GPU 145 | # back into the texture. I mean, we know that it works because the quad 146 | # on the screen is green, but where is the data to prove it? 147 | # Well, here: 148 | c = LColor(0, 0, 0, 0) 149 | texture_out.peek().fetch_pixel(c, 0, 0) 150 | print(f"Texture color: {c}") 151 | print(f"Image color : {image_out.get_point(0, 0)}") 152 | print(f"Extracting texture data...") 153 | base.graphicsEngine.extract_texture_data(texture_out, base.win.get_gsg()) 154 | texture_out.peek().fetch_pixel(c, 0, 0) 155 | print(f"Texture color: {c}") 156 | print(f"Image color : {image_out.get_point(0, 0)}") 157 | print(f"Storing texture to image...") 158 | texture_out.store(image_out) 159 | print(f"Texture color: {c}") 160 | print(f"Image color : {image_out.get_point(0, 0)}") 161 | 162 | # ...and now you know to run compute shaders, and get data onto the GPU, 163 | # and back down again. Have fun! 164 | base.run() 165 | -------------------------------------------------------------------------------- /shaders/minimal/shader.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import random 5 | 6 | from direct.showbase.ShowBase import ShowBase 7 | from direct.task import Task 8 | from panda3d.core import Shader 9 | 10 | 11 | # These are GLES shaders, because why not. 12 | vertex_shader = """ 13 | #version 100 14 | 15 | uniform mat4 p3d_ModelViewProjectionMatrix; 16 | attribute vec4 vertex; 17 | attribute vec2 p3d_MultiTexCoord0; 18 | 19 | varying vec2 v_texcoord; 20 | 21 | void main() { 22 | gl_Position = p3d_ModelViewProjectionMatrix * vertex; 23 | v_texcoord = p3d_MultiTexCoord0; 24 | } 25 | """ 26 | 27 | 28 | fragment_shader = """ 29 | #version 100 30 | 31 | precision mediump float; 32 | uniform sampler2D p3d_Texture0; 33 | varying vec2 v_texcoord; 34 | 35 | void main () { 36 | gl_FragColor = texture2D(p3d_Texture0, v_texcoord); 37 | } 38 | """ 39 | 40 | 41 | class Base(ShowBase): 42 | def __init__(self): 43 | # The basics 44 | ShowBase.__init__(self) 45 | base.disableMouse() 46 | base.setBackgroundColor(0.1, 0.1, 0.1) 47 | base.setFrameRateMeter(True) 48 | self.accept("escape", sys.exit) 49 | # Camera 50 | self.camera_orbit = base.render.attach_new_node("Camera orbit") 51 | self.camera_pitch = self.camera_orbit.attach_new_node("Camera pitch") 52 | base.camera.reparent_to(self.camera_pitch) 53 | base.camera.set_pos(0, -5, 0) 54 | # Camera control 55 | self.camera_movement = (0, 0) 56 | self.accept("arrow_up", self.change_camera_movement, [ 0, -1]) 57 | self.accept("arrow_up-up", self.change_camera_movement, [ 0, 1]) 58 | self.accept("arrow_down", self.change_camera_movement, [ 0, 1]) 59 | self.accept("arrow_down-up", self.change_camera_movement, [ 0, -1]) 60 | self.accept("arrow_left", self.change_camera_movement, [-1, 0]) 61 | self.accept("arrow_left-up", self.change_camera_movement, [ 1, 0]) 62 | self.accept("arrow_right", self.change_camera_movement, [ 1, 0]) 63 | self.accept("arrow_right-up", self.change_camera_movement, [-1, 0]) 64 | base.taskMgr.add(self.move_camera, "Move camera") 65 | # Object 66 | self.model = loader.loadModel("models/smiley") 67 | self.model.reparent_to(base.render) 68 | shader = Shader.make( 69 | Shader.SL_GLSL, 70 | vertex = vertex_shader, 71 | fragment = fragment_shader, 72 | ) 73 | self.model.set_shader(shader) 74 | # taskMgr.add(self.refresh_shader_vars, "Refresh shaders variables", sort = 49) 75 | 76 | def change_camera_movement(self, turn, pitch): 77 | self.camera_movement = (self.camera_movement[0] + turn, 78 | self.camera_movement[1] + pitch) 79 | 80 | def move_camera(self, task): 81 | self.camera_orbit.set_h(self.camera_orbit, self.camera_movement[0] * globalClock.get_dt() * 360.0 / 3.0) 82 | new_pitch = self.camera_pitch.get_p() + self.camera_movement[1] * globalClock.get_dt() * 360.0 / 3.0 83 | self.camera_pitch.set_p(min(max(new_pitch, -89), 89)) 84 | return Task.cont 85 | 86 | #def refresh_shader_vars(self, task): 87 | # self.model.set_shader_input("time", task.time) 88 | # return Task.cont 89 | 90 | 91 | if __name__ == '__main__': 92 | app = Base() 93 | app.run() 94 | -------------------------------------------------------------------------------- /shape_keys/box.blend: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/shape_keys/box.blend -------------------------------------------------------------------------------- /shape_keys/box.egg: -------------------------------------------------------------------------------- 1 | { Z-Up } 2 | 3 | { 4 | "egg-trans -tbnall -o /home/baribal/src/panda_examples/shape_keys/box.egg /home/baribal/src/panda_examples/shape_keys/box.egg" 5 | } 6 | Material { 7 | diffr { 0.64 } 8 | diffg { 0.64 } 9 | diffb { 0.64 } 10 | emitr { 0 } 11 | emitg { 0 } 12 | emitb { 0 } 13 | specr { 0.5 } 14 | specg { 0.5 } 15 | specb { 0.5 } 16 | shininess { 12.5 } 17 | } 18 | Cube { 19 | { 1 } 20 | Cube { 21 | 0 { 22 | 1 1 -1 23 | Key2 { -0.5 -0.5 0 } 24 | } 25 | 1 { 26 | 1 -1 -1 27 | Key2 { -0.5 0.5 0 } 28 | } 29 | 2 { 30 | -1 -1 -1 31 | Key2 { 0.5 0.5 0 } 32 | } 33 | 3 { 34 | -1 1 -1 35 | Key2 { 0.5 -0.5 0 } 36 | } 37 | 4 { 38 | 1 0.999999 1 39 | Key1 { -0.5 -0.5 0 } 40 | } 41 | 5 { 42 | -1 1 1 43 | Key1 { 0.5 -0.5 0 } 44 | } 45 | 6 { 46 | -1 -1 1 47 | Key1 { 0.5 0.5 0 } 48 | } 49 | 7 { 50 | 0.999999 -1 1 51 | Key1 { -0.5 0.5 0 } 52 | } 53 | } 54 | { 55 | { 0 0 -1 } 56 | { Material } 57 | { 0 1 2 3 { Cube } } 58 | } 59 | { 60 | { 0 0 1 } 61 | { Material } 62 | { 4 5 6 7 { Cube } } 63 | } 64 | { 65 | { 1 0 0 } 66 | { Material } 67 | { 0 4 7 1 { Cube } } 68 | } 69 | { 70 | { 0 -1 0 } 71 | { Material } 72 | { 1 7 6 2 { Cube } } 73 | } 74 | { 75 | { -1 0 0 } 76 | { Material } 77 | { 2 6 5 3 { Cube } } 78 | } 79 | { 80 | { 0 1 0 } 81 | { Material } 82 | { 4 0 3 5 { Cube } } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /shape_keys/shape_key.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python2 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from direct.task import Task 5 | from direct.actor.Actor import Actor 6 | from math import sin, cos, pi 7 | import sys 8 | 9 | class ShapeKeyDemo(ShowBase): 10 | def __init__(self): 11 | ShowBase.__init__(self) 12 | self.accept("escape", sys.exit) 13 | base.disableMouse() 14 | base.camera.set_pos(0, -10, 0) 15 | base.camera.look_at(0, 0, 0) 16 | a = Actor('box.egg') 17 | a.reparentTo(render) 18 | self.j_1 = a.controlJoint(None,'modelRoot' ,'Key1') 19 | self.j_2 = a.controlJoint(None,'modelRoot' ,'Key2') 20 | base.taskMgr.add(self.animate, "Shape Key Animation") 21 | def animate(self, task): 22 | self.j_1.setX(sin(task.time)) 23 | self.j_2.setX(cos(task.time)) 24 | return Task.cont 25 | 26 | app = ShapeKeyDemo() 27 | app.run() 28 | 29 | -------------------------------------------------------------------------------- /skybox/display_skybox.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from direct.showbase.ShowBase import ShowBase 4 | from panda3d.core import TextureStage, TexGenAttrib 5 | from direct.task import Task 6 | from math import sin, cos, pi 7 | import sys 8 | 9 | cam_speed = 0.2 10 | 11 | class MyApp(ShowBase): 12 | def __init__(self): 13 | # The basics 14 | ShowBase.__init__(self) 15 | base.disableMouse() 16 | # Some models for orientation 17 | for x in [-30,0,30]: 18 | for y in [-30,0,30]: 19 | for z in [-30,0,30]: 20 | m = self.loader.loadModel("models/smiley") 21 | m.reparentTo(self.render) 22 | m.setPos(x,y,z) 23 | # The skybox part 24 | skybox = self.loader.loadModel("skybox_1024") 25 | skybox.reparentTo(self.camera) 26 | skybox.set_two_sided(True) 27 | skybox.set_bin("background", 0) 28 | skybox.set_depth_write(False) 29 | skybox.set_compass() 30 | # Bookkeeping for the rotation around the model 31 | self.angle = 0.0 32 | self.pitch = 0.0 33 | self.adjust_angle = 0 34 | self.adjust_pitch = 0 35 | self.last_time = 0.0 36 | # Initial camera setup 37 | self.camera.set_pos(sin(self.angle)*20,-cos(self.angle)*20,0) 38 | self.camera.look_at(0,0,0) 39 | # Key events and camera movement task 40 | self.accept("arrow_left", self.adjust_turning, [-1.0, 0.0]) 41 | self.accept("arrow_left-up", self.adjust_turning, [1.0, 0.0]) 42 | self.accept("arrow_right", self.adjust_turning, [1.0, 0.0]) 43 | self.accept("arrow_right-up", self.adjust_turning, [-1.0, 0.0]) 44 | self.accept("arrow_up", self.adjust_turning, [0.0, 1.0]) 45 | self.accept("arrow_up-up", self.adjust_turning, [0.0, -1.0]) 46 | self.accept("arrow_down", self.adjust_turning, [0.0, -1.0]) 47 | self.accept("arrow_down-up", self.adjust_turning, [0.0, 1.0]) 48 | self.accept("escape", sys.exit) 49 | self.taskMgr.add(self.update_camera, 'adjust camera', sort = 10) 50 | def adjust_turning(self, heading, pitch): 51 | self.adjust_angle += heading 52 | self.adjust_pitch += pitch 53 | def update_camera(self, task): 54 | if task.time != 0.0: 55 | dt = task.time - self.last_time 56 | self.last_time = task.time 57 | self.angle += pi * dt * self.adjust_angle * cam_speed 58 | self.pitch += pi * dt * self.adjust_pitch * cam_speed 59 | # Why /2.001 and not an even 2.0? Because then we'd have to set_Hpr 60 | # explicitly, as look_at can't deduce the heading when the camera is 61 | # exactly above/below the spheres center. 62 | if self.pitch > pi/2.001: 63 | self.pitch = pi/2.001 64 | if self.pitch < -pi/2.001: 65 | self.pitch = -pi/2.001 66 | self.camera.set_pos(sin(self.angle)*cos(abs(self.pitch))*20, 67 | -cos(self.angle)*cos(abs(self.pitch))*20, 68 | sin(self.pitch)*20) 69 | self.camera.look_at(0,0,0) 70 | return Task.cont 71 | 72 | app = MyApp() 73 | app.run() 74 | 75 | -------------------------------------------------------------------------------- /skybox/skybox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Schwarzbaer/panda_examples/ea24a9e89685335264bbb4b68d872678f6f754c9/skybox/skybox.png -------------------------------------------------------------------------------- /skybox/skybox_1024.egg: -------------------------------------------------------------------------------- 1 | { Z-up } 2 | Material.012 { 3 | diffr { 0.000000 } 4 | diffg { 0.000000 } 5 | diffb { 0.000000 } 6 | specr { 0.000000 } 7 | specg { 0.000000 } 8 | specb { 0.000000 } 9 | shininess { 0.0 } 10 | emitr { 0.000000 } 11 | emitg { 0.000000 } 12 | emitb { 0.000000 } 13 | } 14 | 15 | Texture.004 { 16 | "./skybox.png" 17 | envtype { MODULATE } 18 | minfilter { LINEAR_MIPMAP_LINEAR } 19 | magfilter { LINEAR_MIPMAP_LINEAR } 20 | wrap { REPEAT } 21 | } 22 | 23 | Cube.002 { 24 | { 25 | { 26 | 10.000000 0.000000 0.000000 0.000000 27 | 0.000000 10.000000 0.000000 0.000000 28 | 0.000000 0.000000 10.000000 0.000000 29 | 0.000000 0.000000 0.000000 1.000000 30 | } 31 | } 32 | 33 | Cube.002 { 34 | 35 | 0 { 36 | -10.0 -10.0 10.0 37 | 38 | { 39 | 0.33317057291666663 0.499755859375 40 | } 41 | } 42 | 1 { 43 | -10.0 -10.0 -10.0 44 | 45 | { 46 | 0.333170572917 0.000244140625 47 | } 48 | } 49 | 2 { 50 | -10.0 10.0 -10.0 51 | 52 | { 53 | 0.000162760416667 0.000244140625 54 | } 55 | } 56 | 3 { 57 | -10.0 10.0 10.0 58 | 59 | { 60 | 0.000162760416667 0.499755859375 61 | } 62 | } 63 | 4 { 64 | -10.0 10.0 10.0 65 | 66 | { 67 | 0.999837239583 0.499755859375 68 | } 69 | } 70 | 5 { 71 | -10.0 10.0 -10.0 72 | 73 | { 74 | 0.999837239583 0.000244140625 75 | } 76 | } 77 | 6 { 78 | 10.0 10.0 -10.0 79 | 80 | { 81 | 0.666829427083 0.000244140625 82 | } 83 | } 84 | 7 { 85 | 10.0 10.0 10.0 86 | 87 | { 88 | 0.666829427083 0.499755859375 89 | } 90 | } 91 | 8 { 92 | 10.0 10.0 10.0 93 | 94 | { 95 | 0.66650390625 0.999755859375 96 | } 97 | } 98 | 9 { 99 | 10.0 10.0 -10.0 100 | 101 | { 102 | 0.66650390625 0.500244140625 103 | } 104 | } 105 | 10 { 106 | 10.0 -10.0 -10.0 107 | 108 | { 109 | 0.33349609375 0.500244140625 110 | } 111 | } 112 | 11 { 113 | 10.0 -10.0 10.0 114 | 115 | { 116 | 0.33349609375 0.999755859375 117 | } 118 | } 119 | 12 { 120 | 10.0 -10.0 10.0 121 | 122 | { 123 | 0.66650390625 0.499755859375 124 | } 125 | } 126 | 13 { 127 | 10.0 -10.0 -10.0 128 | 129 | { 130 | 0.66650390625 0.000244140625 131 | } 132 | } 133 | 14 { 134 | -10.0 -10.0 -10.0 135 | 136 | { 137 | 0.33349609375 0.000244140625 138 | } 139 | } 140 | 15 { 141 | -10.0 -10.0 10.0 142 | 143 | { 144 | 0.33349609375 0.499755859375 145 | } 146 | } 147 | 16 { 148 | -10.0 -10.0 -10.0 149 | 150 | { 151 | 0.666829427083 0.999755859375 152 | } 153 | } 154 | 17 { 155 | 10.0 -10.0 -10.0 156 | 157 | { 158 | 0.999837239583 0.999755859375 159 | } 160 | } 161 | 18 { 162 | 10.0 10.0 -10.0 163 | 164 | { 165 | 0.999837239583 0.500244140625 166 | } 167 | } 168 | 19 { 169 | -10.0 10.0 -10.0 170 | 171 | { 172 | 0.666829427083 0.500244140625 173 | } 174 | } 175 | 20 { 176 | 10.0 -10.0 10.0 177 | 178 | { 179 | 0.333170572917 0.500244140625 180 | } 181 | } 182 | 21 { 183 | -10.0 -10.0 10.0 184 | 185 | { 186 | 0.000162760416667 0.500244140625 187 | } 188 | } 189 | 22 { 190 | -10.0 10.0 10.0 191 | 192 | { 193 | 0.000162760416667 0.999755859375 194 | } 195 | } 196 | 23 { 197 | 10.0 10.0 10.0 198 | 199 | { 200 | 0.333170572917 0.999755859375 201 | } 202 | }} 203 | 204 | 205 | { 206 | { Texture.004 } 207 | { Material.012 } 208 | {1.000000 0.000000 0.000000} 209 | { 0 1 2 3 { Cube.002 }} 210 | } 211 | { 212 | { Texture.004 } 213 | { Material.012 } 214 | {0.000000 -1.000000 0.000000} 215 | { 4 5 6 7 { Cube.002 }} 216 | } 217 | { 218 | { Texture.004 } 219 | { Material.012 } 220 | {-1.000000 0.000000 0.000000} 221 | { 8 9 10 11 { Cube.002 }} 222 | } 223 | { 224 | { Texture.004 } 225 | { Material.012 } 226 | {0.000000 1.000000 0.000000} 227 | { 12 13 14 15 { Cube.002 }} 228 | } 229 | { 230 | { Texture.004 } 231 | { Material.012 } 232 | {0.000000 0.000000 1.000000} 233 | { 16 17 18 19 { Cube.002 }} 234 | } 235 | { 236 | { Texture.004 } 237 | { Material.012 } 238 | {0.000000 0.000000 -1.000000} 239 | { 20 21 22 23 { Cube.002 }} 240 | } 241 | } 242 | --------------------------------------------------------------------------------