1
0
mirror of https://github.com/rwengine/openrw.git synced 2024-09-18 16:32:32 +02:00

Allow ModelFrame, Atomic, Geometry to exist outside of a Clump

This changes the Clump data structure to simplify it and move some
of the concerns into the Atomic and Frame types.
This commit is contained in:
Daniel Evans 2017-01-04 21:08:21 +00:00
parent a0eaf5b8b0
commit 34c70b0be2
5 changed files with 269 additions and 144 deletions

View File

@ -9,37 +9,78 @@ Geometry::Geometry() : flags(0) {
Geometry::~Geometry() {
}
ModelFrame::ModelFrame(unsigned int index, ModelFrame* parent, glm::mat3 dR,
glm::vec3 dT)
ModelFrame::ModelFrame(unsigned int index, glm::mat3 dR, glm::vec3 dT)
: index(index)
, defaultRotation(dR)
, defaultTranslation(dT)
, parentFrame(parent) {
if (parent != nullptr) {
parent->childs.push_back(this);
}
, parent_(nullptr) {
reset();
}
void ModelFrame::reset() {
matrix = glm::translate(glm::mat4(), defaultTranslation) *
glm::mat4(defaultRotation);
updateHierarchyTransform();
}
void ModelFrame::addGeometry(size_t idx) {
geometries.push_back(idx);
void ModelFrame::updateHierarchyTransform() {
// Update our own transformation
if (parent_) {
worldtransform_ = parent_->getWorldTransform() * matrix;
} else {
worldtransform_ = matrix;
}
for (const auto& child : children_) {
child->updateHierarchyTransform();
}
}
void ModelFrame::addChild(ModelFramePtr& child) {
// Make sure the child is an orphan
if (child->getParent()) {
auto& other_children = child->getParent()->children_;
other_children.erase(
std::remove(other_children.begin(), other_children.end(), child),
other_children.end());
}
child->parent_ = this;
children_.push_back(child);
}
ModelFrame* ModelFrame::findDescendant(const std::string& name) const {
for (const auto& frame : children_) {
if (frame->getName() == name) {
return frame.get();
}
auto result = frame->findDescendant(name);
if (result) {
return result;
}
}
return nullptr;
}
ModelFrame* Clump::findFrame(const std::string& name) const {
if (rootframe_->getName() == name) {
return rootframe_.get();
}
return rootframe_->findDescendant(name);
}
Clump::~Clump() {
for (auto mf : frames) {
delete mf;
}
}
void Clump::recalculateMetrics() {
boundingRadius = std::numeric_limits<float>::min();
for (size_t g = 0; g < geometries.size(); g++) {
RW::BSGeometryBounds& bounds = geometries[g]->geometryBounds;
for (const auto& atomic : atomics_) {
const auto& geometry = atomic->getGeometry();
if (!geometry) {
continue;
}
const auto& bounds = geometry->geometryBounds;
boundingRadius = std::max(boundingRadius,
glm::length(bounds.center) + bounds.radius);
}

View File

@ -12,26 +12,43 @@
#include <gl/TextureData.hpp>
#include <loaders/RWBinaryStream.hpp>
// Forward Declerations
class ModelFrame;
struct Geometry;
class Atomic;
class Clump;
// Pointer types
using ModelFramePtr = std::shared_ptr<ModelFrame>;
using GeometryPtr = std::shared_ptr<Geometry>;
using AtomicPtr = std::shared_ptr<Atomic>;
using AtomicList = std::vector<AtomicPtr>;
using ClumpPtr = std::shared_ptr<Clump>;
/**
* ModelFrame stores the hierarchy of a model's geometry as well as default
* transformations.
* ModelFrame stores transformation hierarchy
*/
class ModelFrame {
unsigned int index;
glm::mat3 defaultRotation;
glm::vec3 defaultTranslation;
glm::mat4 matrix;
ModelFrame* parentFrame;
glm::mat4 worldtransform_;
ModelFrame* parent_;
std::string name;
std::vector<size_t> geometries;
std::vector<ModelFrame*> childs;
std::vector<ModelFramePtr> children_;
public:
ModelFrame(unsigned int index, ModelFrame* parent, glm::mat3 dR,
glm::vec3 dT);
ModelFrame(unsigned int index = 0, glm::mat3 dR = glm::mat3(),
glm::vec3 dT = glm::vec3());
void reset();
void setTransform(const glm::mat4& m);
void setTransform(const glm::mat4& m) {
matrix = m;
updateHierarchyTransform();
}
const glm::mat4& getTransform() const {
return matrix;
}
@ -40,8 +57,6 @@ public:
name = fname;
}
void addGeometry(size_t idx);
unsigned int getIndex() const {
return index;
}
@ -54,25 +69,45 @@ public:
return defaultRotation;
}
glm::mat4 getMatrix() const {
return (parentFrame ? parentFrame->getMatrix() : glm::mat4()) * matrix;
void setTranslation(const glm::vec3& t) {
matrix[3] = glm::vec4(t, matrix[3][3]);
updateHierarchyTransform();
}
void setRotation(const glm::mat3& r) {
for (unsigned int i = 0; i < 3; i++) {
matrix[i] = glm::vec4(r[i], matrix[i][3]);
}
updateHierarchyTransform();
}
/**
* Updates the cached matrix
*/
void updateHierarchyTransform();
/**
* @return the cached world transformation for this Frame
*/
const glm::mat4& getWorldTransform() const {
return worldtransform_;
}
ModelFrame* getParent() const {
return parentFrame;
return parent_;
}
const std::vector<ModelFrame*>& getChildren() const {
return childs;
void addChild(ModelFramePtr& child);
const std::vector<ModelFramePtr>& getChildren() const {
return children_;
}
const std::string& getName() const {
return name;
}
const std::vector<size_t>& getGeometries() const {
return geometries;
}
ModelFrame* findDescendant(const std::string& name) const;
};
/**
@ -149,9 +184,27 @@ struct Geometry {
/**
* @brief The Atomic struct
*/
struct Atomic {
uint32_t frame;
uint32_t geometry;
class Atomic {
ModelFramePtr frame_;
GeometryPtr geometry_;
public:
void setFrame(ModelFramePtr& frame) {
frame_ = frame;
}
const ModelFramePtr& getFrame() const {
return frame_;
}
void setGeometry(GeometryPtr& geom) {
geometry_ = geom;
}
const GeometryPtr& getGeometry() const {
return geometry_;
}
};
/**
@ -159,22 +212,12 @@ struct Atomic {
*/
class Clump {
public:
std::uint32_t numAtomics;
// This should be gone
std::vector<ModelFrame*> frames;
// This should be gone
std::vector<std::shared_ptr<Geometry>> geometries;
std::vector<Atomic> atomics;
int32_t rootFrameIdx;
ModelFrame* findFrame(const std::string& name) const {
auto fit =
std::find_if(frames.begin(), frames.end(),
[&](ModelFrame* f) { return f->getName() == name; });
return fit != frames.end() ? *fit : nullptr;
}
/**
* @brief findFrame Locates frame with name anywhere in the hierarchy
* @param name
* @return
*/
ModelFrame* findFrame(const std::string& name) const;
~Clump();
@ -184,8 +227,26 @@ public:
return boundingRadius;
}
void addAtomic(AtomicPtr& atomic) {
atomics_.push_back(atomic);
}
const AtomicList& getAtomics() const {
return atomics_;
}
void setFrame(ModelFramePtr& root) {
rootframe_ = root;
}
const ModelFramePtr& getFrame() const {
return rootframe_;
}
private:
float boundingRadius;
AtomicList atomics_;
ModelFramePtr rootframe_;
};
#endif

View File

@ -1,5 +1,6 @@
#include <data/Clump.hpp>
#include <loaders/LoaderDFF.hpp>
#include <rw/defines.hpp>
#include <algorithm>
#include <cstring>
@ -40,7 +41,7 @@ struct RWBSFrame {
uint32_t matrixflags; // Not used
};
void LoaderDFF::readFrameList(Clump *model, const RWBStream &stream) {
LoaderDFF::FrameList LoaderDFF::readFrameList(const RWBStream &stream) {
auto listStream = stream.getInnerStream();
auto listStructID = listStream.getNextChunk();
@ -53,21 +54,22 @@ void LoaderDFF::readFrameList(Clump *model, const RWBStream &stream) {
unsigned int numFrames = *(std::uint32_t *)headerPtr;
headerPtr += sizeof(std::uint32_t);
model->frames.reserve(numFrames);
FrameList framelist;
framelist.reserve(numFrames);
for (size_t f = 0; f < numFrames; ++f) {
auto data = (RWBSFrame *)headerPtr;
headerPtr += sizeof(RWBSFrame);
auto frame =
std::make_shared<ModelFrame>(f, data->rotation, data->position);
ModelFrame *parent = nullptr;
if (data->index != -1) {
parent = model->frames[data->index];
} else {
model->rootFrameIdx = f;
RW_CHECK(data->index < int(framelist.size()),
"Frame parent out of bounds");
if (data->index != -1 && data->index < int(framelist.size())) {
framelist[data->index]->addChild(frame);
}
auto frame = new ModelFrame(f, parent, data->rotation, data->position);
model->frames.push_back(frame);
framelist.push_back(frame);
}
size_t namedFrames = 0;
@ -87,8 +89,8 @@ void LoaderDFF::readFrameList(Clump *model, const RWBStream &stream) {
std::transform(fname.begin(), fname.end(),
fname.begin(), ::tolower);
if (namedFrames < model->frames.size()) {
model->frames[namedFrames++]->setName(fname);
if (namedFrames < framelist.size()) {
framelist[namedFrames++]->setName(fname);
}
} break;
default:
@ -100,9 +102,11 @@ void LoaderDFF::readFrameList(Clump *model, const RWBStream &stream) {
break;
}
}
return framelist;
}
void LoaderDFF::readGeometryList(Clump *model, const RWBStream &stream) {
LoaderDFF::GeometryList LoaderDFF::readGeometryList(const RWBStream &stream) {
auto listStream = stream.getInnerStream();
auto listStructID = listStream.getNextChunk();
@ -115,21 +119,24 @@ void LoaderDFF::readGeometryList(Clump *model, const RWBStream &stream) {
unsigned int numGeometries = *(std::uint32_t *)headerPtr;
headerPtr += sizeof(std::uint32_t);
model->geometries.reserve(numGeometries);
std::vector<GeometryPtr> geometrylist;
geometrylist.reserve(numGeometries);
for (auto chunkID = listStream.getNextChunk(); chunkID != 0;
chunkID = listStream.getNextChunk()) {
switch (chunkID) {
case CHUNK_GEOMETRY:
readGeometry(model, listStream);
break;
case CHUNK_GEOMETRY: {
geometrylist.push_back(readGeometry(listStream));
} break;
default:
break;
}
}
return geometrylist;
}
void LoaderDFF::readGeometry(Clump *model, const RWBStream &stream) {
GeometryPtr LoaderDFF::readGeometry(const RWBStream &stream) {
auto geomStream = stream.getInnerStream();
auto geomStructID = geomStream.getNextChunk();
@ -137,7 +144,7 @@ void LoaderDFF::readGeometry(Clump *model, const RWBStream &stream) {
throw DFFLoaderException("Geometry missing struct chunk");
}
std::shared_ptr<Geometry> geom(new Geometry);
auto geom = std::make_shared<Geometry>();
char *headerPtr = geomStream.getCursor();
@ -216,26 +223,24 @@ void LoaderDFF::readGeometry(Clump *model, const RWBStream &stream) {
}
}
// Add the geometry to the model now so that it can be accessed.
model->geometries.push_back(geom);
// Process the geometry child sections
for (auto chunkID = geomStream.getNextChunk(); chunkID != 0;
chunkID = geomStream.getNextChunk()) {
switch (chunkID) {
case CHUNK_MATERIALLIST:
readMaterialList(model, geomStream);
readMaterialList(geom, geomStream);
break;
case CHUNK_EXTENSION:
readGeometryExtension(model, geomStream);
readGeometryExtension(geom, geomStream);
break;
default:
break;
}
}
geom->dbuff.setFaceType(
geom->facetype == Geometry::Triangles ? GL_TRIANGLES : GL_TRIANGLE_STRIP);
geom->dbuff.setFaceType(geom->facetype == Geometry::Triangles
? GL_TRIANGLES
: GL_TRIANGLE_STRIP);
geom->gbuff.uploadVertices(verts);
geom->dbuff.addGeometry(&geom->gbuff);
@ -251,9 +256,11 @@ void LoaderDFF::readGeometry(Clump *model, const RWBStream &stream) {
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, sg.start * sizeof(uint32_t),
sizeof(uint32_t) * sg.numIndices, sg.indices.data());
}
return geom;
}
void LoaderDFF::readMaterialList(Clump *model, const RWBStream &stream) {
void LoaderDFF::readMaterialList(GeometryPtr &geom, const RWBStream &stream) {
auto listStream = stream.getInnerStream();
auto listStructID = listStream.getNextChunk();
@ -263,13 +270,13 @@ void LoaderDFF::readMaterialList(Clump *model, const RWBStream &stream) {
unsigned int numMaterials = *(std::uint32_t *)listStream.getCursor();
model->geometries.back()->materials.reserve(numMaterials);
geom->materials.reserve(numMaterials);
RWBStream::ChunkID chunkID;
while ((chunkID = listStream.getNextChunk())) {
switch (chunkID) {
case CHUNK_MATERIAL:
readMaterial(model, listStream);
readMaterial(geom, listStream);
break;
default:
break;
@ -277,7 +284,7 @@ void LoaderDFF::readMaterialList(Clump *model, const RWBStream &stream) {
}
}
void LoaderDFF::readMaterial(Clump *model, const RWBStream &stream) {
void LoaderDFF::readMaterial(GeometryPtr &geom, const RWBStream &stream) {
auto materialStream = stream.getInnerStream();
auto matStructID = materialStream.getNextChunk();
@ -306,21 +313,22 @@ void LoaderDFF::readMaterial(Clump *model, const RWBStream &stream) {
matData += sizeof(float);
material.flags = 0;
model->geometries.back()->materials.push_back(material);
RWBStream::ChunkID chunkID;
while ((chunkID = materialStream.getNextChunk())) {
switch (chunkID) {
case CHUNK_TEXTURE:
readTexture(model, materialStream);
readTexture(material, materialStream);
break;
default:
break;
}
}
geom->materials.push_back(material);
}
void LoaderDFF::readTexture(Clump *model, const RWBStream &stream) {
void LoaderDFF::readTexture(Geometry::Material &material,
const RWBStream &stream) {
auto texStream = stream.getInnerStream();
auto texStructID = texStream.getNextChunk();
@ -343,18 +351,18 @@ void LoaderDFF::readTexture(Clump *model, const RWBStream &stream) {
TextureData::Handle textureinst =
texturelookup ? texturelookup(name, alpha) : nullptr;
model->geometries.back()->materials.back().textures.push_back(
{name, alpha, textureinst});
material.textures.push_back({name, alpha, textureinst});
}
void LoaderDFF::readGeometryExtension(Clump *model, const RWBStream &stream) {
void LoaderDFF::readGeometryExtension(GeometryPtr &geom,
const RWBStream &stream) {
auto extStream = stream.getInnerStream();
RWBStream::ChunkID chunkID;
while ((chunkID = extStream.getNextChunk())) {
switch (chunkID) {
case CHUNK_BINMESHPLG:
readBinMeshPLG(model, extStream);
readBinMeshPLG(geom, extStream);
break;
default:
break;
@ -362,11 +370,10 @@ void LoaderDFF::readGeometryExtension(Clump *model, const RWBStream &stream) {
}
}
void LoaderDFF::readBinMeshPLG(Clump *model, const RWBStream &stream) {
void LoaderDFF::readBinMeshPLG(GeometryPtr &geom, const RWBStream &stream) {
auto data = stream.getCursor();
model->geometries.back()->facetype =
static_cast<Geometry::FaceType>(*(std::uint32_t *)data);
geom->facetype = static_cast<Geometry::FaceType>(*(std::uint32_t *)data);
data += sizeof(std::uint32_t);
unsigned int numSplits = *(std::uint32_t *)data;
@ -375,7 +382,7 @@ void LoaderDFF::readBinMeshPLG(Clump *model, const RWBStream &stream) {
// Number of triangles.
data += sizeof(std::uint32_t);
model->geometries.back()->subgeom.reserve(numSplits);
geom->subgeom.reserve(numSplits);
size_t start = 0;
@ -393,11 +400,13 @@ void LoaderDFF::readBinMeshPLG(Clump *model, const RWBStream &stream) {
sizeof(std::uint32_t) * sg.numIndices);
data += sizeof(std::uint32_t) * sg.numIndices;
model->geometries.back()->subgeom.push_back(sg);
geom->subgeom.push_back(sg);
}
}
void LoaderDFF::readAtomic(Clump *model, const RWBStream &stream) {
AtomicPtr LoaderDFF::readAtomic(FrameList &framelist,
GeometryList &geometrylist,
const RWBStream &stream) {
auto atomicStream = stream.getInnerStream();
auto atomicStructID = atomicStream.getNextChunk();
@ -405,15 +414,26 @@ void LoaderDFF::readAtomic(Clump *model, const RWBStream &stream) {
throw DFFLoaderException("Atomic missing struct chunk");
}
Atomic atom;
auto data = atomicStream.getCursor();
atom.frame = *(std::uint32_t *)data;
auto frame = *(std::uint32_t *)data;
data += sizeof(std::uint32_t);
atom.geometry = *(std::uint32_t *)data;
model->frames[atom.frame]->addGeometry(atom.geometry);
model->atomics.push_back(atom);
auto geometry = *(std::uint32_t *)data;
/// @todo are any atomic extensions important?
// Verify the atomic's particulars
RW_CHECK(frame < framelist.size(), "atomic frame " << frame
<< " out of bounds");
RW_CHECK(geometry < geometrylist.size(),
"atomic geometry " << geometry << " out of bounds");
auto atomic = std::make_shared<Atomic>();
if (geometry < geometrylist.size()) {
atomic->setGeometry(geometrylist[geometry]);
}
if (frame < framelist.size()) {
atomic->setFrame(framelist[frame]);
}
return atomic;
}
Clump *LoaderDFF::loadFromMemory(FileHandle file) {
@ -434,26 +454,40 @@ Clump *LoaderDFF::loadFromMemory(FileHandle file) {
}
// There is only one value in the struct section.
model->numAtomics = *(std::uint32_t *)rootStream.getCursor();
auto numAtomics = *(std::uint32_t *)rootStream.getCursor();
RW_UNUSED(numAtomics);
GeometryList geometrylist;
FrameList framelist;
// Process everything inside the clump stream.
RWBStream::ChunkID chunkID;
while ((chunkID = modelStream.getNextChunk())) {
switch (chunkID) {
case CHUNK_FRAMELIST:
readFrameList(model, modelStream);
framelist = readFrameList(modelStream);
break;
case CHUNK_GEOMETRYLIST:
readGeometryList(model, modelStream);
break;
case CHUNK_ATOMIC:
readAtomic(model, modelStream);
geometrylist = readGeometryList(modelStream);
break;
case CHUNK_ATOMIC: {
auto atomic = readAtomic(framelist, geometrylist, modelStream);
RW_CHECK(atomic, "Failed to read atomic");
if (!atomic) {
// Abort reading the rest of the clump
return nullptr;
}
model->addAtomic(atomic);
} break;
default:
break;
}
}
if (!framelist.empty()) {
model->setFrame(framelist[0]);
}
// Ensure the model has cached metrics
model->recalculateMetrics();

View File

@ -2,6 +2,7 @@
#ifndef _LOADERDFF_HPP_
#define _LOADERDFF_HPP_
#include <data/Clump.hpp>
#include <gl/TextureData.hpp>
#include <loaders/RWBinaryStream.hpp>
#include <platform/FileHandle.hpp>
@ -9,8 +10,6 @@
#include <functional>
#include <string>
class Clump;
class DFFLoaderException {
std::string _message;
@ -24,40 +23,39 @@ public:
};
class LoaderDFF {
/**
* @brief loads a Frame List chunk from stream into model.
* @param model
* @param stream
*/
void readFrameList(Clump* model, const RWBStream& stream);
void readGeometryList(Clump* model, const RWBStream& stream);
void readGeometry(Clump* model, const RWBStream& stream);
void readMaterialList(Clump* model, const RWBStream& stream);
void readMaterial(Clump* model, const RWBStream& stream);
void readTexture(Clump* model, const RWBStream& stream);
void readGeometryExtension(Clump* model, const RWBStream& stream);
void readBinMeshPLG(Clump* model, const RWBStream& stream);
void readAtomic(Clump* model, const RWBStream& stream);
public:
using TextureLookupCallback = std::function<TextureData::Handle(
const std::string&, const std::string&)>;
using GeometryList = std::vector<GeometryPtr>;
using FrameList = std::vector<ModelFramePtr>;
Clump* loadFromMemory(FileHandle file);
void setTextureLookupCallback(TextureLookupCallback tlc) {
texturelookup = tlc;
}
private:
TextureLookupCallback texturelookup;
FrameList readFrameList(const RWBStream& stream);
GeometryList readGeometryList(const RWBStream& stream);
GeometryPtr readGeometry(const RWBStream& stream);
void readMaterialList(GeometryPtr& geom, const RWBStream& stream);
void readMaterial(GeometryPtr& geom, const RWBStream& stream);
void readTexture(Geometry::Material& material, const RWBStream& stream);
void readGeometryExtension(GeometryPtr& geom, const RWBStream& stream);
void readBinMeshPLG(GeometryPtr& geom, const RWBStream& stream);
AtomicPtr readAtomic(FrameList& framelist, GeometryList& geometrylist,
const RWBStream& stream);
};
#endif

View File

@ -15,22 +15,13 @@ BOOST_AUTO_TEST_CASE(test_load_dff) {
BOOST_REQUIRE(m != nullptr);
BOOST_REQUIRE_EQUAL(m->frames.size(), 40);
BOOST_REQUIRE(m->getFrame());
BOOST_REQUIRE_EQUAL(m->geometries.size(), 16);
BOOST_REQUIRE(!m->getAtomics().empty());
const auto& atomic = m->getAtomics()[0];
BOOST_REQUIRE_EQUAL(m->geometries[0]->subgeom.size(), 5);
for (auto& g : m->geometries) {
BOOST_CHECK_GT(g->geometryBounds.radius, 0.f);
}
BOOST_REQUIRE(m->atomics.size() > 0);
for (auto& a : m->atomics) {
BOOST_CHECK(a.frame < m->frames.size());
BOOST_CHECK(a.geometry < m->geometries.size());
}
BOOST_REQUIRE(atomic->getGeometry());
BOOST_REQUIRE(atomic->getFrame());
delete m;
}