Deprecated: Creation of dynamic property wpdb::$categories is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$post2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Creation of dynamic property wpdb::$link2cat is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 668

Deprecated: Return type of WP_Theme::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 554

Deprecated: Return type of WP_Theme::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 595

Deprecated: Return type of WP_Theme::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 535

Deprecated: Return type of WP_Theme::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-theme.php on line 544

Deprecated: Return type of WP_REST_Request::offsetExists($offset) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 960

Deprecated: Return type of WP_REST_Request::offsetGet($offset) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 980

Deprecated: Return type of WP_REST_Request::offsetSet($offset, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 992

Deprecated: Return type of WP_REST_Request::offsetUnset($offset) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/rest-api/class-wp-rest-request.php on line 1003

Deprecated: Return type of WP_Block_List::current() should either be compatible with Iterator::current(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 151

Deprecated: Return type of WP_Block_List::next() should either be compatible with Iterator::next(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 175

Deprecated: Return type of WP_Block_List::key() should either be compatible with Iterator::key(): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 164

Deprecated: Return type of WP_Block_List::valid() should either be compatible with Iterator::valid(): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 186

Deprecated: Return type of WP_Block_List::rewind() should either be compatible with Iterator::rewind(): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 138

Deprecated: Return type of WP_Block_List::offsetExists($index) should either be compatible with ArrayAccess::offsetExists(mixed $offset): bool, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 75

Deprecated: Return type of WP_Block_List::offsetGet($index) should either be compatible with ArrayAccess::offsetGet(mixed $offset): mixed, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 89

Deprecated: Return type of WP_Block_List::offsetSet($index, $value) should either be compatible with ArrayAccess::offsetSet(mixed $offset, mixed $value): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 110

Deprecated: Return type of WP_Block_List::offsetUnset($index) should either be compatible with ArrayAccess::offsetUnset(mixed $offset): void, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 127

Deprecated: Return type of WP_Block_List::count() should either be compatible with Countable::count(): int, or the #[\ReturnTypeWillChange] attribute should be used to temporarily suppress the notice in /var/www/twodee/blog/wp-includes/class-wp-block-list.php on line 199

Deprecated: Creation of dynamic property Automattic\Jetpack\Connection\Manager::$error_handler is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-connection/src/class-manager.php on line 91

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Instagram_Gallery::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-instagram-gallery.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Mailchimp::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-mailchimp.php on line 17

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 21

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Tweetstorm_Gather::$is_wpcom is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/class-wpcom-rest-api-v2-endpoint-tweetstorm-gather.php on line 22

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Gutenberg_Available_Extensions::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/gutenberg-available-extensions.php on line 20

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 23

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Memberships::$wpcom_is_site_specific_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/memberships.php on line 24

Deprecated: Creation of dynamic property WPCOM_REST_API_V2_Endpoint_Subscribers::$wpcom_is_wpcom_only_endpoint is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/_inc/lib/core-api/wpcom-endpoints/subscribers.php on line 15

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property Automattic\Jetpack\Sync\Queue::$random_int is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/vendor/automattic/jetpack-sync/src/class-queue.php on line 38

Deprecated: Creation of dynamic property WPCOM_Widget_Goodreads::$shelves is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/widgets/goodreads.php on line 33

Deprecated: DateTime::__construct(): Passing null to parameter #1 ($datetime) of type string is deprecated in /var/www/twodee/blog/wp-includes/script-loader.php on line 348

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_classic_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 347

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 349

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$toolbar_block_side is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 350

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$panels_block is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 351

Deprecated: Creation of dynamic property Advanced_Editor_Tools::$used_block_buttons is deprecated in /var/www/twodee/blog/wp-content/plugins/tinymce-advanced/tinymce-advanced.php on line 354

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: Creation of dynamic property WP_Block_Type::$plan_check is deprecated in /var/www/twodee/blog/wp-includes/class-wp-block-type.php on line 333

Deprecated: ltrim(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 3030

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$object_id is deprecated in /var/www/twodee/blog/wp-includes/class-wp-term.php on line 198

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Warning: Cannot modify header information - headers already sent by (output started at /var/www/twodee/blog/wp-includes/class-wp-block-list.php:14) in /var/www/twodee/blog/wp-includes/feed-rss2.php on line 8
buster – teaching machines https://twodee.org/blog Fri, 15 Mar 2013 14:37:27 +0000 en-US hourly 1 https://wordpress.org/?v=5.7.1 A texture renderer on the Raspberry Pi https://twodee.org/blog/6255 https://twodee.org/blog/6255#respond Wed, 19 Dec 2012 21:54:03 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=6255 [...]]]>
Deprecated: ltrim(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 3030

Deprecated: parse_str(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/class.jetpack.php on line 4255

Deprecated: Creation of dynamic property Jetpack_Carousel::$localize_strings is deprecated in /var/www/twodee/blog/wp-content/plugins/jetpack/modules/carousel/jetpack-carousel.php on line 339
A collaborator is working on using a cluster of Raspberry Pis to build a multi-monitor display wall. We’re using textures in OpenGL to blit each frame across the display. For his benefit, I pieced together a simple texture renderer, where the texture is procedurally generated by XORing the texel indices. XOR textures are interesting:

A Raspberry Pi powering an XOR texture display.

The renderer, which has lots of dependencies not included here, follows:

#!/usr/bin/env python

import time
import sys

from pyopengles import *
from geometry import *
from glop import *

WIDTH = 1680
HEIGHT = 1050

class XortexRenderer(Renderer):
  def __init__(self):
    Renderer.__init__(self)
    self.context = EGL(depth_size=0)
    self.startTime = time.time()

    # Position attributes are in [-1, 1] x [-1, 1]. We can turn these into
    # texture coordinates ourselves, so we only take in one vertex attribute.
    self.vertexShader = """
      attribute vec2 position;

      varying vec2 texcoord;

      void main() {
        gl_Position = vec4(position, 0.0, 1.0);
        texcoord = position * 0.5 + 0.5;
      }
    """

    # Color is determined entirely by the texture.
    self.fragmentShader = """
      precision mediump float;
      uniform sampler2D frame;  

      varying vec2 texcoord; 

      void main() {
        gl_FragColor = texture2D(frame, texcoord);
      }
    """

    # The position attribute is in slot 0.
    self.binding = ((0, 'position'),)
    self.program = self.context.get_program(self.vertexShader, self.fragmentShader, self.binding)

    # Fill the viewport.
    self.vertices = eglfloats((-1.0, -1.0,
                                1.0, -1.0,
                               -1.0,  1.0,
                                1.0,  1.0))

    # The will be uploaded to texture unit 0.
    opengles.glUseProgram(self.program)
    self.frameTextureUniformID = opengles.glGetUniformLocation(self.program, "frame")
    opengles.glUniform1i(self.frameTextureUniformID, 0)
    opengles.glUseProgram(0)

    # Let's make that texture!
    self.frameTextureID = eglint()
    opengles.glActiveTexture(GL_TEXTURE0) 
    opengles.glGenTextures(1, ctypes.byref(self.frameTextureID)) 
    opengles.glBindTexture(GL_TEXTURE_2D, ctypes.byref(self.frameTextureID)) 
    opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    opengles.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

    # An RGB image. We'll create a colorful XOR texture, 'cuz it's pretty.
    width = 128
    height = 128
    maxIntensity = max(width, height) - 1.0
    vals = [0] * width * height * 3
    for r in range(height):
      for c in range(width):
        intensity = int((r ^ c) / maxIntensity * 255)
        vals[(r * width + c) * 3 + 0] = intensity * (r < height / 2)
        vals[(r * width + c) * 3 + 1] = intensity * (r > height / 2)
        vals[(r * width + c) * 3 + 2] = intensity * (c > height / 2)

    # Now we ship the texture off. Bye-bye.
    opengles.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, eglubytes(vals))

    self.checkError('after init')

  def onKeyEvent(self, keycode):
    pass

  def onMouseEvent(self, event):
    pass

  def onDraw(self):
    opengles.glViewport(0, 0, self.context.width, self.context.height);
    opengles.glClear(GL_COLOR_BUFFER_BIT)

    opengles.glUseProgram(self.program)

    opengles.glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, self.vertices)
    opengles.glEnableVertexAttribArray(0)
    opengles.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)

    self.checkError('after draw')

    openegl.eglSwapBuffers(self.context.display, self.context.surface)

  def checkError(self, msg):
    e = opengles.glGetError()
    if e:
      print msg, 'Error: ', hex(e)
      sys.exit(1)

if __name__ == "__main__":
  with XortexRenderer() as renderer:
    render.goMouseless(WIDTH, HEIGHT, 0.02, renderer)
]]>
https://twodee.org/blog/6255/feed 0
Getting video data via OpenCV https://twodee.org/blog/6045 https://twodee.org/blog/6045#respond Thu, 06 Dec 2012 04:02:45 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=6045 [...]]]> I was able to get image data out of a .mpg file using the OpenCV Python wrapper.

After installing the following:

sudo apt-get install
gstreamer0.10-plugins-bad
gstreamer0.10-plugins-really-bad
gstreamer0.10-plugins-ugly
gstreamer0.10-ffmpeg

OpenCV was able to to read the .mpg file form the “CaputreFromFile” function.

I then grabbed the frame with “QueryFrame” which returned an iplImage object which has useful information such as width, height and nChannels among other things.

The raw pixel data can be retrieved by calling Get2D and passing in the image and the [x,y] coordinates, which retrieves a pixel. The pixel is an array of 3  (nChannels) different integers which can be accessed like so: pixel[0] pixel[1] pixel[2].

After all that here is the code:

#!/usr/bin/env python
import cv2.cv as cv
import sys

files = sys.argv[1:]

for f in files:
    print f

    capture = cv.CaptureFromFile(f)

    frame = cv.QueryFrame(capture)

    width = frame.width
    height = frame.height

    #just get the first 10 frame for brevity
    for i in xrange(10):   
        frame = cv.QueryFrame(capture)
        if frame:
            cloned_frame = cv.CloneImage(frame)
            print "pixel BGR"
            for i in range(0, int(height)):
                for j in range(0, int(width)):
                    pixel = cv.Get2D(frame, i, j)
                    #in the order: Blue Green Red
                    print repr(pixel[0]) + ", " + repr(pixel[1]) + ", " + repr(pixel[2])

It prints out all the pixel values, which does take quite some time, but isn’t necessary for any other reason but demonstration.

 

 

]]>
https://twodee.org/blog/6045/feed 0
OpenCV + Python https://twodee.org/blog/5394 https://twodee.org/blog/5394#respond Fri, 09 Nov 2012 01:37:49 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=5394 [...]]]> In order to build the display wall, potentially each Pi will need to be able to grab a frame from a video file and display a section of it. So the first step is grabbing and displaying one frame on one Pi. OpenCV (Open Computer Vision) has the capabilities to do this, but it doesn’t come installed on the Raspian OS. So here is where my fun began:

To install OpenCV and the Python bindings for it I followed the instructions found on this website.

However I found an error in the last section of code, specifically the “cmake” line, it should read as follows:

cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_NEW_PYTHON_SUPPORT=ON -D BUILD_EXAMPLES=ON ..

The difference is the”NEW_” between “BUILD_” and “PYTHON”.

Let the “make” command run overnight. It took the two Pies I was running around 12 hours each to compile the libraries. My guess as to why it took so long is because the limited amount of RAM on the devices.

Now to grab a frame! I found this code on a StackOverflow post and looked up the OpenCV function calls to actually figure out what it’s doing.

#!/usr/bin/env python
import cv2.cv as cv
import sys
files = sys.argv[1:]
for f in files:
    printf f
    capture = cv.CaptureFromFile(f)
    print capture
    frame = cv. QueryFrame(capture)
    print cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_WIDTH)
    print cv.GetCaptureProperty(capture, cv.CV_CAP_PROP_FRAME_HEIGHT)
    for i in xrange(10):
        print i
        # this frame can be written to a file
        frame = cv.QueryFrame(capture)
        if frame:
            print frame

I downloaded a few different small video files. A .avi, .m4v, .mpg, .mp4

When executed, I ran into the following error:

GLib-GObject-CRITICAL **: g_object_set: assertion 'G_IS_OBJECT (object)' failed
]]>
https://twodee.org/blog/5394/feed 0
FourLords Part I https://twodee.org/blog/5275 https://twodee.org/blog/5275#respond Mon, 29 Oct 2012 12:27:30 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=5275 [...]]]> At last week’s Buster meeting, we decided to implement a multi-pilayer version of Warlords, an Atari game that formed my young mind. I’m going to call it FourLords, because really the game doesn’t involve armies or anything on a war-scale. Plus, I want to stress the multiple Pis, multiple players aspect of the game. I drafted three initial steps to get a prototype up and running:

  1. Write a Rectangle class.
  2. Write a bouncing ball renderer.
  3. Write a collision-aware renderer.

1. Write a Rectangle class

Rectangles will be used for the bouncing ball, the forts, and the ultra-sensitive kings hidden away inside the forts. They also need to be movable and able to be checked for collisions with other rectangles.

class Rectangle:
  def __init__(self, l, r, b, t):
    self.l = l
    self.r = r
    self.b = b
    self.t = t

  def collidesWith(self, other):
    return not (self.r < other.l or self.l > other.r or self.t < other.b or self.b > other.t)

  def getVertices(self):
    return [self.l, self.b, 0.0,
            self.r, self.b, 0.0,
            self.l, self.t, 0.0,
            self.r, self.t, 0.0]

  def translate(self, dx, dy):
    self.l += dx
    self.r += dx
    self.b += dy
    self.t += dy

2. Write a bouncing ball renderer

This is the obligatory first step to any game.

The code behind it is a modification of our previous OpenGL renderers:

#!/usr/bin/env python

import time
import sys

from pyopengles import *
from geometry import *
from glop import *

WIDTH = 1680
HEIGHT = 1050
ASPECT = WIDTH / float(HEIGHT)

class FourLordsRenderer(Renderer):
  def __init__(self):
    Renderer.__init__(self)
    self.context = EGL(depth_size=8)
    self.startTime = time.time()

    self.vertexShader = """
      uniform mat4 projection;
      uniform mat4 modelview;

      attribute vec3 position;
      attribute vec3 normal;

      void main() {
        gl_Position = projection * modelview * vec4(position, 1.0);
      }
    """

    self.fragmentShader = """
      precision mediump float;

      uniform float time;
      uniform vec2 resolution;

      void main() {
        gl_FragColor = vec4(vec3(0), 1.0);
      }
    """

    self.binding = ((0, 'position'),)
    self.program = self.context.get_program(self.vertexShader, self.fragmentShader, self.binding)

    opengles.glUseProgram(self.program)
    self.modelviewUID = opengles.glGetUniformLocation(self.program, "modelview")
    self.projectionUID = opengles.glGetUniformLocation(self.program, "projection")
    self.resolutionUID = opengles.glGetUniformLocation(self.program, "resolution")
    self.timeUID = opengles.glGetUniformLocation(self.program, "time")
    opengles.glUseProgram(0)

    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUseProgram(0)

    opengles.glClearColor(eglfloat(0.85), eglfloat(0.35), eglfloat(0.15), eglfloat(1.0))

    # Let's make a new rectangle centered in the viewport with a random velocity.
    self.rectangle = Rectangle(-0.1, 0.1, -0.1, 0.1)
    self.velocity = Vector2.getRandom(1.0)

    self.vertices = eglfloats(self.rectangle.getVertices())
    self.checkError('after init')

    self.modelview = Matrix4.getIdentity()

    # Let's always show at least [-1, 1] x [-1, 1]. If our aspect ratio gives
    # us more real estate than that on either x or y, let's increase the span
    # shown on that axis.

    # If we have more height than width, show more on the y-axis.
    if ASPECT < 1.0:
      self.right = 1.0
      self.top = 1.0 / ASPECT

    # Otherwise, show more of the x-axis.
    else:
      self.right = 1.0 * ASPECT
      self.top = 1.0

    self.left = -self.right
    self.bottom = -self.top
    self.projection = Matrix4.getOrthoMatrix(self.left, self.right, self.bottom, self.top, -50.0, 50.0)

  def onKeyEvent(self, keycode):
    pass

  def onMouseEvent(self, event):
    pass

  def onDraw(self):
    opengles.glViewport(0, 0, self.context.width, self.context.height);
    opengles.glClear(GL_COLOR_BUFFER_BIT)

    opengles.glUseProgram(self.program)

    opengles.glUniform1f(self.timeUID, eglfloat(time.time() - self.startTime))
    opengles.glUniformMatrix4fv(self.modelviewUID, 1, GL_FALSE, eglfloats(self.modelview.data))
    opengles.glUniformMatrix4fv(self.projectionUID, 1, GL_FALSE, eglfloats(self.projection.data))

    opengles.glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, self.vertices)
    opengles.glEnableVertexAttribArray(0)

    opengles.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
    opengles.glUseProgram(0)

    self.checkError('after draw')

    openegl.eglSwapBuffers(self.context.display, self.context.surface)

    # Update rectangle's position based on its velocity and the time elapsed
    # since the last frame.
    self.rectangle.translate(self.deltaTime * self.velocity[0], self.deltaTime * self.velocity[1])
    self.vertices = eglfloats(self.rectangle.getVertices())

    # If we exceed the bounds of the visible space, let's reflect the velocity
    # vector about the normal of the boundary we crossed.
    if self.rectangle.l < self.left or self.rectangle.r > self.right:
      self.velocity[0] = -self.velocity[0]
    if self.rectangle.b < self.bottom or self.rectangle.t > self.top:
      self.velocity[1] = -self.velocity[1]

  def checkError(self, msg):
    e = opengles.glGetError()
    if e:
      print msg, 'Error: ', hex(e)
      sys.exit(1)

if __name__ == "__main__":
  with FourLordsRenderer() as renderer:
    render.go(WIDTH, HEIGHT, 0.02, renderer)

To store velocity, I also created a simple Vector2 class:

import math
import random

class Vector2:
  def __init__(self, x = 0.0, y = 0.0):
    self.coords = [x, y]

  def getLength(self):
    return math.sqrt(self.coords[0] * self.coords[0] + self.coords[1] * self.coords[1])

  def normalize(self):
    length = self.getLength()
    if length > 0.0:
      self.coords[0] /= length
      self.coords[1] /= length

  def __getitem__(self, i):
    return self.coords[i]

  def __setitem__(self, i, value):
    self.coords[i] = value

  def __str__(self):
    return repr(self.coords)

  @staticmethod
  def getRandom(magnitude = 1.0):
    angle = random.random() * 2.0 * math.pi
    return Vector2(math.cos(angle) * magnitude, math.sin(angle) * magnitude)

I’m finding that I do not appreciate Python’s mandatory requirements of including the invoking object in methods’ parameter lists and qualifying instance variable references with that object. Python provides a very thin veneer of object-orientation. The customary notation is only seen on method calls. Everywhere else methods look just like functions taking in structs. It doesn’t capitalize on the context benefits that a class provides. Most of my variable accesses are going to be to instance variables. Can’t we make the most common thing the easiest thing? Outlaw shadows of instance variables, require qualification for access to globals, and let’s eliminate qualified access to instance variables. I feel like I’m writing C code, not object-oriented code.

3. Write a collision-aware renderer

The next step is to write a renderer that bounces the ball around a field strewn with randomly-placed rectangles, which disappear when collided with. This one’s not done yet. I need to think about the best way to dynamically remove elements from the vertex array that won’t impose a linear time operation to reshuffle the array’s contents. Probably swapping out the last item in the list for the one collided with will work.

This is work for another day.

]]>
https://twodee.org/blog/5275/feed 0
A rotating 3-D model with Raspberry Pi https://twodee.org/blog/5212 https://twodee.org/blog/5212#respond Thu, 25 Oct 2012 13:05:27 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=5212 [...]]]> I left off with a visible but maligned rendering.

The malignancy is gone. I did not have a depth buffer in my EGL context. EGL(depth_size=8) fixed that.

I also wanted to be able to handle key events. Peter insisted on a select-driven event loop, so I went searching to figure out how to read the keyboard via a file descriptor. Termios needed to be called upon to get immediate key events, and I ran into the problem of the ages. Most keys are just one byte, but cursor keys are codified with a 3-byte sequence. Up, for example, is sent as <Esc>-[-A. This meant I needed to read more than one byte—if there was more than one to read. My first approach failed: I read 1 byte and then called select() with a timeout of 0. It never reported stdin as having more input to read, even when I pressed multibyte keys. Then I tried reading multiple bytes at a time with file.read. That blocked on normal keys. Finally I found a solution that used os.read(fd, nbytes) instead of file.read(nbytes). That seems to work. I don’t know why os.read is different.

This person had what sounds like the same problem.

After a good amount of trial and error, this is the resulting Keyboard class:

#!/usr/bin/env python

import os
import select
import sys
import termios

class Keyboard:
  ESCAPE = 27
  LEFT = 1000
  RIGHT = 1001
  DOWN = 1002
  UP = 1003

  keylist = {
    '\x1b' : ESCAPE,
    '\x1b[A' : UP,
    '\x1b[B' : DOWN,
    '\x1b[C' : RIGHT,
    '\x1b[D' : LEFT,
  }

  def __init__(self):
    self.fd = sys.stdin.fileno()
    self.old = termios.tcgetattr(self.fd)
    self.new = termios.tcgetattr(self.fd)
    self.new[3] = self.new[3] & ~termios.ICANON & ~termios.ECHO
    self.new[6][termios.VMIN] = 1
    self.new[6][termios.VTIME] = 0
    termios.tcsetattr(self.fd, termios.TCSANOW, self.new)

  def __enter__(self):
    return self

  def __exit__(self, type, value, traceback):
    termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old)

  def getFile(self):
    return self.fd

  def read(self):
    keys = os.read(self.fd, 4)
    if keys in Keyboard.keylist:
      return Keyboard.keylist[keys]
    else:
      return None

if __name__ == "__main__":
  with Keyboard() as keyboard:
    key = keyboard.read()
    while key != Keyboard.ESCAPE:
      print '%d' % key
      key = keyboard.read()

I really like Python’s with statement.

My rendering had to change to accommodate a second source of events:

#!/usr/bin/env python

import select

from mouse import Mouse
from keyboard import Keyboard
from mouseevent import MouseEvent

class Renderer:
  def onMouseEvent(self, event):
    print event

  def onKeyEvent(self, keycode):
    pass

  def onDraw(self):
    print 'drawing'

  def __enter__(self):
    return self

  def __exit__(self, type, value, traceback):
    print 'clean up'

def go(width, height, secondsPerFrame, renderer):
  with Mouse(width, height) as mouse, Keyboard() as keyboard:
    keycode = 0
    while keycode != Keyboard.ESCAPE:
      toReads, _, _ = select.select([mouse.getFile(), keyboard.getFile()], [], [], secondsPerFrame)
      for toRead in toReads:
        if toRead is mouse.getFile():
          event = mouse.update()
          renderer.onMouseEvent(event)
        elif toRead is keyboard.getFile():
          keycode = keyboard.read()
          renderer.onKeyEvent(keycode)
      renderer.onDraw()

if __name__ == "__main__":
  with Renderer() as renderer:
    go(640, 480, 3.0, renderer)

To allow for rotations and to get a proper projection matrix in OpenGL, I whipped up a minimal Matrix4 class:

import math

class Matrix4:
  @staticmethod
  def getRotationMatrix(degrees, axis):
    radians = degrees * math.pi / 180.0
    sine = math.sin(radians)
    cosine = math.cos(radians)
    cosineComplement = 1.0 - cosine

    m = Matrix4()

    m[0, 0] = cosineComplement * axis[0] * axis[0] + cosine
    m[0, 1] = cosineComplement * axis[0] * axis[1] - sine * axis[2]
    m[0, 2] = cosineComplement * axis[0] * axis[2] + sine * axis[1]

    m[1, 0] = cosineComplement * axis[1] * axis[0] + sine * axis[2]
    m[1, 1] = cosineComplement * axis[1] * axis[1] + cosine
    m[1, 2] = cosineComplement * axis[1] * axis[2] + sine * axis[0]

    m[2, 0] = cosineComplement * axis[2] * axis[0] - sine * axis[1]
    m[2, 1] = cosineComplement * axis[2] * axis[1] + sine * axis[0]
    m[2, 2] = cosineComplement * axis[2] * axis[2] + cosine

    m[3, 3] = 1

    return m

  @staticmethod
  def getOrthoMatrix(l, r, b, t, n, f):
    m = Matrix4()

    m[0, 0] = 2.0 / (r - l)
    m[1, 1] = 2.0 / (t - b)
    m[2, 2] = 2.0 / (f - n)

    m[0, 3] = -(r + l) / (r - l)
    m[1, 3] = -(t + b) / (t - b)
    m[2, 3] = -(f + n) / (f - n)

    m[3, 3] = 1.0

    return m

  def __init__(self):
    self.data = [0] * 16

  def __getitem__(self, rc):
    r, c = rc
    return self.data[r + c * 4]

  def __setitem__(self, rc, value):
    r, c = rc
    self.data[r + c * 4] = value

The auto-tupling of r, c in Python was new to me. To allow for multi-index subscripting into a matrix, the row and column get tuplefied and passed in to the __getitem__ and __setitem__ methods as a collective. I decompose that tuple into its separate components. The matrix is serialized as a list in column-major order. OpenGL expects matrix data in columns, not rows.

Finally, I was able to pull all this together into a working mesh renderer:

from pyopengles import *
import render
import time
import sys

from render import Renderer
from eric import TriMesh
from keyboard import Keyboard
from matrix import Matrix4

print sys.argv

WIDTH = 1680
HEIGHT = 1050
ASPECT = WIDTH / float(HEIGHT)

class TriMoveRenderer(Renderer):
  def __init__(self):
    self.context = EGL(depth_size=8)
    self.startTime = time.time()

    self.vertexShader = """
      uniform mat4 projection;
      uniform mat4 modelview;

      attribute vec3 position;
      attribute vec3 normal;

      varying vec3 fnormal;

      void main() {
        gl_Position = projection * modelview * vec4(position, 1.0);
        fnormal = (modelview * vec4(normal, 0.0)).xyz;
      }
    """

    self.fragmentShader = """
      precision mediump float;

      uniform float time;
      uniform vec2 mouse;
      uniform vec2 resolution;

      const vec3 albedo = vec3(1.0);
      const float ambientStrength = 0.1;
      const float diffuseStrength = 1.0 - ambientStrength;
      const vec3 lightDirection = normalize(vec3(5.0, 5.0, -5.0));
      const vec3 eyeDirection = vec3(0.0, 0.0, -1.0);
      const float shininess = 0.0;

      varying vec3 fnormal;

      void main() {
        vec3 normal = normalize(fnormal);

        vec3 ambient = ambientStrength * albedo;

        float nDotL = max(dot(normal, lightDirection), 0.0);
        vec3 diffuse = diffuseStrength * nDotL * albedo;

        vec3 halfVector = normalize(eyeDirection + lightDirection);
        float nDotH = max(dot(normal, halfVector), 0.0);
        vec3 specular = vec3(0.0); //pow(nDotH, cos(shininess)) * vec3(1.0);

        vec3 color = ambient + diffuse + specular;

        gl_FragColor = vec4(color, 1.0);
        // gl_FragColor = vec4(normal, 1.0); 
      }
    """

    self.binding = ((0, 'position'),
                    (1, 'normal'),)
    self.program = self.context.get_program(self.vertexShader, self.fragmentShader, self.binding)

    opengles.glUseProgram(self.program)
    self.modelviewUID = opengles.glGetUniformLocation(self.program, "modelview")
    self.projectionUID = opengles.glGetUniformLocation(self.program, "projection")
    self.mouseUID = opengles.glGetUniformLocation(self.program, "mouse")
    self.resolutionUID = opengles.glGetUniformLocation(self.program, "resolution")
    self.timeUID = opengles.glGetUniformLocation(self.program, "time")
    opengles.glUseProgram(0)

    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUniform2f(self.mouseUID, eglfloat(0.5), eglfloat(0.5))
    opengles.glUseProgram(0)

    opengles.glClearColor(eglfloat(0.45), eglfloat(0.35), eglfloat(0.15), eglfloat(1.0))

    mesh = TriMesh(sys.argv[1])
    self.vertices = eglfloats(mesh.verts)
    self.normals = eglfloats(mesh.vNorms)
    self.faces = eglshorts(mesh.faces)
    self.checkError('after init')

    opengles.glClearDepthf(eglfloat(1.0))
    opengles.glEnable(GL_DEPTH_TEST)
    opengles.glEnable(GL_CULL_FACE)
    opengles.glFrontFace(GL_CW)

    self.yAngle = 0.0
    self.rotation = Matrix4.getRotationMatrix(self.yAngle, [0.0, 1.0, 0.0])

    if ASPECT < 1.0:
      self.projection = Matrix4.getOrthoMatrix(-1.0, 1.0, -1.0 / ASPECT, 1.0 / ASPECT, -50.0, 50.0)
    else:
      self.projection = Matrix4.getOrthoMatrix(-1.0 * ASPECT, 1.0 * ASPECT, -1.0, 1.0, -50.0, 50.0)

  def onKeyEvent(self, keycode):
    if keycode == Keyboard.LEFT:
      self.yAngle -= 4
      self.rotation = Matrix4.getRotationMatrix(self.yAngle, [0.0, 1.0, 0.0])
    elif keycode == Keyboard.RIGHT:
      self.yAngle += 4
      self.rotation = Matrix4.getRotationMatrix(self.yAngle, [0.0, 1.0, 0.0])

  def onMouseEvent(self, event):
    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.mouseUID, eglfloat(event.x / float(WIDTH)), eglfloat(event.y / float(HEIGHT)))
    opengles.glUseProgram(0)

  def onDraw(self):
    opengles.glViewport(0, 0, self.context.width, self.context.height);
    opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    opengles.glPointSize(eglfloat(5.0))

    self.checkError('before use program')
    opengles.glUseProgram(self.program)

    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUniform1f(self.timeUID, eglfloat(time.time() - self.startTime))
    opengles.glUniformMatrix4fv(self.modelviewUID, 1, GL_FALSE, eglfloats(self.rotation.data))
    opengles.glUniformMatrix4fv(self.projectionUID, 1, GL_FALSE, eglfloats(self.projection.data))

    opengles.glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, self.vertices)
    opengles.glEnableVertexAttribArray(0)

    opengles.glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, self.normals)
    opengles.glEnableVertexAttribArray(1)

    opengles.glDrawElements(GL_TRIANGLES, len(self.faces), GL_UNSIGNED_SHORT, self.faces)
    opengles.glUseProgram(0)

    self.checkError('after draw')

    openegl.eglSwapBuffers(self.context.display, self.context.surface)

  def checkError(self, msg):
    e = opengles.glGetError()
    if e:
      print msg, 'Error: ', hex(e)
      sys.exit(1)

if __name__ == "__main__":
  with TriMoveRenderer() as renderer:
    render.go(WIDTH, HEIGHT, 0.02, renderer)

Face winding is clockwise here because I like a different handedness than the rest of the world.

The result? Breaktaking.

There’s a triangle missing from Al’s chin. I’m not sure why.

]]>
https://twodee.org/blog/5212/feed 0
Showing a 3-D model on our Raspberry Pi https://twodee.org/blog/5151 https://twodee.org/blog/5151#comments Mon, 22 Oct 2012 12:45:55 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=5151 [...]]]>
Deprecated: ltrim(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 3030
Eric got his OBJ loader working, we think, so it came time for me to integrate his code with my demo renderers. It kind of worked. I added a normal attribute to the vertex shader and sent it along to the fragment shader, where I calculated some simple directional lighting in eye space. I drew the indexed geometry. And the result is a bit off:

Al’s shadiness is shady.

We’re looking at Al’s backside, but we’re seeing through some of him. It looks like my depth test isn’t working. That’s work for another day.

Our test renderer looks like this:

from pyopengles import *

from render import Renderer
from eric import TriMesh
import render
import time
import sys

WIDTH = 1680
HEIGHT = 1050

class TriMoveRenderer(Renderer):
  def __init__(self):
    self.context = EGL()
    self.startTime = time.time()

    self.vertexShader = """
      attribute vec3 position;
      attribute vec3 normal;

      varying vec3 fnormal;

      void main() {
        gl_Position = vec4(position, 1.0);
        fnormal = normal;
      }
    """

    self.fragmentShader = """
      precision mediump float;

      uniform float time;
      uniform vec2 mouse;
      uniform vec2 resolution;

      const vec3 albedo = vec3(1.0);
      const float ambientStrength = 0.1;
      const float diffuseStrength = 1.0 - ambientStrength;
      const vec3 lightDirection = normalize(vec3(5.0, 5.0, -5.0));
      const vec3 eyeDirection = vec3(0.0, 0.0, -1.0);
      const float shininess = 50.0;

      varying vec3 fnormal;

      void main() {
        vec3 normal = normalize(fnormal);

        vec3 ambient = ambientStrength * albedo;

        float nDotL = dot(normal, lightDirection);
        vec3 diffuse = diffuseStrength * nDotL * albedo;

        vec3 halfVector = normalize(eyeDirection + lightDirection);
        float nDotH = dot(normal, halfVector);
        vec3 specular = pow(nDotH, cos(shininess)) * vec3(1.0);

        vec3 color = ambient + diffuse + specular;

        gl_FragColor = vec4(color, 1.0);
      }
    """

    self.binding = ((0, 'position'),
                    (1, 'normal'),)
    self.program = self.context.get_program(self.vertexShader, self.fragmentShader, self.binding)

    opengles.glUseProgram(self.program)
    self.mouseUID = opengles.glGetUniformLocation(self.program, "mouse")
    self.resolutionUID = opengles.glGetUniformLocation(self.program, "resolution")
    self.timeUID = opengles.glGetUniformLocation(self.program, "time")
    opengles.glUseProgram(0)

    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUniform2f(self.mouseUID, eglfloat(0.5), eglfloat(0.5))
    opengles.glUseProgram(0)

    opengles.glClearColor(eglfloat(0.45), eglfloat(0.35), eglfloat(0.15), eglfloat(1.0))

    mesh = TriMesh('../al.obj')
    self.vertices = eglfloats(mesh.verts)
    self.normals = eglfloats(mesh.vNorms)
    self.faces = eglshorts(mesh.faces)
    self.checkError('after init')

    opengles.glEnable(GL_DEPTH_TEST)

  def onMouseEvent(self, event):
    opengles.glUseProgram(self.program)
    opengles.glUniform2f(self.mouseUID, eglfloat(event.x / float(WIDTH)), eglfloat(event.y / float(HEIGHT)))
    opengles.glUseProgram(0)

  def onDraw(self):
    opengles.glViewport(0, 0, self.context.width, self.context.height);
    opengles.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
    opengles.glPointSize(eglfloat(5.0))

    self.checkError('before use program')

    opengles.glUseProgram(self.program)

    opengles.glUniform2f(self.resolutionUID, eglfloat(WIDTH), eglfloat(HEIGHT))
    opengles.glUniform1f(self.timeUID, eglfloat(time.time() - self.startTime))

    opengles.glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, self.vertices)
    opengles.glEnableVertexAttribArray(0)

    opengles.glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, self.normals)
    opengles.glEnableVertexAttribArray(1)

    opengles.glDrawElements(GL_TRIANGLES, len(self.faces), GL_UNSIGNED_SHORT, self.faces)
    opengles.glUseProgram(0)

    self.checkError('after draw')

    openegl.eglSwapBuffers(self.context.display, self.context.surface)

  def checkError(self, msg):
    e = opengles.glGetError()
    if e:
      print msg, 'Error: ', hex(e)
      sys.exit(1)

if __name__ == "__main__":
  with TriMoveRenderer() as renderer:
    render.go(WIDTH, HEIGHT, 0.02, renderer)

I added an eglshorts to pyopengles, as glDrawElements wouldn’t accept ints. I also learned how to make a package in Python: make a directory named with your package name and add an __init__.py file inside of it. I made a package named eric and dropped TriMesh.py inside.

]]>
https://twodee.org/blog/5151/feed 1
I OBJect to objects https://twodee.org/blog/5101 https://twodee.org/blog/5101#comments Fri, 19 Oct 2012 05:08:44 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=5101 Deprecated: ltrim(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 3030
I’m going to go out on a limb here and guess that the title will have taken longer to come up with that the rest of this post…oh well. Since we want to run some nifty graphics demos on our raspberry pi’s/ raspberries pi (no sure how to pluralize this), we thought it would be [...]]]>

Deprecated: ltrim(): Passing null to parameter #1 ($string) of type string is deprecated in /var/www/twodee/blog/wp-includes/wp-db.php on line 3030
I’m going to go out on a limb here and guess that the title will have taken longer to come up with that the rest of this post…oh well.

Since we want to run some nifty graphics demos on our raspberry pi’s/ raspberries pi (no sure how to pluralize this), we thought it would be handy to have a way to loading some models. The representation we decided on was the Wavefront OBJ, as it is very popular.

Deciding that we only needed the most basic features, we limited the specs so that only triangles are accepted. Also, we only wanted to read in the  verticies and the faces, leaving the calculation of the normals up to the loader.

Here it (slightly reformatted to fit nicely):

import re

class TriMesh:

    def __init__(self, fileName):
        self.verts = []
        self.faces = []
        self.fNorms = []
        self.vNorms = []

        # read file
        with open(fileName) as f:
            for line in f:
                token = re.split('\s*', line.strip())        

                if token[0] == 'v':
                    self.verts.extend(
                        [float(token[i]) for i in range(1,4)])                   
                elif line[0] == 'f':
                    self.faces.extend(
                        [int(token[i]) - 1 for i in range(1,4)])

        #initialize the vertex normals
        self.vNorms = [0] * len(self.verts)

        x,y,z = 0,1,2
        for i in xrange(0, len(self.faces), 3):

            #get the index of the x coordinate for the three points
            A = self.faces[i] * 3
            B = self.faces[i+1] * 3
            C = self.faces[i+2] * 3

            #create two vectors from the three points of the triangle
            vBA = (self.verts[B] - self.verts[A], 
                   self.verts[B+1] - self.verts[A+1], 
                   self.verts[B+2] - self.verts[A+2])

            vCA = (self.verts[C] - self.verts[A], 
                   self.verts[C+1] - self.verts[A+1], 
                   self.verts[C+2] - self.verts[A+2])

            #do the cross product to get the face normal          
            normal = (vBA[y] * vCA[z] - vBA[z] * vCA[y], 
                      vBA[z] * vCA[x] - vBA[x] * vCA[z], 
                      vBA[x] * vCA[y] - vBA[y] * vCA[x])

            #unitize the face normal
            magnitude = (normal[x]**2  + normal[y]**2 + normal[z]**2)**.5

            #dont divide by zero
            if magnitude != 0:
                unitNormal = (normal[x] / magnitude, 
                              normal[y] / magnitude, 
                              normal[z] / magnitude)
            else:
                unitNormal = (0.0,0.0,0.0)

            #incrementally calculate vertex normals
            for j in range(3):
                self.vNorms[A + j] += unitNormal[j]
                self.vNorms[B + j] += unitNormal[j]
                self.vNorms[C + j] += unitNormal[j]

            self.fNorms.extend(unitNormal)

        #unitize vertex normals
        for i in xrange(0, len(self.vNorms), 3):
            magnitude = ( self.vNorms[i]**2 + 
                          self.vNorms[i+1]**2 + 
                          self.vNorms[i+2]**2)**.5

            if magnitude != 0:
                self.vNorms[i] /= magnitude
                self.vNorms[i+1] /= magnitude
                self.vNorms[i+2] /= magnitude

    def printObj(self):
        tmp = ''

        for i in xrange(0, len(self.verts), 3):
            tmp += "v  " + str(self.verts[i]) 
            tmp +=  "  " + str(self.verts[i+1]) 
            tmp += "  " + str(self.verts[i+2]) + "\n"   
        tmp += "\n"

        for i in xrange(0, len(self.vNorms), 3):
            tmp += "vn  "+ str(self.vNorms[i]) 
            tmp += "  " + str(self.vNorms[i+1])
            tmp += "  " + str(self.vNorms[i+2]) + "\n"
        tmp += "\n"

        for i in xrange(0, len(self.faces), 3):
            tmp += "f  " + str(self.faces[i] + 1)  
            tmp += "  " + str(self.faces[i+1] + 1) 
            tmp += "  " + str(self.faces[i+2] + 1) + "\n"
        tmp += "\n"

        print tmp

The title refers to the fact that I have also wrote a version that is much more object oriented, but never quite managed to finish it.

Here’s a render, in Blender, of a teapot loaded in from a OBJ:

The OBJ I used to load this,along with quite a few others, can be found at http://people.sc.fsu.edu/~jburkardt/data/obj/obj.html (the link tag keeps getting deleted for some reason)

]]>
https://twodee.org/blog/5101/feed 1
Synchronized Drawing https://twodee.org/blog/5080 https://twodee.org/blog/5080#respond Thu, 18 Oct 2012 01:51:13 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=5080 [...]]]> I was tasked with using MPI_Barrier to synchronously draw two triangles on two different raspberry pi boards. I was unsuccessful, but not unproductive.

The reason I wasn’t successful is this: When I executed Dr. Johnson’s triangle.py program, the program would give me the following error:

numconfig= c_long(1)
Traceback (most recent call last):
  File "triangle.py", line 27, in <module>
    program = context.get_program(vertexShader, fragmentShader, binding)
AttributeError: 'EGL' object has no attribute 'get_program'

This is after installing the pyopengles library. Googling provided little help.

Order Matters

One lesson I reminded myself of is that “Order matters” order referring to the sequence of things. I spent all too much time trying to get Dr. Bui’s barrier.py code up and running again with both our machines on my local network. I was trying this command:

mpiexec -np 2 python barrier.py -f machines

Instead of:

mpiexec -np 2 -f machines python barrier.py

It just putting the program to execute at the end made the difference.

Time Analysis

The next thing I accomplished was some analysis on the delay it took between when the initial board would write its output and the other boards to receive the message and write their own. If this time is too long, they videos will not look as if they are playing at the same time.

Taking the log files generated from Dr. Bui’s barrier.py program, here are the results of analyzing the difference in time between when bramble00 and bramble01 wrote the same line to their log files. There are 736 samples. All values are in seconds:

avg min max range std dev
0.009541388 0.008450031 0.011140108 0.00269 0.000529

 

]]>
https://twodee.org/blog/5080/feed 0
Buster Minutes from 2012/10/04 https://twodee.org/blog/4917 https://twodee.org/blog/4917#respond Thu, 04 Oct 2012 18:23:11 +0000
Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
http://www.twodee.org/blog/?p=4917 [...]]]>
  • The OS shell assignment is due, and our student workers are sagging.
  • Three more Pis are ordered, with an undetermined ETA.
  • We need monitors. (James)
  • We need a switch. (link from Peter, order from Chris)
  • To show meshes, we’ll need a mesh reader. Let’s do OBJ. It’s been done, but let’s use this as an opportunity to learn. Right, fellas? (Eric)
  • Do we need an event scheduler. Chris suggested a multithreaded solution, when all input-reading threads populating an event queue and the renderer draining it. Peter was leary. Instead, we’re going to shoot for an event poller. Look into select in Python. (Chris)
  • ]]>
    https://twodee.org/blog/4917/feed 0
    OpenGL and Mouse Interaction on Raspberry Pi https://twodee.org/blog/4899 https://twodee.org/blog/4899#respond Thu, 04 Oct 2012 11:09:22 +0000
    Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

    Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

    Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

    Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

    Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

    Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383

    Deprecated: Creation of dynamic property WP_Term::$cat_ID is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 378

    Deprecated: Creation of dynamic property WP_Term::$category_count is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 379

    Deprecated: Creation of dynamic property WP_Term::$category_description is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 380

    Deprecated: Creation of dynamic property WP_Term::$cat_name is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 381

    Deprecated: Creation of dynamic property WP_Term::$category_nicename is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 382

    Deprecated: Creation of dynamic property WP_Term::$category_parent is deprecated in /var/www/twodee/blog/wp-includes/category.php on line 383
    http://www.twodee.org/blog/?p=4899 [...]]]> Last time I was able to get a simple triangle up on my Raspberry Pi using Python bindings for OpenGL ES. My goal this week was to get mouse interaction going. My big question was, “Can I move the triangle?”

    Normally I’d use a windowing toolkit like wxWidgets or SDL to get mouse events. But I’m not even running an X server on my Raspberry Pi! I looked at the demos in pyopengles and found that they could run without X and still support mouse interaction. Having grown up on supporting libraries, this was not a world I was familiar with. I found the details in pymouse.py.

    To get mouse interaction in my demo, I followed this progression:

    • I tried to understand the implied protocol in pymouse.py. Three bytes were read from /dev/input/mouse0. Ran “man mouse” and searched for documentation to find explicit protocol. I failed.
    • I reverse engineered the protocol to the best of my knowledge. The first byte is ??YX1MRL. L is 1 if the left button is down, R is 1 if the right button is down, and M is 1 if the middle button is down. 1 is apparently always 1. I don’t know anything about bits 6 and 7. (They don’t correspond to wheel clicks. On those, I get an event, but only 1 is set.)
    • The second and third bytes are the horizontal and vertical displacements, respectively. If the Y bit is set in the first byte, the third byte should be interpreted as a negative value. If the X bit is set in the first byte, the second byte should be interpreted as a negative value. In pymouse.py, if the X or Y bits are set, the author interpreted the corresponding displacement in a positive way and then subtracted off 256. The magic of that expression tripped me up considerably, but I eventually figured out what he was doing. If those bits are set, the displacement bytes are a negative number stored in twos complement. To convert a twos complement byte to its corresponding positive decimal value, one 1) flips all bits and 2) adds one. Said another way, ~B + 1, where B is your byte. Flipping B is the same as subtracting B from 1111 1111, since 1 take away 0 is 1 and 1 take away 1 is 0. 1111 1111 is 255 decimal. We can rewrite our previous expression to 255 – B + 1. Simplifying, we have 256 – B. We’re almost to the author’s original expression! The last step is to realize that this conversion produced the positive value of the twos complement number. We know we want the negative value, so let’s multiply by -1. -1 * (256 – B) = -256 + B = B – 256.
    • I was fascinated by the fact that the mouse is a completely relative input device. Of course this is obvious, but any mapping to absolute pixel coordinates is completely done outside of this low hardware layer. I only get displacements from the bytes.
    • Next I simply started up a mouse listening thread and wrapped up my OpenGL renderer with a loop that read the mouse state each iteration. Possibly I may want to rig up my own event queue and loop. The triangle’s positive became a function of the mouse position.
    • I have no idea how many mice will follow this same protocol.

    And it all worked!

    Here’s my crude mouse listener:

    #!/usr/bin/env python
    
    import threading
    import time
    
    class MouseListener(threading.Thread):
      def __init__(self):
        threading.Thread.__init__(self)
        self.fd = open('/dev/input/mouse0', 'r')
        self.height = 1080
        self.width = 1920
        self.x = self.height / 2
        self.y = self.width / 2
        self.finished = False
    
      def run(self):
        while 1:
          while 1:
            buttons, dx, dy = map(ord, self.fd.read(3))
    
            # Bit 3 is always set. Always! Move on.
            if buttons & (1 << 3):
              break
    
            # Maybe not always? Maybe a read failed and bit 3 wasn't set.
            # The original author suggests we try to sync up again with a
            # read of one byte. Why only one?
            self.fd.read(1)
    
          if buttons & (1 << 1) and buttons & (1 << 0):
            print 'both pressed'
            self.finished = True
            break
          elif buttons & (1 << 2):
            print 'middle press'
          elif buttons & (1 << 1):
            print 'right press'
          elif buttons & (1 << 0):
            print 'left press'
    
          # Going left 
          if buttons & (1 << 4):
            dx -= 256
    
          # Going right
          if buttons & (1 << 5):
            dy -= 256
    
          self.x += dx
          self.y += dy
    
          if self.x < 0:
            self.x = 0
          elif self.x > self.width:
            self.x = self.width
    
          if self.y < 0:
            self.y = 0
          elif self.y > self.height:
            self.y = self.height
    
    def startMouse():
      thread = MouseListener()
      thread.start()
      return thread
    
    startMouse()
    ]]>
    https://twodee.org/blog/4899/feed 0