#include "ag.h" #include "anaglym.h" #include "Engine.h" #include #include /* exit() */ #include #include #include #include #include #include #include /* fabs() */ #include #include using namespace std; Engine * g_engine; SDL_Event Engine::userEvent; Engine::Engine(const string & path) { m_next_object_index = 1; m_eye[0] = 0; m_eye[1] = -1; m_eye[2] = 0; m_center[0] = 0; m_center[1] = 0; m_center[2] = 0; m_up[0] = 0; m_up[1] = 0; m_up[2] = 1; m_drawing = false; m_autoPhysics = true; m_autoStartFrame = true; m_autoEndFrame = true; m_autoDrawObjects = true; m_fileLoader = new EngineFileLoader(this); size_t pos = path.find_last_of("\\/"); m_engine_path = (pos != string::npos) ? string(path, 0, pos) : "."; /* setup redraw SDL event structure */ userEvent.type = SDL_USEREVENT; userEvent.user.code = 0; } Engine::~Engine() { lua_close(m_luaState); for (std::map::iterator it = m_objects.begin(); it != m_objects.end(); it++) { delete it->second; } delete m_fileLoader; } bool Engine::load(const char * program) { m_program_path = program; size_t pos = m_program_path.find_last_of("/\\"); m_program_path = (pos != string::npos) ? m_program_path.substr(0, pos) : "."; m_luaState = lua_open(); registerLibraries(); int s = luaL_loadfile(m_luaState, program); if (s == 0) { // execute Lua program s = lua_pcall(m_luaState, 0, LUA_MULTRET, 0); } else { cerr << "Warning: problem loading '" << program << "'" << endl; return false; } if (s != 0) { reportErrors(s); return false; } return true; } void Engine::reportErrors(int status) { if (status != 0) { cerr << "Engine: Error: " << lua_tostring(m_luaState, -1) << endl; lua_pop(m_luaState, 1); // remove error message } } void Engine::registerLibraries() { /* Load the Lua string library */ lua_pushcfunction(m_luaState, luaopen_string); lua_pcall(m_luaState, 0, 0, 0); /* Load the Lua math library */ lua_pushcfunction(m_luaState, luaopen_math); lua_pcall(m_luaState, 0, 0, 0); /* Load the Lua table library */ lua_pushcfunction(m_luaState, luaopen_table); lua_pcall(m_luaState, 0, 0, 0); ag::register_functions(m_luaState); } string Engine::locateResource(const string & shortname) { string try_path; /* look for the resource relative to the loaded script's directory */ try_path = m_program_path + "/" + shortname; if (fileExists(try_path)) return try_path; /* next look for the resource in the engine's library directory */ try_path = m_engine_path + "/lib/" + shortname; if (fileExists(try_path)) return try_path; return ""; } bool Engine::fileExists(const string & path) { struct stat st; if (stat(path.c_str(), &st) == 0) { return S_ISREG(st.st_mode) && (st.st_size > 0); } return false; } int Engine::addObject(WFObj * obj, bool is_static, float scale) { int id = m_next_object_index++; Object * o = createObject(is_static, obj->render(), scale); m_objects[id] = o; return id; } void Engine::removeObject(int id) { if (getObject(id) != NULL) m_objects.erase(id); } int Engine::cloneObject(const Engine::Object * obj) { int id = m_next_object_index++; Object * o = new Object(*obj); m_objects[id] = o; return id; } Engine::Object * Engine::getObject(int id) { return m_objects.find(id) != m_objects.end() ? m_objects[id] : NULL; } void Engine::startFrame() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(m_eye[0], m_eye[1], m_eye[2], m_center[0], m_center[1], m_center[2], m_up[0], m_up[1], m_up[2]); } void Engine::endFrame() { SDL_GL_SwapBuffers(); } int Engine::setCamera(lua_State * L) { int argc = lua_gettop(L); vector args; for (int i = 1; i <= argc; i++) { int type = lua_type(L, i); if (type == LUA_TNUMBER || type == LUA_TSTRING) args.push_back(lua_tonumber(L, i)); else args.push_back(0); } if (argc >= 3) { m_eye[0] = args[0]; m_eye[1] = args[1]; m_eye[2] = args[2]; } if (argc >= 6) { m_center[0] = args[3]; m_center[1] = args[4]; m_center[2] = args[5]; } if (argc >= 9) { m_up[0] = args[6]; m_up[1] = args[7]; m_up[2] = args[8]; } return 0; } int Engine::loadModel(const string & name, bool static_data, float scale) { size_t pos = name.find_first_not_of(FILENAME_SAFE_CHARS); if (pos == string::npos) { FileLoader::Path model_path("", name + ".obj"); string phys_path = locateResource(name + ".phy"); WFObj * obj = new WFObj(*m_fileLoader, m_textureCache); if (obj->load(model_path)) { int id = addObject(obj, static_data, scale); if (phys_path != "") { Engine::Object * engine_obj = getObject(id); if (engine_obj != NULL) { engine_obj->loadPhy(phys_path); } } return id; } else { delete obj; cerr << "error loading object" << endl; } } return 0; } /* called by SDL when the update timer expires */ Uint32 Engine::updateCallback(Uint32 interval, void * param) { Engine * engine = (Engine *) param; return engine->updateCallback(interval); } /* member update function to be called by our registered * SDL callback non-member function */ Uint32 Engine::updateCallback(Uint32 interval) { if (!m_drawing) { SDL_PushEvent(&userEvent); } return interval; } void Engine::run() { /* register a screen redrawing SDL event */ SDL_AddTimer(25, &updateCallback, this); SDL_Event event; while (SDL_WaitEvent(&event)) { switch (event.type) { case SDL_KEYDOWN: if (event.key.keysym.sym == SDLK_ESCAPE) { goto RET; } break; case SDL_QUIT: goto RET; break; case SDL_USEREVENT: if (event.user.code == 0) { update(); } break; } } RET: ; } void Engine::update() { m_drawing = true; if (m_autoPhysics) doPhysics(); if (m_autoStartFrame) startFrame(); lua_getfield(m_luaState, LUA_GLOBALSINDEX, "update"); if (lua_type(m_luaState, -1) == LUA_TFUNCTION) { /* call the update function - pops the function ref from the stack */ int s = lua_pcall(m_luaState, 0, LUA_MULTRET, 0); reportErrors(s); } else { lua_pop(m_luaState, 1); } if (m_autoDrawObjects) drawObjects(); if (m_autoEndFrame) endFrame(); m_drawing = false; } void Engine::doPhysics() { static Uint32 last_updated = 0; Uint32 current_ticks = SDL_GetTicks(); if (last_updated > 0) { Uint32 msec_steps = current_ticks - last_updated; for (Uint32 i = 0; i < msec_steps; i++) m_world.step(); } last_updated = current_ticks; } void Engine::drawObjects() { std::map::iterator it; for (it = m_objects.begin(); it != m_objects.end(); it++) { it->second->draw(); } } Engine::Object::Object(bool is_static, OdeWorld & world, GLuint dl, float scale) { m_ode_object = world.createObject(is_static, scale); m_display_list = dl; m_display_list_refcnt = new int; *m_display_list_refcnt = 1; m_is_visible = true; m_scale = scale; m_is_scaled = ! (fabs(scale - 1.0) < 0.0001); } Engine::Object::Object(const Engine::Object & orig) { m_is_visible = orig.m_is_visible; m_ode_object = new OdeWorld::Object(*orig.m_ode_object); m_display_list = orig.m_display_list; m_display_list_refcnt = orig.m_display_list_refcnt; (*m_display_list_refcnt)++; m_scale = orig.m_scale; m_is_scaled = orig.m_is_scaled; } Engine::Object::~Object() { delete m_ode_object; (*m_display_list_refcnt)--; if (*m_display_list_refcnt < 1) { /* we hold the last reference to the OpenGL display list */ delete m_display_list_refcnt; glDeleteLists(m_display_list, 1); } } void Engine::Object::draw() { if (m_is_visible) { const dReal * pos = m_ode_object->getPosition(); const dReal * rot = m_ode_object->getRotation(); bool transform = (pos != NULL && rot != NULL); if (transform) OdeWorld::pushTransform(pos, rot); if (m_is_scaled) { glPushAttrib(GL_TRANSFORM_BIT); glEnable(GL_NORMALIZE); glScalef(m_scale, m_scale, m_scale); } glCallList(m_display_list); if (m_is_scaled) glPopAttrib(); if (transform) glPopMatrix(); } } /******** Engine::EngineFileLoader functions ********/ Engine::EngineFileLoader::EngineFileLoader(Engine * engine) { m_engine = engine; } int Engine::EngineFileLoader::getSize(const Path & path) { struct stat st; string file_path = resolvePath(path); if (file_path == "") return -1; if (stat(file_path.c_str(), &st)) return -2; return st.st_size; } FileLoader::Buffer Engine::EngineFileLoader::load(const Path & path) { string file_path = resolvePath(path); int size = getSize(path); if (size > 0) { int fd = open(file_path.c_str(), O_RDONLY); if (fd > 0) { Buffer buf(size); int num_read = read(fd, buf.data, size); close(fd); if (num_read > 0) return buf; } } return Buffer(0); } string Engine::EngineFileLoader::resolvePath(const Path & path) { string file_path = m_engine->locateResource(path.shortPath); if (file_path == "") file_path = path.fullPath; return file_path; }