Hi folks,
exciting news. with a bit of tinkering i got panda3d to run on a raspberry pi.
so far there are quite a number of limitations:
most important one:no propper X11 windowing support, everything is scaled to fullscreen and rendered on top of the desktop.
aside from that it runs rather well.
an experimental deb package for the rasbian wheezy can be found here http://thomaseg1.p3dp.com/samples/panda3d1.8_1.8.0_armhf.deb
for those who want to compile panda on the raspberry, continue reading.
assuming you have your rasperry running already you need to set up cross-compile. there’s a nice tutorial here:
http://www.raspberrypi.org/phpBB3/viewtopic.php?p=101026
after you chrooted into your raspberry environment, install the dependencies like you would when compiling on the desktop.
copy the panda source over to the sd card. you need to make the following changes:
in /panda/src/egldisplay/eglGraphicsPipe.cxx
-add
#include "bcm_host.h"
to the includes
-add
bcm_host_init();
at the top of the constructor eglGraphicsPipe::eglGraphicsPipe
i messed around in this file so i have no idea what part is original and what is modified… so in /panda/src/egldisplay/eglGraphicsWindow.cxx replace the entire openWindow() function with
////////////////////////////////////////////////////////////////////
// Function: eglGraphicsWindow::open_window
// Access: Protected, Virtual
// Description: Opens the window right now. Called from the window
// thread. Returns true if the window is successfully
// opened, or false if there was a problem.
////////////////////////////////////////////////////////////////////
bool eglGraphicsWindow::
open_window() {
eglGraphicsPipe *egl_pipe;
DCAST_INTO_R(egl_pipe, _pipe, false);
// GSG Creation/Initialization
eglGraphicsStateGuardian *eglgsg;
if (_gsg == 0) {
// There is no old gsg. Create a new one.
eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, NULL);
eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
_gsg = eglgsg;
} else {
// If the old gsg has the wrong pixel format, create a
// new one that shares with the old gsg.
DCAST_INTO_R(eglgsg, _gsg, false);
if (!eglgsg->get_fb_properties().subsumes(_fb_properties)) {
eglgsg = new eglGraphicsStateGuardian(_engine, _pipe, eglgsg);
eglgsg->choose_pixel_format(_fb_properties, egl_pipe->get_display(), egl_pipe->get_screen(), false, false);
_gsg = eglgsg;
}
}
XVisualInfo *visual_info = eglgsg->_visual;
if (visual_info == NULL) {
// No X visual for this fbconfig; how can we open the window?
egldisplay_cat.error()
<< "No X visual: cannot open window.\n";
return false;
}
Visual *visual = visual_info->visual;
int depth = visual_info->depth;
if (!_properties.has_origin()) {
_properties.set_origin(0, 0);
}
if (!_properties.has_size()) {
_properties.set_size(100, 100);
}
X11_Window parent_window = egl_pipe->get_root();
WindowHandle *window_handle = _properties.get_parent_window();
if (window_handle != NULL) {
egldisplay_cat.info()
<< "Got parent_window " << *window_handle << "\n";
WindowHandle::OSHandle *os_handle = window_handle->get_os_handle();
if (os_handle != NULL) {
egldisplay_cat.info()
<< "os_handle type " << os_handle->get_type() << "\n";
if (os_handle->is_of_type(NativeWindowHandle::X11Handle::get_class_type())) {
NativeWindowHandle::X11Handle *x11_handle = DCAST(NativeWindowHandle::X11Handle, os_handle);
parent_window = x11_handle->get_handle();
} else if (os_handle->is_of_type(NativeWindowHandle::IntHandle::get_class_type())) {
NativeWindowHandle::IntHandle *int_handle = DCAST(NativeWindowHandle::IntHandle, os_handle);
parent_window = (X11_Window)int_handle->get_handle();
}
}
}
_parent_window_handle = window_handle;
setup_colormap(visual_info);
_event_mask =
ButtonPressMask | ButtonReleaseMask |
KeyPressMask | KeyReleaseMask |
EnterWindowMask | LeaveWindowMask |
PointerMotionMask |
FocusChangeMask |
StructureNotifyMask;
// Initialize window attributes
XSetWindowAttributes wa;
wa.background_pixel = XBlackPixel(_display, _screen);
wa.border_pixel = 0;
wa.colormap = _colormap;
wa.event_mask = _event_mask;
unsigned long attrib_mask =
CWBackPixel | CWBorderPixel | CWColormap | CWEventMask;
_xwindow = XCreateWindow
(_display, parent_window,
_properties.get_x_origin(), _properties.get_y_origin(),
_properties.get_x_size(), _properties.get_y_size(),
0, depth, InputOutput, visual, attrib_mask, &wa);
if (_xwindow == (X11_Window)0) {
egldisplay_cat.error()
<< "failed to create X window.\n";
return false;
}
set_wm_properties(_properties, false);
// We don't specify any fancy properties of the XIC. It would be
// nicer if we could support fancy IM's that want preedit callbacks,
// etc., but that can wait until we have an X server that actually
// supports these to test it on.
XIM im = egl_pipe->get_im();
_ic = NULL;
if (im) {
_ic = XCreateIC
(im,
XNInputStyle, XIMPreeditNothing | XIMStatusNothing,
(void*)NULL);
if (_ic == (XIC)NULL) {
egldisplay_cat.warning()
<< "Couldn't create input context.\n";
}
}
if (_properties.get_cursor_hidden()) {
XDefineCursor(_display, _xwindow, egl_pipe->get_hidden_cursor());
}
//try to setup the broadcom way.
static EGL_DISPMANX_WINDOW_T nativewindow;
DISPMANX_ELEMENT_HANDLE_T dispman_element;
DISPMANX_DISPLAY_HANDLE_T dispman_display;
DISPMANX_UPDATE_HANDLE_T dispman_update;
VC_RECT_T dst_rect;
VC_RECT_T src_rect;
int display_width;
int display_height;
// You can hardcode the resolution here:
display_width = _properties.get_x_size();
display_height = _properties.get_y_size();
dst_rect.x = (DISPMANX_TRANSFORM_T) _properties.get_x_origin();
dst_rect.y = (DISPMANX_TRANSFORM_T) _properties.get_y_origin();
dst_rect.width = (DISPMANX_TRANSFORM_T) display_width;
dst_rect.height = (DISPMANX_TRANSFORM_T) display_height;
src_rect.x = 0;
src_rect.y = 0;
src_rect.width = display_width << 16;
src_rect.height = display_height << 16;
dispman_display = vc_dispmanx_display_open( 0 /* LCD */);
dispman_update = vc_dispmanx_update_start( 0 );
dispman_element = vc_dispmanx_element_add ( dispman_update,
dispman_display, 0/*layer*/, &dst_rect, 0/*src*/,
&src_rect, DISPMANX_PROTECTION_NONE, 0 /*alpha*/,
0/*clamp*/, (DISPMANX_TRANSFORM_T)0/*transform*/);
///////
nativewindow.element = dispman_element;
nativewindow.width = display_width;
nativewindow.height = display_height;
vc_dispmanx_update_submit_sync( dispman_update );
_egl_display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (_egl_display == EGL_NO_DISPLAY )
{
egldisplay_cat.error()
<< "Failed to eglGetDisplay.\n";
return false;
}
_egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig,
&nativewindow, NULL);
if ( _egl_surface == EGL_NO_SURFACE )
{
egldisplay_cat.error()
<< "Failed to eglCreateWindowSurface.\n";
return false;
}
if ( !eglMakeCurrent(_egl_display,_egl_surface,_egl_surface, eglgsg->_context) )
{
egldisplay_cat.error()
<< "Failed to eglMakeCurrent.\n";
return false;
}
//_egl_surface = eglCreateWindowSurface(_egl_display, eglgsg->_fbconfig, (NativeWindowType) _xwindow, NULL);
//if (eglGetError() != EGL_SUCCESS) {
// egldisplay_cat.error()
// << "Failed to create window surface.\n";
// return false;
//}
/*
if (!eglMakeCurrent(_egl_display, _egl_surface, _egl_surface, eglgsg->_context)) {
egldisplay_cat.error() << "Failed to call eglMakeCurrent: "
<< get_egl_error_string(eglGetError()) << "\n";
}
*/
eglgsg->reset_if_new();
if (!eglgsg->is_valid()) {
close_window();
return false;
}
if (!eglgsg->get_fb_properties().verify_hardware_software
(_fb_properties, eglgsg->get_gl_renderer())) {
close_window();
return false;
}
_fb_properties = eglgsg->get_fb_properties();
XMapWindow(_display, _xwindow);
if (_properties.get_raw_mice()) {
open_raw_mice();
} else {
if (egldisplay_cat.is_debug()) {
egldisplay_cat.debug()
<< "Raw mice not requested.\n";
}
}
// Create a WindowHandle for ourselves
_window_handle = NativeWindowHandle::make_x11(_xwindow);
// And tell our parent window that we're now its child.
if (_parent_window_handle != (WindowHandle *)NULL) {
_parent_window_handle->attach_child(_window_handle);
}
return true;
}
a few compiler and linker settings need to be exported. (asuming you are using the raspian image, if not you may need to fix the pathes to the bcm_host.h)
export CFLAGS="-DHAVE_LIBOPENMAX=2 -DOMX -DOMX_SKIP64BIT -ftree-vectorize -pipe -DUSE_EXTERNAL_OMX -DHAVE_LIBBCM_HOST -DUSE_EXTERNAL_LIBBCM_HOST -DUSE_VCHIQ_ARM -I/opt/vc/include/ -I/opt/vc/include/interface/vcos/pthreads"
export LDFLAGS="-L/opt/vc/lib/ -lbcm_host "
all that is left do do is call makepanda with --no-sse2 options and your desired parameters.
python makepanda/makepanda.py --no-sse2 --everything --installer
crosscompiling can be slow. on my dualcore it takes 6 to 8 hours for a complete build. so be patient.