Qhimm.com Forums
Miscellaneous Forums => Scripting and Reverse Engineering => Topic started by: Micky on 2009-04-05 06:34:35
-
I dug out my old Xenogears field viewer again:
(http://bin.mypage.sk/FILES/Xenogears.png)
As you can see, some background parts and the walkmesh are rotated relative to each other. Has anyone seen a transform set, animations or a skeleton in the files? Or is that set from the script?
-
Xenogears brings back memories. It's too bad Namco messed up Xenosaga 2 so badly that only one game release was left (3).
12 year old games :D (xenogears)
Interesting view, it looks familiar.
Cyb
-
Xenogears brings back memories. It's too bad Namco messed up Xenosaga 2 so badly that only one game release was left (3).
12 year old games :D (xenogears)
Who knows? Didn't many people resign during or after Xenosaga 2? That normally points to internal trouble.
The only thing I know is that Xenosaga 3 was a lot better, especially the battle systems. And it has a lot of Xenogears cameos and references. I was still hoping that they load the Zohar onto the Eldridge at the end, but it was just another cliffhanger. Sigh.
Interesting view, it looks familiar.
That's inside Aveh castle. It's an extreme case where one half of the meshes match the walk mesh, and the other half is rotated relative to the walkmesh. In most other levels all meshes have the same rotation relative to the walkmesh, and there are only a few where it matches.
-
Sorry for the thread necromancy, but if anybody cares:
(http://bin.mypage.sk/FILES/Xenogears2.jpg)
There is a displaylist between the model/data/text/script offset data and the beginning of the model data:
0x0184: u16: unknown
0x0186: u16: unknown
0x0188: u32: unknown
0x018c: u32: number of instances
0x0190: number * 16 byte instance data
instance:
u16 flags : mostly unknown, though items with value 480 may not be visible
u16 rot_x : 1024 units = 90 degree
u16 rot_y
u16 rot_z
s16 trans_x
s16 trans_y
s16 trans_z
u16 mesh_index : which mesh to draw from the model data
-
You fixed it?
-
You fixed it?
That is what I was trying to say. I can display all the field maps with correct placement of individual meshes, though I'll have to work out the flags some time. So if anyone has a chance to update the Wiki (uploading Akari's X-Gears first)...
-
Hmmm, interesting. Is animations for field meshes also stores there? Or just idle?
-
What do the colored control lines mean green = walkmesh, yellow = camera?
-
Hmmm, interesting. Is animations for field meshes also stores there? Or just idle?
I haven't found that, yet. Maybe they just place a field model into the scene and animate that if they want an animation. Or the animation is controlled by the script, like doors. The table only contains the base transform for each instance.
What do the colored control lines mean green = walkmesh, yellow = camera?
The data is a list of meshes with connectivity, and I draw each one with a new colour. I assume the decision if they're a camera constraint or a walkmesh is done based on flags or from the script, but I haven't found that, yet. For areas where the character can walk on several levels there are actually several overlapping walkmeshes. It's possible they set the current walkmesh based on trigger zones.
-
// http://forums.qhimm.com/index.php?topic=5146.0
// http://forums.qhimm.com/index.php?topic=5423.msg70264#msg70264
// http://forums.qhimm.com/index.php?topic=5923.msg76045#msg76045
// OpenGL and GLUT
#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <OpenGL/glu.h>
#include <OpenGL/glext.h>
// c runtime
#include <cmath>
#include <wchar.h>
#include <locale.h>
// c++ stl
#include <map>
// utility
#include "file.h"
#include "vector.h"
#include "endian.h"
#include "lzsload.h"
#include "textMap.h"
using namespace std;
// ---------------------------------------------------------------------------
class XenoModel {
private:
struct TexInfo {
u16 status;
u16 clut;
u8 alpha;
bool abe;
bool operator<(const TexInfo & rhs) const {
if (status != rhs.status) {
return status < rhs.status;
}
if (clut != rhs.clut) {
return clut < rhs.clut;
}
if (alpha != rhs.alpha) {
return alpha < rhs.alpha;
}
return abe < rhs.abe;
}
};
map<TexInfo, int> texInfoMap;
u8 * m_model;
u8 * m_walkmesh;
u8 * m_vram;
u8 * m_archive;
public:
XenoModel(void * archive, void * model, void * walkmesh);
~XenoModel();
void draw();
void drawModel();
void drawWalkmesh();
void uploadTextures(void *data, int size);
void dumpVRAM();
int getTexture(u16 status, u16 clut, bool adb, int alpha);
};
XenoModel::XenoModel(void * archive, void * model, void * walkmesh): m_archive((u8*)archive), m_model((u8*)model) , m_walkmesh((u8*)walkmesh)
{
// generate virtual vram to make texture page calculations easier
m_vram = new u8[2048 * 512];
memset(m_vram, 0xAA, 2048 * 512);
}
XenoModel::~XenoModel()
{
delete m_vram;
}
int
XenoModel::getTexture(u16 status, u16 clut, bool abe, int alpha)
{
alpha = 128;
TexInfo texInfo;
texInfo.status = status;
texInfo.clut = clut;
texInfo.abe = abe;
texInfo.alpha = alpha;
if (texInfoMap.find(texInfo) != texInfoMap.end()) {
return texInfoMap[texInfo];
}
u8 *texture = new u8[256*256*4];
int tx = (status & 0xF) * 64 * 2;
int ty = ((status >> 4) & 1) * 256;
int tp = (status >> 7) & 3;
int px = clut & 63;
int py = clut >> 6;
switch (tp) {
case 0: {
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
u8 val = m_vram[(y + ty) * 2048 + (x/2) + tx];
int idx = (x & 1) ? val >> 4 : val & 0xF;
u16 col = get_u16le(m_vram + (py) * 2048 + idx * 2 + px * 2);
bool stp = (col & 0x8000) != 0;
int r = (((col ) & 31) * 255 + 15) / 31;
int g = (((col >> 5) & 31) * 255 + 15) / 31;
int b = (((col >> 10) & 31) * 255 + 15) / 31;
int a = /*(idx == 0) || */(col == 0) ? 0 : (abe && stp && ((col & 0x7FFF) != 0)) ? alpha : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
break;
}
case 1: {
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
int idx = m_vram[(y + ty) * 2048 + x + tx];
u16 col = get_u16le(m_vram + (py) * 2048 + idx * 2 + px * 2);
bool stp = (col & 0x8000) != 0;
int r = (((col ) & 31) * 255 + 15) / 31;
int g = (((col >> 5) & 31) * 255 + 15) / 31;
int b = (((col >> 10) & 31) * 255 + 15) / 31;
int a = /* (idx == 0) ||*/ (col == 0) ? 0 : (abe && stp && ((col & 0x7FFF) != 0)) ? alpha : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
break;
}
case 2: {
for (int y=0; y<256; y++) {
for (int x=0; x<256; x++) {
u16 col = get_u16le(m_vram + (y + ty) * 2048 + x * 2 + tx);
bool stp = (col & 0x8000) != 0;
int r = (((col ) & 31) * 255 + 15) / 31;
int g = (((col >> 5) & 31) * 255 + 15) / 31;
int b = (((col >> 10) & 31) * 255 + 15) / 31;
int a = (col == 0) ? 0 : (abe && stp && ((col & 0x7FFF) != 0)) ? alpha : 255;
int addr = (y*256+x) * 4;
texture[addr+0] = r;
texture[addr+1] = g;
texture[addr+2] = b;
texture[addr+3] = a;
}
}
break;
}
}
GLuint textureId;
glGenTextures(1, &textureId);
glBindTexture(GL_TEXTURE_2D, textureId);
#if 0
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
#else
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
#endif
//glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
gluBuild2DMipmaps(GL_TEXTURE_2D, 4, 256, 256, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, texture);
delete texture;
texInfoMap[texInfo] = textureId;
return textureId;
}
void XenoModel::uploadTextures(void *data, int size)
{
// expand TIM into "vram"
int offset = 0;
while (offset < size) {
u8 * texture = ((u8*)data) + offset;
u32 type = get_u16le(texture + 0x00);
u16 pos_x = get_u16le(texture + 0x04);
u16 pos_y = get_u16le(texture + 0x06);
u16 move_x = get_u16le(texture + 0x08);
u16 move_y = get_u16le(texture + 0x0a);
u16 width = get_u16le(texture + 0x0c);
u16 height = get_u16le(texture + 0x0e);
u16 chunks = get_u16le(texture + 0x12);
if (width > 2048 || height > 512 || width == 0 || height == 0) {
return;
}
int blockSize = 0x1C + chunks * 2;
offset += (blockSize + 2047) & ~2047;
for (int i=0; i<chunks; i++) {
height = get_u16le(texture + 0x1C + i * 2);
for (int j=0; j < height; j++) {
memcpy(m_vram + (pos_y + move_y + j) * 2048 + (pos_x + move_x) * 2, ((u8*)data) + offset + j * width * 2, width * 2);
}
pos_y += height;
blockSize = width * height * 2;
offset += (blockSize + 2047) & ~2047;
}
}
}
void XenoModel::dumpVRAM()
{
save("vram.bin", m_vram, 2048 * 512);
}
void XenoModel::draw()
{
drawModel();
drawWalkmesh();
}
#if 0
bool first = true;
#endif
void XenoModel::drawWalkmesh()
{
glDisable(GL_CULL_FACE);
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glMatrixMode(GL_MODELVIEW);
// glLoadIdentity();
//glDisable(GL_DEPTH_TEST);
u32 block_count = get_u32le(m_walkmesh + 0);
//printf("drawWalkmesh, block_count = %i\n", block_count);
for (int i = 0; i < block_count; ++i) {
u32 block_size = get_u32le(m_walkmesh + 0x04 + i * 0x04);
u32 block_start = get_u32le(m_walkmesh + 0x18 + i * 0x08);
u32 block_vertex = get_u32le(m_walkmesh + 0x1c + i * 0x08);
// Walkmesh
glBegin(GL_TRIANGLES);
glColor3f((i + 1) & 1 ? 1 : 0, (i + 1) & 2 ? 1 : 0, (i + 1) & 4 ? 1 : 0);
for (int j = 0; j < block_size; j += 0x0E) {
for (int k=0; k<3; k++) {
u16 offset = block_vertex + get_u16le(m_walkmesh + block_start + j + k * 2) * 0x08;
int x = (s16)get_u16le(m_walkmesh + 0x00 + offset);
int y = (s16)get_u16le(m_walkmesh + 0x02 + offset);
int z = (s16)get_u16le(m_walkmesh + 0x04 + offset);
glVertex3i(x, y, z);
#if 0
if (first) {
printf("%i: %x\n", j, get_u16le(m_walkmesh + 0x06 + offset));
}
#endif
}
}
glEnd();
// Connectivity
glBegin(GL_LINES);
glColor3f((i + 1) & 1 ? 0 : 1, (i + 1) & 2 ? 0 : 1, (i + 1) & 4 ? 0 : 1);
for (int j = 0; j < (block_size / 0x0E); j ++) {
#if 0
if (first) {
printf("%i: %x\n", j, get_u16le(m_walkmesh + block_start + 0x0C));
}
#endif
int sx = 0;
int sy = 0;
int sz = 0;
for (int k=0; k<3; k++) {
u16 offset = block_vertex + get_u16le(m_walkmesh + block_start + j * 0x0E + k * 2) * 0x08;
sx += (s16)get_u16le(m_walkmesh + 0x00 + offset);
sy += (s16)get_u16le(m_walkmesh + 0x02 + offset);
sz += (s16)get_u16le(m_walkmesh + 0x04 + offset);
}
sx /= 3;
sy /= 3;
sz /= 3;
for (int l=0; l<3; l++) {
int to = get_u16le(m_walkmesh + block_start + j * 0x0E + 0x06 + l * 2);
if (to != 0xFFFF) {
int tx = 0;
int ty = 0;
int tz = 0;
for (int k=0; k<3; k++) {
u16 offset = block_vertex + get_u16le(m_walkmesh + block_start + to * 0x0E + k * 2) * 0x08;
tx += (s16)get_u16le(m_walkmesh + 0x00 + offset);
ty += (s16)get_u16le(m_walkmesh + 0x02 + offset);
tz += (s16)get_u16le(m_walkmesh + 0x04 + offset);
}
tx /= 3;
ty /= 3;
tz /= 3;
glVertex3i(sx, sy, sz);
glVertex3i(tx, ty, tz);
}
}
}
glEnd();
}
//glEnable(GL_DEPTH_TEST);
//exit(0);
#if 0
first = false;
#endif
}
void XenoModel::drawModel()
{
glDisable(GL_CULL_FACE);
glPolygonMode(GL_FRONT, GL_FILL);
glPolygonMode(GL_BACK, GL_LINE);
#if 0
printf("Unknown:\n");
u16 unknown1 = get_u16le(((u8*)archiveData) + 0x0184);
u16 unknown2 = get_u16le(((u8*)archiveData) + 0x0186);
u32 unknown3 = get_u32le(((u8*)archiveData) + 0x0188);
u32 count = get_u32le(((u8*)archiveData) + 0x018C);
printf("??:%i ??:%i ??:%i count:%i\n", unknown1, unknown2, unknown3, count);
for (u32 i=0; i<count; i++) {
void * base = ((u8*)archiveData) + 0x0190 + i * 16;
for(u32 j=0; j<8; j++) {
int val = (s16)get_u16le(((u8*)base) + j * 2);
printf("%6i ", val);
}
printf("\n");
}
#endif
u32 item_count = get_u32le(((u8*)m_archive) + 0x018C);
for(u32 i=0; i<item_count; i++) {
void * item_base = ((u8*)m_archive) + 0x0190 + i * 16;
u32 model_count = get_u32le(m_model);
//printf("drawModel, model_count = %i\n", model_count);
//for (int m=0; m<model_count; m++) {
int item_flags = get_u16le(((u8*)item_base) + 0 * 2);
int item_rotx = get_u16le(((u8*)item_base) + 1 * 2);
int item_roty = get_u16le(((u8*)item_base) + 2 * 2);
int item_rotz = get_u16le(((u8*)item_base) + 3 * 2);
int item_posx = (s16)get_u16le(((u8*)item_base) + 4 * 2);
int item_posy = (s16)get_u16le(((u8*)item_base) + 5 * 2);
int item_posz = (s16)get_u16le(((u8*)item_base) + 6 * 2);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(item_posx, item_posy, item_posz);
glRotatef(item_rotx * 90.0 / 1024.0, 1, 0, 0);
glRotatef(item_roty * 90.0 / 1024.0, 0, 1, 0);
glRotatef(item_rotz * 90.0 / 1024.0, 0, 0, 1);
if(item_flags != 480) {
int m = get_u16le(((u8*)item_base) + 7 * 2);
u32 offset_model = get_u32le(m_model + 4 + m * 4);
u8 * model = m_model + offset_model;
u32 number_blocks = get_u32le(model);
//printf("drawModel, number_blocks = %i\n", number_blocks);
for (int i=0; i<number_blocks; i++) {
u8 * block = model + 16 + i * 0x38;
u16 number_vertices = get_u16le(block + 2);
u16 number_mesh = get_u16le(block + 4);
u16 number_mesh_block = get_u16le(block + 6);
u32 offset_vertices = get_u32le(block + 8);
u32 offset_normals = get_u32le(block + 12);
u32 offset_mesh_blocks = get_u32le(block + 16);
u32 offset_displaylist = get_u32le(block + 20);
#if 0
if (first) {
printf("%i/%i\n", m, i);
for (int i=0; i<16; i++) {
s16 val = get_u16le(block + 24 + i*2);
printf("%5i ", val);
}
printf("\n");
}
#endif
u8 * vertices = model + offset_vertices;
u8 * mesh_blocks = model + offset_mesh_blocks;
u8 * normals = model + offset_normals;
u8 * displaylist = model + offset_displaylist;
int poly_available = 0;
glColor3f(1,1,1);
bool quad_block = false;
u16 status = 0;
u16 clut = 0;
while (poly_available > 0 || number_mesh_block > 0) {
// init the mesh block
if (poly_available == 0) {
quad_block = (mesh_blocks[0] & 16) != 0;
poly_available = get_u16le(mesh_blocks + 2);
mesh_blocks+=4;
number_mesh_block--;
}
// decode command
u32 cmd = get_u32le(displaylist);
bool hp = ((cmd >> 24) & 16) != 0; // geraud shading
bool quad = ((cmd >> 24) & 8) != 0; // quad or tri
bool tme = ((cmd >> 24) & 4) != 0; // texture mapping
bool abe = ((cmd >> 24) & 2) != 0; // semi transparency
bool fullbright = ((cmd >> 24) & 1) != 0; // bypass lighting
int srcAlpha = 255;
int dstAlpha = 255;
if (abe) {
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
switch ((status >> 3) & 7) {
case 0:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
break;
case 1:
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
break;
case 2:
glBlendFunc(GL_ONE, GL_ONE);
glBlendEquation(GL_FUNC_SUBTRACT);
break;
case 3:
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
glBlendEquation(GL_FUNC_ADD);
break;
}
} else {
glDisable(GL_BLEND);
}
// printf("%8.8x\n", cmd);
displaylist += 4;
switch ((cmd >> 24) & ~(16|2|1)) { // (careful not to mask 0xc4 or 0xc8!)
case 0xC4: // texture page
status = cmd & 0xFFFF;
switch ((status >> 3) & 7) {
case 0:
srcAlpha = dstAlpha = 128;
break;
case 1:
srcAlpha = dstAlpha = 255;
break;
case 2:
srcAlpha = dstAlpha = 255;
break;
case 3:
srcAlpha = 64;
dstAlpha = 255;
break;
}
break;
case 0xC8: // clut
clut = cmd & 0xFFFF;
break;
case 0x24: { // triangle with texture
Vector::CVector2f uv[3];
uv[0][0] = displaylist[0];
uv[0][1] = displaylist[1];
uv[1][0] = displaylist[2];
uv[1][1] = displaylist[3];
uv[2][0] = cmd & 255;
uv[2][1] = (cmd >> 8) & 255;
displaylist += 4;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, getTexture(status, clut, abe, srcAlpha));
glBegin(GL_TRIANGLES);
for (int j=0; j<3; j++) {
int vtx = get_u16le(mesh_blocks + j * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
if (hp) {
float nx = (s16)get_u16le(normals + vtx * 8 + 0) / 4096.0f;
float ny = (s16)get_u16le(normals + vtx * 8 + 2) / 4096.0f;
float nz = (s16)get_u16le(normals + vtx * 8 + 4) / 4096.0f;
glNormal3f(nx, ny, nz);
} else {
// no normal
glNormal3f(0.0f, 0.0f, 0.0f);
}
glTexCoord2i(uv[j][0],uv[j][1]);
glVertex3i(x, y, z);
}
glEnd();
glDisable(GL_TEXTURE_2D);
mesh_blocks += 8;
poly_available--;
break;
}
case 0x2c: { // quad with texture
Vector::CVector2f uv[4];
uv[0][0] = displaylist[0];
uv[0][1] = displaylist[1];
uv[1][0] = displaylist[2];
uv[1][1] = displaylist[3];
uv[2][0] = displaylist[4];
uv[2][1] = displaylist[5];
uv[3][0] = displaylist[6];
uv[3][1] = displaylist[7];
displaylist += 8;
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, getTexture(status, clut, abe, srcAlpha));
glBegin(GL_QUADS);
const int quad_idx[] = { 0, 1, 3, 2 };
for (int j=0; j<4; j++) {
int vtx = get_u16le(mesh_blocks + quad_idx[j] * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
glTexCoord2i(uv[quad_idx[j]][0],uv[quad_idx[j]][1]);
glVertex3i(x, y, z);
}
glEnd();
glDisable(GL_TEXTURE_2D);
mesh_blocks += 8;
poly_available--;
break;
}
case 0x20: { // monochrome triangle
glBegin(GL_TRIANGLES);
glColor4ub((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, srcAlpha);
for (int j=0; j<3; j++) {
int vtx = get_u16le(mesh_blocks + j * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
glVertex3i(x, y, z);
}
glEnd();
mesh_blocks += 8;
poly_available--;
break;
}
case 0x28: { // monochrome quad
glBegin(GL_QUADS);
glColor4ub((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, srcAlpha);
const int quad_idx[] = { 0, 1, 3, 2 };
for (int j=0; j<4; j++) {
int vtx = get_u16le(mesh_blocks + quad_idx[j] * 2);
int x = (s16)get_u16le(vertices + vtx * 8 + 0);
int y = (s16)get_u16le(vertices + vtx * 8 + 2);
int z = (s16)get_u16le(vertices + vtx * 8 + 4);
glVertex3i(x, y, z);
}
glEnd();
mesh_blocks += 8;
poly_available--;
break;
}
default:
printf("unknown cmd: %8.8x\n", cmd);
break;
}
number_mesh--;
}
}
glPopMatrix();
//}
}
}
// first = false;
}
// ---------------------------------------------------------------------------
XenoModel * xenoModel;
int rot_x, rot_y;
Vector::CVector3f position;
void ReshapeWindowFunc(int width, int height)
{
const double kFOVy = 0.57735f;
const double kZNear = 0.1f;
const double kZFar = 1000.0f;
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLdouble aspect = ((GLfloat)width / (GLfloat)height) * (kZNear * kFOVy);
glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, kZFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void MainRenderLoop(void)
{
// render
glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 1.0f/255.0f);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
glDisable(GL_LIGHTING);
//glDisable(GL_BLEND);
//glEnable(GL_TEXTURE_2D);
//glEnable(GL_TEXTURE_RECTANGLE_EXT);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glRotatef((float)rot_x * 360.0f / 256.0f, 1.0f, 0.0f, 0.0f);
glRotatef((float)rot_y * 360.0f / 256.0f, 0.0f, 1.0f, 0.0f);
glTranslatef(position[0], position[1], position[2]);
glScalef(1.0f/256.0f, -1.0f/256.0f, 1.0f/256.0f);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glScalef(1.0f/256.0f, 1.0f/256.0f, 0);
glTranslatef(0.5f, 0.5f, 0);
xenoModel->draw();
glFlush();
}
void SpecialHandler(int key, int x, int y)
{
switch (key) {
case GLUT_KEY_UP:
rot_x -= 4;
if (rot_x < -64) {
rot_x = -64;
}
break;
case GLUT_KEY_DOWN:
rot_x += 4;
if (rot_x > 64) {
rot_x = 64;
}
break;
case GLUT_KEY_LEFT:
rot_y -= 5;
break;
case GLUT_KEY_RIGHT:
rot_y += 5;
break;
}
glutPostRedisplay();
}
void KeyboardHandler(unsigned char key, int x, int y)
{
switch (key) {
case 'w': {
float sx = sin((float)rot_x * M_PI * 2.0f / 256.0f);
float cx = -cos((float)rot_x * M_PI * 2.0f / 256.0f);
float sy = sin((float)rot_y * M_PI * 2.0f / 256.0f);
float cy = -cos((float)rot_y * M_PI * 2.0f / 256.0f);
position[0] += sy * cx;
position[1] += sx;
position[2] += cy * cx;
break;
}
case 'x': {
float sx = sin((float)rot_x * M_PI * 2.0f / 256.0f);
float cx = -cos((float)rot_x * M_PI * 2.0f / 256.0f);
float sy = sin((float)rot_y * M_PI * 2.0f / 256.0f);
float cy = -cos((float)rot_y * M_PI * 2.0f / 256.0f);
position[0] -= sy * cx;
position[1] -= sx;
position[2] -= cy * cx;
break;
}
case 27:
exit(0);
break;
}
}
u32 getRawData(void * archiveData, int archiveSize, void ** data, int index)
{
u32 offset = get_u32le(((u8*)archiveData) + 0x0130 + index * 4);
u32 compLen = (index < 8 ? get_u32le(((u8*)archiveData) + 0x0134 + index * 4) : archiveSize) - offset;
*data = ((u8*)archiveData) + offset;
return compLen;
}
u32 getData(void * archiveData, int archiveSize, void ** data, int index)
{
u32 offset = get_u32le(((u8*)archiveData) + 0x0130 + index * 4);
u32 compLen = (index < 8 ? get_u32le(((u8*)archiveData) + 0x0134 + index * 4) : archiveSize) - offset;
u32 uncompLen = get_u32le(((u8*)archiveData) + 0x010c + index * 4);
u32 uncompLenHeader = get_u32le(((u8*)archiveData) + offset);
// printf("h:%4.4x b:%4.4x\n", uncompLen, uncompLenHeader);
*data = malloc(uncompLenHeader);
*data = decompress_lzs(((u8*)archiveData) + offset + 4, compLen, *data, uncompLenHeader);
return uncompLen;
}
void dumpText(void * textData, unsigned int textSize)
{
u32 textCount = get_u32le(((u8*)textData)) + 1;
for (int i=0; i<textCount; i++) {
u32 textOffset = get_u16le(((u8*)textData) + i * 2 + 4);
if (textOffset > 0) {
if (textOffset > textSize) {
printf("--- overflow ---\n");
return;
}
u8 textPosX = ((u8*)textData)[4 + textCount * 2 + i * 2];
u8 textPosY = ((u8*)textData)[4 + textCount * 2 + i * 2 + 1];
printf("%4.4x %i/%i\n", textOffset, textPosX, textPosY);
u8 * text = ((u8*)textData) + textOffset;
while (*text != 0) {
switch (*text) {
case 0x01: printf("\n"); break;
case 0x02: printf("\n[new dialog]\n"); break;
case 0x03: printf("[input]"); break;
case 0x0F: {
text++;
u8 opcode = *(text++);
u8 value = *text;
switch (opcode) {
case 0x00: printf("[delay %i]", value); break;
case 0x01: printf("[accelerator %i]", value); break;
case 0x02: printf("[delayed close %i]", value); break;
case 0x04: printf("[item %i]", value); break;
case 0x05: {
switch (value) {
case 7:
printf("[Chu Chu]");
break;
default:
printf("[char name %i]", value);
break;
}
break;
}
default: printf("[unknown %2.2x %i]", opcode, value); break;
}
break;
}
default:
if (textMap[*text] != 0) {
wprintf(L"%lc", textMap[*text]);
} else {
printf("[%2.2x]", *text);
}
break;
}
text++;
}
printf("\n----\n");
}
}
}
struct OpCode {
u8 code;
const char * name;
const char * args;
};
const OpCode opCodeList[] = {
{ 0x00, "Return", "" },
{ 0x01, "Jump", "w" },
{ 0x02, "ConditionalJumpTo", "bbwbw" },
{ 0x0b, "SpriteSet", "bb" },
{ 0x19, "SpriteSetPosition", "wwb" },
{ 0x20, "SpriteSetSolid", "bb" },
{ 0x26, "Wait", "bb" },
{ 0x2C, "SpritePlayAnimation", "b" },
{ 0x36, "VariableSetTrue", "bb" },
{ 0x37, "VariableSetFalse", "bb" },
{ 0x4A, "SpriteGoToPosition", "wwb" },
{ 0x69, "SpriteSetDirection", "bb" },
{ 0x6b, "SpriteRotateClockwise", "bb" },
{ 0x6C, "SpriteRotateAntiClockwise", "bb" },
{ 0x6F, "SpriteRotateToEntity", "b" },
{ 0x98, "MapLoad", "wbb" },
{ 0xA8, "VariableRandom", "bbbb" },
{ 0xB3, "FadeOut", "bb" },
{ 0xB4, "FadeIn", "bb" },
{ 0xB5, "CameraSetDirection", "bb" },
{ 0xC7, "CameraRotate", "bb" },
{ 0xC8, "CameraRotate2", "bb" },
{ 0xD2, "DialogShow", "bw" },
{ 0x1e, "Unknown", "b" },
{ 0x28, "Unknown", "b" },
{ 0xC4, "Unknown", "b" },
{ 0xC5, "Unknown", "b" },
{ 0x3c, "Unknown", "b" },
{ 0x5d, "Unknown", "bb" },
{ 0xf4, "Unknown", "bb" },
{ 0xfe, "Unknown", "bbb" },
{ 0xA4, "Unknown", "bbbb" },
{ 0x35, "Unknown", "w" }
};
const unsigned opCodeCount = sizeof(opCodeList) / sizeof(opCodeList[0]);
void dumpScript(void * scriptData, unsigned int scriptSize)
{
u16 entityCount = get_u32le((u8*)scriptData + 0x80);
u8 * code = (u8*)scriptData + entityCount * 0x40 + 0x84;
int codeLen = scriptSize - 0x84 - entityCount * 0x40;
int codePtr = 0;
while (codePtr < codeLen) {
for (int i=0; i<entityCount; i++) {
u8 * entityStart = (u8*)scriptData + i * 0x40 + 0x84;
for (int j=0; j<32; j++) {
u16 addr = get_u16le(entityStart + j * 2);
if (addr != 0 && addr == codePtr) {
printf("e%is%i:\n", i, j);
}
}
}
printf("[%4.4x] ", codePtr);
u8 cmd = code[codePtr++];
printf("%2.2x", cmd);
int opIndex = -1;
for(unsigned int i = 0; i < opCodeCount && opIndex < 0; i++) {
if( opCodeList[i].code == cmd ) {
opIndex = i;
}
}
if(opIndex < 0) {
printf(" Unknown\n");
} else {
int cmdSize = 0;
const char * arg = opCodeList[opIndex].args;
while(*arg) {
switch(*arg) {
case 'b': cmdSize += 1; break;
case 'w': cmdSize += 2; break;
}
arg++;
}
for (int i = 0; i<cmdSize; i++) {
printf(" %2.2x", code[codePtr+i]);
}
for (int i = cmdSize; i<10; i++) {
printf(" ");
}
printf("%s(", opCodeList[opIndex].name);
int argPos = 0;
arg = opCodeList[opIndex].args;
while(*arg) {
if(argPos > 0) {
printf(", ");
}
switch(*arg) {
case 'b':
printf("0x%2.2x", code[codePtr+argPos]);
argPos += 1;
break;
case 'w':
printf("0x%4.4x", get_u16le(code + codePtr + argPos));
argPos += 2;
break;
}
arg++;
}
printf(")\n");
codePtr += cmdSize;
}
}
}
const char * section[] =
{
"unknown",
"walkmesh",
"models",
"unknown",
"unknown",
"scripts",
"unknown",
"text",
"unknown"
};
void dumpDisplaylist(void * archiveData)
{
u16 unknown1 = get_u16le(((u8*)archiveData) + 0x0184);
u16 unknown2 = get_u16le(((u8*)archiveData) + 0x0186);
u32 unknown3 = get_u32le(((u8*)archiveData) + 0x0188);
u32 count = get_u32le(((u8*)archiveData) + 0x018C);
printf("??:%i ??:%i ??:%i count:%i\n", unknown1, unknown2, unknown3, count);
for (u32 i=0; i<count; i++) {
void * base = ((u8*)archiveData) + 0x0190 + i * 16;
for(u32 j=0; j<8; j++) {
int val = (s16)get_u16le(((u8*)base) + j * 2);
printf("%6i ", val);
}
printf("\n");
}
}
int main(int argc, char ** argv)
{
if (argc != 2) {
printf("view_level <index>\n");
} else {
#if 1
// init opengl and glut
glutInit(&argc, (char **) argv);
glutInitWindowSize(640, 480);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA | GLUT_DEPTH);
glutCreateWindow(argv[0]);
glutDisplayFunc(MainRenderLoop);
glutIdleFunc(MainRenderLoop);
glutReshapeFunc(ReshapeWindowFunc);
glutKeyboardFunc(KeyboardHandler);
glutSpecialFunc(SpecialHandler);
#endif
setlocale(LC_CTYPE, "en_US");
#if 0
for (int i=0; i<255; i++) {
wprintf(L"%lc", textCypher[i]);
if ((i & 15) == 15) { printf("\n"); }
}
#endif
// access files by index
//for (int i=0; i<730; i++) {
// printf("Room:%i\n", i);
int index = atoi(argv[1]);
int disk = 1;
char path[256];
// 730 rooms
// load data
void * archiveData = NULL;
sprintf(path, "disk%i/dir%i/file%i.bin", disk, 11, index * 2);
int archiveSize = load(path, &archiveData);
if (strncmp((char*)archiveData, "It's", 4) == 0) {
printf("%s\n", (char*)archiveData);
return 0;
}
void * textureData = NULL;
sprintf(path, "disk%i/dir%i/file%i.bin", disk, 11, index * 2 + 1);
int textureSize = load(path, &textureData);
// dumpDisplaylist(archiveData);
void * walkmeshData = NULL;
int walkmeshSize = getData(archiveData, archiveSize, &walkmeshData, 1);
void * modelData = NULL;
int modelSize = getData(archiveData, archiveSize, &modelData, 2);
void * scriptData = NULL;
int scriptSize = getData(archiveData, archiveSize, &scriptData, 5);
void * textData = NULL;
int textSize = getData(archiveData, archiveSize, &textData, 7);
#if 0
for (int i=0; i<9; i++) {
void * sectionData = NULL;
int sectionSize = getData(archiveData, archiveSize, §ionData, i);
char name [256];
sprintf(name, "file%is%i-%s.bin", index, i, section[i]);
save(name, sectionData, sectionSize);
}
#endif
//dumpText(textData, textSize);
//dumpScript(scriptData, scriptSize);
// init camera
position[0] = 0;
position[1] = -1;
position[2] = 0;
rot_y = rot_x = 0;
#if 1
// setup viewing code
xenoModel = new XenoModel(archiveData, modelData, walkmeshData);
xenoModel->uploadTextures(textureData, textureSize);
//xenoModel->dumpVRAM();
// main (this will never return)
glutMainLoop();
#endif
//}
}
return 0;
}
I was asked for the code, so here it is... very hacky...Mostly based on Akari's information.
-
It's always appreciated when people share Micky LOL
I haven't really done anything with FF7/8/9 in 2 years scarey huh?
However I may have some time to tinker.
I have WAY too many games... SIGH. :D
Cyb
-
To avoid thread necromancy (http://forums.qhimm.com/index.php?topic=8334.msg100658):
import sys, struct, array, math, os
from OpenGL.GL import *
from OpenGL.GLU import *
import pygame
from pygame.locals import *
def decompressLzs(data, size):
"""Decompress Xenogears-style compressed data. The data must exclude the initial size word"""
ibuf = array.array("B", data)
obuf = array.array("B")
iofs = 0
cmd = 0
bit = 0
while iofs < len(ibuf) and len(obuf) < size:
if bit == 0:
cmd = ibuf[iofs]
bit = 8
iofs += 1
if cmd & 1:
a = ibuf[iofs]
iofs += 1
b = ibuf[iofs]
iofs += 1
o = a | ((b & 0x0F) << 8)
l = ((b & 0xF0) >> 4) + 3
rofs = len(obuf) - o
for j in xrange(l):
if rofs < 0:
obuf.append(0)
else:
obuf.append(obuf[rofs])
rofs += 1
else:
obuf.append(ibuf[iofs])
iofs += 1
cmd >>= 1
bit -= 1
return obuf.tostring()
def loadLzs(name):
"""Read a compressed file from disc"""
f = open(name, "rb")
buf = f.read()
f.close()
(size,) = struct.unpack_from("<I", buf, 0)
return decompressLzs(buf[4:], size)
def getData(archiveData, index):
"""Get the compressed data block out of the field map archive"""
(offset,) = struct.unpack_from("<I", archiveData, 0x0130 + index * 4)
dataEnd = len(archiveData)
if index < 8:
(dataEnd,) = struct.unpack_from("<I", archiveData, 0x0134 + index * 4)
(size,) = struct.unpack_from("<I", archiveData, 0x010c + index * 4)
dataStart = offset + 4
return decompressLzs(archiveData[dataStart:dataEnd], size)
class ShaderBuilder:
def __init__(self):
self.shaders = {}
def getShader(self, data):
try:
return self.shaders[data]
except KeyError:
i = len(self.shaders)
self.shaders[data] = i
return i
def getList(self):
items = [(val, key) for (key, val) in self.shaders.items()]
items.sort()
return [x for _,x in items]
abe_alpha = [128, 0, 0, 64]
def loadModel(data):
shaderBuilder = ShaderBuilder()
object = {}
object["parts"] = []
(partCount,) = struct.unpack_from("<I", data, 0)
for partIndex in xrange(partCount):
(partOffset,) = struct.unpack_from("<I", data, 4 + partIndex * 4)
part = {}
part["blocks"] = []
(blockCount,) = struct.unpack_from("<I", data, partOffset)
for blockIndex in xrange(blockCount):
blockOffset = partOffset + 16 + blockIndex * 0x38
(vertexCount, meshCount, meshBlockCount, vertexOffset, normalOffset, meshBlockOffset, displayListOffset) = struct.unpack_from("<xxHHHIIII", data, blockOffset)
tri_tex = []
quad_tex = []
tri_mono = []
quad_mono = []
status = 0
clut = 0
for meshBlockIndex in xrange(meshBlockCount):
# init the mesh block
quad_block, polyCount = struct.unpack_from("<BxH", data, partOffset + meshBlockOffset)
meshBlockOffset += 4
while polyCount > 0:
# decode command
(cmd,) = struct.unpack_from("<I", data, partOffset + displayListOffset)
hp = ((cmd >> 24) & 16) != 0 # geraud shading
quad = ((cmd >> 24) & 8) != 0 # quad or tri
tme = ((cmd >> 24) & 4) != 0 # texture mapping
abe = ((cmd >> 24) & 2) != 0 # semi transparency
fullbright = ((cmd >> 24) & 1) != 0 # bypass lighting
op = (cmd >> 24) & 255 # operator
pop = op & ~(16|2|1) # operator, with shading and lighting mask
displayListOffset += 4
if op == 0xC4: # texture page
status = cmd & 0xFFFF
elif op == 0xC8: # clut
clut = cmd & 0xFFFF
elif pop == 0x24: # triangle with texture
(ua, va, ub, vb) = struct.unpack_from("<BBBB", data, partOffset + displayListOffset)
uc = cmd & 255
vc = (cmd >> 8) & 255
displayListOffset += 4
shader = shaderBuilder.getShader((status, clut, abe, True))
vertex = []
for j in xrange(3):
(vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)
(x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)
if hp:
(nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)
else:
(nx, ny, nz) = (0, 0, 0)
vertex.append(((x, y, z), (nx, ny, nz)))
tri_tex.append((shader, ((vertex[0][0], vertex[0][1], (ua, va)), (vertex[2][0], vertex[2][1], (uc, vc)), (vertex[1][0], vertex[1][1], (ub, vb)))))
meshBlockOffset += 8
polyCount -= 1
elif pop == 0x2C: # quad with texture
(ua, va, ub, vb, uc, vc, ud, vd) = struct.unpack_from("<BBBBBBBB", data, partOffset + displayListOffset)
displayListOffset += 8
shader = shaderBuilder.getShader((status, clut, abe, True))
vertex = []
for j in xrange(4):
(vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)
(x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)
if hp:
(nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)
else:
(nx, ny, nz) = (0, 0, 0)
vertex.append(((x, y, z), (nx, ny, nz)))
quad_tex.append((shader, ((vertex[1][0], vertex[1][1], (ub, vb)), (vertex[0][0], vertex[0][1], (ua, va)), (vertex[2][0], vertex[2][1], (uc, vc)), (vertex[3][0], vertex[3][1], (ud, vd)))))
meshBlockOffset += 8
polyCount -= 1
elif pop == 0x20: # monochrome triangle
if abe:
abr = (status >> 5) & 3
alpha = abe_alpha[abr]
else:
alpha = 255
col = ((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, alpha)
shader = shaderBuilder.getShader((status, clut, abe, False))
vertex = []
for j in xrange(3):
(vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)
(x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)
if hp:
(nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)
else:
(nx, ny, nz) = (0, 0, 0)
vertex.append(((x, y, z), (nx, ny, nz)))
tri_mono.append((shader, ((vertex[0][0], vertex[0][1], col), (vertex[2][0], vertex[2][1], col), (vertex[1][0], vertex[1][1], col))))
meshBlockOffset += 8
polyCount -= 1
elif pop == 0x28: # monochrome quad
if abe:
abr = (status >> 5) & 3
alpha = abe_alpha[abr]
else:
alpha = 255
col = ((cmd >> 16) & 255, (cmd >> 8) & 255, (cmd) & 255, alpha)
shader = shaderBuilder.getShader((status, clut, abe, False))
vertex = []
for j in xrange(4):
(vtx,) = struct.unpack_from("<H", data, partOffset + meshBlockOffset + j * 2)
(x, y, z) = struct.unpack_from("<hhh", data, partOffset + vertexOffset + vtx * 8)
if hp:
(nx, ny, nz) = struct.unpack_from("<hhh", data, partOffset + normalOffset + vtx * 8)
else:
(nx, ny, nz) = (0, 0, 0)
vertex.append(((x, y, z), (nx, ny, nz)))
quad_mono.append((shader, ((vertex[1][0], vertex[1][1], col), (vertex[0][0], vertex[0][1], col), (vertex[2][0], vertex[2][1], col), (vertex[3][0], vertex[3][1], col))))
meshBlockOffset += 8
polyCount -= 1
else:
print("unknown cmd: %8.8x\n" % cmd)
block = {}
block["tri_tex"] = tri_tex
block["quad_tex"] = quad_tex
block["tri_mono"] = tri_mono
block["quad_mono"] = quad_mono
part["blocks"].append(block)
object["parts"].append(part)
object["shaders"] = shaderBuilder.getList()
return object
def getColour(col, abe, alpha):
stp = (col & 0x8000) != 0
r = (((col ) & 31) * 255 + 15) / 31
g = (((col >> 5) & 31) * 255 + 15) / 31
b = (((col >> 10) & 31) * 255 + 15) / 31
if (col & 0x7FFF) == 0:
if stp:
a = 255
else:
a = 0
elif stp and abe:
a = alpha
else:
a = 255
return (r<<24)|(g<<16)|(b<<8)|a
def loadTextures(textureData, shaderList):
vram = array.array("B", [0] * (2048 * 1024))
# unpack MIM data into "VRAM"
offset = 0
while offset < len(textureData):
header = offset
(type, pos_x, pos_y, move_x, move_y, width, height, chunks) = struct.unpack_from("<IHHHHHHxxH", textureData, header)
# print (type, pos_x, pos_y, move_x, move_y, width, height, chunks)
blockSize = 0x1C + chunks * 2
offset += (blockSize + 2047) & ~2047
for i in xrange(chunks):
(height,) = struct.unpack_from("<H", textureData, header + 0x1C)
for j in xrange(height):
vramAddr = (pos_y + move_y + j) * 2048 + (pos_x + move_x) * 2
texAddr = offset + j * width * 2
for k in xrange(width * 2):
vram[vramAddr] = ord(textureData[texAddr])
vramAddr += 1
texAddr += 1
pos_y += height
blockSize = width * height * 2
offset += (blockSize + 2047) & ~2047
if False:
f = open("vram.bin", "wb")
vram.tofile(f)
f.close()
# convert textures with their palette
textures = []
for shader in shaderList:
(status, clut, abe, tme) = shader
if tme:
tx = (status & 0xF) * 64 * 2
ty = ((status >> 4) & 1) * 256
abr = (status >> 5) & 3
tp = (status >> 7) & 3
px = (clut & 63) * 16
py = clut >> 6
if abe:
alpha = abe_alpha[abr]
else:
alpha = 0
image = array.array("I")
if tp == 0: # 4-bit
pal = array.array("I")
for idx in xrange(16):
vaddr = py * 2048 + idx * 2 + px * 2
col = vram[vaddr] + vram[vaddr+1] * 256
pal.append(getColour(col, abe, alpha))
for y in xrange(256):
for x in xrange(256):
val = vram[(y + ty) * 2048 + (x/2) + tx]
if x & 1:
idx = val >> 4
else:
idx = val & 0xF
image.append(pal[idx])
del pal
elif tp == 1:
pal = array.array("I")
for idx in xrange(256):
vaddr = py * 2048 + idx * 2 + px * 2
col = vram[vaddr] + vram[vaddr+1] * 256
pal.append(getColour(col, abe, alpha))
for y in xrange(256):
for x in xrange(256):
idx = vram[(y + ty) * 2048 + x + tx];
image.append(pal[idx])
del pal
elif tp == 2:
for y in xrange(256):
for x in xrange(256):
vaddr = (y + ty) * 2048 + x * 2 + tx
col = vram[vaddr] + vram[vaddr+1] * 256
image.append(getColour(col, abe, alpha))
textures.append(image.tostring())
del image
else:
textures.append(None)
del vram
return textures
class OpenGLObject:
def __init__(self, model):
self.model = model
self.drawNormals = False
self.list = None
self.abe = None
self.abr = None
self.texture = None
self.textureList = []
for t in model["textures"]:
if t is not None:
texIndex = glGenTextures(1)
glBindTexture(GL_TEXTURE_2D, texIndex)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, t)
if True:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
else:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE)
self.textureList.append(texIndex)
else:
self.textureList.append(None)
def setBlend(self, status, abe):
if abe:
if self.abe != abe:
self.abe = abe
glEnable(GL_BLEND)
glDisable(GL_ALPHA_TEST)
abr = (status >> 3) & 3
if self.abr != abr:
self.abr = abr
if abr == 0:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
elif abr == 1:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
elif abr == 2:
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_SUBTRACT);
elif abr == 3:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBlendEquation(GL_FUNC_ADD);
else:
if self.abe != abe:
self.abe = abe
glDisable(GL_BLEND)
glEnable(GL_ALPHA_TEST)
glAlphaFunc(GL_GREATER, 0.0)
def setTexture(self, texture):
if self.texture != texture:
self.texture = texture
glBindTexture(GL_TEXTURE_2D, texture)
def drawPart(self, part, trans):
for block in part["blocks"]:
glDisable(GL_TEXTURE_2D);
# glColor4ub(255,255,255,255)
if len(block["tri_mono"]) > 0:
for tri_mono in block["tri_mono"]:
(status, _, abe, tme) = self.model["shaders"][tri_mono[0]]
if (abe and trans) or (not abe and not trans):
self.setBlend(status, abe)
glBegin(GL_TRIANGLES)
for j in xrange(3):
pos, normal, colour = tri_mono[1][j]
glColor4ub(colour[0], colour[1], colour[2], colour[3])
glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
if len(block["quad_mono"]) > 0:
for quad_mono in block["quad_mono"]:
(status, _, abe, tme) = self.model["shaders"][quad_mono[0]]
if (abe and trans) or (not abe and not trans):
self.setBlend(status, abe)
glBegin(GL_QUADS)
for j in xrange(4):
pos, normal, colour = quad_mono[1][j]
glColor4ub(colour[0], colour[1], colour[2], colour[3])
glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
# glColor4ub(255,255,255,255)
if True:
glEnable(GL_TEXTURE_2D)
if len(block["tri_tex"]) > 0:
for tri_tex in block["tri_tex"]:
(status, _, abe, tme) = self.model["shaders"][tri_tex[0]]
if (abe and trans) or (not abe and not trans):
self.setBlend(status, abe)
self.setTexture(self.textureList[tri_tex[0]])
glBegin(GL_TRIANGLES)
for j in xrange(3):
pos, normal, uv = tri_tex[1][j]
glTexCoord2i(uv[0], uv[1])
glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
if len(block["quad_tex"]) > 0:
for quad_tex in block["quad_tex"]:
(status, _, abe, tme) = self.model["shaders"][quad_tex[0]]
if (abe and trans) or (not abe and not trans):
self.setBlend(status, abe)
self.setTexture(self.textureList[quad_tex[0]])
glBegin(GL_QUADS)
for j in xrange(4):
pos, normal, uv = quad_tex[1][j]
glTexCoord2i(uv[0], uv[1])
glNormal3f(normal[0] / 4096.0, normal[1] / 4096.0, normal[2] / 4096.0)
glVertex3i(pos[0], pos[1], pos[2])
glEnd()
def draw(self):
glMatrixMode(GL_TEXTURE)
glLoadIdentity()
glScalef(1.0/256.0, 1.0/256.0, 0)
glTranslatef(0.5, 0.5, 0.0)
glMatrixMode(GL_MODELVIEW)
glRotatef(180, 1, 0, 0)
if self.list is None:
self.list = glGenLists(1)
glNewList(self.list, GL_COMPILE_AND_EXECUTE)
# render opaque objects
glDepthMask(GL_TRUE)
for item in self.model["nodes"]:
(flags, index, pos, rot) = item
if flags != 480:
glPushMatrix()
glTranslatef(pos[0], pos[1], pos[2])
glRotatef(rot[0] * 90.0 / 1024.0, 1, 0, 0)
glRotatef(rot[1] * 90.0 / 1024.0, 0, 1, 0)
glRotatef(rot[2] * 90.0 / 1024.0, 0, 0, 1)
self.drawPart(self.model["parts"][index], False)
glPopMatrix()
# render transparent objects
glDepthMask(GL_FALSE)
for item in self.model["nodes"]:
(flags, index, pos, rot) = item
if flags != 480:
glPushMatrix()
glTranslatef(pos[0], pos[1], pos[2])
glRotatef(rot[0] * 90.0 / 1024.0, 1, 0, 0)
glRotatef(rot[1] * 90.0 / 1024.0, 0, 1, 0)
glRotatef(rot[2] * 90.0 / 1024.0, 0, 0, 1)
self.drawPart(self.model["parts"][index], True)
glPopMatrix()
glDepthMask(GL_TRUE)
glEndList()
else:
glCallList(self.list)
def clearList(self):
glDeleteLists(self.list, 1)
self.list = None
def getNodes(archiveData):
nodes = []
(itemCount,) = struct.unpack_from("<I", archiveData, 0x018C)
for itemIndex in xrange(itemCount):
(flags, rot_x, rot_y, rot_z, pos_x, pos_y, pos_z, index) = struct.unpack_from("<HHHHhhhH", archiveData, 0x0190 + itemIndex * 16)
nodes.append((flags, index, (pos_x, pos_y, pos_z), (rot_x, rot_y, rot_z)))
return nodes
def writeData(f, node):
f.write("<%s" % node[0])
items = node[1].items()
items.sort()
for key, value in items:
f.write(" %s=\"%s\"" % (key, value))
if len(node[2]) > 0:
f.write(">")
x = []
for item in node[2]:
if type(item) == tuple:
if len(x) > 0:
f.write(" ".join(x))
x = []
writeData(f, item)
else:
x.append(str(item))
if len(x) > 0:
f.write(" ".join(x))
x = []
f.write("</%s>" % node[0])
else:
f.write("/>\n")
def flattenBuffer(buffer):
l = [(value,key) for key,value in buffer.items()]
l.sort()
v = []
for x in [list(key) for _,key in l]:
v.extend(x)
return v
def saveModel(name, model):
basename = os.path.splitext(name)[0]
print("saving textures...")
for index,t in enumerate(model["textures"]):
if t is not None:
n = "%s_%i.tga" % (basename, index)
image = pygame.image.fromstring(t, (256, 256), "RGBA", True)
pygame.image.save(image, n)
del image
print "saving model..."
collada = ("COLLADA", {"xmlns":"http://www.collada.org/2005/11/COLLADASchema", "version":"1.4.0"}, [])
library_visual_scenes = ("library_visual_scenes", {}, [])
visual_scene = ("visual_scene", {"id":"scene", "name":"level"}, [])
for i,n in enumerate(model["nodes"]):
(flags, partIndex, pos, rot) = n
nodeName = "node%i_%4.4x_%i" % (i, flags, partIndex)
node = ("node", {"name":nodeName, "id":nodeName}, [])
if pos[0] != 0 or pos[1] != 0 or pos[2] != 0:
translate = ("translate", {}, [pos[0], pos[1], pos[2]])
node[2].append(translate)
if rot[0] != 0:
rotate = ("rotate", {}, [1,0,0,rot[0] * 90.0 / 1024.0])
node[2].append(rotate)
if rot[1] != 0:
rotate = ("rotate", {}, [0,1,0,rot[1] * 90.0 / 1024.0])
node[2].append(rotate)
if rot[2] != 0:
rotate = ("rotate", {}, [0,0,1,rot[2] * 90.0 / 1024.0])
node[2].append(rotate)
if flags != 480:
for blockIndex, block in enumerate(model["parts"][partIndex]["blocks"]):
shaders = set([shader for shader,_ in block["tri_tex"]]+[shader for shader,_ in block["quad_tex"]]+[shader for shader,_ in block["tri_mono"]]+[shader for shader,_ in block["quad_mono"]])
for s in shaders:
meshName = "#mesh%i_%i_%i" % (partIndex, blockIndex,s)
instance_geometry = ("instance_geometry", {"url":meshName}, [])
bind_material = ("bind_material", {}, [])
technique_common = ("technique_common", {}, [])
shader = model["shaders"][s]
instance_material = ("instance_material", {"symbol":"slot%i" % s, "target":"#material%i" % s}, [])
if shader[3]:
bind_vertex_input = ("bind_vertex_input", {"semantic":"UVSET0", "input_semantic":"TEXCOORD", "input_set":0}, [])
instance_material[2].append(bind_vertex_input)
else:
bind_vertex_input = ("bind_vertex_input", {"semantic":"DIFFUSE", "input_semantic":"COLOUR", "input_set":0}, [])
instance_material[2].append(bind_vertex_input)
technique_common[2].append(instance_material)
bind_material[2].append(technique_common)
instance_geometry[2].append(bind_material)
node[2].append(instance_geometry)
visual_scene[2].append(node)
library_visual_scenes[2].append(visual_scene)
collada[2].append(library_visual_scenes)
library_images = ("library_images", {}, [])
for index,t in enumerate(model["textures"]):
if t is not None:
n = "%s_%i.tga" % (basename, index)
image = ("image", {"id":"image%i" % index}, [
("init_from", {}, [n])
])
library_images[2].append(image)
collada[2].append(library_images)
library_geometries = ("library_geometries", {}, [])
for partIndex, part in enumerate(model["parts"]):
for blockIndex, block in enumerate(part["blocks"]):
# create separate mesh for each shader and for textured and untextured geometry
meshlist = {}
for tri_tex in block["tri_tex"]:
(shader, prim) = tri_tex
try:
d = meshlist[shader]
except KeyError:
d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}
meshlist[shader] = d
poly = []
for vtx in prim:
try:
p = d["position"][vtx[0]]
except KeyError:
p = len(d["position"])
d["position"][vtx[0]] = p
try:
n = d["normal"][vtx[1]]
except KeyError:
n = len(d["normal"])
d["normal"][vtx[1]] = n
try:
t = d["uv"][vtx[2]]
except KeyError:
t = len(d["uv"])
d["uv"][vtx[2]] = t
poly.append((p,n,t))
d["poly"].append(poly)
for quad_tex in block["quad_tex"]:
(shader, prim) = quad_tex
try:
d = meshlist[shader]
except KeyError:
d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}
meshlist[shader] = d
poly = []
for vtx in prim:
try:
p = d["position"][vtx[0]]
except KeyError:
p = len(d["position"])
d["position"][vtx[0]] = p
try:
n = d["normal"][vtx[1]]
except KeyError:
n = len(d["normal"])
d["normal"][vtx[1]] = n
try:
t = d["uv"][vtx[2]]
except KeyError:
t = len(d["uv"])
d["uv"][vtx[2]] = t
poly.append((p,n,t))
d["poly"].append(poly)
for tri_mono in block["tri_mono"]:
(shader, prim) = tri_mono
try:
d = meshlist[shader]
except KeyError:
d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}
meshlist[shader] = d
poly = []
for vtx in prim:
try:
p = d["position"][vtx[0]]
except KeyError:
p = len(d["position"])
d["position"][vtx[0]] = p
try:
n = d["normal"][vtx[1]]
except KeyError:
n = len(d["normal"])
d["normal"][vtx[1]] = n
try:
c = d["colour"][vtx[2]]
except KeyError:
c = len(d["colour"])
d["colour"][vtx[2]] = c
poly.append((p,n,c))
d["poly"].append(poly)
for quad_mono in block["quad_mono"]:
(shader, prim) = quad_mono
try:
d = meshlist[shader]
except KeyError:
d = {"poly":[], "position":{}, "normal":{}, "colour":{}, "uv":{}}
meshlist[shader] = d
poly = []
for vtx in prim:
try:
p = d["position"][vtx[0]]
except KeyError:
p = len(d["position"])
d["position"][vtx[0]] = p
try:
n = d["normal"][vtx[1]]
except KeyError:
n = len(d["normal"])
d["normal"][vtx[1]] = n
try:
c = d["colour"][vtx[2]]
except KeyError:
c = len(d["colour"])
d["colour"][vtx[2]] = c
poly.append((p,n,c))
d["poly"].append(poly)
for shader, meshdata in meshlist.items():
meshName = "mesh%i_%i_%i" % (partIndex, blockIndex, shader)
geometry = ("geometry", {"id":meshName}, [])
mesh = ("mesh", {}, [])
position = meshdata["position"]
if len(position) > 0:
source = ("source", {"id":"%s-position" % meshName}, [])
float_array = ("float_array", {"id":"%s-position-array" % meshName, "count":(len(position) * 3)}, [float(x) for x in flattenBuffer(position)])
source[2].append(float_array)
technique_common = ("technique_common", {}, [
("accessor", {"count":len(position), "source":"%s-position-array" % meshName, "stride":3}, [
("param", {"name":"X", "type":"float"}, []),
("param", {"name":"Y", "type":"float"}, []),
("param", {"name":"Z", "type":"float"}, [])
])
])
source[2].append(technique_common)
mesh[2].append(source)
normal = meshdata["normal"]
if len(normal) > 0:
source = ("source", {"id":"%s-normal" % meshName}, [])
float_array = ("float_array", {"id":"%s-normal-array" % meshName, "count":(len(normal) * 3)}, [float(x) / 4096.0 for x in flattenBuffer(normal)])
source[2].append(float_array)
technique_common = ("technique_common", {}, [
("accessor", {"count":len(normal), "source":"%s-normal-array" % meshName, "stride":3}, [
("param", {"name":"X", "type":"float"}, []),
("param", {"name":"Y", "type":"float"}, []),
("param", {"name":"Z", "type":"float"}, [])
])
])
source[2].append(technique_common)
mesh[2].append(source)
uv = meshdata["uv"]
if len(uv) > 0:
source = ("source", {"id":"%s-uv" % meshName}, [])
float_array = ("float_array", {"id":"%s-uv-array" % meshName, "count":(len(uv) * 2)}, [(float(x) + 0.5) / 256.0 for x in flattenBuffer(uv)])
source[2].append(float_array)
technique_common = ("technique_common", {}, [
("accessor", {"count":len(uv), "source":"%s-uv-array" % meshName, "stride":2}, [
("param", {"name":"U", "type":"float"}, []),
("param", {"name":"V", "type":"float"}, [])
])
])
source[2].append(technique_common)
mesh[2].append(source)
colour = meshdata["colour"]
if len(colour) > 0:
source = ("source", {"id":"%s-colour" % meshName}, [])
float_array = ("float_array", {"id":"%s-colour-array" % meshName, "count":(len(colour) * 4)}, [float(x) / 255.0 for x in flattenBuffer(colour)])
source[2].append(float_array)
technique_common = ("technique_common", {}, [
("accessor", {"count":len(colour), "source":"%s-colour-array" % meshName, "stride":4}, [
("param", {"name":"R", "type":"float"}, []),
("param", {"name":"G", "type":"float"}, []),
("param", {"name":"B", "type":"float"}, []),
("param", {"name":"A", "type":"float"}, [])
])
])
source[2].append(technique_common)
mesh[2].append(source)
vertices = ("vertices", {"id":"%s-vertex" % meshName}, [
("input", {"semantic":"POSITION", "source":"#%s-position" % meshName}, [])
])
mesh[2].append(vertices)
primitives = meshdata["poly"]
polylist = ("polylist", {"count":len(primitives), "material":"slot%i" % shader}, [])
input = ("input", {"semantic":"VERTEX", "source":"#%s-vertex" % meshName, "offset":0}, [])
polylist[2].append(input)
input = ("input", {"semantic":"NORMAL", "source":"#%s-normal" % meshName, "offset":1}, [])
polylist[2].append(input)
if len(uv) > 0:
input = ("input", {"semantic":"TEXCOORD", "source":"#%s-uv" % meshName, "offset":2}, [])
polylist[2].append(input)
if len(colour) > 0:
input = ("input", {"semantic":"COLOUR", "source":"#%s-colour" % meshName, "offset":2}, [])
polylist[2].append(input)
vcount = ("vcount", {}, [len(poly) for poly in primitives])
polylist[2].append(vcount)
p = ("p", {}, [])
for poly in primitives:
for vtx in poly:
p[2].extend(list(vtx))
polylist[2].append(p)
mesh[2].append(polylist)
geometry[2].append(mesh)
library_geometries[2].append(geometry)
collada[2].append(library_geometries)
library_materials = ("library_materials", {}, [])
for shaderIndex,shader in enumerate(model["shaders"]):
matName = "material%i" % shaderIndex
effName = "effect%i" % shaderIndex
material = ("material", {"id":matName}, [
("instance_effect", {"url":"#%s" % effName}, [])
])
library_materials[2].append(material)
collada[2].append(library_materials)
library_effects = ("library_effects", {}, [])
for shaderIndex,shader in enumerate(model["shaders"]):
matName = "material%i" % shaderIndex
effName = "effect%i" % shaderIndex
effect = ("effect", {"id":effName}, [])
profile_COMMON = ("profile_COMMON", {}, [])
if shader[3]:
newparam = ("newparam", {"sid":"surface%i" % shaderIndex}, [
("surface", {"type":"2D"}, [
("init_from", {}, ["image%i" % shaderIndex])
])
])
profile_COMMON[2].append(newparam)
newparam = ("newparam", {"sid":"sampler%i" % shaderIndex}, [
("sampler2D", {}, [
("source", {}, ["surface%i" % shaderIndex])
])
])
profile_COMMON[2].append(newparam)
else:
newparam = ("newparam", {"sid":"colour"}, [
("semantic", {}, ["DIFFUSE"]),
("modifier", {}, ["VARYING"])
])
profile_COMMON[2].append(newparam)
technique = ("technique", {"sid":"COMMON"}, [])
lambert = ("lambert", {}, [])
if shader[3]:
# textured
diffuse = ("diffuse", {}, [
("texture", {"texture":"sampler%i" % shaderIndex, "texcoord":"UVSET0"}, [])
])
lambert[2].append(diffuse)
else:
# vertex colour
diffuse = ("diffuse", {}, [
("param", {"ref":"colour"}, [])
])
lambert[2].append(diffuse)
technique[2].append(lambert)
profile_COMMON[2].append(technique)
effect[2].append(profile_COMMON)
library_effects[2].append(effect)
collada[2].append(library_effects)
scene = ("scene", {}, [
("instance_visual_scene", {"url":"#scene"}, [])
])
collada[2].append(scene)
f = open(name, "wt")
writeData(f, collada)
f.close()
print "done..."
def main(*argv):
print "loading archive..."
diskIndex = 1 # there are disk 1 and disk 2
dirIndex = 11 # 0-based index
fileIndex = int(argv[0]) # 0-based index
archivePath = os.path.join("disk%i" % diskIndex, "dir%i" % dirIndex, "file%i.bin" % (fileIndex * 2))
f = open(archivePath, "rb")
archiveData = f.read()
f.close()
if archiveData[:4] == "It's":
# file was removed from disk image
print "This file was removed from the disk image. Most likely it is a room that is not reachable any more."
return 0
modelData = getData(archiveData, 2)
print "loading texture..."
texturePath = os.path.join("disk%i" % diskIndex, "dir%i" % dirIndex, "file%i.bin" % (fileIndex * 2 + 1))
f = open(texturePath, "rb")
textureData = f.read()
f.close()
print "converting meshes..."
model = loadModel(modelData)
print "converting textures..."
model["textures"] = loadTextures(textureData, model["shaders"])
print "getting nodes..."
model["nodes"] = getNodes(archiveData)
print "starting OpenGL..."
pygame.init()
screen = pygame.display.set_mode((640, 480), HWSURFACE|DOUBLEBUF|OPENGL)
glViewport(0, 0, 640, 480)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
kFOVy = 0.57735
kZNear = 10.0
kZFar = 10000.0
aspect = (640.0 / 480.0) * kZNear * kFOVy
glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, kZFar)
glEnable(GL_DEPTH_TEST)
glDepthFunc(GL_LEQUAL)
glDisable(GL_CULL_FACE)
glDisable(GL_LIGHTING)
glDisable(GL_BLEND)
glPolygonMode(GL_FRONT, GL_FILL)
glPolygonMode(GL_BACK, GL_LINE)
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
wireframe = False
clock = pygame.time.Clock()
key_up = key_down = key_left = key_right = key_front = key_back = False
rot_x = 0.0
rot_y = 0.0
pos_x = 0.0
pos_y = 0
pos_z = -2000
object = OpenGLObject(model)
while True:
for event in pygame.event.get():
if event.type == QUIT:
exit()
elif event.type == KEYDOWN:
if event.key == pygame.K_UP:
key_up = True
elif event.key == pygame.K_DOWN:
key_down = True
elif event.key == pygame.K_LEFT:
key_left = True
elif event.key == pygame.K_RIGHT:
key_right = True
elif event.key == pygame.K_w:
key_front = True
elif event.key == pygame.K_s:
key_back = True
elif event.key == pygame.K_ESCAPE:
exit()
elif event.key == pygame.K_d:
wireframe = not wireframe
if wireframe:
glPolygonMode(GL_FRONT, GL_LINE)
else:
glPolygonMode(GL_FRONT, GL_FILL)
elif event.key == pygame.K_c:
saveModel("level%i.dae" % fileIndex, model)
elif event.type == KEYUP:
if event.key == pygame.K_UP:
key_up = False
elif event.key == pygame.K_DOWN:
key_down = False
elif event.key == pygame.K_LEFT:
key_left = False
elif event.key == pygame.K_RIGHT:
key_right = False
elif event.key == pygame.K_w:
key_front = False
elif event.key == pygame.K_s:
key_back = False
if key_up:
rot_x -= 1.0
elif key_down:
rot_x += 1.0
if key_left:
rot_y -= 1.0
elif key_right:
rot_y += 1.0
if key_front:
sx = math.sin(rot_x * math.pi * 2.0 / 256.0)
cx = -math.cos(rot_x * math.pi * 2.0 / 256.0)
sy = math.sin(rot_y * math.pi * 2.0 / 256.0)
cy = -math.cos(rot_y * math.pi * 2.0 / 256.0)
pos_x += sy * cx * 16
pos_y += sx * 16
pos_z += cy * cx * 16
elif key_back:
sx = math.sin(rot_x * math.pi * 2.0 / 256.0)
cx = -math.cos(rot_x * math.pi * 2.0 / 256.0)
sy = math.sin(rot_y * math.pi * 2.0 / 256.0)
cy = -math.cos(rot_y * math.pi * 2.0 / 256.0)
pos_x -= sy * cx * 16
pos_y -= sx * 16
pos_z -= cy * cx * 16
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glMatrixMode(GL_PROJECTION)
glLoadIdentity()
glFrustum(-aspect, aspect, -(kZNear * kFOVy), (kZNear * kFOVy), kZNear, kZFar)
if False:
glTranslatef(0, 0, -2000)
glRotatef(rot_y * 360.0 / 256.0, 0.0, 1.0, 0.0)
glRotatef(rot_x * 360.0 / 256.0, 1.0, 0.0, 0.0)
else:
glRotatef(rot_x * 360.0 / 256.0, 1.0, 0.0, 0.0)
glRotatef(rot_y * 360.0 / 256.0, 0.0, 1.0, 0.0)
glTranslatef(pos_x, pos_y, pos_z)
glMatrixMode(GL_MODELVIEW)
glLoadIdentity()
object.draw()
clock.tick(60)
pygame.display.flip()
if __name__ == "__main__":
main(*sys.argv[1:])
I wrote a Python version of my Xenogears level viewer. It requires pygame and the python opengl bindings.
It takes a single argument with the room number [<=729], and it expects the data relative to the current directory in disk1/dir11/file<index>.bin. If you're unpacking your ISO, the directory index and file index are 0-based.
There is still a lot to decode, to debug and to optimise. But this version should handle transparencies a lot better then the last version. Texture conversion is a bit slow, though.
Update: Endian fix for Windows
Update: Better endian fix, mipmap fix, (slightly)faster texture conversion
Update: Collada Exporter
-
I don't think anybody would fault you for necro'ing your own thread with something completely on-topic.
I'd check this out if I had Xenogears.
-
Showing off transparency:
(http://bin.mypage.sk/FILES/christmas.png)
I think this is from the Zeboim-era Fei and Elly talking about making Esmeralda.
-
wait, I don't have my copy of Xenogears infront of me.. You have to unpack an archive off the disk to see that, right?
-
wait, I don't have my copy of Xenogears infront of me.. You have to unpack an archive off the disk to see that, right?
Yes. The ISO filesystem only contains the executable and system.cnf file. But LUCKY for you I ported my extraction tool to python as well this morning:
Moved to a separate thread. (http://forums.qhimm.com/index.php?topic=9985.msg140261#msg140261)
You need raw images of your disk (a plain dump of all data, 2352 byte per sector). Just run it with your images as input, and it will create folders in your current directory. And it even automatically detects if your image is disk 1 or disk 2, or not a playstation or xenogears image at all.
I'm using the NTSC U/C version.
-
Showing off transparency:
(http://bin.mypage.sk/FILES/christmas.png)
I think this is from the Zeboim-era Fei and Elly talking about making Esmeralda.
That looks nice!! I wished it was available to do that in ff7 game though. :-\
-
This is perfect!!!!!!. Python!?!?. Damn how many languages are there!...Just kidding, python is the one book I skipped at the library...does it just view the scene or can I dump it somewhere in a 3d format of any kind?.
-
are there currently any programs similar to this for FF7 battle fields?
-
are there currently any programs similar to this for FF7 battle fields?
I'm sure Akari has a better one by now, but you could try this: http://forums.qhimm.com/index.php?topic=3152.0
This is perfect!!!!!!. Python!?!?. Damn how many languages are there!...Just kidding, python is the one book I skipped at the library...does it just view the scene or can I dump it somewhere in a 3d format of any kind?.
At the moment it is only a viewer, but I arranged the code in a way that I can easily write out the model and textures.
Update: I tried it on Windows and noticed there is an endian problem. And gluBuild2DMipmaps didn't work. The version in the first message should be fixed now.
(http://bin.mypage.sk/FILES/screen.jpg)
-
Just a preview:
(http://bin.mypage.sk/FILES/blender.jpg)
It'll take a bit more work until I can release the code. It looks like I'm hitting an internal Blender limit and it needs to be more user friendly.
-
(http://i961.photobucket.com/albums/ae91/venise_chez_vous/Sanstitre.jpg)
Hello and congratulations for your programming skills.
I have tried it on Windows XP but I always get this error message.
I am using Pygame 1.9.1 and Python 3.1.2.
I have also tried on a Macintosh platform but I also have error returns.
Finally I used the script function in Blender but the same problem occurs.
I have an ISO of the game dumped with Clone CD, should I redump it with Alcool 120 to precise the 2352 option ?
Danke ^_^
-
I'm using Python 2.5 and pygame 1.9.1. Additionally the OpenGL bindings 3.0.1. From the screenshot it looks like you're trying to run the viewer and not the extractor. And no version runs inside Blender, my new (unreleased) version creates a Collada 1.4 file.
The extractor complains if the file is not the correct format. The ISO should be identical between clonecd and alcohol, if it is raw and 2352 byte per sector.
I'm not sure what the error complains about, the indentation looks fine. Are you running the script from the command line or from explorer?
Update: The error is new in python 3. Try 2.5.
Something like this on the command line (in the directory with the iso):
c:\python2.5\python extract.py image1filename image2filename
and then
c:\python2.5\python view.py 666
-
Hello there Micky, still no luck in working your program, something must be wrong with my configuration.
Anyway I have a software that allows to extract all 42 folders of a standard ISO image and also extract all of its content as you can see on the picture.
You mentionned directory 11 and the file 729, which can be extracted among the others.
My question is: on the basis of your code, is it possible to write a collada plugin working this Blender so as to view these 3D levels ?
(http://i961.photobucket.com/albums/ae91/venise_chez_vous/Xenostudio.jpg)
-
Hello there Micky, still no luck in working your program, something must be wrong with my configuration.
If you don't tell me what is wrong I can't help you. Maybe you should start with a command line tutorial (http://www.google.com/search?q=windows+command+prompt+tutorial), so you know what you're doing?
Anyway I have a software that allows to extract all 42 folders of a standard ISO image and also extract all of its content as you can see on the picture.
You mentionned directory 11 and the file 729, which can be extracted among the others.
All levels are pairs of files, one for the textures and one for the level. The script should pick the correct files if you give it the room number.
My question is: on the basis of your code, is it possible to write a collada plugin working this Blender so as to view these 3D levels ?
Yes, of course, but I wouldn't make it a Blender plugin. Collada plugin doesn't make sense, collada is a file format.
It would probably be easier to contact the authors of your xenostudio program and tell them about this code. Then they can include it in xenostudio.
-
Yes, of course, but I wouldn't make it a Blender plugin.
Yes this :)!. God I loves me some Xenogears tools thanks Micky.
-
(http://bin.mypage.sk/FILES/Aveh.jpg)
I updated the viewer in the first message and the file extractor in the fifth message.
The viewer now exports the scene into a collada .dae file if you press 'c' while the scene is visible. It's quick and dirty but gives you the geometry and textures. The Blender collada plugin doesn't seem to handle vertex colours, or my export of vertex colours is wrong, as they come out as all white. And transparency is not exported, yet. The file doesn't work with Sketchup, though you can re-export it from Blender and import that into Sketchup. But then you seem to lose the textures? The file name is level<room>.dae .
You may want to adjust the far clipping plane in Blender to something big to avoid the scene to be cut off. You can enable textured view, but shaded view is all black because there is no light.
I'll need to dive into the source for the collada plugin at some point to see if I can improve that, but for now I'm too busy.
The file extractor now calculates the size of movie files correctly.
-
Nice.
Which version of pyopengl you use? When I press "N" I got:
Traceback (most recent call last):
File "model.py", line 1057, in <module>
main(*sys.argv[1:])
File "model.py", line 984, in main
object.draw_normals = not object.draw_normals
AttributeError: OpenGLObject instance has no attribute 'draw_normals'
-
Nice.
Which version of pyopengl you use? When I press "N" I got:
Traceback (most recent call last):
File "model.py", line 1057, in <module>
main(*sys.argv[1:])
File "model.py", line 984, in main
object.draw_normals = not object.draw_normals
AttributeError: OpenGLObject instance has no attribute 'draw_normals'
Sorry, that's a leftover from the FF7 field model/animation viewer, and probably worth removing that line. Maybe I'll implement it if I adapt it for battle models.
-
Awesome work as usual.
Seems transparency is not working fully :(
Btw, exported textures were all red before I've changed line 558 to
image = pygame.image.fromstring(t, (256, 256), "ARGB", True)
but I am on Linux, so this can be the case.
-
Seems transparency is not working fully :(
The textures contain transparency if you look at them in Gimp. Though I haven't found a way for Blender to pick that up, yet.
Btw, exported textures were all red before I've changed line 558 to
image = pygame.image.fromstring(t, (256, 256), "ARGB", True)
but I am on Linux, so this can be the case.
Probably because I'm on a big endian platform and you're using a little endian pc. Alpha is 255 for most of the pixel, which is red if you reverse the byte order. I'll see if I can fix that if I make another version.
-
Seems transparency is not working fully :(
The textures contain transparency if you look at them in Gimp. Though I haven't found a way for Blender to pick that up, yet.
Btw, exported textures were all red before I've changed line 558 to
image = pygame.image.fromstring(t, (256, 256), "ARGB", True)
but I am on Linux, so this can be the case.
Probably because I'm on a big endian platform and you're using a little endian pc. Alpha is 255 for most of the pixel, which is red if you reverse the byte order. I'll see if I can fix that if I make another version.
Does python have an endianess check?
A quick glance suggests it (http://wiki.python.org/moin/ctypes?highlight=%28endian%29) does in ctypes. I know this is a problem sometimes dealing with ARM based architectures as well.
Cyb
-
Does python have an endianess check?
A quick glance suggests it (http://wiki.python.org/moin/ctypes?highlight=%28endian%29) does in ctypes. I know this is a problem sometimes dealing with ARM based architectures as well.
There is a check in the sys (http://docs.python.org/library/sys.html) module as well. I tried to write the OpenGL texture loader in a way that it works correctly both ways, so I'll have to check if there is a good way to do the same for the .tga writer.
I noticed the VRAM contains NPC sprites and I've got some ideas I want to try. Does anyone know where the party sprites (outside a battle) are?
And has anyone looked into the object instance flags, yet? I'm sure some of them are for placing NPCs, but some must be controlling animations (for example for smoke) and billboards (camera-facing quads).