예제 #1
0
파일: test.py 프로젝트: raycode/pybilliard
    def __init__(self, *args, **kwargs):
        super(PoolWindow, self).__init__(*args, **kwargs)
        self.set_size(1680, 1000)
        #self.set_fullscreen()
        self.keys = key.KeyStateHandler()
        self.push_handlers(self.keys)

        # Setup GL
        self.set_vsync(True)
        glClearColor(.15, .15, .15, 1)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        glShadeModel(GL_SMOOTH)

        self.lpos1 = (0, 0.5 * SCALE_FACTOR, 1 * SCALE_FACTOR, 0)
        self.lpos2 = (0, -0.5 * SCALE_FACTOR, 1 * SCALE_FACTOR, 0)
        glLightfv(GL_LIGHT0, GL_POSITION, vec(*self.lpos1))
        glLightfv(GL_LIGHT1, GL_POSITION, vec(*self.lpos2))

        glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE)
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glEnable(GL_LIGHT1)

        # Scroll zoom
        self.wheel_step = PoolWindow.ZOOM_SCROLL_STEP

        # Setup view
        self.view = View(eye=(0, -3 * SCALE_FACTOR, .75 * SCALE_FACTOR),
                         center=(0, 0, -SCALE_FACTOR))
        self.view.set_distance(SCALE_FACTOR)

        # The scene
        self.table = Table('obj/table.obj', self.view)
        self.rails = Rails('obj/rails.obj', self.view)
        self.ball = {}
        self.ball['cue'] = CueBall('obj/ball.obj', self.view, .260)
        self.ball['red'] = CueBall('obj/red.obj', self.view, .260,
                                   eVector3(0, 0.5 * SCALE_FACTOR, 0))
        self.ball['yellow'] = CueBall('obj/yellow.obj', self.view, .260,
                                      eVector3(0, -0.5 * SCALE_FACTOR, 0))
        self.cue = CueStick('obj/cue.obj', self.view, self.ball['cue'].radius)

        self.table.body.setUserPointer("Table")
        self.rails.body.setUserPointer("Rails")
        self.cue.body.setUserPointer("Cue")
        self.ball['cue'].body.setUserPointer("Cue Ball")
        self.ball['red'].body.setUserPointer("Red")
        self.ball['yellow'].body.setUserPointer("Yellow")

        # Set view center
        pos = self.ball['cue'].motion.getWorldTransform().getOrigin()
        self.view.set_center(eVector3(pos.x, pos.y, pos.z))

        # Setup cue (position and orient)
        self._reset_cue()

        # Initialize physics
        self.world = DiscreteDynamicsWorld()
        self.debug = DebugDraw()
        self.world.setDebugDrawer(self.debug)
        self.world.addRigidBody(self.table.body)
        self.world.addRigidBody(self.cue.body)
        self.world.addRigidBody(self.rails.body)
        for ball in self.ball.values():
            self.world.addRigidBody(ball.body)
        self.world.setGravity(bVector3(0, 0, -9.81 * SCALE_FACTOR))

        # Register call-backs
        # A 1/60 call-back to run physics (the same as render)
        pyglet.clock.schedule(self._step)
        # Physic clock callback (usually few times faster than render clock)
        self.world.setInternalTickCallback(self.internal_tick_callback, True)

        self._setup_pan()
        self.topview = False
        self.helper = True

        self.push_handlers(on_key_press=self._on_key_press)
예제 #2
0
파일: test.py 프로젝트: raycode/pybilliard
class PoolWindow(pyglet.window.Window):
    ZOOM_SCROLL_STEP = 5
    BALL_MAX_SPEED = 7500

    def __init__(self, *args, **kwargs):
        super(PoolWindow, self).__init__(*args, **kwargs)
        self.set_size(1680, 1000)
        #self.set_fullscreen()
        self.keys = key.KeyStateHandler()
        self.push_handlers(self.keys)

        # Setup GL
        self.set_vsync(True)
        glClearColor(.15, .15, .15, 1)
        glEnable(GL_DEPTH_TEST)
        glEnable(GL_CULL_FACE)
        glShadeModel(GL_SMOOTH)

        self.lpos1 = (0, 0.5 * SCALE_FACTOR, 1 * SCALE_FACTOR, 0)
        self.lpos2 = (0, -0.5 * SCALE_FACTOR, 1 * SCALE_FACTOR, 0)
        glLightfv(GL_LIGHT0, GL_POSITION, vec(*self.lpos1))
        glLightfv(GL_LIGHT1, GL_POSITION, vec(*self.lpos2))

        glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE)
        glEnable(GL_LIGHTING)
        glEnable(GL_LIGHT0)
        glEnable(GL_LIGHT1)

        # Scroll zoom
        self.wheel_step = PoolWindow.ZOOM_SCROLL_STEP

        # Setup view
        self.view = View(eye=(0, -3 * SCALE_FACTOR, .75 * SCALE_FACTOR),
                         center=(0, 0, -SCALE_FACTOR))
        self.view.set_distance(SCALE_FACTOR)

        # The scene
        self.table = Table('obj/table.obj', self.view)
        self.rails = Rails('obj/rails.obj', self.view)
        self.ball = {}
        self.ball['cue'] = CueBall('obj/ball.obj', self.view, .260)
        self.ball['red'] = CueBall('obj/red.obj', self.view, .260,
                                   eVector3(0, 0.5 * SCALE_FACTOR, 0))
        self.ball['yellow'] = CueBall('obj/yellow.obj', self.view, .260,
                                      eVector3(0, -0.5 * SCALE_FACTOR, 0))
        self.cue = CueStick('obj/cue.obj', self.view, self.ball['cue'].radius)

        self.table.body.setUserPointer("Table")
        self.rails.body.setUserPointer("Rails")
        self.cue.body.setUserPointer("Cue")
        self.ball['cue'].body.setUserPointer("Cue Ball")
        self.ball['red'].body.setUserPointer("Red")
        self.ball['yellow'].body.setUserPointer("Yellow")

        # Set view center
        pos = self.ball['cue'].motion.getWorldTransform().getOrigin()
        self.view.set_center(eVector3(pos.x, pos.y, pos.z))

        # Setup cue (position and orient)
        self._reset_cue()

        # Initialize physics
        self.world = DiscreteDynamicsWorld()
        self.debug = DebugDraw()
        self.world.setDebugDrawer(self.debug)
        self.world.addRigidBody(self.table.body)
        self.world.addRigidBody(self.cue.body)
        self.world.addRigidBody(self.rails.body)
        for ball in self.ball.values():
            self.world.addRigidBody(ball.body)
        self.world.setGravity(bVector3(0, 0, -9.81 * SCALE_FACTOR))

        # Register call-backs
        # A 1/60 call-back to run physics (the same as render)
        pyglet.clock.schedule(self._step)
        # Physic clock callback (usually few times faster than render clock)
        self.world.setInternalTickCallback(self.internal_tick_callback, True)

        self._setup_pan()
        self.topview = False
        self.helper = True

        self.push_handlers(on_key_press=self._on_key_press)

    def _setup_pan(self):
        self.view.pan(0, - self.width / 5.0)

    def internal_tick_callback(self, timeStep):
        if self._cue_ball_rest():
            disp = self.world.getDispatcher()
            n = disp.getNumManifolds()
            for i in xrange(n):
                cm = disp.getManifoldByIndexInternal(i)
                contacts = cm.getNumContacts()
                for c in xrange(contacts):
                    obA = cm.getBody0().getUserPointer()
                    obB = cm.getBody1().getUserPointer()
                    if obA == 'Cue' and obB == 'Cue Ball' \
                    or obA == 'Cue Ball' and obB == 'Cue':
                        p = cm.getContactPoint(c)
                        if p.getDistance() < 0.0: # test for penetration
                            # The cue's strike force is calculated from actual
                            # linear velocity applied to the cues orientation vector
                            b = self.cue.body.getLinearVelocity()
                            v = eVector3(b.x, b.y, b.z)
                            d = self.cue.direction if hasattr(self.cue, 'direction') \
                                                   else self.view.dir
                            # get force
                            impuls = v.magnitude() * SCALE_FACTOR * 25.
                            force = bVector3(d.x * impuls,
                                             d.y * impuls,
                                             0.0)
                            # span offset to [0..1]
                            offset = eVector3(self.cue.dx * 4., 0., self.cue.dy * 4.)

                            # calculate offset from cue's position [convert from eye into object space]
                            # TODO: (rewrite this in compact form of vector vs. matrix multiplication)
                            moffset = self.view.orient.inverse() * Matrix4.new_translate(*offset)
                            offset = eVector3(moffset.d, moffset.h, moffset.l)
                            cue = self.ball['cue']
                            cue.body.clearForces()
                            cue.body.applyGravity()
                            cue.body.applyForce(force, bVector3(offset.x, offset.y, offset.z))

                            # Restore cue
                            self._reset_cue()

        def ball_cant_fly(ball):
            # check for flying
            pos = ball.motion.getWorldTransform().getOrigin()
            if pos.z > ball.radius:
                ball.body.applyCentralForce(bVector3(0, 0, -15 * SCALE_FACTOR))
            # check for ball speed
            v = ball.body.getLinearVelocity()
            vel = eVector3(v.x, v.y, v.z)
            if vel.magnitude_squared() > PoolWindow.BALL_MAX_SPEED:
                ball.body.setLinearVelocity(v * 0.9)

        for ball in self.ball.values():
            ball_cant_fly(ball)

    def on_mouse_press(self, x, y, button, modifiers):
        if button & mouse.RIGHT:
            self.view.zoom_distance = y

    def on_mouse_motion(self, x, y, dx, dy):
        if self.keys[key.LCTRL] and self._cue_ball_rest():
            self.cue.delta = dy * -0.01 * SCALE_FACTOR

    def on_mouse_drag(self, x, y, dx, dy, button, modifiers):
        if button & mouse.LEFT:
            if modifiers & key.MOD_WINDOWS:
                self.view.rotate(dx, dy)
            else:
                self.view.rotate(dx, dy)
                pos = self.ball['cue'].motion.getWorldTransform().getOrigin()
                self.view.set_center(eVector3(pos.x, pos.y, pos.z))
                self.cue.set_position(eVector3(pos.x, pos.y, pos.z + self.cue.pivot.z))
                self.cue.orient_from_direction()
                self._setup_pan()
        elif button & mouse.RIGHT:
            pos = self.ball['cue'].motion.getWorldTransform().getOrigin()
            self.view.set_center(eVector3(pos.x, pos.y, pos.z))
            self.view.zoom(y)
            self._setup_pan()
        elif button & mouse.MIDDLE:
            if modifiers & key.MOD_SHIFT:
                self.cue.move(dx, dy)
            else:
                self.view.pan(dx, dy)

    def _update_scroll(self, dt):
        distance = self.view.distance * self.zmul
        self.view.set_distance(distance)
        pos = self.ball['cue'].motion.getWorldTransform().getOrigin()
        self.view.set_center(eVector3(pos.x, pos.y, pos.z))
        self._setup_pan()
        self.wheel_step -= 1
        if self.wheel_step == 0:
            pyglet.clock.unschedule(self._update_scroll)

    def on_mouse_scroll(self, x, y, scroll_x, scroll_y):
        # Regulate zooming direction depending on scroll wheel direction
        # Schedule scroll update
        self.zmul = .98 if scroll_y > 0 else 1.02
        self.wheel_step += 2 if self.wheel_step > 0 else PoolWindow.ZOOM_SCROLL_STEP
        pyglet.clock.schedule(self._update_scroll)

    def _on_key_press(self, symbol, modifiers):
        if symbol == key.TAB:
            self.topview = not self.topview
        if symbol == key.H:
            self.helper = not self.helper

    def on_resize(self, width, height):
        glViewport(0, 0, width, height)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        gluPerspective(40., width / float(height), .01, 1000.)
        self.view.load_matrix()
        glMatrixMode(GL_MODELVIEW)
        return pyglet.event.EVENT_HANDLED

    def on_draw(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glViewport(0, 0, self.width, self.height)

        def redraw_scene(self, debug=False):
            # table
            self.table.render()

            # rails
            self.rails.render()

            # balls
            for ball in self.ball.values():
                p = ball.motion.getWorldTransform().getOrigin()
                q = ball.motion.getWorldTransform().getRotation()
                a = q.getAxis()
                ball.set_position((p.x, p.y, p.z))
                ball.orient_from_axis_angle(q.getAngle(), eVector3(a.x, a.y, a.z))
                ball.render()

            # cue stick (only if cue ball is resting)
            if self._cue_ball_rest():
                self.cue.render()

            # debug draw
            if debug:
                self.debug.reset()
                self.world.debugDrawWorld()
                glDisable(GL_LIGHTING)
                glBegin(GL_LINES)
                for line in self.debug.lines:
                    glColor3f(*line[6:])
                    glVertex3f(*line[:3])
                    glVertex3f(*line[3:6])
                glEnd()
                glEnable(GL_LIGHTING)

        def render_top_cue(self):
            # Redraw helper top-right (top view)
            glViewport(self.width - self.width / 3, self.height - self.height / 3, self.width / 3, self.height / 3)
            glLoadIdentity()
            # top-view
            pos = self.ball['cue'].position
            gluLookAt(pos.x, pos.y, pos.z + SCALE_FACTOR / 2,
                      pos.x, pos.y, pos.z,
                      1., 0., 0.)
            redraw_scene(self)

        def render_front_cue(self):
            # Redraw helper top-right (front view)
            glViewport(self.width - self.width / 3, self.height - self.height / 3, self.width / 3, self.height / 3)
            glLoadIdentity()
            eye = eVector3(self.cue.mtransform.d, self.cue.mtransform.h, self.cue.mtransform.l)
            eye += self.cue.direction * 6.
            # render front
            pos = self.ball['cue'].position
            gluLookAt(eye.x, eye.y, eye.z,
                      pos.x, pos.y, pos.z,
                      0., 0., 1.)
            redraw_scene(self, True)

        def render_top_table(self):
            # render top
            glLoadIdentity()
            gluLookAt(0, 0, 3 * SCALE_FACTOR,
                      0, 0, 0,
                      1, 0., 0.)
            redraw_scene(self)

        # render table
        if not self.topview:
            self.view.load_matrix()
            redraw_scene(self)
        else:
            render_top_table(self)

        # render helper windows (for aim and shoot)
        if self._cue_ball_rest() and self.helper:
            if self.keys[key.LSHIFT]:
                render_front_cue(self)
            elif not self.topview:
                render_top_cue(self)

    def _step(self, dt):
        timeStep = fixedTimeStep = 1.0 / 300.0
        self.world.stepSimulation(dt, 5, fixedTimeStep)
        now = time.time()
        delay = now % timeStep
        time.sleep(delay)

        # Cancel cue stick motion
        self.cue.delta = 0.0

    def _cue_ball_rest(self):
        vel = self.ball['cue'].body.getLinearVelocity()
        mag = eVector3(vel.x, vel.y, vel.z).magnitude()
        return mag < 0.001

    def _reset_cue(self):
        # Position and orient the cue
        pos = self.ball['cue'].motion.getWorldTransform().getOrigin()
        p = self.cue.org_pivot
        self.cue.set_pivot(eVector3(p.x, p.y, p.z + SCALE_FACTOR * 0.1))
        self.cue.set_position(eVector3(pos.x, pos.y, pos.z + self.cue.pivot.z))
        self.cue.orient_from_direction()