2013-07-02 08:40:43 +02:00
|
|
|
#include <renderwure/loaders/LoaderDFF.hpp>
|
2013-09-11 18:23:31 +00:00
|
|
|
#include <renderwure/engine/GTAData.hpp>
|
2013-09-25 09:05:18 +01:00
|
|
|
#include <renderwure/render/Model.hpp>
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
#include <iostream>
|
2013-07-26 06:40:21 +01:00
|
|
|
#include <algorithm>
|
2013-09-11 18:23:31 +00:00
|
|
|
#include <set>
|
2013-09-25 09:05:18 +01:00
|
|
|
#include <cstring>
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2013-08-18 20:31:37 +01:00
|
|
|
#include <glm/gtc/matrix_transform.hpp>
|
|
|
|
|
2013-09-11 18:23:31 +00:00
|
|
|
Model* LoaderDFF::loadFromMemory(char *data, GTAData *gameData)
|
2013-07-02 07:06:03 +01:00
|
|
|
{
|
2013-07-24 01:44:08 +01:00
|
|
|
auto model = new Model;
|
2013-07-02 07:06:03 +01:00
|
|
|
RW::BinaryStreamSection root(data);
|
|
|
|
|
|
|
|
model->clump = root.readStructure<RW::BSClump>();
|
|
|
|
|
2013-07-05 19:50:04 +01:00
|
|
|
size_t dataI = 0, clumpID = 0;
|
2013-07-02 07:06:03 +01:00
|
|
|
while (root.hasMoreData(dataI)) {
|
|
|
|
auto sec = root.getNextChildSection(dataI);
|
|
|
|
|
|
|
|
switch (sec.header.id) {
|
2013-07-03 19:00:15 +01:00
|
|
|
case RW::SID_FrameList: {
|
|
|
|
auto list = sec.readStructure<RW::BSFrameList>();
|
|
|
|
size_t fdataI = sizeof(RW::BSFrameList) + sizeof(RW::BSSectionHeader);
|
|
|
|
|
|
|
|
for(size_t f = 0; f < list.numframes; ++f) {
|
2013-07-26 06:40:21 +01:00
|
|
|
auto rawframe = sec.readSubStructure<RW::BSFrameListFrame>(fdataI);
|
2013-07-03 19:00:15 +01:00
|
|
|
fdataI += sizeof(RW::BSFrameListFrame);
|
2013-07-26 06:40:21 +01:00
|
|
|
|
|
|
|
Model::Frame frame;
|
|
|
|
frame.defaultRotation = rawframe.rotation;
|
|
|
|
frame.defaultTranslation = rawframe.position;
|
|
|
|
frame.parentFrameIndex = rawframe.index;
|
|
|
|
|
2013-08-18 20:31:37 +01:00
|
|
|
// Initilize the matrix with the correct values.
|
|
|
|
frame.matrix = glm::translate(glm::mat4(frame.defaultRotation), frame.defaultTranslation);
|
|
|
|
|
2013-07-26 06:40:21 +01:00
|
|
|
model->frames.push_back(frame);
|
2013-07-03 19:00:15 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t fldataI = 0;
|
|
|
|
while( sec.hasMoreData(fldataI)) {
|
|
|
|
auto listsec = sec.getNextChildSection(fldataI);
|
|
|
|
if( listsec.header.id == RW::SID_Extension) {
|
|
|
|
size_t extI = 0;
|
|
|
|
while( listsec.hasMoreData(extI)) {
|
|
|
|
auto extSec = listsec.getNextChildSection(extI);
|
|
|
|
if( extSec.header.id == RW::SID_NodeName) {
|
2013-07-26 06:40:21 +01:00
|
|
|
std::string framename(extSec.raw(), extSec.header.size);
|
|
|
|
std::transform(framename.begin(), framename.end(), framename.begin(), ::tolower );
|
2013-09-11 00:26:13 +01:00
|
|
|
|
|
|
|
// !HACK!
|
|
|
|
if(framename == "swaist") {
|
|
|
|
model->rootFrameIdx = model->frameNames.size();
|
|
|
|
}
|
|
|
|
|
2013-07-26 06:40:21 +01:00
|
|
|
model->frameNames.push_back(framename);
|
2013-07-03 19:00:15 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2013-07-02 07:06:03 +01:00
|
|
|
case RW::SID_GeometryList: {
|
2013-10-08 22:26:11 +02:00
|
|
|
/*auto list =*/ sec.readStructure<RW::BSGeometryList>();
|
2013-07-02 07:06:03 +01:00
|
|
|
size_t gdataI = 0;
|
|
|
|
while (sec.hasMoreData(gdataI)) {
|
2013-09-25 09:05:18 +01:00
|
|
|
std::shared_ptr<Model::Geometry> geom(new Model::Geometry);
|
|
|
|
|
2013-07-02 07:06:03 +01:00
|
|
|
auto item = sec.getNextChildSection(gdataI);
|
|
|
|
|
|
|
|
if (item.header.id == RW::SID_Geometry) {
|
|
|
|
size_t dataI = 0, secI = 0;
|
|
|
|
auto geometry = item.readStructure<RW::BSGeometry>();
|
|
|
|
// std::cout << " verts(" << geometry.numverts << ") tris(" << geometry.numtris << ")" << std::endl;
|
2013-07-04 00:23:12 +01:00
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->flags = geometry.flags;
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
item.getNextChildSection(secI);
|
|
|
|
char *data = item.raw() + sizeof(RW::BSSectionHeader) + sizeof(RW::BSGeometry);
|
|
|
|
|
|
|
|
if (item.header.versionid < 0x1003FFFF)
|
2013-10-08 22:26:11 +02:00
|
|
|
/*auto colors =*/ readStructure<RW::BSGeometryColor>(data, dataI);
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
std::vector<glm::vec4> colours;
|
|
|
|
colours.resize(geometry.numverts);
|
2013-07-02 07:06:03 +01:00
|
|
|
if ((geometry.flags & RW::BSGeometry::VertexColors) == RW::BSGeometry::VertexColors) {
|
|
|
|
for (size_t v = 0; v < geometry.numverts; ++v) {
|
2013-07-05 02:15:29 +01:00
|
|
|
auto s = readStructure<RW::BSColor>(data, dataI);
|
|
|
|
size_t R = s % 256; s /= 256;
|
|
|
|
size_t G = s % 256; s /= 256;
|
|
|
|
size_t B = s % 256; s /= 256;
|
|
|
|
size_t A = s % 256;
|
2013-09-25 09:05:18 +01:00
|
|
|
colours[v] = glm::vec4(R/255.f, G/255.f, B/255.f, A/255.f);
|
2013-07-05 02:15:29 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// To save needing another shader, just insert a white colour for each vertex
|
|
|
|
for (size_t v = 0; v < geometry.numverts; ++v) {
|
2013-09-25 09:05:18 +01:00
|
|
|
colours[v] = glm::vec4(1.f);
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
}
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->setColours(colours);
|
|
|
|
|
2013-07-02 07:06:03 +01:00
|
|
|
/** TEX COORDS **/
|
|
|
|
if ((geometry.flags & RW::BSGeometry::TexCoords1) == RW::BSGeometry::TexCoords1 ||
|
|
|
|
(geometry.flags & RW::BSGeometry::TexCoords2) == RW::BSGeometry::TexCoords1) {
|
2013-09-25 09:05:18 +01:00
|
|
|
std::vector<glm::vec2> texcoords;
|
|
|
|
texcoords.resize(geometry.numverts);
|
2013-07-02 07:06:03 +01:00
|
|
|
for (size_t v = 0; v < geometry.numverts; ++v) {
|
2013-09-25 09:05:18 +01:00
|
|
|
texcoords[v] = readStructure<glm::vec2>(data, dataI);
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->setTexCoords(texcoords);
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
//geometryStruct.triangles.reserve(geometry.numtris);
|
2013-07-02 07:06:03 +01:00
|
|
|
for (int j = 0; j < geometry.numtris; ++j) {
|
2013-09-25 09:05:18 +01:00
|
|
|
readStructure<RW::BSGeometryTriangle>(data, dataI);
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** GEOMETRY BOUNDS **/
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->geometryBounds = readStructure<RW::BSGeometryBounds>(data, dataI);
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
/** VERTICES **/
|
2013-09-25 09:05:18 +01:00
|
|
|
std::vector<glm::vec3> positions;
|
|
|
|
positions.resize(geometry.numverts);
|
|
|
|
for (size_t v = 0; v < geometry.numverts; ++v) {
|
|
|
|
positions[v] = readStructure<glm::vec3>(data, dataI);
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->setVertexData(positions);
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
/** NORMALS **/
|
|
|
|
if ((geometry.flags & RW::BSGeometry::StoreNormals) == RW::BSGeometry::StoreNormals) {
|
2013-09-25 09:05:18 +01:00
|
|
|
std::vector<glm::vec3> normals;
|
|
|
|
normals.reserve(geometry.numverts);
|
|
|
|
for (size_t v = 0; v < geometry.numverts; ++v) {
|
|
|
|
normals[v] = readStructure<glm::vec3>(data, dataI);
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->setNormals(normals);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// Generate normals.
|
|
|
|
/*geometryStruct.normals.resize(geometry.numverts);
|
|
|
|
for (auto &subgeom : geometryStruct.subgeom) {
|
|
|
|
glm::vec3 normal{0, 0, 0};
|
|
|
|
for (size_t i = 0; i+2 < subgeom.indices.size(); i += 3) {
|
|
|
|
glm::vec3 p1 = geometryStruct.vertices[subgeom.indices[i]];
|
|
|
|
glm::vec3 p2 = geometryStruct.vertices[subgeom.indices[i+1]];
|
|
|
|
glm::vec3 p3 = geometryStruct.vertices[subgeom.indices[i+2]];
|
|
|
|
|
|
|
|
glm::vec3 U = p2 - p1;
|
|
|
|
glm::vec3 V = p3 - p1;
|
|
|
|
|
|
|
|
normal.x = (U.y * V.z) - (U.z * V.y);
|
|
|
|
normal.y = (U.z * V.x) - (U.x * V.z);
|
|
|
|
normal.z = (U.x * V.y) - (U.y * V.x);
|
|
|
|
|
|
|
|
if (glm::length(normal) > 0.0000001)
|
|
|
|
normal = glm::normalize(normal);
|
|
|
|
|
|
|
|
geometryStruct.normals[subgeom.indices[i]] = normal;
|
|
|
|
geometryStruct.normals[subgeom.indices[i+1]] = normal;
|
|
|
|
geometryStruct.normals[subgeom.indices[i+2]] = normal;
|
|
|
|
}
|
|
|
|
}*/
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** TEXTURES **/
|
|
|
|
auto materiallistsec = item.getNextChildSection(secI);
|
|
|
|
auto materialList = materiallistsec.readStructure<RW::BSMaterialList>();
|
|
|
|
|
|
|
|
// Skip over the per-material byte values that I don't know what do.
|
|
|
|
dataI += sizeof(uint32_t) * materialList.nummaterials;
|
|
|
|
|
|
|
|
size_t matI = 0;
|
|
|
|
materiallistsec.getNextChildSection(matI);
|
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->materials.resize(materialList.nummaterials);
|
2013-09-11 18:23:31 +00:00
|
|
|
std::map<std::string, TextureInfo> availableTextures;
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
for (size_t m = 0; m < materialList.nummaterials; ++m) {
|
|
|
|
auto materialsec = materiallistsec.getNextChildSection(matI);
|
|
|
|
if (materialsec.header.id != RW::SID_Material)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto material = materialsec.readStructure<RW::BSMaterial>();
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->materials[m].textures.resize(material.numtextures);
|
2013-07-04 00:23:12 +01:00
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->materials[m].colour = material.color;
|
|
|
|
geom->materials[m].diffuseIntensity = material.diffuse;
|
|
|
|
geom->materials[m].ambientIntensity = material.ambient;
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
size_t texI = 0;
|
|
|
|
materialsec.getNextChildSection(texI);
|
|
|
|
|
|
|
|
for (size_t t = 0; t < material.numtextures; ++t) {
|
|
|
|
auto texsec = materialsec.getNextChildSection(texI);
|
2013-10-08 22:26:11 +02:00
|
|
|
/*auto texture =*/ texsec.readStructure<RW::BSTexture>();
|
2013-07-02 07:06:03 +01:00
|
|
|
|
|
|
|
std::string textureName, alphaName;
|
|
|
|
size_t yetAnotherI = 0;
|
|
|
|
texsec.getNextChildSection(yetAnotherI);
|
|
|
|
|
|
|
|
auto namesec = texsec.getNextChildSection(yetAnotherI);
|
|
|
|
auto alphasec = texsec.getNextChildSection(yetAnotherI);
|
|
|
|
|
|
|
|
// The data is null terminated anyway.
|
|
|
|
textureName = namesec.raw();
|
|
|
|
alphaName = alphasec.raw();
|
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->materials[m].textures[t] = {textureName, alphaName};
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
2013-09-11 18:23:31 +00:00
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
if(geom->materials[m].textures.size() < 1) continue;
|
2013-09-11 18:23:31 +00:00
|
|
|
|
|
|
|
auto textureIt = gameData->textures
|
2013-09-25 09:05:18 +01:00
|
|
|
.find(geom->materials[m].textures[0].name);
|
2013-09-11 18:23:31 +00:00
|
|
|
if(textureIt != gameData->textures.end()) {
|
|
|
|
availableTextures.insert({textureIt->first, textureIt->second});
|
|
|
|
}
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
2013-09-11 18:23:31 +00:00
|
|
|
|
2013-07-02 07:06:03 +01:00
|
|
|
if(item.hasMoreData(secI))
|
|
|
|
{
|
|
|
|
auto extensions = item.getNextChildSection(secI);
|
|
|
|
size_t extI = 0;
|
|
|
|
while(extensions.hasMoreData(extI))
|
|
|
|
{
|
|
|
|
auto extsec = extensions.getNextChildSection(extI);
|
|
|
|
if(extsec.header.id == RW::SID_BinMeshPLG)
|
|
|
|
{
|
|
|
|
auto meshplg = extsec.readSubStructure<RW::BSBinMeshPLG>(0);
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->subgeom.resize(meshplg.numsplits);
|
|
|
|
geom->facetype = static_cast<Model::FaceType>(meshplg.facetype);
|
2013-07-02 07:06:03 +01:00
|
|
|
size_t meshplgI = sizeof(RW::BSBinMeshPLG);
|
|
|
|
for(size_t i = 0; i < meshplg.numsplits; ++i)
|
|
|
|
{
|
2013-08-18 20:31:37 +01:00
|
|
|
auto plgHeader = extsec.readSubStructure<RW::BSMaterialSplit>(meshplgI);
|
2013-07-02 07:06:03 +01:00
|
|
|
meshplgI += sizeof(RW::BSMaterialSplit);
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->subgeom[i].material = plgHeader.index;
|
|
|
|
geom->subgeom[i].indices = new uint32_t[plgHeader.numverts];
|
|
|
|
geom->subgeom[i].numIndices = plgHeader.numverts;
|
2013-07-02 07:06:03 +01:00
|
|
|
for (int j = 0; j < plgHeader.numverts; ++j) {
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->subgeom[i].indices[j] = extsec.readSubStructure<uint32_t>(meshplgI);
|
2013-07-02 07:06:03 +01:00
|
|
|
meshplgI += sizeof(uint32_t);
|
2013-09-11 18:23:31 +00:00
|
|
|
}
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->buildBuffers();
|
2013-07-02 07:06:03 +01:00
|
|
|
|
2013-09-25 09:05:18 +01:00
|
|
|
geom->clumpNum = clumpID;
|
2013-07-05 19:50:04 +01:00
|
|
|
|
2013-07-02 07:06:03 +01:00
|
|
|
// Add it
|
2013-09-25 09:05:18 +01:00
|
|
|
model->geometries.push_back(geom);
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
}
|
2013-07-05 19:50:04 +01:00
|
|
|
clumpID++;
|
2013-07-03 19:00:15 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case RW::SID_Atomic: {
|
|
|
|
model->atomics.push_back(sec.readStructure<Model::Atomic>());
|
|
|
|
break;
|
2013-07-02 07:06:03 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return model;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<class T> T LoaderDFF::readStructure(char *data, size_t &dataI)
|
|
|
|
{
|
|
|
|
size_t originalOffset = dataI;
|
|
|
|
dataI += sizeof(T);
|
|
|
|
return *reinterpret_cast<T*>(data + originalOffset);
|
|
|
|
}
|
|
|
|
|
|
|
|
RW::BSSectionHeader LoaderDFF::readHeader(char *data, size_t &dataI)
|
|
|
|
{
|
|
|
|
return readStructure<RW::BSSectionHeader>(data, dataI);
|
|
|
|
}
|