Skip to content

Instantly share code, notes, and snippets.

@salty-godzilla
Last active December 28, 2018 08:39
Show Gist options
  • Save salty-godzilla/dc5865965819d0819446d0d6ef602e9b to your computer and use it in GitHub Desktop.
Save salty-godzilla/dc5865965819d0819446d0d6ef602e9b to your computer and use it in GitHub Desktop.
BD1 to OBJ Converter (Licence: CC0)
#include <string>
#include <iostream>
#include <vector>
#include <Windows.h>
#include "MapData.hpp"
void printBd1Structure(MapData map)
{
printf("- Textures\n");
for (int i = 0; i < NUM_TEXTURES; i++) {
printf(" - %d: %s\n", i, map.texturesPath[i].c_str());
}
printf("\n- Num of blocks: %d\n", map.numBlocks);
for (int i = 0; i < map.numBlocks; i++) {
printf("\n- Block %d\n", i);
for (int j = 0; j < NUM_VERTICES; j++) {
printf(" - Vertex %d (X: %f Y: %f Z: %f)\n",
j,
map.block[i].vertex[j].x,
map.block[i].vertex[j].y,
map.block[i].vertex[j].z);
}
for (int j = 0; j < NUM_FACES; j++) {
printf(" - Face %d\n", j);
printf(" - Texture: %d\n", map.block[i].face[j].textureId);
for (int k = 0; k < 4; k++) {
printf(" - UV %d (U: %f V: %f)\n", k, map.block[i].face[j].uv[k].u, map.block[i].face[j].uv[k].v);
}
}
}
}
void outputFaceLine(MapData &map, FILE *fp, int textureId, int v1, int vt1, int v2, int vt2, int v3, int vt3, int v4, int vt4)
{
static int lastTexture = -1;
if (lastTexture != textureId) {
fprintf(fp, "usemtl %s\n", map.texturesPath[textureId].c_str());
lastTexture = textureId;
}
fprintf(fp, "f %d/%d %d/%d %d/%d %d/%d\n", v1, vt1, v2, vt2, v3, vt3, v4, vt4);
};
void exportObjFile(MapData map, std::wstring path)
{
FILE *fp;
fp = _wfopen(path.c_str(), L"wb");
fprintf(fp, "# generated by bd1Parser\n\n");
fprintf(fp, "mtllib temp.mtl\n\n");
for (int i = 0; i < map.numBlocks; i++) {
for (int j = 0; j < NUM_VERTICES; j++) {
fprintf(fp, "v %f %f %f\n", -map.block[i].vertex[j].x / 100, map.block[i].vertex[j].y / 100, map.block[i].vertex[j].z / 100);
}
}
fprintf(fp, "\n\n");
for (int i = 0; i < map.numBlocks; i++) {
for (int j = 0; j < NUM_FACES; j++) {
for (int k = 0; k < 4; k++) {
fprintf(fp, "vt %f %f\n", map.block[i].face[j].uv[k].u, 1 - map.block[i].face[j].uv[k].v);
}
}
}
fprintf(fp, "\n\n");
for (int i = 0; i < map.numBlocks; i++) {
int b = i * 8;
int uvb = i * 24;
outputFaceLine(map, fp, map.block[i].face[0].textureId, b + 1, uvb + 4, b + 2, uvb + 3, b + 3, uvb + 2, b + 4, uvb + 1); // 上
outputFaceLine(map, fp, map.block[i].face[1].textureId, b + 8, uvb + 8, b + 7, uvb + 7, b + 6, uvb + 6, b + 5, uvb + 5); // 下
outputFaceLine(map, fp, map.block[i].face[2].textureId, b + 5, uvb + 12, b + 6, uvb + 11, b + 2, uvb + 10, b + 1, uvb + 9); // 前
outputFaceLine(map, fp, map.block[i].face[3].textureId, b + 6, uvb + 16, b + 7, uvb + 15, b + 3, uvb + 14, b + 2, uvb + 13); // 右
outputFaceLine(map, fp, map.block[i].face[4].textureId, b + 7, uvb + 20, b + 8, uvb + 19, b + 4, uvb + 18, b + 3, uvb + 17); // 後
outputFaceLine(map, fp, map.block[i].face[5].textureId, b + 8, uvb + 24, b + 5, uvb + 23, b + 1, uvb + 22, b + 4, uvb + 21); // 左
}
fclose(fp);
}
void exportMtlFile(MapData map, std::wstring path)
{
FILE *fp;
fp = _wfopen(path.c_str(), L"wb");
fprintf(fp, "# generated by bd1Parser\n");
for (int i = 0; i < 10; i++) {
if (map.texturesPath[i].empty()) {
continue;
}
fprintf(fp, "\nnewmtl %s\n", map.texturesPath[i].c_str());
fprintf(fp, "Ka 0.40000 0.40000 0.40000\n");
fprintf(fp, "Kd 0.90000 0.90000 0.90000\n");
fprintf(fp, "Ks 0.00000 0.00000 0.00000\n");
fprintf(fp, "Ns 5.00000\n");
fprintf(fp, "map_Kd %s\n", map.texturesPath[i].c_str());
}
fclose(fp);
}
int openBd1FileDialog(std::wstring &path) {
OPENFILENAME ofn = {};
wchar_t _path[1024] = {};
ofn.lStructSize = sizeof(OPENFILENAME);
ofn.lpstrFilter = L"Block data(*.bd1)\0*.bd1\0";
ofn.lpstrFile = _path;
ofn.nMaxFile = sizeof(_path);
ofn.lpstrTitle = L"Select a block data file";
ofn.Flags = OFN_FILEMUSTEXIST;
if (!GetOpenFileName(&ofn)) {
return 1;
}
path = _path;
return 0;
}
int parseBd1(std::wstring bd1Path, MapData &map) {
FILE *fp;
fp = _wfopen(bd1Path.c_str(), L"rb");
if (!fp) {
printf("bd1 open failed!\n");
fclose(fp);
return 1;
}
// textures
for (int i = 0; i < NUM_TEXTURES; i++) {
char texturePath[32];
fread(texturePath, sizeof(texturePath) - 1, 1, fp);
map.texturesPath[i] = texturePath;
}
// block count
fread(&map.numBlocks, 1, 1, fp);
// skip 1byte
fseek(fp, 1, SEEK_CUR);
for (int i = 0; i < map.numBlocks; i++) {
float vertexRawData[3 * NUM_VERTICES];
fread(vertexRawData, sizeof(float), (3 * NUM_VERTICES), fp);
// vertex data
for (int j = 0; j < NUM_VERTICES; j++) {
map.block[i].vertex[j].x = vertexRawData[0 + j];
map.block[i].vertex[j].y = vertexRawData[8 + j];
map.block[i].vertex[j].z = vertexRawData[16 + j];
}
// uv data
float uvRawData[48]; // 48 * 4 = 192 bytes
fread(uvRawData, sizeof(float), 48, fp);
for (int face = 0; face < NUM_FACES; face++) {
for (int uvs = 0; uvs < 4; uvs++) {
map.block[i].face[face].uv[uvs].u = uvRawData[face * 4 + uvs];
map.block[i].face[face].uv[uvs].v = uvRawData[face * 4 + uvs + 24];
}
}
// texture selection
uint8_t texSelectRawData[24];
fread(texSelectRawData, 1, 24, fp);
for (int face = 0; face < NUM_FACES; face++) {
map.block[i].face[face].textureId = texSelectRawData[face * 4];
}
// enable / disable flag
fread(&map.block[i].isEnabled, 1, 1, fp);
// skip 3 bytes
fseek(fp, 3, SEEK_CUR);
}
fclose(fp);
return 0;
}
int main()
{
std::wstring bd1Path;
if (openBd1FileDialog(bd1Path)) {
return 1;
}
std::wstring objPath = bd1Path.substr(0, bd1Path.find_last_of('\\')) + L"\\temp.obj";
std::wstring mtlPath = bd1Path.substr(0, bd1Path.find_last_of('\\')) + L"\\temp.mtl";
MapData map;
parseBd1(bd1Path, map);
printBd1Structure(map);
exportObjFile(map, objPath);
exportMtlFile(map, mtlPath);
return 0;
}
#pragma once
#include <iostream>
#include <vector>
const auto NUM_TEXTURES = 10;
const auto NUM_FACES = 6;
const auto NUM_VERTICES = 8;
struct MapData
{
struct Block
{
struct Face
{
struct Uv
{
float u;
float v;
} uv[4];
uint8_t textureId;
} face[NUM_FACES];
struct Vertices
{
float x;
float y;
float z;
} vertex[NUM_VERTICES];
bool isEnabled;
} block[160];
std::string texturesPath[NUM_TEXTURES];
uint8_t numBlocks;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment