From b233e4509b8d585ea30b6c3ccf2c2d5cda43facc Mon Sep 17 00:00:00 2001 From: Janis Born <janis.born@rwth-aachen.de> Date: Tue, 14 May 2013 17:08:22 +0200 Subject: [PATCH] add load / save functions for the ACGL binary VAO format (http://www.graphics.rwth-aachen.de/redmine/projects/acgl/wiki/Vao_File_Format) --- .../OpenGL/Data/VertexArrayObjectLoadStore.hh | 26 +- src/ACGL/OpenGL/Data/GeometryDataLoadStore.cc | 2 - .../OpenGL/Data/VertexArrayObjectLoadStore.cc | 24 +- .../Data/VertexArrayObjectLoadStoreVAO.cc | 322 ++++++++++++++++++ 4 files changed, 365 insertions(+), 9 deletions(-) create mode 100644 src/ACGL/OpenGL/Data/VertexArrayObjectLoadStoreVAO.cc diff --git a/include/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.hh b/include/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.hh index 4b31df83..73519372 100644 --- a/include/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.hh +++ b/include/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.hh @@ -14,8 +14,32 @@ namespace ACGL{ namespace OpenGL{ -//! loads geometry data from a file and attaches the loaded array buffer data to a VertexArrayObject +/////////////////////////////////////////////////////////////////////////////////////////////////// +// generic load/save +/////////////////////////////////////////////////////////////////////////////////////////////////// + +//! Loads geometry data from a file and attaches the loaded array buffer data to a VertexArrayObject +//! Tries to guess the file format from the file extension SharedVertexArrayObject loadVertexArrayObject(const std::string& _filename); +/////////////////////////////////////////////////////////////////////////////////////////////////// +// library specific load +/////////////////////////////////////////////////////////////////////////////////////////////////// + +//! Loads a VertexArrayObject from the ACGL binary VAO format, as specified here: +//! http://www.graphics.rwth-aachen.de/redmine/projects/acgl/wiki/Vao_File_Format +//! A VAO file can contain several named EABs. Only the EAB whose name matches _eabName will be loaded +//! and attached to the VAO. If _eabName is left blank, the first defined EAB will be used. +SharedVertexArrayObject loadVertexArrayObjectVAO(const std::string& _filename, const std::string& _eabName = ""); + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// library specific save +/////////////////////////////////////////////////////////////////////////////////////////////////// + +//! Stores a VertexArrayObject in a file using the ACGL binary VAO format +//! Each attached ArrayBuffer will be stored entirely in the VAO file but only those attributes which +//! are used by _vao will be defined. +bool saveVertexArrayObjectToVAO(ConstSharedVertexArrayObject _vao, const std::string& _filename); + } } diff --git a/src/ACGL/OpenGL/Data/GeometryDataLoadStore.cc b/src/ACGL/OpenGL/Data/GeometryDataLoadStore.cc index bd51162d..42aeb1ce 100644 --- a/src/ACGL/OpenGL/Data/GeometryDataLoadStore.cc +++ b/src/ACGL/OpenGL/Data/GeometryDataLoadStore.cc @@ -5,9 +5,7 @@ **********************************************************************/ #include <ACGL/OpenGL/Data/GeometryDataLoadStore.hh> -//#include <ACGL/OpenGL/Tools.hh> #include <ACGL/Base/FileHelpers.hh> -//#include <ACGL/Utils/Memory.hh> using namespace ACGL; using namespace ACGL::OpenGL; diff --git a/src/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.cc b/src/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.cc index 2c6333f2..da694f36 100644 --- a/src/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.cc +++ b/src/ACGL/OpenGL/Data/VertexArrayObjectLoadStore.cc @@ -6,6 +6,7 @@ #include <ACGL/OpenGL/Data/VertexArrayObjectLoadStore.hh> #include <ACGL/OpenGL/Data/ArrayBufferLoadStore.hh> +#include <ACGL/Base/FileHelpers.hh> using namespace ACGL; using namespace ACGL::OpenGL; @@ -16,16 +17,27 @@ namespace OpenGL{ SharedVertexArrayObject loadVertexArrayObject(const std::string& _filename) { - SharedArrayBuffer ab = loadArrayBuffer(_filename); - if(ab) + // lower case file ending: + std::string fileEnding = Base::FileHelpers::getFileEnding(_filename); + + if(fileEnding == "vao") { - SharedVertexArrayObject vao(new VertexArrayObject); - vao->attachAllAttributes(ab); - return vao; + return loadVertexArrayObjectVAO(_filename); } else { - return SharedVertexArrayObject(); + // Generic load: Try to load the file as an ArrayBuffer and attach it to a VAO + SharedArrayBuffer ab = loadArrayBuffer(_filename); + if(ab) + { + SharedVertexArrayObject vao(new VertexArrayObject); + vao->attachAllAttributes(ab); + return vao; + } + else + { + return SharedVertexArrayObject(); + } } } diff --git a/src/ACGL/OpenGL/Data/VertexArrayObjectLoadStoreVAO.cc b/src/ACGL/OpenGL/Data/VertexArrayObjectLoadStoreVAO.cc new file mode 100644 index 00000000..a6b7376f --- /dev/null +++ b/src/ACGL/OpenGL/Data/VertexArrayObjectLoadStoreVAO.cc @@ -0,0 +1,322 @@ +/*********************************************************************** + * Copyright 2011-2012 Computer Graphics Group RWTH Aachen University. * + * All rights reserved. * + * Distributed under the terms of the MIT License (see LICENSE.TXT). * + **********************************************************************/ + +#include <ACGL/OpenGL/Data/VertexArrayObjectLoadStore.hh> +#include <ACGL/OpenGL/GL.hh> +#include <ACGL/OpenGL/Objects/VertexArrayObject.hh> +#include <ACGL/OpenGL/Objects/ArrayBuffer.hh> +#include <ACGL/OpenGL/Objects/ElementArrayBuffer.hh> + +#include <fstream> +#include <set> + +using namespace ACGL; +using namespace ACGL::OpenGL; +using namespace ACGL::Utils; + +namespace +{ + // Reads _n bytes from _stream and returns them as a std::string. If the string + // in the stream is null terminated, only _n-1 characters are read and the + // trailing '\0' is ignored + std::string readString(std::ifstream& _stream, std::streamsize _n, bool _nullTerminated = true) + { + std::string str; + if(_nullTerminated) + --_n; + str.resize(_n); + _stream.read((char*)str.data(), _n); + if(_nullTerminated) + _stream.ignore(1); + return str; + } + + // Writes _string to _stream. By default, appends a trailing '\0' + void writeString(std::ofstream& _stream, const std::string& _string, bool _nullTerminated = true) + { + _stream.write((char*)_string.data(), _string.size()); + if(_nullTerminated) + _stream.put('\0'); + } +} + +namespace ACGL{ +namespace OpenGL{ + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// library specific load +/////////////////////////////////////////////////////////////////////////////////////////////////// + +SharedVertexArrayObject loadVertexArrayObjectVAO(const std::string& _filename, const std::string& _eabName) +{ + SharedVertexArrayObject vao(new VertexArrayObject()); + + std::ifstream file(_filename.c_str(), std::ios_base::in | std::ios_base::binary); + if(!file.good()) + { + error() << "Could not open file " << _filename << std::endl; + return SharedVertexArrayObject(); + } + + // Header Section + std::string magic = readString(file, 4, false); + if(magic != "VAO1") + { + error() << _filename << " is not an ACGL VAO file" << std::endl; + return SharedVertexArrayObject(); + } + + uint32_t numABs; + uint32_t numEABs; + GLenum defaultMode; + + file.read((char*)&numABs, 4); + file.read((char*)&numEABs, 4); + file.read((char*)&defaultMode, 4); + + // Warn if no AB was specified + if(numABs == 0) + { + warning() << _filename << " contains no ArrayBuffers" << std::endl; + } + + vao->setMode(defaultMode); + + // ArrayBuffer Sections + for(uint32_t abindex = 0; abindex < numABs; ++abindex) + { + // ArrayBuffer Section + SharedArrayBuffer ab = SharedArrayBuffer(new ArrayBuffer()); + + uint32_t abNameLength; + std::string abName; + GLsizei abStride; + uint32_t abNumAttributes; + + file.read((char*)&abNameLength, 4); + abName = readString(file, abNameLength); + file.read((char*)&abStride, 4); + file.read((char*)&abNumAttributes, 4); + + for(uint32_t attrindex = 0; attrindex < abNumAttributes; ++attrindex) + { + // ArrayBuffer Attribute Section + ArrayBuffer::Attribute attr; + uint32_t attrNameLength; + + file.read((char*)&attrNameLength, 4); + attr.name = readString(file, attrNameLength); + file.read((char*)&attr.type, 4); + file.read((char*)&attr.size, 4); + file.read((char*)&attr.offset, 4); + file.read((char*)&attr.normalized, 1); + file.read((char*)&attr.isIntegerInShader, 1); + file.read((char*)&attr.divisor, 4); + + ab->defineAttribute(attr); + } + + // ArrayBuffer Section contd. + uint64_t abDataLength; + char* abData; + + file.read((char*)&abDataLength, 8); + abData = new char[abDataLength]; + file.read(abData, abDataLength); + + ab->setStride(abStride); + ab->setData(abDataLength, (GLvoid*)abData); + + delete[] abData; + + vao->attachAllAttributes(ab); + } + + // ElementArrayBuffer Sections + bool eabMatched = false; // Only match the first EAB fitting _eabName + for(uint32_t i = 0; i < numEABs; ++i) + { + // ElementArrayBuffer Section + uint32_t eabNameLength; + std::string eabName; + GLenum eabMode; + GLenum eabType; + bool eabPR; + GLuint eabPRIndex; + uint64_t eabDataLength; + char* eabData; + + file.read((char*)&eabNameLength, 4); + eabName = readString(file, eabNameLength-1); + file.read((char*)&eabMode, 4); + file.read((char*)&eabType, 4); + file.read((char*)&eabPR, 1); + file.read((char*)&eabPRIndex, 4); + file.read((char*)&eabDataLength, 8); + + if(!eabMatched && (_eabName == "" || _eabName == eabName)) + { + // This is the EAB we are looking for + eabMatched = true; + SharedElementArrayBuffer eab(new ElementArrayBuffer()); + eab->setType(eabType); + if(eabPR) + { + // TODO: Implement Primitive Restart + warning() << "Primitive Restart for EABs is not supported yet" << std::endl; + } + + eabData = new char[eabDataLength]; + file.read(eabData, eabDataLength); + eab->setData(eabDataLength, (GLvoid*)eabData); + delete[] eabData; + + vao->attachElementArrayBuffer(eab); + vao->setMode(eabMode); + } + else + { + // Ignore this EAB + file.ignore(eabDataLength); + } + + // Warn if an EAB name was specified but none matched + if(!_eabName.empty() && !eabMatched) + { + warning() << "No EAB with name " << _eabName << " found in " << _filename << std::endl; + } + } + + return vao; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// library specific save +/////////////////////////////////////////////////////////////////////////////////////////////////// + +bool saveVertexArrayObjectToVAO(ConstSharedVertexArrayObject _vao, const std::string& _filename) +{ + // First, find the set of ArrayBuffers used by _vao + // Each ArrayBuffer will be stored entirely in the VAO file but only those attributes which + // are used by _vao will be defined. + std::set<ConstSharedArrayBuffer> usedArrayBuffers; + for(VertexArrayObject::AttributeVec::const_iterator it = _vao->getAttributes().begin(); + it != _vao->getAttributes().end(); + ++it) + { + const VertexArrayObject::Attribute& attr = *it; + if(attr.arrayBuffer) + usedArrayBuffers.insert(attr.arrayBuffer); + } + + std::ofstream file(_filename.c_str(), std::ios_base::out | std::ios_base::binary); + + // Header Section + uint32_t numABs = usedArrayBuffers.size(); + uint32_t numEABs = _vao->getElementArrayBuffer() ? 1 : 0; + GLenum defaultMode = _vao->getMode(); + + writeString(file, "VAO1", false); + file.write((char*)&numABs, 4); + file.write((char*)&numEABs, 4); + file.write((char*)&defaultMode, 4); + + for(std::set<ConstSharedArrayBuffer>::const_iterator abIt = usedArrayBuffers.begin(); + abIt != usedArrayBuffers.end(); + ++abIt) + { + // Array Buffer Section + const ConstSharedArrayBuffer& ab = *abIt; + + // Find all attributes in _vao that use this AB + std::vector<int32_t> attributeIDs; + for(VertexArrayObject::AttributeVec::const_iterator attrIt = _vao->getAttributes().begin(); + attrIt != _vao->getAttributes().end(); + ++attrIt) + { + const VertexArrayObject::Attribute& attr = *attrIt; + if(attr.arrayBuffer == ab) + attributeIDs.push_back(attr.attributeID); + } + + std::string abName = ""; + uint32_t abNameLength = abName.size() + 1; // Counting the trailing '\0' + GLsizei abStride = ab->getStride(); + uint32_t abNumAttributes = attributeIDs.size(); + + file.write((char*)&abNameLength, 4); + writeString(file, abName); + file.write((char*)&abStride, 4); + file.write((char*)&abNumAttributes, 4); + + for(std::vector<int32_t>::iterator attrIDIt = attributeIDs.begin(); + attrIDIt != attributeIDs.end(); + ++attrIDIt) + { + // ArrayBuffer Attribute Section + int32_t attrID = *attrIDIt; + + std::string attrName = ab->getAttribute(attrID).name; + uint32_t attrNameLength = attrName.size() + 1; // Counting the trailing '\0' + GLenum attrType = ab->getAttribute(attrID).type; + GLint attrSize = ab->getAttribute(attrID).size; + GLuint attrOffset = ab->getAttribute(attrID).offset; + GLboolean attrNormalized = ab->getAttribute(attrID).normalized; + GLboolean attrIsInteger = ab->getAttribute(attrID).isIntegerInShader; + GLuint attrDivisor = ab->getAttribute(attrID).divisor; + + file.write((char*)&attrNameLength, 4); + writeString(file, attrName); + file.write((char*)&attrType, 4); + file.write((char*)&attrSize, 4); + file.write((char*)&attrOffset, 4); + file.write((char*)&attrNormalized, 1); + file.write((char*)&attrIsInteger, 1); + file.write((char*)&attrDivisor, 4); + } + + // Array Buffer Section contd. + uint64_t abDataLength = ab->getSize(); + file.write((char*)&abDataLength, 8); + + ArrayBuffer* abMutable = const_cast<ArrayBuffer*>(ab.get()); + char* abData = (char*)abMutable->map(GL_READ_ONLY); + file.write(abData, abDataLength); + abMutable->unmap(); + } + + if(_vao->getElementArrayBuffer()) + { + // ElementArrayBuffer section + const ConstSharedElementArrayBuffer& eab = _vao->getElementArrayBuffer(); + + std::string eabName = ""; + uint32_t eabNameLength = eabName.size() + 1; // Counting the trailing '\0' + GLenum eabMode = _vao->getMode(); + GLenum eabType = eab->getType(); + bool eabPR = false; // TODO: implement Primitive Reset + GLuint eabPRIndex = 0; // TODO: implement Primitive Reset + uint64_t eabDataLength = eab->getSize(); + + file.write((char*)&eabNameLength, 4); + writeString(file, eabName); + file.write((char*)&eabMode, 4); + file.write((char*)&eabType, 4); + file.write((char*)&eabPR, 1); + file.write((char*)&eabPRIndex, 4); + file.write((char*)&eabDataLength, 8); + + ElementArrayBuffer* eabMutable = const_cast<ElementArrayBuffer*>(eab.get()); + char* eabData = (char*)eabMutable->map(GL_READ_ONLY); + file.write(eabData, eabDataLength); + eabMutable->unmap(); + } + + return true; +} + +} // OpenGL +} // ACGL -- GitLab