mirror of
https://github.com/overte-org/overte.git
synced 2025-07-22 22:34:12 +02:00
Drafting the materials for FBXReader
This commit is contained in:
parent
e06e50cb5c
commit
1e9fce2a61
13 changed files with 816 additions and 467 deletions
|
@ -38,7 +38,7 @@
|
||||||
//#define DEBUG_FBXREADER
|
//#define DEBUG_FBXREADER
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
/*
|
||||||
struct TextureParam {
|
struct TextureParam {
|
||||||
glm::vec2 UVTranslation;
|
glm::vec2 UVTranslation;
|
||||||
glm::vec2 UVScaling;
|
glm::vec2 UVScaling;
|
||||||
|
@ -80,22 +80,27 @@ struct TextureParam {
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
*/
|
||||||
bool FBXMesh::hasSpecularTexture() const {
|
bool FBXMesh::hasSpecularTexture() const {
|
||||||
foreach (const FBXMeshPart& part, parts) {
|
// TODO fix that in model.cpp / payload
|
||||||
|
/* foreach (const FBXMeshPart& part, parts) {
|
||||||
if (!part.specularTexture.filename.isEmpty()) {
|
if (!part.specularTexture.filename.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FBXMesh::hasEmissiveTexture() const {
|
bool FBXMesh::hasEmissiveTexture() const {
|
||||||
|
// TODO Fix that in model cpp / payload
|
||||||
|
/*
|
||||||
foreach (const FBXMeshPart& part, parts) {
|
foreach (const FBXMeshPart& part, parts) {
|
||||||
if (!part.emissiveTexture.filename.isEmpty()) {
|
if (!part.emissiveTexture.filename.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,325 +189,7 @@ static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
|
||||||
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
|
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
|
||||||
static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >();
|
static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >();
|
||||||
|
|
||||||
template<class T> int streamSize() {
|
|
||||||
return sizeof(T);
|
|
||||||
}
|
|
||||||
|
|
||||||
template<bool> int streamSize() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
|
||||||
quint32 arrayLength;
|
|
||||||
quint32 encoding;
|
|
||||||
quint32 compressedLength;
|
|
||||||
|
|
||||||
in >> arrayLength;
|
|
||||||
in >> encoding;
|
|
||||||
in >> compressedLength;
|
|
||||||
position += sizeof(quint32) * 3;
|
|
||||||
|
|
||||||
QVector<T> values;
|
|
||||||
const unsigned int DEFLATE_ENCODING = 1;
|
|
||||||
if (encoding == DEFLATE_ENCODING) {
|
|
||||||
// preface encoded data with uncompressed length
|
|
||||||
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
|
||||||
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
|
||||||
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
|
||||||
position += compressedLength;
|
|
||||||
QByteArray uncompressed = qUncompress(compressed);
|
|
||||||
QDataStream uncompressedIn(uncompressed);
|
|
||||||
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
|
|
||||||
uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
|
||||||
for (quint32 i = 0; i < arrayLength; i++) {
|
|
||||||
T value;
|
|
||||||
uncompressedIn >> value;
|
|
||||||
values.append(value);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (quint32 i = 0; i < arrayLength; i++) {
|
|
||||||
T value;
|
|
||||||
in >> value;
|
|
||||||
position += streamSize<T>();
|
|
||||||
values.append(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return QVariant::fromValue(values);
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant parseBinaryFBXProperty(QDataStream& in, int& position) {
|
|
||||||
char ch;
|
|
||||||
in.device()->getChar(&ch);
|
|
||||||
position++;
|
|
||||||
switch (ch) {
|
|
||||||
case 'Y': {
|
|
||||||
qint16 value;
|
|
||||||
in >> value;
|
|
||||||
position += sizeof(qint16);
|
|
||||||
return QVariant::fromValue(value);
|
|
||||||
}
|
|
||||||
case 'C': {
|
|
||||||
bool value;
|
|
||||||
in >> value;
|
|
||||||
position++;
|
|
||||||
return QVariant::fromValue(value);
|
|
||||||
}
|
|
||||||
case 'I': {
|
|
||||||
qint32 value;
|
|
||||||
in >> value;
|
|
||||||
position += sizeof(qint32);
|
|
||||||
return QVariant::fromValue(value);
|
|
||||||
}
|
|
||||||
case 'F': {
|
|
||||||
float value;
|
|
||||||
in >> value;
|
|
||||||
position += sizeof(float);
|
|
||||||
return QVariant::fromValue(value);
|
|
||||||
}
|
|
||||||
case 'D': {
|
|
||||||
double value;
|
|
||||||
in >> value;
|
|
||||||
position += sizeof(double);
|
|
||||||
return QVariant::fromValue(value);
|
|
||||||
}
|
|
||||||
case 'L': {
|
|
||||||
qint64 value;
|
|
||||||
in >> value;
|
|
||||||
position += sizeof(qint64);
|
|
||||||
return QVariant::fromValue(value);
|
|
||||||
}
|
|
||||||
case 'f': {
|
|
||||||
return readBinaryArray<float>(in, position);
|
|
||||||
}
|
|
||||||
case 'd': {
|
|
||||||
return readBinaryArray<double>(in, position);
|
|
||||||
}
|
|
||||||
case 'l': {
|
|
||||||
return readBinaryArray<qint64>(in, position);
|
|
||||||
}
|
|
||||||
case 'i': {
|
|
||||||
return readBinaryArray<qint32>(in, position);
|
|
||||||
}
|
|
||||||
case 'b': {
|
|
||||||
return readBinaryArray<bool>(in, position);
|
|
||||||
}
|
|
||||||
case 'S':
|
|
||||||
case 'R': {
|
|
||||||
quint32 length;
|
|
||||||
in >> length;
|
|
||||||
position += sizeof(quint32) + length;
|
|
||||||
return QVariant::fromValue(in.device()->read(length));
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw QString("Unknown property type: ") + ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
FBXNode parseBinaryFBXNode(QDataStream& in, int& position) {
|
|
||||||
qint32 endOffset;
|
|
||||||
quint32 propertyCount;
|
|
||||||
quint32 propertyListLength;
|
|
||||||
quint8 nameLength;
|
|
||||||
|
|
||||||
in >> endOffset;
|
|
||||||
in >> propertyCount;
|
|
||||||
in >> propertyListLength;
|
|
||||||
in >> nameLength;
|
|
||||||
position += sizeof(quint32) * 3 + sizeof(quint8);
|
|
||||||
|
|
||||||
FBXNode node;
|
|
||||||
const int MIN_VALID_OFFSET = 40;
|
|
||||||
if (endOffset < MIN_VALID_OFFSET || nameLength == 0) {
|
|
||||||
// use a null name to indicate a null node
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
node.name = in.device()->read(nameLength);
|
|
||||||
position += nameLength;
|
|
||||||
|
|
||||||
for (quint32 i = 0; i < propertyCount; i++) {
|
|
||||||
node.properties.append(parseBinaryFBXProperty(in, position));
|
|
||||||
}
|
|
||||||
|
|
||||||
while (endOffset > position) {
|
|
||||||
FBXNode child = parseBinaryFBXNode(in, position);
|
|
||||||
if (child.name.isNull()) {
|
|
||||||
return node;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
node.children.append(child);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Tokenizer {
|
|
||||||
public:
|
|
||||||
|
|
||||||
Tokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { }
|
|
||||||
|
|
||||||
enum SpecialToken {
|
|
||||||
NO_TOKEN = -1,
|
|
||||||
NO_PUSHBACKED_TOKEN = -1,
|
|
||||||
DATUM_TOKEN = 0x100
|
|
||||||
};
|
|
||||||
|
|
||||||
int nextToken();
|
|
||||||
const QByteArray& getDatum() const { return _datum; }
|
|
||||||
|
|
||||||
void pushBackToken(int token) { _pushedBackToken = token; }
|
|
||||||
void ungetChar(char ch) { _device->ungetChar(ch); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
QIODevice* _device;
|
|
||||||
QByteArray _datum;
|
|
||||||
int _pushedBackToken;
|
|
||||||
};
|
|
||||||
|
|
||||||
int Tokenizer::nextToken() {
|
|
||||||
if (_pushedBackToken != NO_PUSHBACKED_TOKEN) {
|
|
||||||
int token = _pushedBackToken;
|
|
||||||
_pushedBackToken = NO_PUSHBACKED_TOKEN;
|
|
||||||
return token;
|
|
||||||
}
|
|
||||||
|
|
||||||
char ch;
|
|
||||||
while (_device->getChar(&ch)) {
|
|
||||||
if (QChar(ch).isSpace()) {
|
|
||||||
continue; // skip whitespace
|
|
||||||
}
|
|
||||||
switch (ch) {
|
|
||||||
case ';':
|
|
||||||
_device->readLine(); // skip the comment
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ':':
|
|
||||||
case '{':
|
|
||||||
case '}':
|
|
||||||
case ',':
|
|
||||||
return ch; // special punctuation
|
|
||||||
|
|
||||||
case '\"':
|
|
||||||
_datum = "";
|
|
||||||
while (_device->getChar(&ch)) {
|
|
||||||
if (ch == '\"') { // end on closing quote
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (ch == '\\') { // handle escaped quotes
|
|
||||||
if (_device->getChar(&ch) && ch != '\"') {
|
|
||||||
_datum.append('\\');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_datum.append(ch);
|
|
||||||
}
|
|
||||||
return DATUM_TOKEN;
|
|
||||||
|
|
||||||
default:
|
|
||||||
_datum = "";
|
|
||||||
_datum.append(ch);
|
|
||||||
while (_device->getChar(&ch)) {
|
|
||||||
if (QChar(ch).isSpace() || ch == ';' || ch == ':' || ch == '{' || ch == '}' || ch == ',' || ch == '\"') {
|
|
||||||
ungetChar(ch); // read until we encounter a special character, then replace it
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_datum.append(ch);
|
|
||||||
}
|
|
||||||
return DATUM_TOKEN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NO_TOKEN;
|
|
||||||
}
|
|
||||||
|
|
||||||
FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
|
|
||||||
FBXNode node;
|
|
||||||
|
|
||||||
if (tokenizer.nextToken() != Tokenizer::DATUM_TOKEN) {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
node.name = tokenizer.getDatum();
|
|
||||||
|
|
||||||
if (tokenizer.nextToken() != ':') {
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
int token;
|
|
||||||
bool expectingDatum = true;
|
|
||||||
while ((token = tokenizer.nextToken()) != Tokenizer::NO_TOKEN) {
|
|
||||||
if (token == '{') {
|
|
||||||
for (FBXNode child = parseTextFBXNode(tokenizer); !child.name.isNull(); child = parseTextFBXNode(tokenizer)) {
|
|
||||||
node.children.append(child);
|
|
||||||
}
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
if (token == ',') {
|
|
||||||
expectingDatum = true;
|
|
||||||
|
|
||||||
} else if (token == Tokenizer::DATUM_TOKEN && expectingDatum) {
|
|
||||||
QByteArray datum = tokenizer.getDatum();
|
|
||||||
if ((token = tokenizer.nextToken()) == ':') {
|
|
||||||
tokenizer.ungetChar(':');
|
|
||||||
tokenizer.pushBackToken(Tokenizer::DATUM_TOKEN);
|
|
||||||
return node;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
tokenizer.pushBackToken(token);
|
|
||||||
node.properties.append(datum);
|
|
||||||
expectingDatum = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
tokenizer.pushBackToken(token);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
FBXNode parseFBX(QIODevice* device) {
|
|
||||||
// verify the prolog
|
|
||||||
const QByteArray BINARY_PROLOG = "Kaydara FBX Binary ";
|
|
||||||
if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) {
|
|
||||||
// parse as a text file
|
|
||||||
FBXNode top;
|
|
||||||
Tokenizer tokenizer(device);
|
|
||||||
while (device->bytesAvailable()) {
|
|
||||||
FBXNode next = parseTextFBXNode(tokenizer);
|
|
||||||
if (next.name.isNull()) {
|
|
||||||
return top;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
top.children.append(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return top;
|
|
||||||
}
|
|
||||||
QDataStream in(device);
|
|
||||||
in.setByteOrder(QDataStream::LittleEndian);
|
|
||||||
in.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
|
||||||
|
|
||||||
// see http://code.blender.org/index.php/2013/08/fbx-binary-file-format-specification/ for an explanation
|
|
||||||
// of the FBX binary format
|
|
||||||
|
|
||||||
// skip the rest of the header
|
|
||||||
const int HEADER_SIZE = 27;
|
|
||||||
in.skipRawData(HEADER_SIZE);
|
|
||||||
int position = HEADER_SIZE;
|
|
||||||
|
|
||||||
// parse the top-level node
|
|
||||||
FBXNode top;
|
|
||||||
while (device->bytesAvailable()) {
|
|
||||||
FBXNode next = parseBinaryFBXNode(in, position);
|
|
||||||
if (next.name.isNull()) {
|
|
||||||
return top;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
top.children.append(next);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return top;
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<glm::vec4> createVec4Vector(const QVector<double>& doubleVector) {
|
QVector<glm::vec4> createVec4Vector(const QVector<double>& doubleVector) {
|
||||||
QVector<glm::vec4> values;
|
QVector<glm::vec4> values;
|
||||||
|
@ -693,7 +380,7 @@ public:
|
||||||
glm::vec3 rotationMax; // radians
|
glm::vec3 rotationMax; // radians
|
||||||
};
|
};
|
||||||
|
|
||||||
glm::mat4 getGlobalTransform(const QMultiHash<QString, QString>& parentMap,
|
glm::mat4 getGlobalTransform(const QMultiHash<QString, QString>& _connectionParentMap,
|
||||||
const QHash<QString, FBXModel>& models, QString nodeID, bool mixamoHack) {
|
const QHash<QString, FBXModel>& models, QString nodeID, bool mixamoHack) {
|
||||||
glm::mat4 globalTransform;
|
glm::mat4 globalTransform;
|
||||||
while (!nodeID.isNull()) {
|
while (!nodeID.isNull()) {
|
||||||
|
@ -704,7 +391,7 @@ glm::mat4 getGlobalTransform(const QMultiHash<QString, QString>& parentMap,
|
||||||
// there's something weird about the models from Mixamo Fuse; they don't skin right with the full transform
|
// there's something weird about the models from Mixamo Fuse; they don't skin right with the full transform
|
||||||
return globalTransform;
|
return globalTransform;
|
||||||
}
|
}
|
||||||
QList<QString> parentIDs = parentMap.values(nodeID);
|
QList<QString> parentIDs = _connectionParentMap.values(nodeID);
|
||||||
nodeID = QString();
|
nodeID = QString();
|
||||||
foreach (const QString& parentID, parentIDs) {
|
foreach (const QString& parentID, parentIDs) {
|
||||||
if (models.contains(parentID)) {
|
if (models.contains(parentID)) {
|
||||||
|
@ -738,17 +425,6 @@ void printNode(const FBXNode& node, int indentLevel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Material {
|
|
||||||
public:
|
|
||||||
glm::vec3 diffuse;
|
|
||||||
glm::vec3 specular;
|
|
||||||
glm::vec3 emissive;
|
|
||||||
float shininess;
|
|
||||||
float opacity;
|
|
||||||
QString id;
|
|
||||||
model::MaterialPointer _material;
|
|
||||||
};
|
|
||||||
|
|
||||||
class Cluster {
|
class Cluster {
|
||||||
public:
|
public:
|
||||||
QVector<int> indices;
|
QVector<int> indices;
|
||||||
|
@ -756,19 +432,19 @@ public:
|
||||||
glm::mat4 transformLink;
|
glm::mat4 transformLink;
|
||||||
};
|
};
|
||||||
|
|
||||||
void appendModelIDs(const QString& parentID, const QMultiHash<QString, QString>& childMap,
|
void appendModelIDs(const QString& parentID, const QMultiHash<QString, QString>& _connectionChildMap,
|
||||||
QHash<QString, FBXModel>& models, QSet<QString>& remainingModels, QVector<QString>& modelIDs) {
|
QHash<QString, FBXModel>& models, QSet<QString>& remainingModels, QVector<QString>& modelIDs) {
|
||||||
if (remainingModels.contains(parentID)) {
|
if (remainingModels.contains(parentID)) {
|
||||||
modelIDs.append(parentID);
|
modelIDs.append(parentID);
|
||||||
remainingModels.remove(parentID);
|
remainingModels.remove(parentID);
|
||||||
}
|
}
|
||||||
int parentIndex = modelIDs.size() - 1;
|
int parentIndex = modelIDs.size() - 1;
|
||||||
foreach (const QString& childID, childMap.values(parentID)) {
|
foreach (const QString& childID, _connectionChildMap.values(parentID)) {
|
||||||
if (remainingModels.contains(childID)) {
|
if (remainingModels.contains(childID)) {
|
||||||
FBXModel& model = models[childID];
|
FBXModel& model = models[childID];
|
||||||
if (model.parentIndex == -1) {
|
if (model.parentIndex == -1) {
|
||||||
model.parentIndex = parentIndex;
|
model.parentIndex = parentIndex;
|
||||||
appendModelIDs(childID, childMap, models, remainingModels, modelIDs);
|
appendModelIDs(childID, _connectionChildMap, models, remainingModels, modelIDs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1235,11 +911,11 @@ void addBlendshapes(const ExtractedBlendshape& extracted, const QList<WeightedIn
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QString getTopModelID(const QMultiHash<QString, QString>& parentMap,
|
QString getTopModelID(const QMultiHash<QString, QString>& _connectionParentMap,
|
||||||
const QHash<QString, FBXModel>& models, const QString& modelID) {
|
const QHash<QString, FBXModel>& models, const QString& modelID) {
|
||||||
QString topID = modelID;
|
QString topID = modelID;
|
||||||
forever {
|
forever {
|
||||||
foreach (const QString& parentID, parentMap.values(topID)) {
|
foreach (const QString& parentID, _connectionParentMap.values(topID)) {
|
||||||
if (models.contains(parentID)) {
|
if (models.contains(parentID)) {
|
||||||
topID = parentID;
|
topID = parentID;
|
||||||
goto outerContinue;
|
goto outerContinue;
|
||||||
|
@ -1303,10 +979,10 @@ FBXTexture getTexture(const QString& textureID,
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool checkMaterialsHaveTextures(const QHash<QString, Material>& materials,
|
bool checkMaterialsHaveTextures(const QHash<QString, FBXMaterial>& materials,
|
||||||
const QHash<QString, QByteArray>& textureFilenames, const QMultiHash<QString, QString>& childMap) {
|
const QHash<QString, QByteArray>& textureFilenames, const QMultiHash<QString, QString>& _connectionChildMap) {
|
||||||
foreach (const QString& materialID, materials.keys()) {
|
foreach (const QString& materialID, materials.keys()) {
|
||||||
foreach (const QString& childID, childMap.values(materialID)) {
|
foreach (const QString& childID, _connectionChildMap.values(materialID)) {
|
||||||
if (textureFilenames.contains(childID)) {
|
if (textureFilenames.contains(childID)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1530,29 +1206,21 @@ QByteArray fileOnUrl(const QByteArray& filenameString, const QString& url) {
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) {
|
FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QString& url) {
|
||||||
|
const FBXNode& node = _fbxNode;
|
||||||
QHash<QString, ExtractedMesh> meshes;
|
QHash<QString, ExtractedMesh> meshes;
|
||||||
QHash<QString, QString> modelIDsToNames;
|
QHash<QString, QString> modelIDsToNames;
|
||||||
QHash<QString, int> meshIDsToMeshIndices;
|
QHash<QString, int> meshIDsToMeshIndices;
|
||||||
QHash<QString, QString> ooChildToParent;
|
QHash<QString, QString> ooChildToParent;
|
||||||
|
|
||||||
QVector<ExtractedBlendshape> blendshapes;
|
QVector<ExtractedBlendshape> blendshapes;
|
||||||
QMultiHash<QString, QString> parentMap;
|
|
||||||
QMultiHash<QString, QString> childMap;
|
|
||||||
QHash<QString, FBXModel> models;
|
QHash<QString, FBXModel> models;
|
||||||
QHash<QString, Cluster> clusters;
|
QHash<QString, Cluster> clusters;
|
||||||
QHash<QString, AnimationCurve> animationCurves;
|
QHash<QString, AnimationCurve> animationCurves;
|
||||||
QHash<QString, QString> textureNames;
|
|
||||||
QHash<QString, QByteArray> textureFilenames;
|
|
||||||
QHash<QString, TextureParam> textureParams;
|
|
||||||
QHash<QByteArray, QByteArray> textureContent;
|
|
||||||
QHash<QString, Material> materials;
|
|
||||||
QHash<QString, QString> typeFlags;
|
QHash<QString, QString> typeFlags;
|
||||||
QHash<QString, QString> diffuseTextures;
|
|
||||||
QHash<QString, QString> bumpTextures;
|
|
||||||
QHash<QString, QString> specularTextures;
|
|
||||||
QHash<QString, QString> emissiveTextures;
|
|
||||||
QHash<QString, QString> ambientTextures;
|
|
||||||
QHash<QString, QString> localRotations;
|
QHash<QString, QString> localRotations;
|
||||||
QHash<QString, QString> xComponents;
|
QHash<QString, QString> xComponents;
|
||||||
QHash<QString, QString> yComponents;
|
QHash<QString, QString> yComponents;
|
||||||
|
@ -1580,8 +1248,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
QString jointLeftToeID;
|
QString jointLeftToeID;
|
||||||
QString jointRightToeID;
|
QString jointRightToeID;
|
||||||
|
|
||||||
float lightmapOffset = 0.0f;
|
|
||||||
|
|
||||||
QVector<QString> humanIKJointNames;
|
QVector<QString> humanIKJointNames;
|
||||||
for (int i = 0;; i++) {
|
for (int i = 0;; i++) {
|
||||||
QByteArray jointName = HUMANIK_JOINTS[i];
|
QByteArray jointName = HUMANIK_JOINTS[i];
|
||||||
|
@ -1618,6 +1285,8 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
FBXGeometry* geometryPtr = new FBXGeometry;
|
FBXGeometry* geometryPtr = new FBXGeometry;
|
||||||
FBXGeometry& geometry = *geometryPtr;
|
FBXGeometry& geometry = *geometryPtr;
|
||||||
|
|
||||||
|
geometry._asset.reset(new model::Asset());
|
||||||
|
|
||||||
float unitScaleFactor = 1.0f;
|
float unitScaleFactor = 1.0f;
|
||||||
glm::vec3 ambientColor;
|
glm::vec3 ambientColor;
|
||||||
QString hifiGlobalNodeID;
|
QString hifiGlobalNodeID;
|
||||||
|
@ -1942,8 +1611,8 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
textureContent.insert(filename, content);
|
textureContent.insert(filename, content);
|
||||||
}
|
}
|
||||||
} else if (object.name == "Material") {
|
} else if (object.name == "Material") {
|
||||||
Material material = { glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(), 96.0f, 1.0f,
|
FBXMaterial material = { glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(1.0f, 1.0f, 1.0f), glm::vec3(), glm::vec2(0.f, 1.0f), 96.0f, 1.0f,
|
||||||
QString(""), model::MaterialPointer(NULL)};
|
QString(""), model::MaterialTable::INVALID_ID};
|
||||||
foreach (const FBXNode& subobject, object.children) {
|
foreach (const FBXNode& subobject, object.children) {
|
||||||
bool properties = false;
|
bool properties = false;
|
||||||
QByteArray propertyName;
|
QByteArray propertyName;
|
||||||
|
@ -1962,13 +1631,13 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
foreach (const FBXNode& property, subobject.children) {
|
foreach (const FBXNode& property, subobject.children) {
|
||||||
if (property.name == propertyName) {
|
if (property.name == propertyName) {
|
||||||
if (property.properties.at(0) == "DiffuseColor") {
|
if (property.properties.at(0) == "DiffuseColor") {
|
||||||
material.diffuse = getVec3(property.properties, index);
|
material.diffuseColor = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "SpecularColor") {
|
} else if (property.properties.at(0) == "SpecularColor") {
|
||||||
material.specular = getVec3(property.properties, index);
|
material.specularColor = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Emissive") {
|
} else if (property.properties.at(0) == "Emissive") {
|
||||||
material.emissive = getVec3(property.properties, index);
|
material.emissiveColor = getVec3(property.properties, index);
|
||||||
|
|
||||||
} else if (property.properties.at(0) == "Shininess") {
|
} else if (property.properties.at(0) == "Shininess") {
|
||||||
material.shininess = property.properties.at(index).value<double>();
|
material.shininess = property.properties.at(index).value<double>();
|
||||||
|
@ -1999,25 +1668,9 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
material.id = getID(object.properties);
|
material.materialID = getID(object.properties);
|
||||||
|
|
||||||
material._material = make_shared<model::Material>();
|
_fbxMaterials.insert(material.materialID, material);
|
||||||
material._material->setEmissive(material.emissive);
|
|
||||||
if (glm::all(glm::equal(material.diffuse, glm::vec3(0.0f)))) {
|
|
||||||
material._material->setDiffuse(material.diffuse);
|
|
||||||
} else {
|
|
||||||
material._material->setDiffuse(material.diffuse);
|
|
||||||
}
|
|
||||||
material._material->setMetallic(glm::length(material.specular));
|
|
||||||
material._material->setGloss(material.shininess);
|
|
||||||
|
|
||||||
if (material.opacity <= 0.0f) {
|
|
||||||
material._material->setOpacity(1.0f);
|
|
||||||
} else {
|
|
||||||
material._material->setOpacity(material.opacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
materials.insert(material.id, material);
|
|
||||||
|
|
||||||
} else if (object.name == "NodeAttribute") {
|
} else if (object.name == "NodeAttribute") {
|
||||||
#if defined(DEBUG_FBXREADER)
|
#if defined(DEBUG_FBXREADER)
|
||||||
|
@ -2106,11 +1759,11 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
if (!hifiGlobalNodeID.isEmpty() && (parentID == hifiGlobalNodeID)) {
|
if (!hifiGlobalNodeID.isEmpty() && (parentID == hifiGlobalNodeID)) {
|
||||||
std::map< QString, FBXLight >::iterator lit = lights.find(childID);
|
std::map< QString, FBXLight >::iterator lit = lights.find(childID);
|
||||||
if (lit != lights.end()) {
|
if (lit != lights.end()) {
|
||||||
lightmapLevel = (*lit).second.intensity;
|
_lightmapLevel = (*lit).second.intensity;
|
||||||
if (lightmapLevel <= 0.0f) {
|
if (_lightmapLevel <= 0.0f) {
|
||||||
loadLightmaps = false;
|
_loadLightmaps = false;
|
||||||
}
|
}
|
||||||
lightmapOffset = glm::clamp((*lit).second.color.x, 0.f, 1.f);
|
_lightmapOffset = glm::clamp((*lit).second.color.x, 0.f, 1.f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2144,18 +1797,18 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
} else if (type.contains("shininess")) {
|
} else if (type.contains("shininess")) {
|
||||||
counter++;
|
counter++;
|
||||||
|
|
||||||
} else if (loadLightmaps && type.contains("emissive")) {
|
} else if (_loadLightmaps && type.contains("emissive")) {
|
||||||
emissiveTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
emissiveTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
|
|
||||||
} else if (loadLightmaps && type.contains("ambient")) {
|
} else if (_loadLightmaps && type.contains("ambient")) {
|
||||||
ambientTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
ambientTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
} else {
|
} else {
|
||||||
QString typenam = type.data();
|
QString typenam = type.data();
|
||||||
counter++;
|
counter++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
parentMap.insert(getID(connection.properties, 1), getID(connection.properties, 2));
|
_connectionParentMap.insert(getID(connection.properties, 1), getID(connection.properties, 2));
|
||||||
childMap.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
_connectionChildMap.insert(getID(connection.properties, 2), getID(connection.properties, 1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2184,15 +1837,15 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
if (!lights.empty()) {
|
if (!lights.empty()) {
|
||||||
if (hifiGlobalNodeID.isEmpty()) {
|
if (hifiGlobalNodeID.isEmpty()) {
|
||||||
std::map< QString, FBXLight >::iterator l = lights.begin();
|
std::map< QString, FBXLight >::iterator l = lights.begin();
|
||||||
lightmapLevel = (*l).second.intensity;
|
_lightmapLevel = (*l).second.intensity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// assign the blendshapes to their corresponding meshes
|
// assign the blendshapes to their corresponding meshes
|
||||||
foreach (const ExtractedBlendshape& extracted, blendshapes) {
|
foreach (const ExtractedBlendshape& extracted, blendshapes) {
|
||||||
QString blendshapeChannelID = parentMap.value(extracted.id);
|
QString blendshapeChannelID = _connectionParentMap.value(extracted.id);
|
||||||
QString blendshapeID = parentMap.value(blendshapeChannelID);
|
QString blendshapeID = _connectionParentMap.value(blendshapeChannelID);
|
||||||
QString meshID = parentMap.value(blendshapeID);
|
QString meshID = _connectionParentMap.value(blendshapeID);
|
||||||
addBlendshapes(extracted, blendshapeChannelIndices.values(blendshapeChannelID), meshes[meshID]);
|
addBlendshapes(extracted, blendshapeChannelIndices.values(blendshapeChannelID), meshes[meshID]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2209,23 +1862,23 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
QSet<QString> remainingModels;
|
QSet<QString> remainingModels;
|
||||||
for (QHash<QString, FBXModel>::const_iterator model = models.constBegin(); model != models.constEnd(); model++) {
|
for (QHash<QString, FBXModel>::const_iterator model = models.constBegin(); model != models.constEnd(); model++) {
|
||||||
// models with clusters must be parented to the cluster top
|
// models with clusters must be parented to the cluster top
|
||||||
foreach (const QString& deformerID, childMap.values(model.key())) {
|
foreach (const QString& deformerID, _connectionChildMap.values(model.key())) {
|
||||||
foreach (const QString& clusterID, childMap.values(deformerID)) {
|
foreach (const QString& clusterID, _connectionChildMap.values(deformerID)) {
|
||||||
if (!clusters.contains(clusterID)) {
|
if (!clusters.contains(clusterID)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
QString topID = getTopModelID(parentMap, models, childMap.value(clusterID));
|
QString topID = getTopModelID(_connectionParentMap, models, _connectionChildMap.value(clusterID));
|
||||||
childMap.remove(parentMap.take(model.key()), model.key());
|
_connectionChildMap.remove(_connectionParentMap.take(model.key()), model.key());
|
||||||
parentMap.insert(model.key(), topID);
|
_connectionParentMap.insert(model.key(), topID);
|
||||||
goto outerBreak;
|
goto outerBreak;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
outerBreak:
|
outerBreak:
|
||||||
|
|
||||||
// make sure the parent is in the child map
|
// make sure the parent is in the child map
|
||||||
QString parent = parentMap.value(model.key());
|
QString parent = _connectionParentMap.value(model.key());
|
||||||
if (!childMap.contains(parent, model.key())) {
|
if (!_connectionChildMap.contains(parent, model.key())) {
|
||||||
childMap.insert(parent, model.key());
|
_connectionChildMap.insert(parent, model.key());
|
||||||
}
|
}
|
||||||
remainingModels.insert(model.key());
|
remainingModels.insert(model.key());
|
||||||
}
|
}
|
||||||
|
@ -2236,8 +1889,8 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
first = id;
|
first = id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QString topID = getTopModelID(parentMap, models, first);
|
QString topID = getTopModelID(_connectionParentMap, models, first);
|
||||||
appendModelIDs(parentMap.value(topID), childMap, models, remainingModels, modelIDs);
|
appendModelIDs(_connectionParentMap.value(topID), _connectionChildMap, models, remainingModels, modelIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// figure the number of animation frames from the curves
|
// figure the number of animation frames from the curves
|
||||||
|
@ -2298,7 +1951,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
joint.inverseBindRotation = joint.inverseDefaultRotation;
|
||||||
joint.name = model.name;
|
joint.name = model.name;
|
||||||
|
|
||||||
foreach (const QString& childID, childMap.values(modelID)) {
|
foreach (const QString& childID, _connectionChildMap.values(modelID)) {
|
||||||
QString type = typeFlags.value(childID);
|
QString type = typeFlags.value(childID);
|
||||||
if (!type.isEmpty()) {
|
if (!type.isEmpty()) {
|
||||||
geometry.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton"));
|
geometry.hasSkeletonJoints |= (joint.isSkeletonJoint = type.toLower().contains("Skeleton"));
|
||||||
|
@ -2350,8 +2003,11 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
geometry.bindExtents.reset();
|
geometry.bindExtents.reset();
|
||||||
geometry.meshExtents.reset();
|
geometry.meshExtents.reset();
|
||||||
|
|
||||||
|
// Create the Material Library
|
||||||
|
consolidateFBXMaterials();
|
||||||
|
|
||||||
// see if any materials have texture children
|
// see if any materials have texture children
|
||||||
bool materialsHaveTextures = checkMaterialsHaveTextures(materials, textureFilenames, childMap);
|
bool materialsHaveTextures = checkMaterialsHaveTextures(_fbxMaterials, textureFilenames, _connectionChildMap);
|
||||||
|
|
||||||
for (QHash<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
for (QHash<QString, ExtractedMesh>::iterator it = meshes.begin(); it != meshes.end(); it++) {
|
||||||
ExtractedMesh& extracted = it.value();
|
ExtractedMesh& extracted = it.value();
|
||||||
|
@ -2359,8 +2015,8 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
extracted.mesh.meshExtents.reset();
|
extracted.mesh.meshExtents.reset();
|
||||||
|
|
||||||
// accumulate local transforms
|
// accumulate local transforms
|
||||||
QString modelID = models.contains(it.key()) ? it.key() : parentMap.value(it.key());
|
QString modelID = models.contains(it.key()) ? it.key() : _connectionParentMap.value(it.key());
|
||||||
glm::mat4 modelTransform = getGlobalTransform(parentMap, models, modelID, geometry.applicationName == "mixamo.com");
|
glm::mat4 modelTransform = getGlobalTransform(_connectionParentMap, models, modelID, geometry.applicationName == "mixamo.com");
|
||||||
|
|
||||||
// compute the mesh extents from the transformed vertices
|
// compute the mesh extents from the transformed vertices
|
||||||
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
foreach (const glm::vec3& vertex, extracted.mesh.vertices) {
|
||||||
|
@ -2374,14 +2030,19 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
}
|
}
|
||||||
|
|
||||||
// look for textures, material properties
|
// look for textures, material properties
|
||||||
|
// allocate the Part material library
|
||||||
int materialIndex = 0;
|
int materialIndex = 0;
|
||||||
int textureIndex = 0;
|
int textureIndex = 0;
|
||||||
bool generateTangents = false;
|
bool generateTangents = false;
|
||||||
QList<QString> children = childMap.values(modelID);
|
QList<QString> children = _connectionChildMap.values(modelID);
|
||||||
for (int i = children.size() - 1; i >= 0; i--) {
|
for (int i = children.size() - 1; i >= 0; i--) {
|
||||||
|
|
||||||
const QString& childID = children.at(i);
|
const QString& childID = children.at(i);
|
||||||
if (materials.contains(childID)) {
|
if (_fbxMaterials.contains(childID)) {
|
||||||
Material material = materials.value(childID);
|
// the pure material associated with this part
|
||||||
|
FBXMaterial material = _fbxMaterials.value(childID);
|
||||||
|
|
||||||
|
|
||||||
bool detectDifferentUVs = false;
|
bool detectDifferentUVs = false;
|
||||||
FBXTexture diffuseTexture;
|
FBXTexture diffuseTexture;
|
||||||
QString diffuseTextureID = diffuseTextures.value(childID);
|
QString diffuseTextureID = diffuseTextures.value(childID);
|
||||||
|
@ -2389,7 +2050,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
|
||||||
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
||||||
foreach (const QString& childTextureID, childMap.values(diffuseTextureID)) {
|
foreach (const QString& childTextureID, _connectionChildMap.values(diffuseTextureID)) {
|
||||||
if (textureFilenames.contains(childTextureID)) {
|
if (textureFilenames.contains(childTextureID)) {
|
||||||
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
}
|
}
|
||||||
|
@ -2420,12 +2081,12 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
|
|
||||||
FBXTexture emissiveTexture;
|
FBXTexture emissiveTexture;
|
||||||
glm::vec2 emissiveParams(0.f, 1.f);
|
glm::vec2 emissiveParams(0.f, 1.f);
|
||||||
emissiveParams.x = lightmapOffset;
|
emissiveParams.x = _lightmapOffset;
|
||||||
emissiveParams.y = lightmapLevel;
|
emissiveParams.y = _lightmapLevel;
|
||||||
|
|
||||||
QString emissiveTextureID = emissiveTextures.value(childID);
|
QString emissiveTextureID = emissiveTextures.value(childID);
|
||||||
QString ambientTextureID = ambientTextures.value(childID);
|
QString ambientTextureID = ambientTextures.value(childID);
|
||||||
if (loadLightmaps && (!emissiveTextureID.isNull() || !ambientTextureID.isNull())) {
|
if (_loadLightmaps && (!emissiveTextureID.isNull() || !ambientTextureID.isNull())) {
|
||||||
|
|
||||||
if (!emissiveTextureID.isNull()) {
|
if (!emissiveTextureID.isNull()) {
|
||||||
emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
@ -2447,10 +2108,10 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
if (extracted.partMaterialTextures.at(j).first == materialIndex) {
|
if (extracted.partMaterialTextures.at(j).first == materialIndex) {
|
||||||
FBXMeshPart& part = extracted.mesh.parts[j];
|
FBXMeshPart& part = extracted.mesh.parts[j];
|
||||||
|
|
||||||
part._material = material._material;
|
/* part._material = material._material;
|
||||||
part.diffuseColor = material.diffuse;
|
part.diffuseColor = material.diffuseColor;
|
||||||
part.specularColor = material.specular;
|
part.specularColor = material.specularColor;
|
||||||
part.emissiveColor = material.emissive;
|
part.emissiveColor = material.emissiveColor;
|
||||||
part.shininess = material.shininess;
|
part.shininess = material.shininess;
|
||||||
part.opacity = material.opacity;
|
part.opacity = material.opacity;
|
||||||
if (!diffuseTexture.filename.isNull()) {
|
if (!diffuseTexture.filename.isNull()) {
|
||||||
|
@ -2466,8 +2127,8 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
part.emissiveTexture = emissiveTexture;
|
part.emissiveTexture = emissiveTexture;
|
||||||
}
|
}
|
||||||
part.emissiveParams = emissiveParams;
|
part.emissiveParams = emissiveParams;
|
||||||
|
*/
|
||||||
part.materialID = material.id;
|
part.materialID = material.materialID;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
materialIndex++;
|
materialIndex++;
|
||||||
|
@ -2477,7 +2138,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
|
for (int j = 0; j < extracted.partMaterialTextures.size(); j++) {
|
||||||
int partTexture = extracted.partMaterialTextures.at(j).second;
|
int partTexture = extracted.partMaterialTextures.at(j).second;
|
||||||
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {
|
if (partTexture == textureIndex && !(partTexture == 0 && materialsHaveTextures)) {
|
||||||
extracted.mesh.parts[j].diffuseTexture = texture;
|
// extracted.mesh.parts[j].diffuseTexture = texture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
textureIndex++;
|
textureIndex++;
|
||||||
|
@ -2509,8 +2170,8 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
|
|
||||||
// find the clusters with which the mesh is associated
|
// find the clusters with which the mesh is associated
|
||||||
QVector<QString> clusterIDs;
|
QVector<QString> clusterIDs;
|
||||||
foreach (const QString& childID, childMap.values(it.key())) {
|
foreach (const QString& childID, _connectionChildMap.values(it.key())) {
|
||||||
foreach (const QString& clusterID, childMap.values(childID)) {
|
foreach (const QString& clusterID, _connectionChildMap.values(childID)) {
|
||||||
if (!clusters.contains(clusterID)) {
|
if (!clusters.contains(clusterID)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2520,7 +2181,7 @@ FBXGeometry* extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping
|
||||||
|
|
||||||
// see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion
|
// see http://stackoverflow.com/questions/13566608/loading-skinning-information-from-fbx for a discussion
|
||||||
// of skinning information in FBX
|
// of skinning information in FBX
|
||||||
QString jointID = childMap.value(clusterID);
|
QString jointID = _connectionChildMap.value(clusterID);
|
||||||
fbxCluster.jointIndex = modelIDs.indexOf(jointID);
|
fbxCluster.jointIndex = modelIDs.indexOf(jointID);
|
||||||
if (fbxCluster.jointIndex == -1) {
|
if (fbxCluster.jointIndex == -1) {
|
||||||
qCDebug(modelformat) << "Joint not in model list: " << jointID;
|
qCDebug(modelformat) << "Joint not in model list: " << jointID;
|
||||||
|
@ -2773,5 +2434,113 @@ FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const
|
||||||
}
|
}
|
||||||
|
|
||||||
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) {
|
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url, bool loadLightmaps, float lightmapLevel) {
|
||||||
return extractFBXGeometry(parseFBX(device), mapping, url, loadLightmaps, lightmapLevel);
|
FBXReader reader;
|
||||||
|
reader._fbxNode = FBXReader::parseFBX(device);
|
||||||
|
reader._loadLightmaps = loadLightmaps;
|
||||||
|
reader._lightmapLevel = lightmapLevel;
|
||||||
|
|
||||||
|
return reader.extractFBXGeometry(mapping, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool FBXMaterial::needTangentSpace() const {
|
||||||
|
return !normalTexture.isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void FBXReader::consolidateFBXMaterials() {
|
||||||
|
|
||||||
|
// foreach (const QString& materialID, materials) {
|
||||||
|
for (QHash<QString, FBXMaterial>::iterator it = _fbxMaterials.begin(); it != _fbxMaterials.end(); it++) {
|
||||||
|
FBXMaterial& material = (*it);
|
||||||
|
// the pure material associated with this part
|
||||||
|
bool detectDifferentUVs = false;
|
||||||
|
FBXTexture diffuseTexture;
|
||||||
|
QString diffuseTextureID = diffuseTextures.value(material.materialID);
|
||||||
|
if (!diffuseTextureID.isNull()) {
|
||||||
|
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
|
||||||
|
// FBX files generated by 3DSMax have an intermediate texture parent, apparently
|
||||||
|
foreach (const QString& childTextureID, _connectionChildMap.values(diffuseTextureID)) {
|
||||||
|
if (textureFilenames.contains(childTextureID)) {
|
||||||
|
diffuseTexture = getTexture(diffuseTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO associate this per part
|
||||||
|
//diffuseTexture.texcoordSet = matchTextureUVSetToAttributeChannel(diffuseTexture.texcoordSetName, extracted.texcoordSetMap);
|
||||||
|
|
||||||
|
material.diffuseTexture = diffuseTexture;
|
||||||
|
|
||||||
|
detectDifferentUVs = (diffuseTexture.texcoordSet != 0) || (!diffuseTexture.transform.isIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXTexture normalTexture;
|
||||||
|
QString bumpTextureID = bumpTextures.value(material.materialID);
|
||||||
|
if (!bumpTextureID.isNull()) {
|
||||||
|
normalTexture = getTexture(bumpTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
|
||||||
|
// TODO Need to generate tangent space at association per part
|
||||||
|
//generateTangents = true;
|
||||||
|
|
||||||
|
// TODO at per part association time
|
||||||
|
// normalTexture.texcoordSet = matchTextureUVSetToAttributeChannel(normalTexture.texcoordSetName, extracted.texcoordSetMap);
|
||||||
|
|
||||||
|
material.normalTexture = normalTexture;
|
||||||
|
|
||||||
|
detectDifferentUVs |= (normalTexture.texcoordSet != 0) || (!normalTexture.transform.isIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXTexture specularTexture;
|
||||||
|
QString specularTextureID = specularTextures.value(material.materialID);
|
||||||
|
if (!specularTextureID.isNull()) {
|
||||||
|
specularTexture = getTexture(specularTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
// TODO at per part association time
|
||||||
|
// specularTexture.texcoordSet = matchTextureUVSetToAttributeChannel(specularTexture.texcoordSetName, extracted.texcoordSetMap);
|
||||||
|
detectDifferentUVs |= (specularTexture.texcoordSet != 0) || (!specularTexture.transform.isIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXTexture emissiveTexture;
|
||||||
|
glm::vec2 emissiveParams(0.f, 1.f);
|
||||||
|
emissiveParams.x = _lightmapOffset;
|
||||||
|
emissiveParams.y = _lightmapLevel;
|
||||||
|
|
||||||
|
QString emissiveTextureID = emissiveTextures.value(material.materialID);
|
||||||
|
QString ambientTextureID = ambientTextures.value(material.materialID);
|
||||||
|
if (_loadLightmaps && (!emissiveTextureID.isNull() || !ambientTextureID.isNull())) {
|
||||||
|
|
||||||
|
if (!emissiveTextureID.isNull()) {
|
||||||
|
emissiveTexture = getTexture(emissiveTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
emissiveParams.y = 4.0f;
|
||||||
|
} else if (!ambientTextureID.isNull()) {
|
||||||
|
emissiveTexture = getTexture(ambientTextureID, textureNames, textureFilenames, textureContent, textureParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO : do this at per part association
|
||||||
|
//emissiveTexture.texcoordSet = matchTextureUVSetToAttributeChannel(emissiveTexture.texcoordSetName, extracted.texcoordSetMap);
|
||||||
|
|
||||||
|
material.emissiveParams = emissiveParams;
|
||||||
|
material.emissiveTexture = emissiveTexture;
|
||||||
|
|
||||||
|
|
||||||
|
detectDifferentUVs |= (emissiveTexture.texcoordSet != 0) || (!emissiveTexture.transform.isIdentity());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally create the true material representation
|
||||||
|
material._material = make_shared<model::Material>();
|
||||||
|
material._material->setEmissive(material.emissiveColor);
|
||||||
|
if (glm::all(glm::equal(material.diffuseColor, glm::vec3(0.0f)))) {
|
||||||
|
material._material->setDiffuse(material.diffuseColor);
|
||||||
|
} else {
|
||||||
|
material._material->setDiffuse(material.diffuseColor);
|
||||||
|
}
|
||||||
|
material._material->setMetallic(glm::length(material.specularColor));
|
||||||
|
material._material->setGloss(material.shininess);
|
||||||
|
|
||||||
|
if (material.opacity <= 0.0f) {
|
||||||
|
material._material->setOpacity(1.0f);
|
||||||
|
} else {
|
||||||
|
material._material->setOpacity(material.opacity);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@
|
||||||
|
|
||||||
#include <model/Geometry.h>
|
#include <model/Geometry.h>
|
||||||
#include <model/Material.h>
|
#include <model/Material.h>
|
||||||
|
#include <model/Asset.h>
|
||||||
|
|
||||||
class QIODevice;
|
class QIODevice;
|
||||||
class FBXNode;
|
class FBXNode;
|
||||||
|
@ -89,6 +90,15 @@ public:
|
||||||
glm::mat4 inverseBindMatrix;
|
glm::mat4 inverseBindMatrix;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// The true texture image which can be used for different textures
|
||||||
|
class FBXTextureImage {
|
||||||
|
public:
|
||||||
|
QString name;
|
||||||
|
QByteArray filename;
|
||||||
|
QByteArray content;
|
||||||
|
};
|
||||||
|
|
||||||
/// A texture map in an FBX document.
|
/// A texture map in an FBX document.
|
||||||
class FBXTexture {
|
class FBXTexture {
|
||||||
public:
|
public:
|
||||||
|
@ -99,6 +109,8 @@ public:
|
||||||
Transform transform;
|
Transform transform;
|
||||||
int texcoordSet;
|
int texcoordSet;
|
||||||
QString texcoordSetName;
|
QString texcoordSetName;
|
||||||
|
|
||||||
|
bool isNull() const { return name.isEmpty() && filename.isEmpty() && content.isEmpty(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A single part of a mesh (with the same material).
|
/// A single part of a mesh (with the same material).
|
||||||
|
@ -109,7 +121,7 @@ public:
|
||||||
QVector<int> triangleIndices; // original indices from the FBX mesh
|
QVector<int> triangleIndices; // original indices from the FBX mesh
|
||||||
mutable gpu::BufferPointer quadsAsTrianglesIndicesBuffer;
|
mutable gpu::BufferPointer quadsAsTrianglesIndicesBuffer;
|
||||||
|
|
||||||
glm::vec3 diffuseColor;
|
/* glm::vec3 diffuseColor;
|
||||||
glm::vec3 specularColor;
|
glm::vec3 specularColor;
|
||||||
glm::vec3 emissiveColor;
|
glm::vec3 emissiveColor;
|
||||||
glm::vec2 emissiveParams;
|
glm::vec2 emissiveParams;
|
||||||
|
@ -120,15 +132,38 @@ public:
|
||||||
FBXTexture normalTexture;
|
FBXTexture normalTexture;
|
||||||
FBXTexture specularTexture;
|
FBXTexture specularTexture;
|
||||||
FBXTexture emissiveTexture;
|
FBXTexture emissiveTexture;
|
||||||
|
*/
|
||||||
QString materialID;
|
QString materialID;
|
||||||
model::MaterialPointer _material;
|
// model::MaterialPointer _material;
|
||||||
mutable bool trianglesForQuadsAvailable = false;
|
mutable bool trianglesForQuadsAvailable = false;
|
||||||
mutable int trianglesForQuadsIndicesCount = 0;
|
mutable int trianglesForQuadsIndicesCount = 0;
|
||||||
|
|
||||||
gpu::BufferPointer getTrianglesForQuads() const;
|
gpu::BufferPointer getTrianglesForQuads() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FBXMaterial {
|
||||||
|
public:
|
||||||
|
glm::vec3 diffuseColor;
|
||||||
|
glm::vec3 specularColor;
|
||||||
|
glm::vec3 emissiveColor;
|
||||||
|
glm::vec2 emissiveParams;
|
||||||
|
float shininess;
|
||||||
|
float opacity;
|
||||||
|
|
||||||
|
QString materialID;
|
||||||
|
model::MaterialTable::ID _modelMaterialID;
|
||||||
|
model::MaterialPointer _material;
|
||||||
|
|
||||||
|
FBXTexture diffuseTexture;
|
||||||
|
FBXTexture opacityTexture;
|
||||||
|
FBXTexture normalTexture;
|
||||||
|
FBXTexture specularTexture;
|
||||||
|
FBXTexture emissiveTexture;
|
||||||
|
|
||||||
|
bool needTangentSpace() const;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
/// A single mesh (with optional blendshapes) extracted from an FBX document.
|
||||||
class FBXMesh {
|
class FBXMesh {
|
||||||
public:
|
public:
|
||||||
|
@ -220,7 +255,7 @@ public:
|
||||||
bool hasSkeletonJoints;
|
bool hasSkeletonJoints;
|
||||||
|
|
||||||
QVector<FBXMesh> meshes;
|
QVector<FBXMesh> meshes;
|
||||||
|
|
||||||
glm::mat4 offset;
|
glm::mat4 offset;
|
||||||
|
|
||||||
int leftEyeJointIndex = -1;
|
int leftEyeJointIndex = -1;
|
||||||
|
@ -266,6 +301,8 @@ public:
|
||||||
QString getModelNameOfMesh(int meshIndex) const;
|
QString getModelNameOfMesh(int meshIndex) const;
|
||||||
|
|
||||||
QList<QString> blendshapeChannelNames;
|
QList<QString> blendshapeChannelNames;
|
||||||
|
|
||||||
|
model::AssetPointer _asset;
|
||||||
};
|
};
|
||||||
|
|
||||||
Q_DECLARE_METATYPE(FBXGeometry)
|
Q_DECLARE_METATYPE(FBXGeometry)
|
||||||
|
@ -278,4 +315,80 @@ FBXGeometry* readFBX(const QByteArray& model, const QVariantHash& mapping, const
|
||||||
/// \exception QString if an error occurs in parsing
|
/// \exception QString if an error occurs in parsing
|
||||||
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
FBXGeometry* readFBX(QIODevice* device, const QVariantHash& mapping, const QString& url = "", bool loadLightmaps = true, float lightmapLevel = 1.0f);
|
||||||
|
|
||||||
|
struct TextureParam {
|
||||||
|
glm::vec2 UVTranslation;
|
||||||
|
glm::vec2 UVScaling;
|
||||||
|
glm::vec4 cropping;
|
||||||
|
QString UVSet;
|
||||||
|
|
||||||
|
glm::vec3 translation;
|
||||||
|
glm::vec3 rotation;
|
||||||
|
glm::vec3 scaling;
|
||||||
|
uint8_t alphaSource;
|
||||||
|
uint8_t currentTextureBlendMode;
|
||||||
|
bool useMaterial;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool assign(T& ref, const T& v) {
|
||||||
|
if (ref == v) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
ref = v;
|
||||||
|
isDefault = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDefault;
|
||||||
|
|
||||||
|
TextureParam() :
|
||||||
|
UVTranslation(0.0f),
|
||||||
|
UVScaling(1.0f),
|
||||||
|
cropping(0.0f),
|
||||||
|
UVSet("map1"),
|
||||||
|
translation(0.0f),
|
||||||
|
rotation(0.0f),
|
||||||
|
scaling(1.0f),
|
||||||
|
alphaSource(0),
|
||||||
|
currentTextureBlendMode(0),
|
||||||
|
useMaterial(true),
|
||||||
|
isDefault(true)
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
class FBXReader {
|
||||||
|
public:
|
||||||
|
FBXGeometry* _fbxGeometry;
|
||||||
|
|
||||||
|
FBXNode _fbxNode;
|
||||||
|
static FBXNode parseFBX(QIODevice* device);
|
||||||
|
|
||||||
|
FBXGeometry* extractFBXGeometry(const QVariantHash& mapping, const QString& url);
|
||||||
|
|
||||||
|
QHash<QString, FBXTextureImage> _textureImages;
|
||||||
|
|
||||||
|
QHash<QString, QString> textureNames;
|
||||||
|
QHash<QString, QByteArray> textureFilenames;
|
||||||
|
QHash<QByteArray, QByteArray> textureContent;
|
||||||
|
QHash<QString, TextureParam> textureParams;
|
||||||
|
|
||||||
|
|
||||||
|
QHash<QString, QString> diffuseTextures;
|
||||||
|
QHash<QString, QString> bumpTextures;
|
||||||
|
QHash<QString, QString> specularTextures;
|
||||||
|
QHash<QString, QString> emissiveTextures;
|
||||||
|
QHash<QString, QString> ambientTextures;
|
||||||
|
|
||||||
|
QHash<QString, FBXMaterial> _fbxMaterials;
|
||||||
|
|
||||||
|
void consolidateFBXMaterials();
|
||||||
|
|
||||||
|
bool _loadLightmaps = true;
|
||||||
|
float _lightmapOffset = 0.0f;
|
||||||
|
float _lightmapLevel;
|
||||||
|
|
||||||
|
QMultiHash<QString, QString> _connectionParentMap;
|
||||||
|
QMultiHash<QString, QString> _connectionChildMap;
|
||||||
|
};
|
||||||
|
|
||||||
#endif // hifi_FBXReader_h
|
#endif // hifi_FBXReader_h
|
||||||
|
|
342
libraries/fbx/src/FBXReader_Node.cpp
Normal file
342
libraries/fbx/src/FBXReader_Node.cpp
Normal file
|
@ -0,0 +1,342 @@
|
||||||
|
//
|
||||||
|
// FBXReader.cpp
|
||||||
|
// interface/src/renderer
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 9/18/13.
|
||||||
|
// Copyright 2013 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <QBuffer>
|
||||||
|
#include <QDataStream>
|
||||||
|
#include <QIODevice>
|
||||||
|
#include <QStringList>
|
||||||
|
#include <QTextStream>
|
||||||
|
#include <QtDebug>
|
||||||
|
#include <QtEndian>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include "FBXReader.h"
|
||||||
|
|
||||||
|
template<class T> int streamSize() {
|
||||||
|
return sizeof(T);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<bool> int streamSize() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class T> QVariant readBinaryArray(QDataStream& in, int& position) {
|
||||||
|
quint32 arrayLength;
|
||||||
|
quint32 encoding;
|
||||||
|
quint32 compressedLength;
|
||||||
|
|
||||||
|
in >> arrayLength;
|
||||||
|
in >> encoding;
|
||||||
|
in >> compressedLength;
|
||||||
|
position += sizeof(quint32) * 3;
|
||||||
|
|
||||||
|
QVector<T> values;
|
||||||
|
const unsigned int DEFLATE_ENCODING = 1;
|
||||||
|
if (encoding == DEFLATE_ENCODING) {
|
||||||
|
// preface encoded data with uncompressed length
|
||||||
|
QByteArray compressed(sizeof(quint32) + compressedLength, 0);
|
||||||
|
*((quint32*)compressed.data()) = qToBigEndian<quint32>(arrayLength * sizeof(T));
|
||||||
|
in.readRawData(compressed.data() + sizeof(quint32), compressedLength);
|
||||||
|
position += compressedLength;
|
||||||
|
QByteArray uncompressed = qUncompress(compressed);
|
||||||
|
QDataStream uncompressedIn(uncompressed);
|
||||||
|
uncompressedIn.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
uncompressedIn.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
||||||
|
for (quint32 i = 0; i < arrayLength; i++) {
|
||||||
|
T value;
|
||||||
|
uncompressedIn >> value;
|
||||||
|
values.append(value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (quint32 i = 0; i < arrayLength; i++) {
|
||||||
|
T value;
|
||||||
|
in >> value;
|
||||||
|
position += streamSize<T>();
|
||||||
|
values.append(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return QVariant::fromValue(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant parseBinaryFBXProperty(QDataStream& in, int& position) {
|
||||||
|
char ch;
|
||||||
|
in.device()->getChar(&ch);
|
||||||
|
position++;
|
||||||
|
switch (ch) {
|
||||||
|
case 'Y': {
|
||||||
|
qint16 value;
|
||||||
|
in >> value;
|
||||||
|
position += sizeof(qint16);
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'C': {
|
||||||
|
bool value;
|
||||||
|
in >> value;
|
||||||
|
position++;
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'I': {
|
||||||
|
qint32 value;
|
||||||
|
in >> value;
|
||||||
|
position += sizeof(qint32);
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'F': {
|
||||||
|
float value;
|
||||||
|
in >> value;
|
||||||
|
position += sizeof(float);
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'D': {
|
||||||
|
double value;
|
||||||
|
in >> value;
|
||||||
|
position += sizeof(double);
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'L': {
|
||||||
|
qint64 value;
|
||||||
|
in >> value;
|
||||||
|
position += sizeof(qint64);
|
||||||
|
return QVariant::fromValue(value);
|
||||||
|
}
|
||||||
|
case 'f': {
|
||||||
|
return readBinaryArray<float>(in, position);
|
||||||
|
}
|
||||||
|
case 'd': {
|
||||||
|
return readBinaryArray<double>(in, position);
|
||||||
|
}
|
||||||
|
case 'l': {
|
||||||
|
return readBinaryArray<qint64>(in, position);
|
||||||
|
}
|
||||||
|
case 'i': {
|
||||||
|
return readBinaryArray<qint32>(in, position);
|
||||||
|
}
|
||||||
|
case 'b': {
|
||||||
|
return readBinaryArray<bool>(in, position);
|
||||||
|
}
|
||||||
|
case 'S':
|
||||||
|
case 'R': {
|
||||||
|
quint32 length;
|
||||||
|
in >> length;
|
||||||
|
position += sizeof(quint32) + length;
|
||||||
|
return QVariant::fromValue(in.device()->read(length));
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw QString("Unknown property type: ") + ch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXNode parseBinaryFBXNode(QDataStream& in, int& position) {
|
||||||
|
qint32 endOffset;
|
||||||
|
quint32 propertyCount;
|
||||||
|
quint32 propertyListLength;
|
||||||
|
quint8 nameLength;
|
||||||
|
|
||||||
|
in >> endOffset;
|
||||||
|
in >> propertyCount;
|
||||||
|
in >> propertyListLength;
|
||||||
|
in >> nameLength;
|
||||||
|
position += sizeof(quint32) * 3 + sizeof(quint8);
|
||||||
|
|
||||||
|
FBXNode node;
|
||||||
|
const int MIN_VALID_OFFSET = 40;
|
||||||
|
if (endOffset < MIN_VALID_OFFSET || nameLength == 0) {
|
||||||
|
// use a null name to indicate a null node
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node.name = in.device()->read(nameLength);
|
||||||
|
position += nameLength;
|
||||||
|
|
||||||
|
for (quint32 i = 0; i < propertyCount; i++) {
|
||||||
|
node.properties.append(parseBinaryFBXProperty(in, position));
|
||||||
|
}
|
||||||
|
|
||||||
|
while (endOffset > position) {
|
||||||
|
FBXNode child = parseBinaryFBXNode(in, position);
|
||||||
|
if (child.name.isNull()) {
|
||||||
|
return node;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
node.children.append(child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Tokenizer {
|
||||||
|
public:
|
||||||
|
|
||||||
|
Tokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { }
|
||||||
|
|
||||||
|
enum SpecialToken {
|
||||||
|
NO_TOKEN = -1,
|
||||||
|
NO_PUSHBACKED_TOKEN = -1,
|
||||||
|
DATUM_TOKEN = 0x100
|
||||||
|
};
|
||||||
|
|
||||||
|
int nextToken();
|
||||||
|
const QByteArray& getDatum() const { return _datum; }
|
||||||
|
|
||||||
|
void pushBackToken(int token) { _pushedBackToken = token; }
|
||||||
|
void ungetChar(char ch) { _device->ungetChar(ch); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
QIODevice* _device;
|
||||||
|
QByteArray _datum;
|
||||||
|
int _pushedBackToken;
|
||||||
|
};
|
||||||
|
|
||||||
|
int Tokenizer::nextToken() {
|
||||||
|
if (_pushedBackToken != NO_PUSHBACKED_TOKEN) {
|
||||||
|
int token = _pushedBackToken;
|
||||||
|
_pushedBackToken = NO_PUSHBACKED_TOKEN;
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
|
||||||
|
char ch;
|
||||||
|
while (_device->getChar(&ch)) {
|
||||||
|
if (QChar(ch).isSpace()) {
|
||||||
|
continue; // skip whitespace
|
||||||
|
}
|
||||||
|
switch (ch) {
|
||||||
|
case ';':
|
||||||
|
_device->readLine(); // skip the comment
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ':':
|
||||||
|
case '{':
|
||||||
|
case '}':
|
||||||
|
case ',':
|
||||||
|
return ch; // special punctuation
|
||||||
|
|
||||||
|
case '\"':
|
||||||
|
_datum = "";
|
||||||
|
while (_device->getChar(&ch)) {
|
||||||
|
if (ch == '\"') { // end on closing quote
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ch == '\\') { // handle escaped quotes
|
||||||
|
if (_device->getChar(&ch) && ch != '\"') {
|
||||||
|
_datum.append('\\');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_datum.append(ch);
|
||||||
|
}
|
||||||
|
return DATUM_TOKEN;
|
||||||
|
|
||||||
|
default:
|
||||||
|
_datum = "";
|
||||||
|
_datum.append(ch);
|
||||||
|
while (_device->getChar(&ch)) {
|
||||||
|
if (QChar(ch).isSpace() || ch == ';' || ch == ':' || ch == '{' || ch == '}' || ch == ',' || ch == '\"') {
|
||||||
|
ungetChar(ch); // read until we encounter a special character, then replace it
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_datum.append(ch);
|
||||||
|
}
|
||||||
|
return DATUM_TOKEN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NO_TOKEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXNode parseTextFBXNode(Tokenizer& tokenizer) {
|
||||||
|
FBXNode node;
|
||||||
|
|
||||||
|
if (tokenizer.nextToken() != Tokenizer::DATUM_TOKEN) {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
node.name = tokenizer.getDatum();
|
||||||
|
|
||||||
|
if (tokenizer.nextToken() != ':') {
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
int token;
|
||||||
|
bool expectingDatum = true;
|
||||||
|
while ((token = tokenizer.nextToken()) != Tokenizer::NO_TOKEN) {
|
||||||
|
if (token == '{') {
|
||||||
|
for (FBXNode child = parseTextFBXNode(tokenizer); !child.name.isNull(); child = parseTextFBXNode(tokenizer)) {
|
||||||
|
node.children.append(child);
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
if (token == ',') {
|
||||||
|
expectingDatum = true;
|
||||||
|
|
||||||
|
} else if (token == Tokenizer::DATUM_TOKEN && expectingDatum) {
|
||||||
|
QByteArray datum = tokenizer.getDatum();
|
||||||
|
if ((token = tokenizer.nextToken()) == ':') {
|
||||||
|
tokenizer.ungetChar(':');
|
||||||
|
tokenizer.pushBackToken(Tokenizer::DATUM_TOKEN);
|
||||||
|
return node;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
tokenizer.pushBackToken(token);
|
||||||
|
node.properties.append(datum);
|
||||||
|
expectingDatum = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tokenizer.pushBackToken(token);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
FBXNode FBXReader::parseFBX(QIODevice* device) {
|
||||||
|
// verify the prolog
|
||||||
|
const QByteArray BINARY_PROLOG = "Kaydara FBX Binary ";
|
||||||
|
if (device->peek(BINARY_PROLOG.size()) != BINARY_PROLOG) {
|
||||||
|
// parse as a text file
|
||||||
|
FBXNode top;
|
||||||
|
Tokenizer tokenizer(device);
|
||||||
|
while (device->bytesAvailable()) {
|
||||||
|
FBXNode next = parseTextFBXNode(tokenizer);
|
||||||
|
if (next.name.isNull()) {
|
||||||
|
return top;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
top.children.append(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
QDataStream in(device);
|
||||||
|
in.setByteOrder(QDataStream::LittleEndian);
|
||||||
|
in.setVersion(QDataStream::Qt_4_5); // for single/double precision switch
|
||||||
|
|
||||||
|
// see http://code.blender.org/index.php/2013/08/fbx-binary-file-format-specification/ for an explanation
|
||||||
|
// of the FBX binary format
|
||||||
|
|
||||||
|
// skip the rest of the header
|
||||||
|
const int HEADER_SIZE = 27;
|
||||||
|
in.skipRawData(HEADER_SIZE);
|
||||||
|
int position = HEADER_SIZE;
|
||||||
|
|
||||||
|
// parse the top-level node
|
||||||
|
FBXNode top;
|
||||||
|
while (device->bytesAvailable()) {
|
||||||
|
FBXNode next = parseBinaryFBXNode(in, position);
|
||||||
|
if (next.name.isNull()) {
|
||||||
|
return top;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
top.children.append(next);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return top;
|
||||||
|
}
|
||||||
|
|
|
@ -121,21 +121,21 @@ glm::vec2 OBJTokenizer::getVec2() {
|
||||||
|
|
||||||
|
|
||||||
void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) {
|
void setMeshPartDefaults(FBXMeshPart& meshPart, QString materialID) {
|
||||||
meshPart.diffuseColor = glm::vec3(1, 1, 1);
|
/* meshPart.diffuseColor = glm::vec3(1, 1, 1);
|
||||||
meshPart.specularColor = glm::vec3(1, 1, 1);
|
meshPart.specularColor = glm::vec3(1, 1, 1);
|
||||||
meshPart.emissiveColor = glm::vec3(0, 0, 0);
|
meshPart.emissiveColor = glm::vec3(0, 0, 0);
|
||||||
meshPart.emissiveParams = glm::vec2(0, 1);
|
meshPart.emissiveParams = glm::vec2(0, 1);
|
||||||
meshPart.shininess = 40;
|
meshPart.shininess = 40;
|
||||||
meshPart.opacity = 1;
|
meshPart.opacity = 1;*/
|
||||||
|
|
||||||
meshPart.materialID = materialID;
|
meshPart.materialID = materialID;
|
||||||
meshPart.opacity = 1.0;
|
/* meshPart.opacity = 1.0;
|
||||||
meshPart._material = std::make_shared<model::Material>();
|
meshPart._material = std::make_shared<model::Material>();
|
||||||
meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0));
|
meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0));
|
||||||
meshPart._material->setOpacity(1.0);
|
meshPart._material->setOpacity(1.0);
|
||||||
meshPart._material->setMetallic(0.0);
|
meshPart._material->setMetallic(0.0);
|
||||||
meshPart._material->setGloss(96.0);
|
meshPart._material->setGloss(96.0);
|
||||||
meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0));
|
meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0));*/
|
||||||
}
|
}
|
||||||
|
|
||||||
// OBJFace
|
// OBJFace
|
||||||
|
@ -486,7 +486,10 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping,
|
||||||
}
|
}
|
||||||
if (!groupMaterialName.isEmpty()) {
|
if (!groupMaterialName.isEmpty()) {
|
||||||
OBJMaterial* material = &materials[groupMaterialName];
|
OBJMaterial* material = &materials[groupMaterialName];
|
||||||
// The code behind this is in transition. Some things are set directly in the FXBMeshPart...
|
|
||||||
|
// TODO Fix this once the transision is understood
|
||||||
|
|
||||||
|
/*// The code behind this is in transition. Some things are set directly in the FXBMeshPart...
|
||||||
meshPart.materialID = groupMaterialName;
|
meshPart.materialID = groupMaterialName;
|
||||||
meshPart.diffuseTexture.filename = material->diffuseTextureFilename;
|
meshPart.diffuseTexture.filename = material->diffuseTextureFilename;
|
||||||
meshPart.specularTexture.filename = material->specularTextureFilename;
|
meshPart.specularTexture.filename = material->specularTextureFilename;
|
||||||
|
@ -495,6 +498,7 @@ FBXGeometry* OBJReader::readOBJ(QIODevice* device, const QVariantHash& mapping,
|
||||||
meshPart._material->setMetallic(glm::length(material->specularColor));
|
meshPart._material->setMetallic(glm::length(material->specularColor));
|
||||||
meshPart._material->setGloss(material->shininess);
|
meshPart._material->setGloss(material->shininess);
|
||||||
meshPart._material->setOpacity(material->opacity);
|
meshPart._material->setOpacity(material->opacity);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
// qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count();
|
// qCDebug(modelformat) << "OBJ Reader part:" << meshPartCount << "name:" << leadFace.groupName << "material:" << groupMaterialName << "diffuse:" << meshPart._material->getDiffuse() << "faces:" << faceGroup.count() << "triangle indices will start with:" << mesh.vertices.count();
|
||||||
foreach(OBJFace face, faceGroup) {
|
foreach(OBJFace face, faceGroup) {
|
||||||
|
@ -576,15 +580,18 @@ void fbxDebugDump(const FBXGeometry& fbxgeo) {
|
||||||
foreach (FBXMeshPart meshPart, mesh.parts) {
|
foreach (FBXMeshPart meshPart, mesh.parts) {
|
||||||
qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count();
|
qCDebug(modelformat) << " quadIndices.count() =" << meshPart.quadIndices.count();
|
||||||
qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count();
|
qCDebug(modelformat) << " triangleIndices.count() =" << meshPart.triangleIndices.count();
|
||||||
|
/*
|
||||||
qCDebug(modelformat) << " diffuseColor =" << meshPart.diffuseColor << "mat =" << meshPart._material->getDiffuse();
|
qCDebug(modelformat) << " diffuseColor =" << meshPart.diffuseColor << "mat =" << meshPart._material->getDiffuse();
|
||||||
qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getMetallic();
|
qCDebug(modelformat) << " specularColor =" << meshPart.specularColor << "mat =" << meshPart._material->getMetallic();
|
||||||
qCDebug(modelformat) << " emissiveColor =" << meshPart.emissiveColor << "mat =" << meshPart._material->getEmissive();
|
qCDebug(modelformat) << " emissiveColor =" << meshPart.emissiveColor << "mat =" << meshPart._material->getEmissive();
|
||||||
qCDebug(modelformat) << " emissiveParams =" << meshPart.emissiveParams;
|
qCDebug(modelformat) << " emissiveParams =" << meshPart.emissiveParams;
|
||||||
qCDebug(modelformat) << " gloss =" << meshPart.shininess << "mat =" << meshPart._material->getGloss();
|
qCDebug(modelformat) << " gloss =" << meshPart.shininess << "mat =" << meshPart._material->getGloss();
|
||||||
qCDebug(modelformat) << " opacity =" << meshPart.opacity << "mat =" << meshPart._material->getOpacity();
|
qCDebug(modelformat) << " opacity =" << meshPart.opacity << "mat =" << meshPart._material->getOpacity();
|
||||||
|
*/
|
||||||
qCDebug(modelformat) << " materialID =" << meshPart.materialID;
|
qCDebug(modelformat) << " materialID =" << meshPart.materialID;
|
||||||
qCDebug(modelformat) << " diffuse texture =" << meshPart.diffuseTexture.filename;
|
/* qCDebug(modelformat) << " diffuse texture =" << meshPart.diffuseTexture.filename;
|
||||||
qCDebug(modelformat) << " specular texture =" << meshPart.specularTexture.filename;
|
qCDebug(modelformat) << " specular texture =" << meshPart.specularTexture.filename;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count();
|
qCDebug(modelformat) << " clusters.count() =" << mesh.clusters.count();
|
||||||
foreach (FBXCluster cluster, mesh.clusters) {
|
foreach (FBXCluster cluster, mesh.clusters) {
|
||||||
|
|
|
@ -25,14 +25,29 @@ public:
|
||||||
typedef std::vector< T > Vector;
|
typedef std::vector< T > Vector;
|
||||||
typedef int ID;
|
typedef int ID;
|
||||||
|
|
||||||
const ID INVALID_ID = 0;
|
static const ID INVALID_ID = 0;
|
||||||
|
|
||||||
|
typedef size_t Index;
|
||||||
enum Version {
|
enum Version {
|
||||||
DRAFT = 0,
|
DRAFT = 0,
|
||||||
FINAL,
|
FINAL,
|
||||||
NUM_VERSIONS,
|
NUM_VERSIONS,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static Version evalVersionFromID(ID id) {
|
||||||
|
if (ID <= 0) {
|
||||||
|
return DRAFT;
|
||||||
|
} else (ID > 0) {
|
||||||
|
return FINAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static Index evalIndexFromID(ID id) {
|
||||||
|
return Index(id < 0 ? -id : id) - 1;
|
||||||
|
}
|
||||||
|
static ID evalID(Index index, Version version) {
|
||||||
|
return (version == DRAFT ? -int(index + 1) : int(index + 1));
|
||||||
|
}
|
||||||
|
|
||||||
Table() {
|
Table() {
|
||||||
for (auto e : _elements) {
|
for (auto e : _elements) {
|
||||||
e.resize(0);
|
e.resize(0);
|
||||||
|
@ -40,29 +55,54 @@ public:
|
||||||
}
|
}
|
||||||
~Table() {}
|
~Table() {}
|
||||||
|
|
||||||
ID add(const T& element, Version v = FINAL) {
|
Index getNumElements() const {
|
||||||
switch (v) {
|
return _elements[DRAFT].size();
|
||||||
case DRAFT: {
|
}
|
||||||
_elements[DRAFT].push_back(element);
|
|
||||||
return ID(-(_elements[DRAFT].size() - 1));
|
ID add(const T& element) {
|
||||||
break;
|
for (auto e : _elements) {
|
||||||
|
e.push_back(element);
|
||||||
}
|
}
|
||||||
case FINAL: {
|
return evalID(_elements[DRAFT].size(), DRAFT);
|
||||||
_elements[FINAL].push_back(element);
|
}
|
||||||
return ID(_elements[FINAL].size() - 1);
|
|
||||||
break;
|
void set(ID id, const T& element) {
|
||||||
|
Index index = evalIndexFromID(id);
|
||||||
|
if (index < getNumElements()) {
|
||||||
|
_elements[DRAFT][index] = element;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& get(ID id, const T& element) const {
|
||||||
|
Index index = evalIndexFromID(id);
|
||||||
|
if (index < getNumElements()) {
|
||||||
|
return _elements[DRAFT][index];
|
||||||
}
|
}
|
||||||
return INVALID_ID;
|
return _default;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
Vector _elements[NUM_VERSIONS];
|
Vector _elements[NUM_VERSIONS];
|
||||||
|
T _default;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef Table< MaterialPointer > MaterialTable;
|
typedef Table< MaterialPointer > MaterialTable;
|
||||||
|
typedef Table< TextureChannelPointer > TextureChannelTable;
|
||||||
|
|
||||||
typedef Table< MeshPointer > MeshTable;
|
typedef Table< MeshPointer > MeshTable;
|
||||||
|
|
||||||
|
|
||||||
|
class Shape {
|
||||||
|
public:
|
||||||
|
|
||||||
|
MeshTable::ID _meshID{ MeshTable::INVALID_ID };
|
||||||
|
int _partID = 0;
|
||||||
|
|
||||||
|
MaterialTable::ID _materialID{ MaterialTable::INVALID_ID };
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef Table< Shape > ShapeTable;
|
||||||
|
|
||||||
class Asset {
|
class Asset {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -76,10 +116,14 @@ public:
|
||||||
MaterialTable& editMaterials() { return _materials; }
|
MaterialTable& editMaterials() { return _materials; }
|
||||||
const MaterialTable& getMaterials() const { return _materials; }
|
const MaterialTable& getMaterials() const { return _materials; }
|
||||||
|
|
||||||
|
ShapeTable& editShapes() { return _shapes; }
|
||||||
|
const ShapeTable& getShapes() const { return _shapes; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
MeshTable _meshes;
|
MeshTable _meshes;
|
||||||
MaterialTable _materials;
|
MaterialTable _materials;
|
||||||
|
ShapeTable _shapes;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -198,6 +198,15 @@ public:
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class TextureChannel {
|
||||||
|
public:
|
||||||
|
TextureChannel() {}
|
||||||
|
|
||||||
|
gpu::TextureView _texture;
|
||||||
|
};
|
||||||
|
typedef std::shared_ptr< TextureChannel > TextureChannelPointer;
|
||||||
|
|
||||||
|
|
||||||
class Material {
|
class Material {
|
||||||
public:
|
public:
|
||||||
typedef gpu::BufferView UniformBufferView;
|
typedef gpu::BufferView UniformBufferView;
|
||||||
|
|
|
@ -25,20 +25,18 @@ uniform materialBuffer {
|
||||||
Material getMaterial() {
|
Material getMaterial() {
|
||||||
return _mat;
|
return _mat;
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
float componentSRGBToLinear(float cs) {
|
float componentSRGBToLinear(float cs) {
|
||||||
/* sRGB to linear conversion
|
// sRGB to linear conversion
|
||||||
{ cs / 12.92, cs <= 0.04045
|
// { cs / 12.92, cs <= 0.04045
|
||||||
cl = {
|
// cl = {
|
||||||
{ ((cs + 0.055)/1.055)^2.4, cs > 0.04045
|
// { ((cs + 0.055)/1.055)^2.4, cs > 0.04045
|
||||||
|
// constants:
|
||||||
constants:
|
// T = 0.04045
|
||||||
T = 0.04045
|
// A = 1 / 1.055 = 0.94786729857
|
||||||
A = 1 / 1.055 = 0.94786729857
|
// B = 0.055 * A = 0.05213270142
|
||||||
B = 0.055 * A = 0.05213270142
|
// C = 1 / 12.92 = 0.0773993808
|
||||||
C = 1 / 12.92 = 0.0773993808
|
// G = 2.4
|
||||||
G = 2.4
|
|
||||||
*/
|
|
||||||
const float T = 0.04045;
|
const float T = 0.04045;
|
||||||
const float A = 0.947867;
|
const float A = 0.947867;
|
||||||
const float B = 0.052132;
|
const float B = 0.052132;
|
||||||
|
@ -55,9 +53,10 @@ float componentSRGBToLinear(float cs) {
|
||||||
vec3 SRGBToLinear(vec3 srgb) {
|
vec3 SRGBToLinear(vec3 srgb) {
|
||||||
return vec3(componentSRGBToLinear(srgb.x),componentSRGBToLinear(srgb.y),componentSRGBToLinear(srgb.z));
|
return vec3(componentSRGBToLinear(srgb.x),componentSRGBToLinear(srgb.y),componentSRGBToLinear(srgb.z));
|
||||||
}
|
}
|
||||||
|
vec3 getMaterialDiffuse(Material m) { return (gl_FragCoord.x < 800 ? SRGBToLinear(m._diffuse.rgb) : m._diffuse.rgb); }
|
||||||
|
*/
|
||||||
float getMaterialOpacity(Material m) { return m._diffuse.a; }
|
float getMaterialOpacity(Material m) { return m._diffuse.a; }
|
||||||
vec3 getMaterialDiffuse(Material m) { return (gl_FragCoord.x > 800 ? SRGBToLinear(m._diffuse.rgb) : m._diffuse.rgb); }
|
vec3 getMaterialDiffuse(Material m) { return m._diffuse.rgb; }
|
||||||
vec3 getMaterialSpecular(Material m) { return m._specular.rgb; }
|
vec3 getMaterialSpecular(Material m) { return m._specular.rgb; }
|
||||||
float getMaterialShininess(Material m) { return m._specular.a; }
|
float getMaterialShininess(Material m) { return m._specular.a; }
|
||||||
|
|
||||||
|
|
|
@ -412,8 +412,9 @@ void Resource::handleReplyFinished() {
|
||||||
ResourceCache::requestCompleted(this);
|
ResourceCache::requestCompleted(this);
|
||||||
|
|
||||||
finishedLoading(true);
|
finishedLoading(true);
|
||||||
emit loaded(*reply);
|
|
||||||
downloadFinished(reply);
|
downloadFinished(reply);
|
||||||
|
// Signal the VERY end of loading AND processing a resource, at this point even the specialized class is finalized
|
||||||
|
emit loaded(*reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1723,7 +1723,8 @@ void GeometryReader::run() {
|
||||||
NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
NetworkGeometry::NetworkGeometry(const QUrl& url, bool delayLoad, const QVariantHash& mapping, const QUrl& textureBaseUrl) :
|
||||||
_url(url),
|
_url(url),
|
||||||
_mapping(mapping),
|
_mapping(mapping),
|
||||||
_textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url) {
|
_textureBaseUrl(textureBaseUrl.isValid() ? textureBaseUrl : url),
|
||||||
|
_asset() {
|
||||||
|
|
||||||
if (delayLoad) {
|
if (delayLoad) {
|
||||||
_state = DelayState;
|
_state = DelayState;
|
||||||
|
@ -1910,6 +1911,9 @@ static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBas
|
||||||
int totalIndices = 0;
|
int totalIndices = 0;
|
||||||
bool checkForTexcoordLightmap = false;
|
bool checkForTexcoordLightmap = false;
|
||||||
|
|
||||||
|
// process material parts
|
||||||
|
foreach (const FBXMaterial& mat, ) {
|
||||||
|
|
||||||
// process network parts
|
// process network parts
|
||||||
foreach (const FBXMeshPart& part, mesh.parts) {
|
foreach (const FBXMeshPart& part, mesh.parts) {
|
||||||
NetworkMeshPart* networkPart = new NetworkMeshPart();
|
NetworkMeshPart* networkPart = new NetworkMeshPart();
|
||||||
|
@ -2051,11 +2055,14 @@ static NetworkMesh* buildNetworkMesh(const FBXMesh& mesh, const QUrl& textureBas
|
||||||
void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) {
|
void NetworkGeometry::modelParseSuccess(FBXGeometry* geometry) {
|
||||||
// assume owner ship of geometry pointer
|
// assume owner ship of geometry pointer
|
||||||
_geometry.reset(geometry);
|
_geometry.reset(geometry);
|
||||||
|
_asset = _geometry->_asset;
|
||||||
|
|
||||||
foreach(const FBXMesh& mesh, _geometry->meshes) {
|
foreach(const FBXMesh& mesh, _geometry->meshes) {
|
||||||
_meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl));
|
_meshes.emplace_back(buildNetworkMesh(mesh, _textureBaseUrl));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach(const FBXMaterial& material, _geometry->
|
||||||
|
|
||||||
_state = SuccessState;
|
_state = SuccessState;
|
||||||
emit onSuccess(*this, *_geometry.get());
|
emit onSuccess(*this, *_geometry.get());
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,12 @@
|
||||||
#include <gpu/Stream.h>
|
#include <gpu/Stream.h>
|
||||||
|
|
||||||
|
|
||||||
|
#include <model/Asset.h>
|
||||||
|
|
||||||
class NetworkGeometry;
|
class NetworkGeometry;
|
||||||
class NetworkMesh;
|
class NetworkMesh;
|
||||||
class NetworkTexture;
|
class NetworkTexture;
|
||||||
|
class NetworkMaterial;
|
||||||
|
|
||||||
|
|
||||||
typedef glm::vec3 Vec3Key;
|
typedef glm::vec3 Vec3Key;
|
||||||
|
@ -329,6 +332,7 @@ public:
|
||||||
// WARNING: only valid when isLoaded returns true.
|
// WARNING: only valid when isLoaded returns true.
|
||||||
const FBXGeometry& getFBXGeometry() const { return *_geometry; }
|
const FBXGeometry& getFBXGeometry() const { return *_geometry; }
|
||||||
const std::vector<std::unique_ptr<NetworkMesh>>& getMeshes() const { return _meshes; }
|
const std::vector<std::unique_ptr<NetworkMesh>>& getMeshes() const { return _meshes; }
|
||||||
|
const model::AssetPointer getAsset() const { return _asset; }
|
||||||
|
|
||||||
void setTextureWithNameToURL(const QString& name, const QUrl& url);
|
void setTextureWithNameToURL(const QString& name, const QUrl& url);
|
||||||
QStringList getTextureNames() const;
|
QStringList getTextureNames() const;
|
||||||
|
@ -357,6 +361,8 @@ protected slots:
|
||||||
void modelParseSuccess(FBXGeometry* geometry);
|
void modelParseSuccess(FBXGeometry* geometry);
|
||||||
void modelParseError(int error, QString str);
|
void modelParseError(int error, QString str);
|
||||||
|
|
||||||
|
void oneTextureLoaded();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void attemptRequestInternal();
|
void attemptRequestInternal();
|
||||||
void requestMapping(const QUrl& url);
|
void requestMapping(const QUrl& url);
|
||||||
|
@ -378,6 +384,9 @@ protected:
|
||||||
std::unique_ptr<FBXGeometry> _geometry;
|
std::unique_ptr<FBXGeometry> _geometry;
|
||||||
std::vector<std::unique_ptr<NetworkMesh>> _meshes;
|
std::vector<std::unique_ptr<NetworkMesh>> _meshes;
|
||||||
|
|
||||||
|
// The model asset created from this NetworkGeometry
|
||||||
|
model::AssetPointer _asset;
|
||||||
|
|
||||||
// cache for isLoadedWithTextures()
|
// cache for isLoadedWithTextures()
|
||||||
mutable bool _isLoadedWithTextures = false;
|
mutable bool _isLoadedWithTextures = false;
|
||||||
};
|
};
|
||||||
|
@ -413,6 +422,15 @@ public:
|
||||||
bool isTranslucent() const;
|
bool isTranslucent() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class NetworkMaterial {
|
||||||
|
public:
|
||||||
|
model::MaterialTable::ID _materialID;
|
||||||
|
|
||||||
|
typedef std::map<model::MaterialKey::FlagBit, model::TextureChannelTable::ID> TextureChannelIDs;
|
||||||
|
TextureChannelIDs _textureChannelIDs;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
/// The state associated with a single mesh.
|
/// The state associated with a single mesh.
|
||||||
class NetworkMesh {
|
class NetworkMesh {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -768,6 +768,16 @@ public:
|
||||||
QUrl url;
|
QUrl url;
|
||||||
int meshIndex;
|
int meshIndex;
|
||||||
int partIndex;
|
int partIndex;
|
||||||
|
|
||||||
|
// Core definition of a Shape = transform + model/mesh/part + material
|
||||||
|
model::AssetPointer _asset;
|
||||||
|
model::ShapeTable::ID _shapeID;
|
||||||
|
|
||||||
|
Transform _transform;
|
||||||
|
model::MeshPointer _mesh;
|
||||||
|
int _part;
|
||||||
|
model::MaterialPointer _material;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace render {
|
namespace render {
|
||||||
|
|
|
@ -28,14 +28,12 @@ void main(void) {
|
||||||
// Fetch diffuse map
|
// Fetch diffuse map
|
||||||
vec4 diffuse = texture(diffuseMap, _texCoord0);
|
vec4 diffuse = texture(diffuseMap, _texCoord0);
|
||||||
|
|
||||||
vec3 vertexColor = (gl_FragCoord.y > 400 ? SRGBToLinear(_color.rgb) : _color.rgb);
|
|
||||||
|
|
||||||
Material mat = getMaterial();
|
Material mat = getMaterial();
|
||||||
|
|
||||||
packDeferredFragment(
|
packDeferredFragment(
|
||||||
normalize(_normal.xyz),
|
normalize(_normal.xyz),
|
||||||
evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a),
|
evalOpaqueFinalAlpha(getMaterialOpacity(mat), diffuse.a),
|
||||||
getMaterialDiffuse(mat) * diffuse.rgb * vertexColor,
|
getMaterialDiffuse(mat) * diffuse.rgb * _color,
|
||||||
getMaterialSpecular(mat),
|
getMaterialSpecular(mat),
|
||||||
getMaterialShininess(mat));
|
getMaterialShininess(mat));
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,36 @@
|
||||||
|
|
||||||
<$declareStandardTransform()$>
|
<$declareStandardTransform()$>
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
float componentSRGBToLinear(float cs) {
|
||||||
|
// sRGB to linear conversion
|
||||||
|
// { cs / 12.92, cs <= 0.04045
|
||||||
|
// cl = {
|
||||||
|
// { ((cs + 0.055)/1.055)^2.4, cs > 0.04045
|
||||||
|
// constants:
|
||||||
|
// T = 0.04045
|
||||||
|
// A = 1 / 1.055 = 0.94786729857
|
||||||
|
// B = 0.055 * A = 0.05213270142
|
||||||
|
// C = 1 / 12.92 = 0.0773993808
|
||||||
|
// G = 2.4
|
||||||
|
const float T = 0.04045;
|
||||||
|
const float A = 0.947867;
|
||||||
|
const float B = 0.052132;
|
||||||
|
const float C = 0.077399;
|
||||||
|
const float G = 2.4;
|
||||||
|
|
||||||
|
if (cs > T) {
|
||||||
|
return pow((cs * A + B), G);
|
||||||
|
} else {
|
||||||
|
return cs * C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 SRGBToLinear(vec3 srgb) {
|
||||||
|
return vec3(componentSRGBToLinear(srgb.x),componentSRGBToLinear(srgb.y),componentSRGBToLinear(srgb.z));
|
||||||
|
}
|
||||||
|
*/
|
||||||
const int MAX_TEXCOORDS = 2;
|
const int MAX_TEXCOORDS = 2;
|
||||||
|
|
||||||
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
|
uniform mat4 texcoordMatrices[MAX_TEXCOORDS];
|
||||||
|
@ -30,7 +60,9 @@ void main(void) {
|
||||||
|
|
||||||
// pass along the diffuse color
|
// pass along the diffuse color
|
||||||
_color = inColor.xyz;
|
_color = inColor.xyz;
|
||||||
|
// _color = SRGBToLinear(inColor.xyz);
|
||||||
|
|
||||||
|
|
||||||
// and the texture coordinates
|
// and the texture coordinates
|
||||||
_texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st;
|
_texCoord0 = (texcoordMatrices[0] * vec4(inTexCoord0.st, 0.0, 1.0)).st;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue