/* QGLView Copyright (C) 2012 Ivan Pelipenko peri4ko@gmail.com This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "loader_3ds.h" void Loader3DS::init3DSMesh(GLObjectBase * o, const QVector & smooth) { QVector & vertices(o->VBO().vertices()), & normals(o->VBO().normals()), & uvs(o->VBO().texcoords()); QVector & points(o->points), & puvws(o->puvws), fnormals; QVector & faces(o->faces); Vector3d pos = Vector3d(o->pos()); bool has_uv = !puvws.isEmpty(); Vector3i cf; Vector3d v0, v1, v2, cn0, cn1, cn2; fnormals.resize(faces.size()); for (int i = 0; i < points.size(); ++i) points[i] -= pos; for (int i = 0; i < fnormals.size(); ++i) { cf = faces[i]; v0 = points[cf.p0]; v1 = points[cf.p1]; v2 = points[cf.p2]; fnormals[i] = ((v1 - v0) * (v2 - v0)).normalized(); } uint fcnt = faces.size() * 3; vertices.resize(fcnt * 3); normals.resize(vertices.size()); if (has_uv) uvs.resize(fcnt * 2); uint ind = 0, induv = 0, ncnt0, ncnt1, ncnt2, csg; //qDebug() << faces.size(); if (smooth.isEmpty()) { for (int i = 0; i < faces.size(); ++i) { cf = faces[i]; cn0 = fnormals[i]; v0 = points[cf.p0]; v1 = points[cf.p1]; v2 = points[cf.p2]; vertices[ind] = v0.x; normals[ind] = cn0.x; ++ind; vertices[ind] = v0.y; normals[ind] = cn0.y; ++ind; vertices[ind] = v0.z; normals[ind] = cn0.z; ++ind; vertices[ind] = v1.x; normals[ind] = cn0.x; ++ind; vertices[ind] = v1.y; normals[ind] = cn0.y; ++ind; vertices[ind] = v1.z; normals[ind] = cn0.z; ++ind; vertices[ind] = v2.x; normals[ind] = cn0.x; ++ind; vertices[ind] = v2.y; normals[ind] = cn0.y; ++ind; vertices[ind] = v2.z; normals[ind] = cn0.z; ++ind; if (has_uv) { uvs[induv] = puvws[cf.p0].x; ++induv; uvs[induv] = puvws[cf.p0].y; ++induv; uvs[induv] = puvws[cf.p1].x; ++induv; uvs[induv] = puvws[cf.p1].y; ++induv; uvs[induv] = puvws[cf.p2].x; ++induv; uvs[induv] = puvws[cf.p2].y; ++induv; } } } else { for (int i = 0; i < faces.size(); ++i) { cf = faces[i]; csg = smooth[i]; v0 = points[cf.p0]; v1 = points[cf.p1]; v2 = points[cf.p2]; cn0 = cn1 = cn2 = fnormals[i]; ncnt0 = ncnt1 = ncnt2 = 1; for (int j = 0; j < faces.size(); ++j) { if (csg != smooth[j] || j == i) continue; if (faces[j].p0 == cf.p0 || faces[j].p1 == cf.p0 || faces[j].p2 == cf.p0 || points[faces[j].p0] == v0 || points[faces[j].p1] == v0 || points[faces[j].p2] == v0) { cn0 += fnormals[j]; ++ncnt0; } if (faces[j].p0 == cf.p1 || faces[j].p1 == cf.p1 || faces[j].p2 == cf.p1 || points[faces[j].p0] == v1 || points[faces[j].p1] == v1 || points[faces[j].p2] == v1) { cn1 += fnormals[j]; ++ncnt1; } if (faces[j].p0 == cf.p2 || faces[j].p1 == cf.p2 || faces[j].p2 == cf.p2 || points[faces[j].p0] == v2 || points[faces[j].p1] == v2 || points[faces[j].p2] == v2) { cn2 += fnormals[j]; ++ncnt2; } } cn0 /= ncnt0; cn1 /= ncnt1; cn2 /= ncnt2; vertices[ind] = v0.x; normals[ind] = cn0.x; ++ind; vertices[ind] = v0.y; normals[ind] = cn0.y; ++ind; vertices[ind] = v0.z; normals[ind] = cn0.z; ++ind; vertices[ind] = v1.x; normals[ind] = cn1.x; ++ind; vertices[ind] = v1.y; normals[ind] = cn1.y; ++ind; vertices[ind] = v1.z; normals[ind] = cn1.z; ++ind; vertices[ind] = v2.x; normals[ind] = cn2.x; ++ind; vertices[ind] = v2.y; normals[ind] = cn2.y; ++ind; vertices[ind] = v2.z; normals[ind] = cn2.z; ++ind; if (has_uv) { uvs[induv] = puvws[cf.p0].x; ++induv; uvs[induv] = puvws[cf.p0].y; ++induv; uvs[induv] = puvws[cf.p1].x; ++induv; uvs[induv] = puvws[cf.p1].y; ++induv; uvs[induv] = puvws[cf.p2].x; ++induv; uvs[induv] = puvws[cf.p2].y; ++induv; } } } } Material Loader3DS::materialByName(const QVector & materials, const QString & name) { foreach (const Material & m, materials) if (m.name == name) return m; return Material(); } GLObjectBase * loadFrom3DSFile(const QString & filepath, double scale) { QFile f(filepath); if (!f.exists()) { qDebug() << "[Loader 3DS] Error: can`t open \"" + filepath + "\""; return 0; } f.open(QIODevice::ReadOnly); QDataStream stream(&f); QVector materials; QVector smooth; QVector face_mats; GLObjectBase * root = new GLObjectBase(), * co = 0; Material mat; Loader3DS::Chunk cc; Loader3DS::Face face; Vector3d pos; QString str; ushort cnt; QString name; QByteArray ba; int cur_map = 0; float fl, fl1, matrix[3][3]; uint col; root->setName(QFileInfo(f).baseName()); while (!stream.atEnd()) { stream.readRawData((char * )&cc, sizeof(cc)); switch (cc.id) { case LOADER_3DS_CHUNK_MAIN: /*qDebug() << "main" << cc.size;*/ break; case LOADER_3DS_CHUNK_OBJECTS: /*qDebug() << " objects" << cc.size;*/ break; case LOADER_3DS_CHUNK_OBJECT: if (co != 0) { Loader3DS::init3DSMesh(co, smooth); root->addChild(co); } co = new GLObjectBase(); co->setName(readCharsUntilNull(stream)); smooth.clear(); //qDebug() << " object" << co->name(); break; case LOADER_3DS_CHUNK_MESH: /*qDebug() << " mesh" << cc.size;*/ break; case LOADER_3DS_CHUNK_VERTLIST: stream.readRawData((char * )&cnt, sizeof(ushort)); co->points.resize(cnt); //qDebug() << " vertices" << cnt; for (int i = 0; i < cnt; ++i) { stream.readRawData((char * )&co->points[i].x, sizeof(float)); stream.readRawData((char * )&co->points[i].y, sizeof(float)); stream.readRawData((char * )&co->points[i].z, sizeof(float)); co->points[i] *= scale; } break; case LOADER_3DS_CHUNK_FACELIST: stream.readRawData((char * )&cnt, sizeof(ushort)); co->faces.resize(cnt); //qDebug() << " faces" << cnt; for (int i = 0; i < cnt; ++i) { stream.readRawData((char * )&face, sizeof(Loader3DS::Face)); co->faces[i].p0 = face.v0; co->faces[i].p1 = face.v1; co->faces[i].p2 = face.v2; } break; case LOADER_3DS_CHUNK_FACEMAT: name = readCharsUntilNull(stream); stream.readRawData((char * )&cnt, sizeof(ushort)); face_mats.resize(cnt); for (int i = 0; i < cnt; ++i) stream.readRawData((char * )&(face_mats[i]), sizeof(ushort)); //qDebug() << " facemat name" << name << cnt; co->material().name = name; break; case LOADER_3DS_CHUNK_MAPLIST: stream.readRawData((char * )&cnt, sizeof(ushort)); co->puvws.resize(cnt); //qDebug() << " texcoords" << cnt; for (int i = 0; i < cnt; ++i) { stream.readRawData((char * )&co->puvws[i].x, sizeof(float)); stream.readRawData((char * )&co->puvws[i].y, sizeof(float)); } break; case LOADER_3DS_CHUNK_SMOOTH: cnt = co->faces.size(); smooth.resize(cnt); //qDebug() << " smooth" << cnt; for (int i = 0; i < cnt; ++i) stream.readRawData((char * )&smooth[i], sizeof(uint)); break; case LOADER_3DS_CHUNK_TRMATRIX: //qDebug() << co->name(); for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) stream.readRawData((char * )&(matrix[i][j]), sizeof(float)); //qDebug() << matrix[i][0] << matrix[i][1] << matrix[i][2]; } stream.readRawData((char * )&pos, sizeof(Vector3d)); pos *= scale; //qDebug() << "pos =" << pos; co->setPos(pos.toQVector3D()); break; case LOADER_3DS_CHUNK_LIGHT: //qDebug() << " light" << cc.size; str = co->name(); delete co; co = new Light(); co->setName(str); stream.readRawData((char * )&pos, sizeof(Vector3d)); pos *= scale; co->setPos(pos.toQVector3D()); break; case LOADER_3DS_CHUNK_SPOTLIGHT: stream.readRawData((char * )&pos, sizeof(Vector3d)); pos *= scale; globject_cast(co)->light_type = Light::Cone; globject_cast(co)->direction = (pos.toQVector3D() - co->pos()).normalized(); stream.readRawData((char * )&fl1, sizeof(float)); stream.readRawData((char * )&fl, sizeof(float)); fl1 /= 2.f; fl /= 2.f; globject_cast(co)->angle_spread = fl; globject_cast(co)->angle_decay_exp = (fl - fl1) / fl * 16.; //qDebug() << "spotlight" << globject_cast(co)->direction << globject_cast(co)->angle_spread; break; case LOADER_3DS_CHUNK_LIGHT_OFF: stream.skipRawData(cc.size - 6); co->hide(); break; case LOADER_3DS_CHUNK_ATTENUATION_ON: stream.skipRawData(cc.size - 6); fl = globject_cast(co)->decay_end; //fl1 = globject_cast(co)->decay_start; globject_cast(co)->decay_quadratic = 4. / fl; //qDebug() << "decay" << globject_cast(co)->decay_quadratic; break; case LOADER_3DS_CHUNK_COLOR_F: stream.readRawData((char * )&pos, sizeof(Vector3d)); co->setColor(QColor::fromRgbF(pos.x, pos.y, pos.z)); //qDebug() << " color_f" << co->color(); break; case LOADER_3DS_CHUNK_COLOR_B: stream.readRawData((char * )&col, 3); co->setColor(QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2])); //qDebug() << " color_b" << co->color(); break; case LOADER_3DS_CHUNK_MULTIPLIER: stream.readRawData((char * )&fl, sizeof(float)); globject_cast(co)->intensity = fl; //qDebug() << " multiplier" << fl; break; case LOADER_3DS_CHUNK_RANGE_START: stream.readRawData((char * )&fl, sizeof(float)); globject_cast(co)->decay_start = fl; //qDebug() << " range start" << fl; break; case LOADER_3DS_CHUNK_RANGE_END: stream.readRawData((char * )&fl, sizeof(float)); globject_cast(co)->decay_end = fl; //qDebug() << " range end" << fl; break; case LOADER_3DS_CHUNK_MATERIAL: //stream.skipRawData(cc.size - 6); if (!mat.name.isEmpty()) materials << mat; mat = Material(); break; case LOADER_3DS_CHUNK_MATERIAL_NAME: mat.name = readCharsUntilNull(stream); //qDebug() << "matname" << mat.name; break; case LOADER_3DS_CHUNK_AMBIENT_COLOR: stream.skipRawData(cc.size - 9); stream.readRawData((char * )&col, 3); mat.color_self_illumination = QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2]); //qDebug() << "mat diffuse" << mat.color_diffuse; break; case LOADER_3DS_CHUNK_DIFFUSE_COLOR: stream.skipRawData(cc.size - 9); stream.readRawData((char * )&col, 3); mat.color_diffuse = QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2]); //qDebug() << "mat diffuse" << mat.color_diffuse; break; case LOADER_3DS_CHUNK_SPECULAR_COLOR: stream.skipRawData(cc.size - 9); stream.readRawData((char * )&col, 3); mat.color_specular = QColor::fromRgb(((uchar * )&col)[0], ((uchar * )&col)[1], ((uchar * )&col)[2]); //qDebug() << "mat diffuse" << mat.color_diffuse; break; case LOADER_3DS_CHUNK_TEXTURE_MAP: cur_map = LOADER_3DS_CHUNK_TEXTURE_MAP; break; case LOADER_3DS_CHUNK_BUMP_MAP: cur_map = LOADER_3DS_CHUNK_BUMP_MAP; break; case LOADER_3DS_CHUNK_REFLECTION_MAP: cur_map = LOADER_3DS_CHUNK_REFLECTION_MAP; break; case LOADER_3DS_CHUNK_MAP_FILENAME: name = readCharsUntilNull(stream); //qDebug() << " mat map" << QString::number(cur_map, 16) << name; switch (cur_map) { case LOADER_3DS_CHUNK_TEXTURE_MAP: mat.map_diffuse.bitmap_path = name; break; case LOADER_3DS_CHUNK_BUMP_MAP: mat.map_bump.bitmap_path = name; break; } break; default: /*qDebug() << "???" << QString::number(cc.id, 16).rightJustified(4, '0') << cc.size;*/ stream.skipRawData(cc.size - 6); } } if (!mat.name.isEmpty()) materials << mat; foreach (const Material & m, materials) qDebug() << m.name; if (co != 0) { Loader3DS::init3DSMesh(co, smooth); root->addChild(co); } for (int i = 0; i < root->childCount(); ++i) root->child(i)->material() = Loader3DS::materialByName(materials, root->child(i)->material().name); qDebug() << "[Loader 3DS] Loaded" << root->childCount() << "objects from" << filepath; return root; }