Add the Model library

This commit is contained in:
Sam Gateau 2014-12-19 16:19:06 -08:00
parent a7b039fbb9
commit 967c6dbc4b
16 changed files with 549 additions and 15 deletions

View file

@ -6,7 +6,7 @@ include_glm()
# link in the shared libraries
link_hifi_libraries(
audio avatars octree voxels fbx entities metavoxels
audio avatars octree voxels gpu model fbx entities metavoxels
networking animation shared script-engine embedded-webserver
physics
)

View file

@ -107,7 +107,7 @@ endif()
add_executable(${TARGET_NAME} MACOSX_BUNDLE ${INTERFACE_SRCS} ${QM})
# link required hifi libraries
link_hifi_libraries(shared octree voxels gpu fbx metavoxels networking entities avatars audio animation script-engine physics
link_hifi_libraries(shared octree voxels gpu model fbx metavoxels networking entities avatars audio animation script-engine physics
render-utils entities-renderer)
# find any optional and required libraries

View file

@ -3,7 +3,7 @@ set(TARGET_NAME animation)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Network Script)
link_hifi_libraries(shared fbx)
link_hifi_libraries(shared gpu model fbx)
# call macro to link our dependencies and bubble them up via a property on our target
link_shared_dependencies()

View file

@ -5,8 +5,8 @@ setup_hifi_library(Network Script)
include_glm()
link_hifi_libraries(shared octree voxels networking physics)
include_hifi_library_headers(fbx)
link_hifi_libraries(shared octree voxels gpu model fbx networking physics)
include_hifi_library_headers(gpu model fbx)
# call macro to link our dependencies and bubble them up via a property on our target
link_shared_dependencies()

View file

@ -5,7 +5,7 @@ setup_hifi_library(Network Script)
include_glm()
link_hifi_libraries(shared octree fbx networking animation physics)
link_hifi_libraries(shared octree gpu model fbx networking animation physics)
# call macro to link our dependencies and bubble them up via a property on our target
link_shared_dependencies()

View file

@ -5,7 +5,7 @@ setup_hifi_library()
include_glm()
link_hifi_libraries(shared networking octree voxels)
link_hifi_libraries(shared gpu model networking octree voxels)
find_package(ZLIB REQUIRED)
include_directories(SYSTEM "${ZLIB_INCLUDE_DIRS}")

View file

@ -24,8 +24,10 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
class QIODevice;
#include <model/Geometry.h>
#include <model/Material.h>
class QIODevice;
class FBXNode;
typedef QList<FBXNode> FBXNodeList;
@ -131,6 +133,7 @@ public:
FBXTexture emissiveTexture;
QString materialID;
model::MaterialPointer _material;
};
/// A single mesh (with optional blendshapes) extracted from an FBX document.
@ -159,6 +162,8 @@ public:
bool hasSpecularTexture() const;
bool hasEmissiveTexture() const;
model::Mesh _mesh;
};
/// A single animation frame extracted from an FBX document.

11
libraries/model/CMakeLists.txt Executable file
View file

@ -0,0 +1,11 @@
set(TARGET_NAME model)
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library()
include_glm()
link_hifi_libraries(shared gpu)
# call macro to link our dependencies and bubble them up via a property on our target
link_shared_dependencies()

View file

@ -0,0 +1,141 @@
//
// Geometry.cpp
// libraries/model/src/model
//
// Created by Sam Gateau on 12/5/2014.
// Copyright 2014 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 "Geometry.h"
#include <QDebug>
using namespace model;
Mesh::Mesh() :
_vertexBuffer(gpu::Element(gpu::VEC3, gpu::FLOAT, gpu::XYZ)),
_indexBuffer(gpu::Element(gpu::SCALAR, gpu::UINT32, gpu::INDEX)),
_partBuffer(gpu::Element(gpu::VEC4, gpu::UINT32, gpu::PART)) {
}
Mesh::Mesh(const Mesh& mesh) :
_vertexFormat(mesh._vertexFormat),
_vertexBuffer(mesh._vertexBuffer),
_attributeBuffers(mesh._attributeBuffers),
_indexBuffer(mesh._indexBuffer),
_partBuffer(mesh._partBuffer) {
}
Mesh::~Mesh() {
}
void Mesh::setVertexBuffer(const BufferView& buffer) {
_vertexBuffer = buffer;
evalVertexFormat();
}
void Mesh::addAttribute(Slot slot, const BufferView& buffer) {
_attributeBuffers[slot] = buffer;
evalVertexFormat();
}
void Mesh::evalVertexFormat() {
VertexFormat vf;
int channelNum = 0;
if (hasVertexData()) {
vf.setAttribute(gpu::Stream::POSITION, channelNum, _vertexBuffer._element, 0);
channelNum++;
}
for (auto attrib : _attributeBuffers) {
vf.setAttribute(attrib.first, channelNum, attrib.second._element, 0);
channelNum++;
}
_vertexFormat = vf;
}
void Mesh::setIndexBuffer(const BufferView& buffer) {
_indexBuffer = buffer;
}
void Mesh::setPartBuffer(const BufferView& buffer) {
_partBuffer = buffer;
}
const Box Mesh::evalPartBound(int partNum) const {
Box box;
if (partNum < _partBuffer.getNum<Part>()) {
const Part& part = _partBuffer.get<Part>(partNum);
auto index = _indexBuffer.cbegin<Index>();
index += part._startIndex;
auto endIndex = index;
endIndex += part._numIndices;
auto vertices = &_vertexBuffer.get<Vec3>(part._baseVertex);
for (;index != endIndex; index++) {
// skip primitive restart indices
if ((*index) != PRIMITIVE_RESTART_INDEX) {
box += vertices[(*index)];
}
}
}
return box;
}
const Box Mesh::evalPartBounds(int partStart, int partEnd, Boxes& bounds) const {
Box totalBound;
auto part = _partBuffer.cbegin<Part>() + partStart;
auto partItEnd = _partBuffer.cbegin<Part>() + partEnd;
for (;part != partItEnd; part++) {
Box partBound;
auto index = _indexBuffer.cbegin<uint>() + (*part)._startIndex;
auto endIndex = index + (*part)._numIndices;
auto vertices = &_vertexBuffer.get<Vec3>((*part)._baseVertex);
for (;index != endIndex; index++) {
// skip primitive restart indices
if ((*index) != PRIMITIVE_RESTART_INDEX) {
partBound += vertices[(*index)];
}
}
totalBound += partBound;
}
return totalBound;
}
const gpu::BufferStream Mesh::makeBufferStream() const {
gpu::BufferStream stream;
int channelNum = 0;
if (hasVertexData()) {
stream.addBuffer(_vertexBuffer._buffer, _vertexBuffer._offset, _vertexFormat.getChannelStride(channelNum));
channelNum++;
}
for (auto attrib : _attributeBuffers) {
BufferView& view = attrib.second;
stream.addBuffer(view._buffer, view._offset, _vertexFormat.getChannelStride(channelNum));
channelNum++;
}
return stream;
}
Geometry::Geometry() {
}
Geometry::Geometry(const Geometry& geometry):
_mesh(geometry._mesh),
_boxes(geometry._boxes) {
}
Geometry::~Geometry() {
}
void Geometry::setMesh(const MeshPointer& mesh) {
_mesh = mesh;
}

View file

@ -0,0 +1,151 @@
//
// Geometry.h
// libraries/model/src/model
//
// Created by Sam Gateau on 12/5/2014.
// Copyright 2014 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
//
#ifndef hifi_model_Geometry_h
#define hifi_model_Geometry_h
#include <glm/glm.hpp>
#include "AABox.h"
#include "gpu/Resource.h"
#include "gpu/Stream.h"
namespace model {
typedef gpu::BufferView::Index Index;
typedef gpu::BufferView BufferView;
typedef AABox Box;
typedef std::vector< Box > Boxes;
class Mesh {
public:
const static Index PRIMITIVE_RESTART_INDEX = -1;
typedef gpu::BufferView BufferView;
typedef std::vector< BufferView > BufferViews;
typedef gpu::Stream::Slot Slot;
typedef gpu::Stream::Format VertexFormat;
typedef std::map< Slot, BufferView > BufferViewMap;
typedef glm::vec3 Vec3;
Mesh();
Mesh(const Mesh& mesh);
Mesh& operator= (const Mesh& mesh) = default;
virtual ~Mesh();
// Vertex buffer
void setVertexBuffer(const BufferView& buffer);
const BufferView& getVertexBuffer() const { return _vertexBuffer; }
uint getNumVertices() const { return _vertexBuffer.getNumElements(); }
bool hasVertexData() const { return !_vertexBuffer._buffer.isNull(); }
// Attribute Buffers
int getNumAttributes() const { return _attributeBuffers.size(); }
void addAttribute(Slot slot, const BufferView& buffer);
// Stream format
const VertexFormat& getVertexFormat() const { return _vertexFormat; }
// Index Buffer
void setIndexBuffer(const BufferView& buffer);
const BufferView& getIndexBuffer() const { return _indexBuffer; }
uint getNumIndices() const { return _indexBuffer.getNumElements(); }
// Access vertex position value
const Vec3& getPos3(Index index) const { return _vertexBuffer.get<Vec3>(index); }
enum Topology
{
POINTS = 0,
LINES,
LINE_STRIP,
TRIANGLES,
TRIANGLE_STRIP,
QUADS,
QUAD_STRIP,
NUM_TOPOLOGIES,
};
// Subpart of a mesh, describing the toplogy of the surface
class Part {
public:
Index _startIndex;
Index _numIndices;
Index _baseVertex;
Topology _topology;
Part() :
_startIndex(0),
_numIndices(0),
_baseVertex(0),
_topology(TRIANGLES)
{}
Part(Index startIndex, Index numIndices, Index baseVertex, Topology topology) :
_startIndex(startIndex),
_numIndices(numIndices),
_baseVertex(baseVertex),
_topology(topology)
{}
};
void setPartBuffer(const BufferView& buffer);
const BufferView& getPartBuffer() const { return _partBuffer; }
uint getNumParts() const { return _partBuffer.getNumElements(); }
// evaluate the bounding box of A part
const Box evalPartBound(int partNum) const;
// evaluate the bounding boxes of the parts in the range [start, end[ and fill the bounds parameter
// the returned box is the bounding box of ALL the evaluated part bounds.
const Box evalPartBounds(int partStart, int partEnd, Boxes& bounds) const;
// Generate a BufferStream on the mesh vertices and attributes
const gpu::BufferStream makeBufferStream() const;
protected:
VertexFormat _vertexFormat;
BufferView _vertexBuffer;
BufferViewMap _attributeBuffers;
BufferView _indexBuffer;
BufferView _partBuffer;
void evalVertexFormat();
};
typedef QSharedPointer< Mesh > MeshPointer;
class Geometry {
public:
Geometry();
Geometry(const Geometry& geometry);
~Geometry();
void setMesh(const MeshPointer& mesh);
const MeshPointer& getMesh() const { return _mesh; }
protected:
MeshPointer _mesh;
BufferView _boxes;
};
};
#endif

View file

@ -0,0 +1,92 @@
//
// Material.cpp
// libraries/model/src/model
//
// Created by Sam Gateau on 12/10/2014.
// Copyright 2014 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 "Material.h"
using namespace model;
Material::Material() :
_flags(0),
_schemaBuffer(),
_textureMap() {
// only if created from nothing shall we create the Buffer to store the properties
Schema schema;
_schemaBuffer = gpu::BufferView(new gpu::Buffer(sizeof(Schema), (const gpu::Buffer::Byte*) &schema));
}
Material::Material(const Material& material) :
_flags(material._flags),
_schemaBuffer(material._schemaBuffer),
_textureMap(material._textureMap) {
}
Material& Material::operator= (const Material& material) {
_flags = (material._flags);
_schemaBuffer = (material._schemaBuffer);
_textureMap = (material._textureMap);
return (*this);
}
Material::~Material() {
}
void Material::setDiffuse(const Color& diffuse) {
if (glm::any(glm::greaterThan(diffuse, Color(0.0f)))) {
_flags.set(DIFFUSE_BIT);
} else {
_flags.reset(DIFFUSE_BIT);
}
_schemaBuffer.edit<Schema>()._diffuse = diffuse;
}
void Material::setSpecular(const Color& specular) {
if (glm::any(glm::greaterThan(specular, Color(0.0f)))) {
_flags.set(SPECULAR_BIT);
} else {
_flags.reset(SPECULAR_BIT);
}
_schemaBuffer.edit<Schema>()._specular = specular;
}
void Material::setEmissive(const Color& emissive) {
if (glm::any(glm::greaterThan(emissive, Color(0.0f)))) {
_flags.set(EMISSIVE_BIT);
} else {
_flags.reset(EMISSIVE_BIT);
}
_schemaBuffer.edit<Schema>()._emissive = emissive;
}
void Material::setShininess(float shininess) {
if (shininess > 0.0f) {
_flags.set(SHININESS_BIT);
} else {
_flags.reset(SHININESS_BIT);
}
_schemaBuffer.edit<Schema>()._shininess = shininess;
}
void Material::setOpacity(float opacity) {
if (opacity >= 1.0f) {
_flags.reset(TRANSPARENT_BIT);
} else {
_flags.set(TRANSPARENT_BIT);
}
_schemaBuffer.edit<Schema>()._opacity = opacity;
}
void Material::setTextureView(MapChannel channel, const TextureView& view) {
_flags.set(DIFFUSE_MAP_BIT + channel);
_textureMap[channel] = view;
}

View file

@ -0,0 +1,114 @@
//
// Material.h
// libraries/model/src/model
//
// Created by Sam Gateau on 12/10/2014.
// Copyright 2014 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
//
#ifndef hifi_model_Material_h
#define hifi_model_Material_h
#include <bitset>
#include <map>
#include <glm/glm.hpp>
#include "gpu/Resource.h"
namespace model {
typedef gpu::BufferView UniformBufferView;
typedef gpu::TextureView TextureView;
class Material {
public:
typedef glm::vec3 Color;
enum MapChannel {
DIFFUSE_MAP = 0,
SPECULAR_MAP,
SHININESS_MAP,
EMISSIVE_MAP,
OPACITY_MAP,
NORMAL_MAP,
NUM_MAPS,
};
typedef std::map<MapChannel, TextureView> TextureMap;
enum FlagBit {
DIFFUSE_BIT = 0,
SPECULAR_BIT,
SHININESS_BIT,
EMISSIVE_BIT,
TRANSPARENT_BIT,
DIFFUSE_MAP_BIT,
SPECULAR_MAP_BIT,
SHININESS_MAP_BIT,
EMISSIVE_MAP_BIT,
OPACITY_MAP_BIT,
NORMAL_MAP_BIT,
NUM_FLAGS,
};
typedef std::bitset<NUM_FLAGS> Flags;
Material();
Material(const Material& material);
Material& operator= (const Material& material);
virtual ~Material();
const Color& getEmissive() const { return _schemaBuffer.get<Schema>()._emissive; }
const Color& getDiffuse() const { return _schemaBuffer.get<Schema>()._diffuse; }
const Color& getSpecular() const { return _schemaBuffer.get<Schema>()._specular; }
float getShininess() const { return _schemaBuffer.get<Schema>()._shininess; }
float getOpacity() const { return _schemaBuffer.get<Schema>()._opacity; }
void setDiffuse(const Color& diffuse);
void setSpecular(const Color& specular);
void setEmissive(const Color& emissive);
void setShininess(float shininess);
void setOpacity(float opacity);
// Schema to access the attribute values of the material
class Schema {
public:
Color _diffuse;
float _opacity;
Color _specular;
float _shininess;
Color _emissive;
float _spare0;
Schema() :
_diffuse(0.5f),
_opacity(1.0f),
_specular(0.03f),
_shininess(0.1f),
_emissive(0.0f)
{}
};
const UniformBufferView& getSchemaBuffer() const { return _schemaBuffer; }
void setTextureView(MapChannel channel, const TextureView& texture);
const TextureMap& getTextureMap() const { return _textureMap; }
const Schema* getSchema() const { return &_schemaBuffer.get<Schema>(); }
protected:
Flags _flags;
UniformBufferView _schemaBuffer;
TextureMap _textureMap;
};
typedef QSharedPointer< Material > MaterialPointer;
};
#endif

View file

@ -5,7 +5,7 @@ setup_hifi_library(Gui Network Script Widgets)
include_glm()
link_hifi_libraries(shared octree voxels fbx entities animation audio physics)
link_hifi_libraries(shared octree voxels gpu model fbx entities animation audio physics)
# call macro to link our dependencies and bubble them up via a property on our target
link_shared_dependencies()

View file

@ -33,7 +33,7 @@ AABox::AABox(const glm::vec3& corner, const glm::vec3& dimensions) :
_corner(corner), _scale(dimensions) {
};
AABox::AABox() : _corner(0.0f, 0.0f, 0.0f), _scale(0.0f, 0.0f, 0.0f) {
AABox::AABox() : _corner(std::numeric_limits<float>::infinity()), _scale(0.0f) {
};
glm::vec3 AABox::calcCenter() const {
@ -69,7 +69,7 @@ glm::vec3 AABox::getVertex(BoxVertex vertex) const {
return _corner + glm::vec3(0, 0, _scale.z);
case TOP_RIGHT_FAR:
return _corner + glm::vec3(0, _scale.y, _scale.z);
default: //quiet windows warnings
default: //quiet windows warnings
case TOP_LEFT_FAR:
return _corner + _scale;
}
@ -357,7 +357,7 @@ glm::vec3 AABox::getClosestPointOnFace(const glm::vec3& point, BoxFace face) con
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z),
glm::vec3(_corner.x + _scale.z, _corner.y + _scale.y, _corner.z));
default: //quiet windows warnings
default: //quiet windows warnings
case MAX_Z_FACE:
return glm::clamp(point, glm::vec3(_corner.x, _corner.y, _corner.z + _scale.z),
glm::vec3(_corner.x + _scale.x, _corner.y + _scale.y, _corner.z + _scale.z));
@ -438,7 +438,7 @@ glm::vec4 AABox::getPlane(BoxFace face) const {
case MIN_Y_FACE: return glm::vec4(0.0f, -1.0f, 0.0f, _corner.y);
case MAX_Y_FACE: return glm::vec4(0.0f, 1.0f, 0.0f, -_corner.y - _scale.y);
case MIN_Z_FACE: return glm::vec4(0.0f, 0.0f, -1.0f, _corner.z);
default: //quiet windows warnings
default: //quiet windows warnings
case MAX_Z_FACE: return glm::vec4(0.0f, 0.0f, 1.0f, -_corner.z - _scale.z);
}
}
@ -450,7 +450,7 @@ BoxFace AABox::getOppositeFace(BoxFace face) {
case MIN_Y_FACE: return MAX_Y_FACE;
case MAX_Y_FACE: return MIN_Y_FACE;
case MIN_Z_FACE: return MAX_Z_FACE;
default: //quiet windows warnings
default: //quiet windows warnings
case MAX_Z_FACE: return MIN_Z_FACE;
}
}
@ -470,3 +470,18 @@ AABox AABox::clamp(float min, float max) const {
return AABox(clampedCorner, clampedScale);
}
AABox& AABox::operator += (const glm::vec3& point) {
_corner = glm::min(_corner, point);
_scale = glm::max(_scale, point - _corner);
return (*this);
}
AABox& AABox::operator += (const AABox& box) {
if (!box.isInvalid()) {
(*this) += box._corner;
_scale = glm::max(_scale, box.calcTopFarLeft() - _corner);
}
return (*this);
}

View file

@ -72,6 +72,11 @@ public:
AABox clamp(const glm::vec3& min, const glm::vec3& max) const;
AABox clamp(float min, float max) const;
AABox& operator += (const glm::vec3& point);
AABox& operator += (const AABox& box);
bool isInvalid() const { return _corner == glm::vec3(std::numeric_limits<float>::infinity()); }
private:
glm::vec3 getClosestPointOnFace(const glm::vec3& point, BoxFace face) const;
glm::vec3 getClosestPointOnFace(const glm::vec4& origin, const glm::vec4& direction, BoxFace face) const;

View file

@ -5,6 +5,6 @@ setup_hifi_project(Script Network)
include_glm()
# link in the shared libraries
link_hifi_libraries(shared octree voxels fbx metavoxels networking entities avatars audio animation script-engine physics)
link_hifi_libraries(shared octree voxels gpu model fbx metavoxels networking entities avatars audio animation script-engine physics)
link_shared_dependencies()