|
OBJ Import
Idea is understanable
enum class OBJFaceType
{
p,
pu,
pun,
pn
};
struct OBJSimpleArr
{
OBJSimpleArr() {
sz = 0;
}
int data[0x100];
unsigned int sz;
void push_back(int v) { data[sz++] = v; }
unsigned int size() { return sz; }
void reset() { sz = 0; }
};
struct OBJFace
{
OBJFace() {
ft = OBJFaceType::pun;
}
OBJSimpleArr p, u, n;
OBJFaceType ft;
void reset()
{
ft = OBJFaceType::pun;
p.reset();
u.reset();
n.reset();
}
};
unsigned char * OBJNextLine(unsigned char * ptr);
unsigned char * OBJSkipSpaces(unsigned char * ptr);
unsigned char * OBJReadVec2(unsigned char * ptr, v2f& vec2);
unsigned char * OBJReadFloat(unsigned char * ptr, float& value);
unsigned char * OBJReadVec3(unsigned char * ptr, v3f& vec3);
unsigned char * OBJReadFace(unsigned char * ptr, OBJFace& f, char * s);
unsigned char * OBJReadWord(unsigned char * ptr, miString& str);
unsigned char * OBJReadLastWord(unsigned char * ptr, miString& str);
bool g_ImportOBJ_triangulate = false;
bool g_ImportOBJ_readMTL = true;
struct OBJMaterial
{
OBJMaterial() {
m_specularExponent = 10.f;
m_opacity = 1.f;
m_transparency = 0.f;
m_refraction = 1.f;
}
miString m_name; // newmtl
v3f m_ambient; // Ka
v3f m_diffuse; // Kd
v3f m_specular; // Ks
f32 m_specularExponent; // Ns
f32 m_opacity; // d
f32 m_transparency; // Tr
f32 m_refraction; // Ni
miString m_map_diffuse; // map_Kd
miString m_map_ambient; // map_Ka
miString m_map_specular; // map_Ks
miString m_map_specularHighlight; // map_Ns
miString m_map_alpha; // map_d
miString m_map_bump; // map_bump bump
miString m_map_displacement; // disp
miString m_map_reflection; // refl
};
bool OBJStringEqual(const miString& str, const char* c_Str)
{
auto c_str_len = std::strlen(c_Str);
if (c_str_len != str.size())
return false;
for (size_t i = 0, sz = str.size(); i < sz; ++i)
{
if (str[i] != (miString::value_type)c_Str[i])
return false;
}
return true;
}
void miplStd_ImportOBJ_MTL(
miArray<OBJMaterial*>& materials,
const char* obj_fileName,
const char* mtl_fileName
)
{
auto relPath = g_sdk->FileGetRelativePath(obj_fileName);
for (u32 i = 0, sz = relPath.size(); i < sz; ++i)
{
auto c = relPath.pop_back_return();
if (c == L'/' || c == L'\\')
{
relPath += c;
break;
}
}
miString mtlPath = relPath;
mtlPath += mtl_fileName;
if (g_sdk->FileExist(mtlPath.data()))
{
auto mbStr = g_sdk->StringWideToMultiByte(mtlPath.data());
FILE* file = fopen(mbStr.data(), "rb");
auto file_size = (size_t)g_sdk->FileSize(mbStr.data());
std::vector<unsigned char> file_byte_array((unsigned int)file_size + 2);
unsigned char * ptr = file_byte_array.data();
fread(ptr, 1, file_size, file);
fclose(file);
ptr[(unsigned int)file_size] = ' ';
ptr[(unsigned int)file_size + 1] = 0;
OBJMaterial* curMaterial = 0;
while (*ptr)
{
switch (*ptr)
{
case '#':
case 'd':
case 'T':
case 'i':
case 'K':
ptr = OBJNextLine(ptr);
break;
case 'n':
{
miString mtlWord;
ptr = OBJReadWord(ptr, mtlWord);
if (OBJStringEqual(mtlWord, "newmtl"))
{
curMaterial = new OBJMaterial;
materials.push_back(curMaterial);
ptr = OBJReadWord(ptr, curMaterial->m_name);
}
}break;
case 'N':
{
miString word;
ptr = OBJReadWord(ptr, word);
if (OBJStringEqual(word, "Ns"))
{
ptr = OBJSkipSpaces(ptr);
ptr = OBJReadFloat(ptr, curMaterial->m_specularExponent);
}
else if (OBJStringEqual(word, "Ni"))
{
ptr = OBJSkipSpaces(ptr);
ptr = OBJReadFloat(ptr, curMaterial->m_refraction);
}
else
ptr = OBJNextLine(ptr);
}break;
case 'm':
{
miString word;
ptr = OBJReadWord(ptr, word);
if (OBJStringEqual(word, "map_Kd"))
{
ptr = OBJReadLastWord(ptr, word);
miString mapPath = word;
if (!g_sdk->FileExist(mapPath.data()))
{
mapPath = relPath;
mapPath += word.data();
//if (g_sdk->FileExist(mapPath.data()))
// printf("OK\n");
}
curMaterial->m_map_diffuse = mapPath;
/*ptr = OBJSkipSpaces(ptr);
if (*ptr == L'-')
{
ptr = OBJReadWord(ptr, word);
if (OBJStringEqual(word, "-bm"))
{
}
}*/
}
else
ptr = OBJNextLine(ptr);
}break;
default:
++ptr;
break;
}
}
}
}
OBJMaterial* OBJGetMaterial(
miArray<OBJMaterial*>& materials,
const miString& name
)
{
for (u32 i = 0; i < materials.m_size; ++i)
{
if (materials.m_data[i]->m_name == name)
return materials.m_data[i];
}
return 0;
}
void miplStd_ImportOBJ(const wchar_t* fileName) {
assert(fileName);
auto mbStr = g_sdk->StringWideToMultiByte(fileName);
miArray<OBJMaterial*> obj_materials;
FILE* file = fopen(mbStr.data(), "rb");
auto file_size = (size_t)g_sdk->FileSize(mbStr.data());
std::vector<unsigned char> file_byte_array((unsigned int)file_size + 2);
unsigned char * ptr = file_byte_array.data();
fread(ptr, 1, file_size, file);
fclose(file);
ptr[(unsigned int)file_size] = ' ';
ptr[(unsigned int)file_size + 1] = 0;
bool groupBegin = false;
bool isModel = false;
bool grpFound = false;
v2f tcoords;
v3f pos;
v3f norm;
std::vector<v3f> position;
std::vector<v2f> uv;
std::vector<v3f> normal;
position.reserve(0xffff);
uv.reserve(0xffff);
normal.reserve(0xffff);
position.reserve(0xffff);
uv.reserve(0xffff);
normal.reserve(0xffff);
//std::string name_word;
miString tmp_word;
miString curr_word;
miString prev_word;
OBJFace f;
char s[0xff];
int last_counter[3] = {0,0,0};
std::unordered_map<std::string, unsigned int> map;
std::string hash;
miSDKImporterHelper importerHelper;
miPolygonCreator triangulationPolygonCreator;
OBJMaterial* currMaterial = 0;
while (*ptr)
{
switch (*ptr)
{
case 'm'://mtllib
{
miString word;
ptr = OBJReadWord(ptr, word);
if (OBJStringEqual(word, "mtllib"))
{
ptr = OBJReadWord(ptr, word);
//wprintf(L"MTL: %s\n", mtlWord.data());
miStringA stra;
stra = word.data();
miplStd_ImportOBJ_MTL(obj_materials, mbStr.data(), stra.data());
}
}break;
case 'u'://usemtl
{
miString word;
ptr = OBJReadWord(ptr, word);
if (OBJStringEqual(word, "usemtl"))
{
ptr = OBJReadWord(ptr, word);
currMaterial = OBJGetMaterial(obj_materials, word);
}
}break;
case 's':
case 'l':
case 'c'://curv
case 'p'://parm
case 'd'://deg
case '#':
case 'e'://end
ptr = OBJNextLine(ptr);
break;
case 'v':
{
++ptr;
if (groupBegin)
groupBegin = false;
switch (*ptr)
{
case 't':
ptr = OBJReadVec2(++ptr, tcoords);
uv.push_back(tcoords);
++last_counter[1];
break;
case 'n':
ptr = OBJReadVec3(++ptr, norm);
normal.push_back(norm);
++last_counter[2];
break;
default:
ptr = OBJReadVec3(ptr, pos);
position.push_back(pos);
++last_counter[0];
//newModel->m_aabb.add(pos);
break;
}
}break;
case 'f':
{
isModel = true;
f.reset();
ptr = OBJReadFace(++ptr, f, s);
importerHelper.m_polygonCreator.Clear();
if (!importerHelper.m_meshBuilder->m_isBegin)
{
importerHelper.m_meshBuilder->Begin();
}
bool weld = false;
bool genNormals = true;
for (size_t sz2 = f.p.size(), i2 = 0; i2 < sz2; ++i2)
{
auto index = i2;
auto pos_index = f.p.data[index];
if (pos_index < 0)
{
// если индекс отрицательный то он указывает на позицию относительно последнего элемента
// -1 = последний элемент
pos_index = last_counter[0] + pos_index + 1;
}
{
hash.clear();
hash += pos_index;
// это я не помню зачем сделал
// когда дойду до control вершин, станет ясно зачем это здесь
auto it = map.find(hash);
if (it == map.end())
{
map[hash] = pos_index;
}
else
{
weld = true;
}
}
auto v = position[pos_index];
//geometry_creator->AddPosition(v.x, v.y, v.z);
v3f pcPos, pcNorm;
v2f pcUV;
pcPos = v;
if (f.ft == OBJFaceType::pu || f.ft == OBJFaceType::pun)
{
auto uv_index = f.u.data[index];
if (uv_index < 0)
{
uv_index = last_counter[1] + uv_index + 1;
}
auto u = uv[uv_index];
// geometry_creator->AddUV(u.x, u.y);
pcUV.x = u.x;
pcUV.y = 1.f - u.y;
}
if (f.ft == OBJFaceType::pn || f.ft == OBJFaceType::pun)
{
auto nor_index = f.n.data[index];
if (nor_index < 0)
{
nor_index = last_counter[2] + nor_index + 1;
}
auto n = normal[nor_index];
// geometry_creator->AddNormal(n.x, n.y, n.z);
pcNorm = n;
genNormals = false;
}
importerHelper.m_polygonCreator.Add(pcPos, weld, false, pcNorm, pcUV);
}
if (g_ImportOBJ_triangulate && importerHelper.m_polygonCreator.Size() > 3)
{
u32 triCount = importerHelper.m_polygonCreator.Size() - 2;
auto _positions = importerHelper.m_polygonCreator.GetPositions();
auto _normals = importerHelper.m_polygonCreator.GetNormals();
auto _tcoords = importerHelper.m_polygonCreator.GetTCoords();
for (u32 i = 0; i < triCount; ++i)
{
triangulationPolygonCreator.Clear();
triangulationPolygonCreator.Add(_positions[0].m_first, weld, false, _normals[0], _tcoords[0]);
triangulationPolygonCreator.Add(_positions[i+1].m_first, weld, false, _normals[i+1], _tcoords[i+1]);
triangulationPolygonCreator.Add(_positions[i+2].m_first, weld, false, _normals[i+2], _tcoords[i+2]);
importerHelper.m_meshBuilder->AddPolygon(&triangulationPolygonCreator, genNormals);
}
}
else
{
importerHelper.m_meshBuilder->AddPolygon(&importerHelper.m_polygonCreator, genNormals);
}
}break;
case 'o':
case 'g':
{
if (!groupBegin)
groupBegin = true;
else
{
ptr = OBJNextLine(ptr);
break;
}
/*std::string tmp_word;
ptr = OBJReadWord(++ptr, tmp_word);
if (tmp_word.size())
{
if (!name_word.size())
name_word = tmp_word;
}*/
ptr = OBJReadWord(++ptr, tmp_word);
if (tmp_word.size())
{
prev_word = curr_word;
curr_word = tmp_word;
}
if (grpFound)
{
if (importerHelper.m_meshBuilder->m_isBegin)
{
importerHelper.m_meshBuilder->End();
miMaterial* m = 0;
if (currMaterial)
{
m = g_sdk->CreateMaterial(currMaterial->m_name.data());
if (currMaterial->m_map_diffuse.size())
m->m_maps[m->mapSlot_Diffuse].m_texturePath = currMaterial->m_map_diffuse;
}
g_sdk->CreateSceneObjectFromHelper(&importerHelper, prev_word.data(), m);
// just create again
importerHelper.Create();
importerHelper.m_meshBuilder->Begin();
}
}
grpFound = true;
}break;
default:
++ptr;
break;
}
}
if (importerHelper.m_meshBuilder->m_isBegin)
{
importerHelper.m_meshBuilder->End();
miMaterial* m = 0;
if (currMaterial)
{
m = g_sdk->CreateMaterial(currMaterial->m_name.data());
if (currMaterial->m_map_diffuse.size())
m->m_maps[m->mapSlot_Diffuse].m_texturePath = currMaterial->m_map_diffuse;
}
g_sdk->CreateSceneObjectFromHelper(&importerHelper, curr_word.data(), m);
}
g_sdk->UpdateSceneAabb();
for (u32 i = 0; i < obj_materials.m_size; ++i)
{
delete obj_materials.m_data[i];
}
}
unsigned char * OBJNextLine(unsigned char * ptr)
{
while (*ptr)
{
if (*ptr == '\n')
{
ptr++;
return ptr;
}
ptr++;
}
return ptr;
}
unsigned char * OBJReadVec2(unsigned char * ptr, v2f& vec2)
{
ptr = OBJSkipSpaces(ptr);
float x, y;
if (*ptr == '\n')
{
ptr++;
}
else
{
ptr = OBJReadFloat(ptr, x);
ptr = OBJSkipSpaces(ptr);
ptr = OBJReadFloat(ptr, y);
ptr = OBJNextLine(ptr);
vec2.x = x;
vec2.y = y;
}
return ptr;
}
unsigned char * OBJSkipSpaces(unsigned char * ptr)
{
while (*ptr)
{
if (*ptr != '\t' && *ptr != ' ')
break;
ptr++;
}
return ptr;
}
unsigned char * OBJReadFloat(unsigned char * ptr, float& value)
{
char str[32u];
memset(str, 0, 32);
char * p = &str[0u];
while (*ptr) {
if (!isdigit(*ptr) && (*ptr != '-') && (*ptr != '+')
&& (*ptr != 'e') && (*ptr != 'E') && (*ptr != '.')) break;
*p = *ptr;
++p;
++ptr;
}
value = (float)atof(str);
return ptr;
}
unsigned char * OBJReadVec3(unsigned char * ptr, v3f& vec3) {
ptr = OBJSkipSpaces(ptr);
float x, y, z;
if (*ptr == '\n') {
ptr++;
}
else {
ptr = OBJReadFloat(ptr, x);
ptr = OBJSkipSpaces(ptr);
ptr = OBJReadFloat(ptr, y);
ptr = OBJSkipSpaces(ptr);
ptr = OBJReadFloat(ptr, z);
ptr = OBJNextLine(ptr);
vec3.x = x;
vec3.y = y;
vec3.z = z;
}
return ptr;
}
unsigned char * OBJSkipSpace(unsigned char * ptr) {
while (*ptr) {
if (*ptr != ' ' && *ptr != '\t') break;
ptr++;
}
return ptr;
}
unsigned char * OBJGetInt(unsigned char * p, int& i)
{
char str[8u];
memset(str, 0, 8);
char * pi = &str[0u];
while (*p)
{
/*if( *p == '-' )
{
++p;
continue;
}*/
if (!isdigit(*p) && *p != '-') break;
*pi = *p;
++pi;
++p;
}
i = atoi(str);
return p;
}
unsigned char * OBJReadFace(unsigned char * ptr, OBJFace& f, char * s) {
ptr = OBJSkipSpaces(ptr);
if (*ptr == '\n')
{
ptr++;
}
else
{
while (true)
{
int p = 1;
int u = 1;
int n = 1;
ptr = OBJGetInt(ptr, p);
if (*ptr == '/')
{
ptr++;
if (*ptr == '/')
{
ptr++;
f.ft = OBJFaceType::pn;
ptr = OBJGetInt(ptr, n);
}
else
{
ptr = OBJGetInt(ptr, u);
if (*ptr == '/')
{
ptr++;
f.ft = OBJFaceType::pun;
ptr = OBJGetInt(ptr, n);
}
else
{
f.ft = OBJFaceType::pu;
}
}
}
else
{
f.ft = OBJFaceType::p;
}
f.n.push_back(n - 1);
f.u.push_back(u - 1);
f.p.push_back(p - 1);
ptr = OBJSkipSpace(ptr);
if (*ptr == '\r')
break;
else if (*ptr == '\n')
break;
}
}
return ptr;
}
unsigned char * OBJReadWord(unsigned char * ptr, miString& str)
{
ptr = OBJSkipSpaces(ptr);
str.clear();
while (*ptr)
{
if (isspace(*ptr))
break;
str += (wchar_t)*ptr;
ptr++;
}
return ptr;
}
unsigned char * OBJReadLastWord(unsigned char * ptr, miString& str) {
while (true)
{
ptr = OBJSkipSpaces(ptr);
ptr = OBJReadWord(ptr, str);
ptr = OBJSkipSpaces(ptr);
if (*ptr == '\r' || *ptr == '\n')
{
break;
}
if (*ptr == 0)
break;
}
return ptr;
}
OBJ Export
bool g_ExportOBJ_triangulate = false;
bool g_ExportOBJ_optimize = true;
bool g_ExportOBJ_writeNormals = true;
bool g_ExportOBJ_writeUVs = true;
bool g_ExportOBJ_createMTL = true;
bool g_ExportOBJ_onlySelected = false;
u32 g_ExportOBJ_vIndex = 0;
u32 g_ExportOBJ_vtIndex = 0;
u32 g_ExportOBJ_vnIndex = 0;
class OBJWriter
{
miStringA str_for_map;
public:
OBJWriter() {}
~OBJWriter() {}
void WriteF( miStringA& f_str, u32 vI, u32 vtI, u32 vnI)
{
f_str += vI;
if (g_ExportOBJ_writeUVs && g_ExportOBJ_writeNormals)
{
f_str += "/";
f_str += vtI;
f_str += "/";
f_str += vnI;
}
else if (g_ExportOBJ_writeUVs && !g_ExportOBJ_writeNormals)
{
f_str += "/";
f_str += vtI;
}
else if (!g_ExportOBJ_writeUVs && g_ExportOBJ_writeNormals)
{
f_str += "/";
f_str += "/";
f_str += vnI;
}
f_str += " ";
}
void WritePosition(
const v3f& position,
miStringA& v_str
)
{
v_str += "v ";
v_str.append_float(position.x);
v_str += " ";
v_str.append_float(position.y);
v_str += " ";
v_str.append_float(position.z);
v_str += "\r\n";
}
void WriteUV(
const v2f& UVs,
miStringA& vt_str
)
{
vt_str += "vt ";
vt_str.append_float(UVs.x);
vt_str += " ";
vt_str.append_float(1.f - UVs.y);
vt_str += " ";
vt_str.append_float(0.f);
vt_str += "\r\n";
}
void WriteNormal(
const v3f& normal,
miStringA& vn_str
)
{
vn_str += "vn ";
vn_str.append_float(normal.x);
vn_str += " ";
vn_str.append_float(normal.y);
vn_str += " ";
vn_str.append_float(normal.z);
vn_str += "\r\n";
}
void WriteData(
const v3f& position,
const v2f& UV,
const v3f& normal,
miStringA& v_str,
miStringA& vt_str,
miStringA& vn_str
)
{
WritePosition(position, v_str);
if (g_ExportOBJ_writeUVs)
WriteUV(UV, vt_str);
if (g_ExportOBJ_writeNormals)
WriteNormal(normal, vn_str);
}
void WriteMaterial(FILE* mtl_file, miMaterial* m)
{
fprintf(mtl_file, "\r\n");
miStringA astr;
astr += m->m_name.data();
fprintf(mtl_file, "newmtl %s\r\n", astr.data());
fprintf(mtl_file, "\tNs %.4f\r\n", m->m_specularExponent);
fprintf(mtl_file, "\tNi %.4f\r\n", m->m_refraction);
fprintf(mtl_file, "\td %.4f\r\n", m->m_opacity);
fprintf(mtl_file, "\tTr %.4f\r\n", 1.f - m->m_opacity);
fprintf(mtl_file, "\tKa %.4f %.4f %.4f\r\n", m->m_colorAmbient.x(), m->m_colorAmbient.y(), m->m_colorAmbient.z());
fprintf(mtl_file, "\tKd %.4f %.4f %.4f\r\n", m->m_colorDiffuse.x(), m->m_colorDiffuse.y(), m->m_colorDiffuse.z());
fprintf(mtl_file, "\tKs %.4f %.4f %.4f\r\n", m->m_colorSpecular.x(), m->m_colorSpecular.y(), m->m_colorSpecular.z());
if (m->m_maps[m->mapSlot_Diffuse].m_texturePath.size())
{
auto s = g_sdk->FileGetName(m->m_maps[m->mapSlot_Diffuse].m_texturePath.data());
astr = s.data();
fprintf(mtl_file, "\tmap_Kd %s\r\n", astr.data());
}
}
void WriteObject(FILE* file, FILE* mtl_file, miSceneObject* object)
{
auto objectType = object->GetObjectType();
if (objectType == miSceneObjectType::MeshObject)
{
bool good = true;
if (g_ExportOBJ_onlySelected)
good = object->IsSelected();
auto meshCount = object->GetMeshCount();
if (meshCount && good)
{
miStringA stra;
stra = object->GetName().data();
fprintf(file, "\r\no %s \r\n", stra.data());
if (g_ExportOBJ_createMTL && object->m_material)
{
if (object->m_material)
{
miStringA astr;
astr += object->m_material->m_name.c_str();
fprintf(file, "usemtl %s \r\n", astr.data());
WriteMaterial(mtl_file, object->m_material);
}
}
miStringA v_str; v_str.reserve(0xffff);
miStringA vn_str; vn_str.reserve(0xffff);
miStringA vt_str; vt_str.reserve(0xffff);
miStringA f_str; f_str.reserve(0xffff);
u32 v_count = 0;
u32 vn_count = 0;
u32 vt_count = 0;
u32 f_count = 0;
auto pivot = object->GetGlobalPosition();
for (s32 i = 0; i < meshCount; ++i)
{
auto mesh = object->GetMesh(i);
if (!mesh->m_first_polygon)
continue;
auto cp = mesh->m_first_polygon;
auto lp = cp->m_left;
while (true)
{
if (g_ExportOBJ_triangulate)
{
auto vertex_1 = cp->m_verts.m_head;
auto vertex_3 = vertex_1->m_right;
auto vertex_2 = vertex_3->m_right;
while (true)
{
f_str += "f ";
++f_count;
auto vertex = vertex_1;
v3f position = math::mulBasis(vertex->m_data.m_vertex->m_position, *object->GetWorldMatrix()) + *pivot;
v2f UVs = vertex->m_data.m_uv;
v3f normals = vertex->m_data.m_normal;
if (g_ExportOBJ_optimize)
{
WriteOptimize(position, UVs, normals, v_count, vt_count, vn_count, f_str, v_str, vn_str, vt_str);
}
else
{
++g_ExportOBJ_vIndex;
++g_ExportOBJ_vtIndex;
++g_ExportOBJ_vnIndex;
++v_count;
++vt_count;
++vn_count;
WriteData(position, UVs, normals, v_str, vt_str, vn_str);
WriteF(f_str, g_ExportOBJ_vIndex, g_ExportOBJ_vtIndex, g_ExportOBJ_vnIndex);
}
vertex = vertex_2;
position = math::mulBasis(vertex->m_data.m_vertex->m_position, *object->GetWorldMatrix()) + *pivot;
UVs = vertex->m_data.m_uv;
normals = vertex->m_data.m_normal;
if (g_ExportOBJ_optimize)
{
WriteOptimize(position, UVs, normals, v_count, vt_count, vn_count, f_str, v_str, vn_str, vt_str);
}
else
{
++g_ExportOBJ_vIndex;
++g_ExportOBJ_vtIndex;
++g_ExportOBJ_vnIndex;
++v_count;
++vt_count;
++vn_count;
WriteData(position, UVs, normals, v_str, vt_str, vn_str);
WriteF(f_str, g_ExportOBJ_vIndex, g_ExportOBJ_vtIndex, g_ExportOBJ_vnIndex);
}
vertex = vertex_3;
position = math::mulBasis(vertex->m_data.m_vertex->m_position, *object->GetWorldMatrix()) + *pivot;
UVs = vertex->m_data.m_uv;
normals = vertex->m_data.m_normal;
if (g_ExportOBJ_optimize)
{
WriteOptimize(position, UVs, normals, v_count, vt_count, vn_count, f_str, v_str, vn_str, vt_str);
}
else
{
++g_ExportOBJ_vIndex;
++g_ExportOBJ_vtIndex;
++g_ExportOBJ_vnIndex;
++v_count;
++vt_count;
++vn_count;
WriteData(position, UVs, normals, v_str, vt_str, vn_str);
WriteF(f_str, g_ExportOBJ_vIndex, g_ExportOBJ_vtIndex, g_ExportOBJ_vnIndex);
}
f_str += "\r\n";
vertex_2 = vertex_2->m_right;
vertex_3 = vertex_3->m_right;
if (vertex_2 == vertex_1)
break;
}
}
else
{
f_str += "f ";
++f_count;
auto cv = cp->m_verts.m_head;
auto lv = cv->m_left;
while (true)
{
v3f position = math::mulBasis(cv->m_data.m_vertex->m_position, *object->GetWorldMatrix()) + *pivot;
v2f UVs = cv->m_data.m_uv;
v3f normals = cv->m_data.m_normal;
if (g_ExportOBJ_optimize)
{
WriteOptimize(position, UVs, normals, v_count, vt_count, vn_count, f_str, v_str, vn_str, vt_str);
}
else
{
++g_ExportOBJ_vIndex;
++g_ExportOBJ_vtIndex;
++g_ExportOBJ_vnIndex;
++v_count;
++vt_count;
++vn_count;
WriteData(position, UVs, normals, v_str, vt_str, vn_str);
WriteF(f_str, g_ExportOBJ_vIndex, g_ExportOBJ_vtIndex, g_ExportOBJ_vnIndex);
}
if (cv == lv)
break;
cv = cv->m_right;
}
f_str += "\r\n";
}
if (cp == lp)
break;
cp = cp->m_right;
}
}
fprintf(file, "%s \r\n", v_str.data());
fprintf(file, "# %u vertices \r\n\r\n", v_count);
if (g_ExportOBJ_writeUVs)
{
fprintf(file, "%s \r\n", vt_str.data());
fprintf(file, "# %u texture coordinates \r\n\r\n", vt_count);
}
if (g_ExportOBJ_writeNormals)
{
fprintf(file, "%s \r\n", vn_str.data());
fprintf(file, "# %u normals \r\n\r\n", vn_count);
}
fprintf(file, "%s \r\n", f_str.data());
fprintf(file, "# %u faces \r\n\r\n", f_count);
}
}
// other objects on scene
auto children = object->GetChildren();
if (children->m_head)
{
auto c = children->m_head;
auto l = c->m_left;
while (true)
{
WriteObject(file, mtl_file, c->m_data);
if (c == l)
break;
c = c->m_right;
}
}
}
void WriteOptimize(
const v3f& position,
const v2f& UVs,
const v3f& normals,
u32& v_count,
u32& vt_count,
u32& vn_count,
miStringA& f_str,
miStringA& v_str,
miStringA& vn_str,
miStringA& vt_str
)
{
u32 vI = 0;
u32 vtI = 0;
u32 vnI = 0;
str_for_map.clear();
str_for_map.append_float(position.x);
str_for_map.append_float(position.y);
str_for_map.append_float(position.z);
auto itP = map_position.find(str_for_map.data());
if (itP == map_position.end())
{
++g_ExportOBJ_vIndex;
++v_count;
map_position.insert(std::pair<const char*, u32>(str_for_map.data(), g_ExportOBJ_vIndex));
WritePosition(position, v_str);
vI = g_ExportOBJ_vIndex;
}
else
{
vI = itP->second;
}
if (g_ExportOBJ_writeUVs)
{
str_for_map.clear();
str_for_map.append_float(UVs.x);
str_for_map.append_float(UVs.y);
auto itU = map_UV.find(str_for_map.data());
if (itU == map_UV.end())
{
++g_ExportOBJ_vtIndex;
++vt_count;
map_UV.insert(std::pair<const char*, u32>(str_for_map.data(), g_ExportOBJ_vtIndex));
WriteUV(UVs, vt_str);
vtI = g_ExportOBJ_vtIndex;
}
else
{
vtI = itU->second;
}
}
if (g_ExportOBJ_writeNormals)
{
str_for_map.clear();
str_for_map.append_float(normals.x);
str_for_map.append_float(normals.y);
str_for_map.append_float(normals.z);
auto itN = map_normal.find(str_for_map.data());
if (itN == map_normal.end())
{
++g_ExportOBJ_vnIndex;
++vn_count;
map_normal.insert(std::pair<const char*, u32>(str_for_map.data(), g_ExportOBJ_vnIndex));
WriteNormal(normals, vn_str);
vnI = g_ExportOBJ_vnIndex;
}
else
{
vnI = itN->second;
}
}
WriteF(f_str, vI, vtI, vnI);
}
std::map<std::string, u32> map_position;
std::map<std::string, u32> map_UV;
std::map<std::string, u32> map_normal;
};
void miplStd_ExportOBJ(const wchar_t* fileName) {
//wprintf(L"Export: %s\n", fileName);
g_ExportOBJ_vIndex = 0;
g_ExportOBJ_vtIndex = 0;
g_ExportOBJ_vnIndex = 0;
auto mbStr = g_sdk->StringWideToMultiByte(fileName);
FILE* file = fopen(mbStr.data(), "wb");
fprintf(file, "# Mixer Wavefront OBJ Exporter v1.0\r\n\r\n");
FILE* mtl_file = 0;
if (g_ExportOBJ_createMTL)
{
miString s = g_sdk->FileGetName(fileName);
while (true)
{
auto c = s.pop_back_return();
if (c == L'.')
break;
if (s.size() == 0)
break;
}
miStringA astr;
astr += s.data();
fprintf(file, "mtllib %s.mtl \r\n", astr.data());
astr = mbStr;
astr.replace('\\', '/');
astr.pop_back_before('/');
astr += s.data();
astr += ".mtl";
mtl_file = fopen(astr.data(), "wb");
fprintf(mtl_file, "# Mixer Wavefront OBJ Exporter v1.0\r\n\r\n");
}
OBJWriter o;
o.WriteObject(file, mtl_file, g_sdk->GetRootObject());
if (mtl_file)
fclose(mtl_file);
fclose(file);
}
Download
|
|