Estudando o Gamekit OGRE
http://code.google.com/p/gamekit/
Este projeto me chama atenção, pois já implementa OGRE e Lua numa tacada só.
Possui um grupo completo de 7 integrantes em seu trabalho e uma atividade realmente alta no desenvolvimento (até o fechamento deste post eles atualizavam o Google Code todos os dias).
Não tem muita documentação no site, mas ao baixar o source ele vem junto com toda a API de Lua.
Lua
Bastante completa, e com um modelo de programação lindíssimo.
Notei até que possui um objeto de maquina de estado já pronto ^_^.
Nos exemplos que vem no doc a coisa parece bem interessante.
C++
Na pasta Engine tem muitos arquivos que indicam as potencialidades da engine.
gkEngine.h mostra que ele usa uma instância de OGRESingleton.
E o gkSceneManager parece receber um nome como parâmetro. Apesar de eu ainda não achar na API Lua onde se pode influenciar isso, já é algo promissor.
Dá pra alterar facilmente conforme minhas necessidades?
Olhando com atenção o mecanismo de carga de arquivos, vê-se que ele é bem estruturado em várias classes.
A função loadFile na verdade é apenas um manipulador caso ocorra algum erro.
gkBlendFile *gkBlendLoader::loadFile(const gkString &fname, int options, const gkString &inResourceGroup)
{
bool resetLoad = false;
try {
return loadAndCatch(fname, options, inResourceGroup);
}
Depois o arquivo é pedido emloadAndCatch:
gkBlendFile *gkBlendLoader::loadAndCatch(const gkString &fname, int options, const gkString &inResourceGroup)
{
m_activeFile = getFileByName(fname);
if (m_activeFile != 0)
return m_activeFile;
m_activeFile = new gkBlendFile(fname, inResourceGroup);
Ela tem um mecanismo de segurança para evitar que o arquivo em execução atualmente seja recarregado. Não vejo um motivo muito sustentável para essa segurança, mas trata-se de uma engine predestinada a usuário intermediário talvez por isso.
Essa classe de carregamento é na verdade um manipulador de recursos, já que nada mais é que um gerente para uma lista de arquivos.
Indo reversamente ao código acho:
bool gkBlendFile::parse(int opts)
{
utMemoryStream fs;
fs.open(m_name.c_str(), utStream::SM_READ);
Esse objeto utMemoryStream tem um nome sinistro pra quem procura um stream de arquivo...
Mas o prefixo indica que esta na pasta Utils então espero que seja mesmo útil XD
class utMemoryStream : public utStream
{
public:
utMemoryStream();
~utMemoryStream();
void clear(void);
void open(const char *path, utStream::StreamMode mode);
void open(const utFileStream &fs, utStream::StreamMode mode);
void open(const void *buffer, UTsize size, utStream::StreamMode mode);
bool isOpen(void) const {return m_buffer != 0;}
bool eof(void) const {return !m_buffer || m_pos >= m_size;}
UTsize position(void) const {return m_pos;}
UTsize size(void) const {return m_size;}
UTsize read(void *dest, UTsize nr) const;
UTsize write(const void *src, UTsize nr);
void seek(const UTsize pos, int dir) const;
void *ptr(void) {return m_buffer;}
const void *ptr(void) const {return m_buffer;}
protected:
void reserve(UTsize nr);
char *m_buffer;
mutable UTsize m_pos;
UTsize m_size, m_capacity;
int m_mode;
};Notem o método sobrecarregado open, a criança parece "bonbadinha". Mas aqui já parece que estou encontrando as referências a stream de arquivo clássicas, o lance é subistituilas por um manipulador de buffer (que a classe já tem).
void utFileStream::open(const char *p, utStream::StreamMode mode)
{
if (m_handle != 0 && m_file != p)
utFileWrapper::close(m_handle);
m_file = p;
m_handle= utFileWrapper::open(m_file.c_str(), mode);
if (m_handle)
{
if (!(mode & SM_WRITE))
m_size= utFileWrapper::size(m_handle);
}
}
Vamos ao utFileWrapper, uma função grandinha:
utFileHandle utFileWrapper::open(const char *filename, int mode)
{
#if UT_PLATFORM == UT_PLATFORM_WIN32 && defined(UT_WIN32_FILE)
DWORD dwDesiredAccess= 0;
if (mode & utStream::SM_READ)
dwDesiredAccess |= GENERIC_READ;
if (mode & utStream::SM_WRITE)
dwDesiredAccess |= GENERIC_WRITE;
DWORD dwShareMode= 0;
if (mode & utStream::SM_READ)
dwShareMode |= FILE_SHARE_READ;
if (mode & utStream::SM_WRITE)
dwShareMode |= FILE_SHARE_WRITE;
DWORD dwCreationDisposition= OPEN_EXISTING;
if (mode == utStream::SM_WRITE)
dwCreationDisposition= CREATE_ALWAYS;
HANDLE h= ::CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, 0,
dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);
return h == INVALID_HANDLE_VALUE ? 0 : h;
#else
char fm[3] = {0,0,0};
char *mp = &fm[0];
if (mode & utStream::SM_READ)
*mp++ = 'r';
else if (mode & utStream::SM_WRITE)
*mp++ = 'w';
*mp++ = 'b';
fm[2] = 0;
return fopen(filename, fm);
#endif
}
80% dela é para o manipulador no ambiente Windows (Windows não se divide em módulos bonitinhos e fáceis de organizar) oque não deve fazer muita diferênça já que a ideia é desviar para um manipulador SQLite.
Ok, deste ponto em diante é um stream de arquivo simples. Em teoria basta trocar as chamadas de arquivo por um manipulador de campo BLOB do SQLite (sqlite3_blob_open(), sqlite3_blob_read()...)
Até aqui parece humanamente fácil, pesquizei um pouco mais para ver se não havia como diminuir a possibilidade de criar buffers de dados em excesso. E aparentemente não tem sem ter um arduo trabalho que poderia se justificar em portes para o iPhone (por exemplo a segunda geração conta com 256MB de memória apenas por tanto não se pode esperar muito!)
Este projeto me chama atenção, pois já implementa OGRE e Lua numa tacada só.
Possui um grupo completo de 7 integrantes em seu trabalho e uma atividade realmente alta no desenvolvimento (até o fechamento deste post eles atualizavam o Google Code todos os dias).
Não tem muita documentação no site, mas ao baixar o source ele vem junto com toda a API de Lua.
Lua
Bastante completa, e com um modelo de programação lindíssimo.
Notei até que possui um objeto de maquina de estado já pronto ^_^.
Nos exemplos que vem no doc a coisa parece bem interessante.
Demo = BaseClass(OgreKit.Engine)
function Demo:constructor()
function Demo:constructor()
self.scene = self:getActiveScene()
end
function Demo:OnUpdate(delta)
OgreKit.DebugPrint("Demo main loop running ==> " .. self.scene:getName())
end
end
demo = Demo()
demo:connect(OgreKit.EVT_TICK, demo, Demo.OnUpdate)
C++
Na pasta Engine tem muitos arquivos que indicam as potencialidades da engine.
gkEngine.h mostra que ele usa uma instância de OGRESingleton.
E o gkSceneManager parece receber um nome como parâmetro. Apesar de eu ainda não achar na API Lua onde se pode influenciar isso, já é algo promissor.
Dá pra alterar facilmente conforme minhas necessidades?
Olhando com atenção o mecanismo de carga de arquivos, vê-se que ele é bem estruturado em várias classes.
A função loadFile na verdade é apenas um manipulador caso ocorra algum erro.
gkBlendFile *gkBlendLoader::loadFile(const gkString &fname, int options, const gkString &inResourceGroup)
{
bool resetLoad = false;
try {
return loadAndCatch(fname, options, inResourceGroup);
}
Depois o arquivo é pedido emloadAndCatch:
gkBlendFile *gkBlendLoader::loadAndCatch(const gkString &fname, int options, const gkString &inResourceGroup)
{
m_activeFile = getFileByName(fname);
if (m_activeFile != 0)
return m_activeFile;
m_activeFile = new gkBlendFile(fname, inResourceGroup);
Ela tem um mecanismo de segurança para evitar que o arquivo em execução atualmente seja recarregado. Não vejo um motivo muito sustentável para essa segurança, mas trata-se de uma engine predestinada a usuário intermediário talvez por isso.
Essa classe de carregamento é na verdade um manipulador de recursos, já que nada mais é que um gerente para uma lista de arquivos.
Indo reversamente ao código acho:
bool gkBlendFile::parse(int opts)
{
utMemoryStream fs;
fs.open(m_name.c_str(), utStream::SM_READ);
Esse objeto utMemoryStream tem um nome sinistro pra quem procura um stream de arquivo...
Mas o prefixo indica que esta na pasta Utils então espero que seja mesmo útil XD
class utMemoryStream : public utStream
{
public:
utMemoryStream();
~utMemoryStream();
void clear(void);
void open(const char *path, utStream::StreamMode mode);
void open(const utFileStream &fs, utStream::StreamMode mode);
void open(const void *buffer, UTsize size, utStream::StreamMode mode);
bool isOpen(void) const {return m_buffer != 0;}
bool eof(void) const {return !m_buffer || m_pos >= m_size;}
UTsize position(void) const {return m_pos;}
UTsize size(void) const {return m_size;}
UTsize read(void *dest, UTsize nr) const;
UTsize write(const void *src, UTsize nr);
void seek(const UTsize pos, int dir) const;
void *ptr(void) {return m_buffer;}
const void *ptr(void) const {return m_buffer;}
protected:
void reserve(UTsize nr);
char *m_buffer;
mutable UTsize m_pos;
UTsize m_size, m_capacity;
int m_mode;
};Notem o método sobrecarregado open, a criança parece "bonbadinha". Mas aqui já parece que estou encontrando as referências a stream de arquivo clássicas, o lance é subistituilas por um manipulador de buffer (que a classe já tem).
void utFileStream::open(const char *p, utStream::StreamMode mode)
{
if (m_handle != 0 && m_file != p)
utFileWrapper::close(m_handle);
m_file = p;
m_handle= utFileWrapper::open(m_file.c_str(), mode);
if (m_handle)
{
if (!(mode & SM_WRITE))
m_size= utFileWrapper::size(m_handle);
}
}
Vamos ao utFileWrapper, uma função grandinha:
utFileHandle utFileWrapper::open(const char *filename, int mode)
{
#if UT_PLATFORM == UT_PLATFORM_WIN32 && defined(UT_WIN32_FILE)
DWORD dwDesiredAccess= 0;
if (mode & utStream::SM_READ)
dwDesiredAccess |= GENERIC_READ;
if (mode & utStream::SM_WRITE)
dwDesiredAccess |= GENERIC_WRITE;
DWORD dwShareMode= 0;
if (mode & utStream::SM_READ)
dwShareMode |= FILE_SHARE_READ;
if (mode & utStream::SM_WRITE)
dwShareMode |= FILE_SHARE_WRITE;
DWORD dwCreationDisposition= OPEN_EXISTING;
if (mode == utStream::SM_WRITE)
dwCreationDisposition= CREATE_ALWAYS;
HANDLE h= ::CreateFile((LPCTSTR)filename, dwDesiredAccess, dwShareMode, 0,
dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, 0);
return h == INVALID_HANDLE_VALUE ? 0 : h;
#else
char fm[3] = {0,0,0};
char *mp = &fm[0];
if (mode & utStream::SM_READ)
*mp++ = 'r';
else if (mode & utStream::SM_WRITE)
*mp++ = 'w';
*mp++ = 'b';
fm[2] = 0;
return fopen(filename, fm);
#endif
}
80% dela é para o manipulador no ambiente Windows (Windows não se divide em módulos bonitinhos e fáceis de organizar) oque não deve fazer muita diferênça já que a ideia é desviar para um manipulador SQLite.
Ok, deste ponto em diante é um stream de arquivo simples. Em teoria basta trocar as chamadas de arquivo por um manipulador de campo BLOB do SQLite (sqlite3_blob_open(), sqlite3_blob_read()...)
Até aqui parece humanamente fácil, pesquizei um pouco mais para ver se não havia como diminuir a possibilidade de criar buffers de dados em excesso. E aparentemente não tem sem ter um arduo trabalho que poderia se justificar em portes para o iPhone (por exemplo a segunda geração conta com 256MB de memória apenas por tanto não se pode esperar muito!)
Comentários
Postar um comentário
Como fiquei sabendo que um pobre blogueiro foi processado por um comentário anonimo esses dias, so me resta me precaver contra a ineficácia da legislação em tratar o meio online. Mas eu prometo que libero rapido ^_~