| 1 | from panda3d.core import CullFaceAttrib, Point3, NodePath, Point2, Texture |
| 2 | from panda3d.bullet import BulletBoxShape, BulletRigidBodyNode |
| 3 | from direct.gui.OnscreenText import OnscreenText |
| 4 | from lib.lib.p3d.gfx import P3dGfxMgr |
| 5 | |
| 6 | class Box: |
| 7 | |
| 8 | def __init__(self, world, plane_node, count, cb_inst): |
| 9 | self._world = world |
| 10 | self._plane_node = plane_node |
| 11 | self._count = count |
| 12 | self._cb_inst = cb_inst |
| 13 | shape = BulletBoxShape((.5, .5, .5)) |
| 14 | self.node = BulletRigidBodyNode('box') |
| 15 | self.node.add_shape(shape) |
| 16 | self._np = render.attach_new_node(self.node) |
| 17 | world.attach_rigid_body(self.node) |
| 18 | model = loader.load_model('assets/gltf/box/box.gltf') |
| 19 | model.flatten_light() |
| 20 | model.reparent_to(self._np) |
| 21 | self._set_outline_model() |
| 22 | self._start_drag_pos = None |
| 23 | self._prev_rot_info = None |
| 24 | self._instantiated = False |
| 25 | taskMgr.add(self.on_frame, 'on_frame') |
| 26 | self._repos() |
| 27 | |
| 28 | def _repos(self): |
| 29 | p_from, p_to = P3dGfxMgr.world_from_to((-1, 1)) |
| 30 | for hit in self._world.ray_test_all(p_from, p_to).get_hits(): |
| 31 | if hit.get_node() == self._plane_node: |
| 32 | pos = hit.get_hit_pos() |
| 33 | corner = P3dGfxMgr.screen_coord(pos) |
| 34 | bounds = self._np.get_tight_bounds() |
| 35 | bounds = bounds[0] - self._np.get_pos(), bounds[1] - self._np.get_pos() |
| 36 | self._np.set_pos(pos) |
| 37 | dist = -1, -1 |
| 38 | def __update(set, get, delta): |
| 39 | set(get() + delta) |
| 40 | top_left = self._np.get_pos() + (bounds[0][0], bounds[0][1], bounds[1][2]) |
| 41 | tl2d = P3dGfxMgr.screen_coord(top_left) |
| 42 | tmpnode = NodePath('tmp') |
| 43 | tmpnode.set_pos(tl2d[0], 0, tl2d[1]) |
| 44 | cornernode = NodePath('corner') |
| 45 | cornernode.set_pos(corner[0], 0, corner[1]) |
| 46 | dist = tmpnode.get_pos(cornernode) |
| 47 | tmpnode.remove_node() |
| 48 | cornernode.remove_node() |
| 49 | return dist |
| 50 | while dist[0] < .01: |
| 51 | dist = __update(self._np.set_x, self._np.get_x, .01) |
| 52 | while dist[2] > -.01: |
| 53 | dist = __update(self._np.set_z, self._np.get_z, -.01) |
| 54 | if not hasattr(self, '_txt'): |
| 55 | font = base.loader.load_font('assets/fonts/Hanken-Book.ttf') |
| 56 | font.clear() |
| 57 | font.set_pixels_per_unit(60) |
| 58 | font.set_minfilter(Texture.FTLinearMipmapLinear) |
| 59 | font.set_outline((0, 0, 0, 1), .8, .2) |
| 60 | self._txt = OnscreenText( |
| 61 | str(self._count), font=font, scale=0.06, fg=(.9, .9, .9, 1)) |
| 62 | pos = self._np.get_pos() + (bounds[1][0], bounds[0][1], bounds[0][2]) |
| 63 | p2d = P3dGfxMgr.screen_coord(pos) |
| 64 | self._txt['pos'] = p2d |
| 65 | |
| 66 | def get_corner(self): |
| 67 | bounds = self._np.get_tight_bounds() |
| 68 | return bounds[1][0], bounds[1][1], bounds[0][2] |
| 69 | |
| 70 | def _set_outline_model(self): |
| 71 | self._outline_model = loader.load_model('assets/gltf/box/box.gltf') |
| 72 | clockw = CullFaceAttrib.MCullClockwise |
| 73 | self._outline_model.set_attrib(CullFaceAttrib.make(clockw)) |
| 74 | self._outline_model.reparent_to(self._np) |
| 75 | self._outline_model.set_scale(-1.08, -1.08, -1.08) |
| 76 | self._outline_model.set_light_off() |
| 77 | self._outline_model.set_color(.4, .4, .4, 1) |
| 78 | self._outline_model.set_color_scale(.4, .4, .4, 1) |
| 79 | self._outline_model.hide() |
| 80 | |
| 81 | def on_frame(self, task): |
| 82 | self._np.set_y(0) |
| 83 | return task.cont |
| 84 | |
| 85 | def play(self): |
| 86 | self._world.remove_rigid_body(self.node) |
| 87 | self.node.set_mass(1) |
| 88 | self._world.attach_rigid_body(self.node) |
| 89 | |
| 90 | def on_click_l(self, pos): |
| 91 | self._start_drag_pos = pos, self._np.get_pos() |
| 92 | loader.load_sfx('assets/audio/sfx/grab.ogg').play() |
| 93 | if not self._instantiated: |
| 94 | self._instantiated = True |
| 95 | self._txt.destroy() |
| 96 | self._count -= 1 |
| 97 | if self._count: |
| 98 | box = Box(self._world, self._plane_node, self._count, self._cb_inst) |
| 99 | self._cb_inst(box) |
| 100 | |
| 101 | def on_click_r(self, pos): |
| 102 | self._prev_rot_info = pos, self._np.get_pos(), self._np.get_r() |
| 103 | loader.load_sfx('assets/audio/sfx/grab.ogg').play() |
| 104 | |
| 105 | def on_release(self): |
| 106 | if self._start_drag_pos or self._prev_rot_info: |
| 107 | loader.load_sfx('assets/audio/sfx/release.ogg').play() |
| 108 | self._start_drag_pos = self._prev_rot_info = None |
| 109 | |
| 110 | def on_mouse_on(self): |
| 111 | self._outline_model.show() |
| 112 | |
| 113 | def on_mouse_off(self): |
| 114 | if self._start_drag_pos or self._prev_rot_info: return |
| 115 | self._outline_model.hide() |
| 116 | |
| 117 | def on_mouse_move(self, pos): |
| 118 | if self._start_drag_pos: |
| 119 | d_pos = pos - self._start_drag_pos[0] |
| 120 | self._np.set_pos(self._start_drag_pos[1] + d_pos) |
| 121 | if self._prev_rot_info: |
| 122 | start_vec = self._prev_rot_info[0] - self._prev_rot_info[1] |
| 123 | curr_vec = pos - self._prev_rot_info[1] |
| 124 | d_angle = curr_vec.signed_angle_deg(start_vec, (0, -1, 0)) |
| 125 | self._np.set_r(self._prev_rot_info[2] + d_angle) |
| 126 | self._prev_rot_info = pos, self._np.get_pos(), self._np.get_r() |
| 127 | |
| 128 | def on_aspect_ratio_changed(self): |
| 129 | if not self._instantiated: |
| 130 | self._repos() |