[Solved] OGRE + Boost.Python - Blank viewport

Problems building or running the engine, queries about how to use features etc.
Post Reply
User avatar
Mind Calamity
Ogre Magi
Posts: 1255
Joined: Sat Dec 25, 2010 2:55 pm
Location: Macedonia
x 81

[Solved] OGRE + Boost.Python - Blank viewport

Post by Mind Calamity »

I'm not sure if it would be better to post here, or in the "Using OGRE in practice" forum, but I'll give it a shot, as this is the most frequented section of the boards.

So I'm making bindings for my own framework which uses OGRE internally, I've bound the core objects, and for the most part they work fine (input and windowing works properly), but I've had this one issue which I can't get to the bottom of - the viewport of the Python application is always black (I have no objects other than the camera, just setting the background color of the viewport).

The weird thing is - the exact equivalent in C++ works fine, yet the python-bound code does not. Nothing of importance is shown in the log.

Here's how I bind my objects:

Code: Select all

#include "boost/python.hpp"
#include "boost/exception/diagnostic_information.hpp"
#include "Demo.hpp"
#include "Demo.cpp"
#include "XMLLevelLoader.h"

void addResourceLocation(const Crimson::String& group, const Crimson::String& loc)
{
	Ogre::ResourceGroupManager* resmgr = Ogre::ResourceGroupManager::getSingletonPtr();
	resmgr->addResourceLocation(loc, "FileSystem", group);
}

void initResourceGroup(const Crimson::String& group)
{
	Ogre::ResourceGroupManager* resmgr = Ogre::ResourceGroupManager::getSingletonPtr();
	resmgr->initialiseResourceGroup(group);
}

class AppWrapper : public Crimson::Application, public boost::python::wrapper<Crimson::Application>
{
public:
	void onKeyDown(const SDL_KeyboardEvent& e)
	{
		boost::python::override over = this->get_override("onKeyDown");
		if (over)
			over(e);
	}
	using Crimson::Application::mQuit;
};

BOOST_PYTHON_MODULE(PyEngine)
{
	using namespace boost::python;
	def("initResourceGroup", initResourceGroup);
	def("addResourceLocation", addResourceLocation);
	
	enum_<SDL_Scancode>("SDL_Scancode");

	class_<Ogre::ColourValue>("Color", init<const float, const float, const float, const float>())
		.def_readwrite("r", &Ogre::ColourValue::r)
		.def_readwrite("g", &Ogre::ColourValue::g)
		.def_readwrite("b", &Ogre::ColourValue::b)
		.def_readwrite("a", &Ogre::ColourValue::a);

	class_<Ogre::Viewport>("Viewport", init<Ogre::Camera* , Ogre::RenderTarget*, float, float, float, float, int>())
		.def("setBackgroundColour", &Ogre::Viewport::setBackgroundColour);
	
	class_<Crimson::WindowInfo>("WindowInfo")
		.def_readonly("renderWnd", &Crimson::WindowInfo::renderWnd)
		.def_readonly("sdlWnd", &Crimson::WindowInfo::sdlWnd)
		.def("addViewport", &Crimson::WindowInfo::addViewport, return_value_policy<reference_existing_object>())
		.def("getAspectRatio", &Crimson::WindowInfo::getAspectRatio)
		.def("getWidth", &Crimson::WindowInfo::getWidth)
		.def("getHeight", &Crimson::WindowInfo::getHeight);

	class_<Crimson::Kernel>("Kernel")
		.def("initialize", &Crimson::Kernel::initialize)
		.def("createRenderWindow", &Crimson::Kernel::createRenderWindow, return_value_policy<reference_existing_object>())
		.def("getWindowInfo", &Crimson::Kernel::getWindowInfo, return_value_policy<reference_existing_object>())
		.def("destroy", &Crimson::Kernel::destroy)
		.def("render", &Crimson::Kernel::render);

	class_<Crimson::Actor>("Actor", init<Crimson::Level*, const Crimson::String&, Crimson::Actor*>())
		.def("setPosition", static_cast<void(Crimson::Actor::*)(const Crimson::Vector3&)>(&Crimson::Actor::setPosition))
		.def("getPosition", &Crimson::Actor::getPosition);

	class_ <Crimson::Mesh, bases<Crimson::Actor>>("Mesh", init<Crimson::Level*, const Crimson::String&, const Crimson::String&, Crimson::Actor*>());

	class_ <Crimson::Camera, bases<Crimson::Actor>>("Camera", init<Crimson::Level*, const Crimson::String&, Crimson::Actor*>())
		.def("setAspectRatio", &Crimson::Camera::setAspectRatio);

	class_<rapidxml::xml_node<>, boost::noncopyable>("xml_node", init<rapidxml::node_type>())
		.def("first_node", &rapidxml::xml_node<>::first_node, return_value_policy<reference_existing_object>());

	class_<rapidxml::xml_document<>, boost::noncopyable>("xml_document")
		.def("first_node", &rapidxml::xml_document<>::first_node, return_value_policy<reference_existing_object>())
		.def("parse", &rapidxml::xml_document<>::parse<0>);

	class_<Crimson::Level>("Level", init<Crimson::Kernel*>())
		.def("initialize", &Crimson::Level::initialize)
		.def("destroy", &Crimson::Level::destroy)
		.def("createActor", &Crimson::Level::createActor, return_value_policy<reference_existing_object>())
		.def("createMesh", &Crimson::Level::createMesh, return_value_policy<reference_existing_object>())
		.def("createCamera", &Crimson::Level::createCamera, return_value_policy<reference_existing_object>());

	class_<Crimson::XMLLevelLoader>("XMLLevelLoader", init<Crimson::String>())
		.def("load", &Crimson::XMLLevelLoader::load)
		.def("loadFileAsString", &Crimson::XMLLevelLoader::loadFileAsString)
		.def("loadResources", &Crimson::XMLLevelLoader::loadResources)
		.staticmethod("loadFileAsString")
		.staticmethod("loadResources");
	
	class_<AppWrapper, boost::noncopyable>("Application")
		.def("initialize", &Crimson::Application::initialize)
		.def("destroy", &Crimson::Application::destroy)
		.def("onKeyDown", &Crimson::Application::onKeyDown)
		.def("start", &Crimson::Application::start)
		.def("updateGraphics", &Crimson::Application::updateGraphics)
		.def("updateInput", &Crimson::Application::updateInput)
		.def("isRunning", &Crimson::Application::isRunning)
		.def("quit", &Crimson::Application::quit)
		.def("getKernel", &Crimson::Application::getKernel, return_value_policy<reference_existing_object>());
	
	class_<SDL_Keysym>("SDL_Keysym")
		.def_readonly("mod", &SDL_Keysym::mod)
		.def_readonly("scancode", &SDL_Keysym::scancode);

	class_<SDL_KeyboardEvent>("SDL_KeyboardEvent")
		.def_readonly("keysym", &SDL_KeyboardEvent::keysym);

	class_<Ogre::Vector3>("Vector3", init<const float, const float, const float>())
		.def_readwrite("x", &Ogre::Vector3::x)
		.def_readwrite("y", &Ogre::Vector3::y)
		.def_readwrite("z", &Ogre::Vector3::z);
}
The Python code (blank viewport):

Code: Select all

import sys
from PyEngine import *

# Class definition
class TestPythonApp(Application):
	def initialize(self):
		Application.initialize(self)

		mLevel = Level(self.getKernel())
		mLevel.initialize()

		mCamera = mLevel.createCamera("test", None)
		vp = self.getKernel().getWindowInfo().addViewport(mCamera)
		vp.setBackgroundColour(Color(0.8, 0, 0, 1))
	def start(self):
		while self.isRunning():
			self.updateInput()
			self.updateGraphics(1)

	mLevel 	= None
	mCamera 	= None

# /Class definition

# Script execution
app = TestPythonApp()
app.initialize()
app.start()
app.destroy()
The C++ equivalent (colored viewport):
-- Header

Code: Select all

#ifndef PYAPP_HPP
#define PYAPP_HPP
#include "Application.h"
#include "XMLLevelLoader.h"

class PythonApp : public Crimson::Application
{
public:
	bool initialize();
	void start();

protected:
	Crimson::Camera*	mCamera;
	Crimson::Level*		mLevel;
};
#endif
-- Source:

Code: Select all

bool PythonApp::initialize()
{
	Application::initialize();

	mLevel = new Crimson::Level(mKernel);
	mLevel->initialize();
	mCamera = mLevel->createCamera("cam");
	Ogre::Viewport* vp = mKernel->getWindowInfo()->addViewport(mCamera);
	vp->setBackgroundColour(Ogre::ColourValue(0.7f, 0.8f, 0.7f));

	return true;
}

void PythonApp::start()
{
	while (isRunning())
	{
		updateInput();
		updateGraphics(1);
	}
}

int main(int argc, char* argv[])
{
	PythonApp game;
	game.initialize();
	game.start();
	game.destroy();

	return 0;
}
I did a check - mBackColor (in Ogre::Viewport) is properly set through Python, so that's not the problem.

Also, the Python code works fine for some reason if I put the render loop inside the initialize function, but not if it's outside of that. Because of this, I have a feeling it's a context issue (maybe the data gets corrupted when the interpreter gets out of the initialize function or something.

Here's the oddly working Python code:

Code: Select all

import sys
from PyEngine import *

# Class definition
class TestPythonApp(Application):
	def initialize(self):
		Application.initialize(self)

		mLevel = Level(self.getKernel())
		mLevel.initialize()

		mCamera = mLevel.createCamera("test", None)
		vp = self.getKernel().getWindowInfo().addViewport(mCamera)
		vp.setBackgroundColour(Color(0.8, 0, 0, 1))
		self.start() # Moved line app.start() to here.
	def start(self):
		while self.isRunning():
			self.updateInput()
			self.updateGraphics(1)

	mLevel 	= None
	mCamera 	= None

# /Class definition

# Script execution
app = TestPythonApp()
app.initialize()
#app.start() # This moved up to initialize makes it work
app.destroy()
-- Some info on my framework: Level contains a scene manager, and the camera is a regular OGRE camera, just wrapped through my own API for ease of use WindowInfo is another wrapper class that handles window settings (between OGRE and SDL) and I just call renderOneFrame in my updateGraphics(delta) function.

Edit: Posted on StackOverflow: http://stackoverflow.com/questions/2571 ... alent-code
Last edited by Mind Calamity on Sun Sep 07, 2014 9:25 pm, edited 1 time in total.
BitBucket username changed to iboshkov (from MindCalamity)
Do you need help? What have you tried?
- xavier
---------------------
HkOgre - a Havok Integration for OGRE | Simple SSAO | My Blog | My YouTube | My DeviantArt
User avatar
Mind Calamity
Ogre Magi
Posts: 1255
Joined: Sat Dec 25, 2010 2:55 pm
Location: Macedonia
x 81

Re: OGRE + Boost.Python - Blank viewport, equivalent C++ cod

Post by Mind Calamity »

Cross-posting my answer from StackOverflow:
Okay, so after setting a lot of breakpoints, I found that when the interpreter exited the function initialize it destroyed the mLevel and mCamera objects because me being the Python newbie that I am forgot that in Python self/this isn't implied, and thus my objects got destroyed because of the SceneManager's destruction (upon deletion of mLevel).
Here's the working/fixed code:

Code: Select all

import sys
from PyEngine import *

# Class definition
class TestPythonApp(Application):
	def initialize(self):
		Application.initialize(self)

		self.mLevel = Level(self.getKernel())
		self.mLevel.initialize()

		self.mCamera = self.mLevel.createCamera("test", None)
		self.mViewport = self.getKernel().getWindowInfo().addViewport(self.mCamera)
		self.mViewport.setBackgroundColour(Color(0.8, 0, 0, 1))
		print("Object: %s, of size: %d" % (self.mViewport, sys.getsizeof(self.mViewport)))
		#self.start();
	def start(self):
		while self.isRunning():
			self.updateInput()
			self.updateGraphics(1)

	mLevel 	 = None
	mViewport = None
	mCamera 	 = None

# /Class definition

# Script execution
app = TestPythonApp()
print("Initing");
app.initialize()
print("Object: %s, of size: %d" % (app.mViewport, sys.getsizeof(app.mViewport)))
app.start() # This moved up to initialize makes it work
app.destroy()
BitBucket username changed to iboshkov (from MindCalamity)
Do you need help? What have you tried?
- xavier
---------------------
HkOgre - a Havok Integration for OGRE | Simple SSAO | My Blog | My YouTube | My DeviantArt
Post Reply