Commit 3c7bf8c0 authored by Christian Schulte zu Berge's avatar Christian Schulte zu Berge
Browse files

Merge branch 'OpenGL3-core-refactoring' of...

Merge branch 'OpenGL3-core-refactoring' of /mnt/bigone/git/repositories/berge/campvis into development
parents 129aa353 14062458
......@@ -60,19 +60,19 @@ void main() {
// perform MIP
out_Color = vec4(0.0);
for (float slice = 0.0; slice < 1.0; slice += _3dTextureParams._sizeRCP.z) {
out_Color = max(out_Color, lookupTF(_transferFunction, _transferFunctionParams, getElement3DNormalized(_texture3d, _3dTextureParams, vec3(ex_TexCoord.xy, slice)).a));
out_Color = max(out_Color, lookupTF(_transferFunction, _transferFunctionParams, getElement3DNormalized(_texture3d, _3dTextureParams, vec3(ex_TexCoord.xy, slice)).r));
}
}
else {
// render the corresponding slice
vec3 coord = vec3(ex_TexCoord.xy, (_sliceNumber + 0.5) / (_3dTextureParams._size.z));
out_Color = lookupTF(_transferFunction, _transferFunctionParams, getElement3DNormalized(_texture3d, _3dTextureParams, coord).a);
out_Color = lookupTF(_transferFunction, _transferFunctionParams, getElement3DNormalized(_texture3d, _3dTextureParams, coord).r);
}
}
else {
vec4 texel = getElement2DNormalized(_texture2d, _2dTextureParams, ex_TexCoord.xy);
if (_2dTextureParams._numChannels == 1) {
out_Color = lookupTF(_transferFunction, _transferFunctionParams, (_isDepthTexture ? texel.r : texel.a));
out_Color = lookupTF(_transferFunction, _transferFunctionParams, texel.r);
}
else if (_2dTextureParams._numChannels == 3) {
out_Color = vec4(abs(texel.rgb), 1.0);
......
......@@ -38,6 +38,7 @@
#include "core/datastructures/renderdata.h"
#include "core/datastructures/imagerepresentationgl.h"
#include "core/datastructures/facegeometry.h"
#include "core/datastructures/geometrydatafactory.h"
#include "core/tools/job.h"
#include "core/classification/tfgeometry1d.h"
#include "core/classification/geometry1dtransferfunction.h"
......@@ -194,20 +195,9 @@ namespace campvis {
}
void DataContainerInspectorCanvas::createQuad() {
std::vector<tgt::vec3> vertices, texCorods;
vertices.push_back(tgt::vec3( 0.f, 0.f, 0.f));
vertices.push_back(tgt::vec3(1.f, 0.f, 0.f));
vertices.push_back(tgt::vec3(1.f, 1.f, 0.f));
vertices.push_back(tgt::vec3( 0.f, 1.f, 0.f));
texCorods.push_back(tgt::vec3(0.f, 1.f, 0.f));
texCorods.push_back(tgt::vec3(1.f, 1.f, 0.f));
texCorods.push_back(tgt::vec3(1.f, 0.f, 0.f));
texCorods.push_back(tgt::vec3(0.f, 0.f, 0.f));
delete _quad;
_quad = new FaceGeometry(vertices, texCorods);
_quad->createGLBuffers();
_quad = 0;
_quad = GeometryDataFactory::createQuad(tgt::vec3(0.f), tgt::vec3(1.f), tgt::vec3(0.f), tgt::vec3(1.f));
}
void DataContainerInspectorCanvas::repaint() {
......
......@@ -43,6 +43,8 @@ MACRO(PARSE_HEADER_FOR_PIPELINE FileName)
ENDMACRO(PARSE_HEADER_FOR_PIPELINE)
MACRO(INCLUDE_MODULE ModuleDirectory ModuleListFile)
STRING(TOUPPER ${ModuleDirectory} ModuleDirectoryUpper)
LIST(APPEND CampvisEnabledModules ${ModuleDirectory})
SET(ThisModDir ${ModulesDir}/${ModuleDirectory})
......
......@@ -135,7 +135,6 @@ namespace campvis {
_shader = ShdrMgr.loadSeparate("core/glsl/passthrough.vert", "core/glsl/passthrough.frag", "", false);
if (_shader != 0) {
_shader->setAttributeLocation(0, "in_Position");
_shader->setAttributeLocation(1, "in_TexCoord");
_shader->setAttributeLocation(2, "in_Color");
}
else {
......@@ -152,9 +151,6 @@ namespace campvis {
_geometries.clear();
if (_fbo != 0) {
_fbo->activate();
_fbo->detachAll();
_fbo->deactivate();
delete _fbo;
_fbo = 0;
}
......@@ -260,6 +256,7 @@ namespace campvis {
// deactivate Shader and FBO
_shader->deactivate();
_fbo->detachTexture(GL_COLOR_ATTACHMENT0);
_fbo->deactivate();
LGL_ERROR;
......
......@@ -84,7 +84,6 @@ namespace campvis {
}
void TFGeometry1D::render() const {
// TODO: get rid of intermediade mode?
if (_keyPoints.size() < 2)
return;
......@@ -100,7 +99,7 @@ namespace campvis {
colors.push_back(tgt::vec4(a->_color) / 255.f);
}
FaceGeometry fg(vertices, vertices, colors);
FaceGeometry fg(vertices, std::vector<tgt::vec3>(), colors);
fg.render(GL_TRIANGLE_STRIP);
}
TFGeometry1D* TFGeometry1D::createQuad(const tgt::vec2& interval, const tgt::col4& leftColor, const tgt::vec4& rightColor) {
......
......@@ -39,6 +39,7 @@ namespace tgt {
}
namespace campvis {
class FaceGeometry;
/**
* Defines a single shape for the GeometryTransferFunction class.
......@@ -110,6 +111,8 @@ namespace campvis {
protected:
std::vector<KeyPoint> _keyPoints; ///< vector of KeyPoints, KeyPoints are sorted by x-coordinate of the position
FaceGeometry* _tfRenderFace; ///< FaceGeometry used to render TF into the TF texture
FaceGeometry* _tfEditorFace; ///< FaceGeometry used to render TF into editor window
};
// ================================================================================================
......
......@@ -39,6 +39,13 @@ namespace campvis {
const std::string FaceGeometry::loggerCat_ = "CAMPVis.core.datastructures.FaceGeometry";
FaceGeometry::FaceGeometry()
: GeometryData()
, _faceNormal(0.f)
{
}
FaceGeometry::FaceGeometry(const std::vector<tgt::vec3>& vertices, const std::vector<tgt::vec3>& textureCoordinates /*= std::vector<tgt::vec3>()*/, const std::vector<tgt::vec4>& colors /*= std::vector<tgt::vec4>()*/, const std::vector<tgt::vec3>& normals /*= std::vector<tgt::vec3>() */)
: GeometryData()
, _vertices(vertices)
......@@ -105,20 +112,20 @@ namespace campvis {
void FaceGeometry::render(GLenum mode) const {
createGLBuffers();
if (! _buffersInitialized) {
if (_buffersDirty) {
LERROR("Cannot render without initialized OpenGL buffers.");
return;
}
tgt::VertexArrayObject vao;
if (_verticesBuffer)
vao.addVertexAttribute(tgt::VertexArrayObject::VerticesAttribute, _verticesBuffer);
vao.setVertexAttributePointer(0, _verticesBuffer);
if (_texCoordsBuffer)
vao.addVertexAttribute(tgt::VertexArrayObject::TextureCoordinatesAttribute, _texCoordsBuffer);
vao.setVertexAttributePointer(1, _texCoordsBuffer);
if (_colorsBuffer)
vao.addVertexAttribute(tgt::VertexArrayObject::ColorsAttribute, _colorsBuffer);
vao.setVertexAttributePointer(2, _colorsBuffer);
if (_normalsBuffer)
vao.addVertexAttribute(tgt::VertexArrayObject::NormalsAttribute, _normalsBuffer);
vao.setVertexAttributePointer(3, _normalsBuffer);
LGL_ERROR;
glDrawArrays(mode, 0, static_cast<GLsizei>(_vertices.size()));
......@@ -126,9 +133,9 @@ namespace campvis {
}
void FaceGeometry::createGLBuffers() const {
GeometryData::createGLBuffers();
if (_buffersDirty) {
deleteBuffers();
if (! _buffersInitialized) {
try {
_verticesBuffer = new tgt::BufferObject(tgt::BufferObject::ARRAY_BUFFER, tgt::BufferObject::USAGE_STATIC_DRAW);
_verticesBuffer->data(&_vertices.front(), _vertices.size() * sizeof(tgt::vec3), tgt::BufferObject::FLOAT, 3);
......@@ -148,12 +155,12 @@ namespace campvis {
}
catch (tgt::Exception& e) {
LERROR("Error creating OpenGL Buffer objects: " << e.what());
_buffersInitialized = false;
_buffersDirty = true;
return;
}
LGL_ERROR;
_buffersInitialized = true;
_buffersDirty = false;
}
}
......@@ -167,7 +174,7 @@ namespace campvis {
}
}
campvis::FaceGeometry FaceGeometry::clipAgainstPlane(float p, const tgt::vec3& pNormal, float epsilon /*= 1e-8f*/) const {
campvis::FaceGeometry FaceGeometry::clipAgainstPlane(float p, const tgt::vec3& pNormal, float epsilon /*= 1e-4f*/) const {
tgtAssert(epsilon >= 0, "Epsilon must be positive.");
std::vector<tgt::vec3> verts, texCoords, norms;
......
......@@ -48,13 +48,16 @@ namespace campvis {
*
* \note This class expects all vertices lying within one plane. Everything other that that
* leads to undefined behavior.
* \note Like all Geometry classes FaceGeometry has value-sematics: Once created, the
* vertices/colors/etc. cannot be altered anymore.
*/
class FaceGeometry : public GeometryData {
public:
/**
* Creates a new FaceGeometry.
* Creates a new empty FaceGeometry.
*/
explicit FaceGeometry();
/**
* Creates a new FaceGeometry initialized from the given data.
* \param vertices The list of the vertex positions of the face.
* \param textureCoordinates The list of vertex texture coordinates, may be empty.
* \param colors The list of vertex colors, may be empty.
......@@ -68,7 +71,7 @@ namespace campvis {
);
/**
* Destructor, deletes VBOs/VAO if necessary. Hence, needs a valid OpenGL context
* Destructor, deletes VBOs/VAO if necessary.
*/
virtual ~FaceGeometry();
......@@ -131,18 +134,18 @@ namespace campvis {
* \param mode OpenGL rendering mode for this face
*/
virtual void render(GLenum mode) const;
/**
* Creates the OpenGL VBOs and the VAO for this face's geometry.
* Must be called from a valid OpenGL context.
*/
virtual void createGLBuffers() const;
/// \see GeometryData::getWorldBounds
virtual tgt::Bounds getWorldBounds() const;
protected:
/**
* Creates the OpenGL VBOs and the VAO for this face's geometry.
* Must be called from a valid OpenGL context.
*/
void createGLBuffers() const;
std::vector<tgt::vec3> _vertices; ///< The list of the vertex positions of the face.
std::vector<tgt::vec3> _textureCoordinates; ///< The list of vertex texture coordinates, may be empty.
std::vector<tgt::vec4> _colors; ///< The list of vertex colors, may be empty.
......
......@@ -29,27 +29,29 @@
#include "geometrydata.h"
#include "tgt/buffer.h"
#include "tgt/vertexarrayobject.h"
#include "tgt/logmanager.h"
#include "tgt/glcontextmanager.h"
#include "tgt/vertexarrayobject.h"
#include "core/tools/opengljobprocessor.h"
namespace campvis {
const std::string GeometryData::loggerCat_ = "CAMPVis.core.datastructures.GeometryData";;
GeometryData::GeometryData()
: AbstractData()
, _buffersInitialized(false)
, _buffersDirty(true)
, _verticesBuffer(0)
, _texCoordsBuffer(0)
, _colorsBuffer(0)
, _normalsBuffer(0)
, _context(0)
{
}
GeometryData::GeometryData(const GeometryData& rhs)
: AbstractData(rhs)
, _buffersInitialized(false)
, _buffersDirty(true)
, _verticesBuffer(0)
, _texCoordsBuffer(0)
, _colorsBuffer(0)
......@@ -59,12 +61,7 @@ namespace campvis {
}
GeometryData::~GeometryData() {
if (_buffersInitialized) {
delete _verticesBuffer;
delete _texCoordsBuffer;
delete _colorsBuffer;
delete _normalsBuffer;
}
deleteBuffers();
}
GeometryData& GeometryData::operator=(const GeometryData& rhs) {
......@@ -74,25 +71,16 @@ namespace campvis {
AbstractData::operator=(rhs);
// delete old VBOs and null pointers
if (_buffersInitialized) {
delete _verticesBuffer;
delete _texCoordsBuffer;
delete _colorsBuffer;
delete _normalsBuffer;
}
_verticesBuffer = 0;
_texCoordsBuffer = 0;
_colorsBuffer = 0;
_normalsBuffer = 0;
_buffersInitialized = false;
deleteBuffers();
return *this;
}
void GeometryData::deleteBuffers(std::vector<tgt::BufferObject*> buffers) {
for (std::vector<tgt::BufferObject*>::iterator it = buffers.begin(); it != buffers.end(); ++it)
delete *it;
void GeometryData::deleteBuffers() const {
for (int i = 0; i < NUM_BUFFERS; ++i) {
delete _buffers[i];
_buffers[i] = 0;
}
}
size_t GeometryData::getVideoMemoryFootprint() const {
......@@ -110,11 +98,6 @@ namespace campvis {
return sum;
}
void GeometryData::createGLBuffers() const {
if (! _buffersInitialized)
_context = tgt::GlContextManager::getRef().getCurrentContext();
}
const tgt::BufferObject* GeometryData::getVerticesBuffer() const {
return _verticesBuffer;
}
......
......@@ -42,30 +42,48 @@ namespace tgt {
namespace campvis {
/**
* Abstract base class for data handled by a DataHandle and stored in a DataContainer.
* Abstract base class for geometry data in CAMPVis.
*
* \todo
*/
* GeometryData consists of a set of vertices (vec3) and optionally colors (vec4), texture
* coordinates (vec3) and/or normals (vec3) for each vertex.
* GeometryData stores the geometry on the CPU side (local RAM) and takes care of transparently
* mapping it into GPU memory in order to render it using OpenGL. The mapping of vertex
* information to OpenGL vertex attributes is as follows:
* - Vertex positions: Vertex attribute 0
* - Vertex texture coordinates: Vertex attribute 1
* - Vertex colors: Vertex attribute 2
* - Vertex normals: Vertex attribute 3
*
*/
class GeometryData : public AbstractData, public IHasWorldBounds {
public:
GeometryData();
/**
* Constructor
* \param vertexFeatures List of features present for each vertex
*/
explicit GeometryData();
/**
* Copy constructor
* \param rhs GeometryData to copy
*/
GeometryData(const GeometryData& rhs);
/**
* Destructor, deletes VBOs/VAO if necessary. Hence, needs a valid OpenGL context
* Destructor, deletes VBOs/VAO if necessary.
*/
virtual ~GeometryData();
/**
* Assignment operator.
* \param rhs GeometryData to assign to this.
* \return *this after assignment
*/
GeometryData& operator=(const GeometryData& rhs);
static void deleteBuffers(std::vector<tgt::BufferObject*> buffers);
/// \see AbstractData::clone()
virtual AbstractData* clone() const = 0;
/// \see AbstractData::getVideoMemoryFootprint()
virtual size_t getVideoMemoryFootprint() const;
/**
* Renders this GeometryData.
* Must be called from a valid OpenGL context.
......@@ -79,39 +97,61 @@ namespace campvis {
virtual tgt::Bounds getWorldBounds() const = 0;
/**
* Creates the OpenGL VBOs and the VAO for this geometry.
* Must be called from a valid OpenGL context.
* \note When overwriting this method, make sure to call base class method first!
* Returns the Pointer to the OpenGL Buffer with the vertex positions.
* May be 0 if not yet created.
* \return _verticesBuffer
*/
virtual void createGLBuffers() const;
const tgt::BufferObject* getVerticesBuffer() const;
/**
* Returns the Pointer to the OpenGL Buffer with the vertex texture coordinates.
* May be 0 if none are present or not yet created.
* \return _texCoordsBuffer
*/
const tgt::BufferObject* getTextureCoordinatesBuffer() const;
/**
* Returns the Pointer to the OpenGL Buffer with the vertex colors.
* May be 0 if none are present or not yet created.
* \return _colorsBuffer
*/
const tgt::BufferObject* getColorsBuffer() const;
/**
* Returns the Pointer to the OpenGL Buffer with the vertex normals.
* May be 0 if none are present or not yet created.
* \return _normalsBuffer
*/
const tgt::BufferObject* getNormalsBuffer() const;
/// \see AbstractData::getVideoMemoryFootprint()
virtual size_t getVideoMemoryFootprint() const;
protected:
/**
* Deletes all OpenGL BufferObjects.
*/
void deleteBuffers() const;
// mutable to support const lazy initialization
mutable bool _buffersInitialized;
mutable bool _buffersDirty; ///< Flag whether the buffers are dirty (i.e. need to be (re)initialized)
enum { NUM_BUFFERS = 4 };
enum { NUM_BUFFERS = 4 }; ///< Number of buffers in _buffers array
union {
struct {
mutable tgt::BufferObject* _verticesBuffer;
mutable tgt::BufferObject* _texCoordsBuffer;
mutable tgt::BufferObject* _colorsBuffer;
mutable tgt::BufferObject* _normalsBuffer;
mutable tgt::BufferObject* _verticesBuffer; ///< Pointer to the OpenGL Buffer with the vertex positions
mutable tgt::BufferObject* _texCoordsBuffer; ///< Pointer to the OpenGL Buffer with the vertex texture coordinates
mutable tgt::BufferObject* _colorsBuffer; ///< Pointer to the OpenGL Buffer with the vertex colors
mutable tgt::BufferObject* _normalsBuffer; ///< Pointer to the OpenGL Buffer with the vertex normals
};
mutable tgt::BufferObject* _buffers[NUM_BUFFERS];
mutable tgt::BufferObject* _buffers[NUM_BUFFERS]; ///< Array of all buffers
};
private:
mutable tgt::GLCanvas* _context; ///< OpenGL context the buffers were created in (so that they can be deleted correctly)
static const std::string loggerCat_;
};
}
......
// ================================================================================================
//
// This file is part of the CAMPVis Software Framework.
//
// If not explicitly stated otherwise: Copyright (C) 2012, all rights reserved,
// Christian Schulte zu Berge <christian.szb@in.tum.de>
// Chair for Computer Aided Medical Procedures
// Technische Universität München
// Boltzmannstr. 3, 85748 Garching b. München, Germany
// For a full list of authors and contributors, please refer to the file "AUTHORS.txt".
//
// The licensing of this softare is not yet resolved. Until then, redistribution in source or
// binary forms outside the CAMP chair is not permitted, unless explicitly stated in legal form.
// However, the names of the original authors and the above copyright notice must retain in its
// original state in any case.
//
// Legal disclaimer provided by the BSD license:
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//
// ================================================================================================
#include "geometrydatafactory.h"
#include "tgt/assert.h"
namespace campvis {
FaceGeometry* GeometryDataFactory::createQuad(const tgt::vec3& llf, const tgt::vec3& urb, const tgt::vec3& texLlf, const tgt::vec3& texUrb) {
std::vector<tgt::vec3> vertices, texCorods;
vertices.push_back(tgt::vec3(llf.x, llf.y, llf.z));
vertices.push_back(tgt::vec3(urb.x, llf.y, llf.z));
vertices.push_back(tgt::vec3(urb.x, urb.y, llf.z));
vertices.push_back(tgt::vec3(llf.x, urb.y, llf.z));
texCorods.push_back(tgt::vec3(texLlf.x, texLlf.y, texLlf.z));
texCorods.push_back(tgt::vec3(texUrb.x, texLlf.y, texLlf.z));
texCorods.push_back(tgt::vec3(texUrb.x, texUrb.y, texLlf.z));
texCorods.push_back(tgt::vec3(texLlf.x, texUrb.y, texLlf.z));
return new FaceGeometry(vertices, texCorods);
}
MeshGeometry* GeometryDataFactory::createCube(const tgt::Bounds& bounds, const tgt::Bounds& texBounds) {
const tgt::vec3& llf = bounds.getLLF();
const tgt::vec3& urb = bounds.getURB();
const tgt::vec3& tLlf = texBounds.getLLF();
const tgt::vec3& tUrb = texBounds.getURB();
// not the most elegant method, but it works...
std::vector<tgt::vec3> vertices, texCoords;
std::vector<FaceGeometry> faces;
// front
texCoords.push_back(tgt::vec3(tLlf.x, tUrb.y, tLlf.z));
vertices.push_back(tgt::vec3(llf.x, urb.y, llf.z));
texCoords.push_back(tgt::vec3(tUrb.x, tUrb.y, tLlf.z));
vertices.push_back(tgt::vec3(urb.x, urb.y, llf.z));
texCoords.push_back(tgt::vec3(tUrb.x, tLlf.y, tLlf.z));
vertices.push_back(tgt::vec3(urb.x, llf.y, llf.z));
texCoords.push_back(tgt::vec3(tLlf.x, tLlf.y, tLlf.z));
vertices.push_back(tgt::vec3(llf.x, llf.y, llf.z));
faces.push_back(FaceGeometry(vertices, texCoords));
vertices.clear();
texCoords.clear();
// right
texCoords.push_back(tgt::vec3(tUrb.x, tUrb.y, tLlf.z));
vertices.push_back(tgt::vec3(urb.x, urb.y, llf.z));
texCoords.push_back(tgt::vec3(tUrb.x, tUrb.y, tUrb.z));
vertices.push_back(tgt::vec3(urb.x, urb.y, urb.z));
texCoords.push_back(tgt::vec3(tUrb.x, tLlf.y, tUrb.z));
vertices.push_back(tgt::vec3(urb.x, llf.y, urb.z));
texCoords.push_back(tgt::vec3(tUrb.x, tLlf.y, tLlf.z));
vertices.push_back(tgt::vec3(urb.x, llf.y, llf.z));
faces.push_back(FaceGeometry(vertices, texCoords));
vertices.clear();
texCoords.clear();
// top
texCoords.push_back(tgt::vec3(tLlf.x, tUrb.y, tUrb.z));
vertices.push_back(tgt::vec3(llf.x, urb.y, urb.z));
texCoords.push_back(tgt::vec3(tUrb.x, tUrb.y, tUrb.z));
vertices.push_back(tgt::vec3(urb.x, urb.y, urb.z));
texCoords.push_back(tgt::vec3(tUrb.x, tUrb.y, tLlf.z));
vertices.push_back(tgt::vec3(urb.x, urb.y, llf.z));
texCoords.push_back(tgt::vec3(tLlf.x, tUrb.y, tLlf.z));
vertices.push_back(tgt::vec3(llf.x, urb.y, llf.z));
faces.push_back(FaceGeometry(vertices, texCoords));
vertices.clear();
texCoords.clear();
// left
texCoords.push_back(tgt::vec3(tLlf.x, tUrb.y, tUrb.z));
vertices.push_back(tgt::vec3(llf.x, urb.y, urb.z));
texCoords.push_back(tgt::vec3(tLlf.x, tUrb.y, tLlf.z));
vertices.push_back(tgt::vec3(llf.x, urb.y, llf.z));
texCoords.push_back(tgt::vec3(tLlf.x, tLlf.y, tLlf.z));
vertices.push_back(tgt::vec3(llf.x, llf.y, llf.z));
texCoords.push_back(tgt::vec3(tLlf.x, tLlf.y, tUrb.z));
vertices.push_back(tgt::vec3(llf.x, llf.y, urb.z));
faces.push_back(FaceGeometry(vertices, texCoords));
vertices.clear();
texCoords.clear();