349 lines
12 KiB
C++
349 lines
12 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "loader_3ds.h"
|
|
|
|
|
|
void Loader3DS::init3DSMesh(GLObjectBase * o, const QVector<uint> & smooth) {
|
|
QVector<GLfloat> & vertices(o->VBO().vertices()), & normals(o->VBO().normals()), & uvs(o->VBO().texcoords());
|
|
QVector<Vector3d> & points(o->points), & puvws(o->puvws), fnormals;
|
|
QVector<Vector3i> & 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<Material> & 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<Material> materials;
|
|
QVector<uint> smooth;
|
|
QVector<ushort> 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<Light * >(co)->light_type = Light::Cone;
|
|
globject_cast<Light * >(co)->direction = (pos.toQVector3D() - co->pos()).normalized();
|
|
stream.readRawData((char * )&fl1, sizeof(float));
|
|
stream.readRawData((char * )&fl, sizeof(float));
|
|
globject_cast<Light * >(co)->angle_start = fl1;
|
|
globject_cast<Light * >(co)->angle_end = fl;
|
|
//qDebug() << "spotlight" << globject_cast<Light * >(co)->direction << globject_cast<Light * >(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<Light * >(co)->decay_end;
|
|
//fl1 = globject_cast<Light * >(co)->decay_start;
|
|
globject_cast<Light * >(co)->decay_quadratic = 4. / fl;
|
|
//qDebug() << "decay" << globject_cast<Light * >(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<Light * >(co)->intensity = fl;
|
|
//qDebug() << " multiplier" << fl;
|
|
break;
|
|
case LOADER_3DS_CHUNK_RANGE_START:
|
|
stream.readRawData((char * )&fl, sizeof(float));
|
|
globject_cast<Light * >(co)->decay_start = fl;
|
|
//qDebug() << " range start" << fl;
|
|
break;
|
|
case LOADER_3DS_CHUNK_RANGE_END:
|
|
stream.readRawData((char * )&fl, sizeof(float));
|
|
globject_cast<Light * >(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_normal.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;
|
|
}
|