-
Notifications
You must be signed in to change notification settings - Fork 6
GEO
The GEO file contains geometry and animations organized as a scene graph. Similar to the GLTF file format.
Name | Description |
---|---|
Header | geofileHeader |
Transformations | vector<geofileTransformation> |
Alpha Keyframes | vector<geofileAlphaKeyframe> - Animations |
Scene Nodes | vector<geofileTreeNode> |
Empty Space | Fill to next 16 bytes |
Meshes | vector<geofileMesh> |
├── Mesh header | geofileMeshHeader |
├── Surfaces | vector<geofileSurface> - Maps Triangles to textures |
├── Vertices | vector<vec3> - Triangles, size must be divisible by 3 |
├── Tangent | vector<vec3> - Optional: If new is set, used for normal mapping |
├── Bitangent | vector<vec3> - Optional: If new is set, used for normal mapping |
├── UV | vector<vec2> - Texture coordinates |
├── Empty Space | Fill to next 16 bytes |
└── Textures | vector<geofileTexture> |
|
vector<geofileTextureHeader> |
|
vector<rgba> - Used deepening on texture format |
|
vector<char> - Texture binary data |
The geo file starts with a header:
struct geofileHeader
{
int unk1;
int number_of_nodes; // Total number of scene nodes
int unk3;
int number_of_transformations; // Total number of transformations
int number_of_keyframes; // Total number of keyframes
int offset_to_first_mesh; // Offset to the very first mesh (geometry)
int number_of_meshes; // Total number of meshes (geometry)
int unk6;
}
A list of transformations, used for both the initial scene node transformation and keyframes.
struct geofileTransformation
{
glm::vec3 position; // (xyz-float) All values are multiplied by 10.
float unk7;
glm::vec3 rotation; // (xyz-float) Euler angles YXZ in degree. All values are multiplied by 10.
float unk8;
glm::vec3 scale; // (xyz-float) All values are multiplied by 100.
float unk9;
float time; // When Transformation used as a keyframe then this declares its length
float unk11, unk12, unk13;
}
I use Open GL, to convert the values to the correct space I use the following functions:
glm::vec3 fromDisaPosition(const glm::vec3& position) { return glm::vec3(-position.x, -position.y, position.z) / 10.0f; }
glm::quat fromDisaRotation(const glm::vec3& rotation){
const glm::vec3 eulerRotation = glm::radians(glm::vec3(
-rotation.z / 10.0f,
-rotation.y / 10.0f,
rotation.x / 10.0f
));
return glm::quat(glm::eulerAngleYXZ(
eulerRotation.y, eulerRotation.z, eulerRotation.x
));
}
glm::vec3 fromDisaScale(const glm::vec3& scale) { return scale / 100.0f; }
Used for "sudo" particle systems to control the alpha values of each particle (node).
struct geofileAlphaKeyframe{
unsigned short value;
unsigned short time;
};
A Scene Tree is a hierarchical structure to organize scene meshes. Each node can have multiple child nodes, enabling transformations and animations between each other. So if you rotate the parent all children will be rotated too. (same with alpha)
struct geofileTreeNode
{
short parent_index; // The index of the parent node. 0 is root, so 1 is first entry
short transformations_start_index;
short number_of_transformations; // Must be at least 1. If more then animated (position, rotation, scale)
short mesh_index; // If -1, then node is a pure transformation node.
short transformation_mode; // 2 or 3 is a Billboard
short alpha_keyframe_start_index; // Is 0, if node then not alpha animated
short alpha_number_of_keyframes; // Is 0, if node then not alpha animated
}
The actual geometry data that can be attached to a scene node. The very first mesh starts at address 'offset_to_first_mesh'. Inside the mesh all addresses are relative to the origin address of mesh.
struct geofileMesh {
geofileMeshHeader header;
std::vector<geofileSurface> surfaces;
//buffers
std::vector<glm::vec3> vertices;
std::vector<glm::vec3> tangent; //optinal, if new is set, used for normal mapping
std::vector<glm::vec3> bitangent; //optinal, if new is set, used for normal mapping
std::vector<glm::vec2> uv;
std::vector<geofileTexture> textures;
};
struct geofileMeshHeader {
unsigned short number_of_triangles, isNew;
int unk9, number_of_surfaces, number_of_textures;
int start_offset_texture, end_offset_mesh, size_of_triangles, unk12; //addresses are relative to the origin address of mesh
}
isNew
marks if the mesh has tangent and bitangent. Usually this also means that there normal textures as well. This is only used for the HD mode.
Defines what texture to apply to the triangles. Plus some effects.
struct geofileSurface{
unsigned char rMult, gMult, bMult, aMult; //RGBA color texture multiplication
unsigned short index; // Index to texture starting from 1. Can be be 0, then this surface has no texture. (Default texture is Black?)
unsigned short length; // Number of vertices inside surface (start is the sum of previous lenghts)
unsigned char additive, animated, unk5, unk6; //texture effects, for particles or lava scroll
}
struct geofileTexture{
geofileTextureHeader header;
std::vector<rgba> colortable; //is filled depending on texture format
std::vector<char> data; //binary texture data
}
They already have a fine texture format, but no, they decided to invent a new one. However the tx2 format also creeps into it.
struct geofileTextureHeader {
unsigned short totalSize; //total size if the texture, including the header and everything else
unsigned char unk0;
unsigned char type : 7;
bool unkFlag : 1; //maybe if texture has normal map?!
unsigned short width, height;
unsigned short infoField; //usage depends on the type. Can be colortable size or additional type.
unsigned char powerWidth, powerHeight;
unsigned int unk4;
}
Right now its not 100% clear how it works, but here what I know. The type
can be either 0, for old textures, or 16, for a variation of the tx2 texture format. Based on the values, the types can be determined as follows:
if(type == 0){
// infoField is used as color table size
return infoField <= 16? TX2::COLORTABLE_BGRA16: TX2::COLORTABLE_BGRA256;
}else if(type == 16){
// infoField corresponds to a subset of TX2 types
switch(infoField){
case 0: return TX2::DXT1;
case 1: return TX2::DXT4;
case 2: return TX2::DXT5;
case 3: return TX2::BGRA;
case 4: return TX2::FONT;
// Other types are likely not supported
}
throw "No idea what it is!?";
}
Note that totalSize
is an unsigned short, which is used to jump to the next texture inside the list. This limits the size of any texture to 65,535 bytes, except for the last texture, which can be of any size :P