v0.8.1.0 Release

v0.8.1.0 Release
This commit is contained in:
jdsouza90
2022-09-20 09:59:34 -07:00
parent 3bd2be62a4
commit cf68600c4f
691 changed files with 181019 additions and 22147 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,231 @@
/*###############################################################################
#
# Copyright 2019-2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifndef __FACE_IO__
#define __FACE_IO__
#include <stdint.h>
enum FaceIOErr {
kIOErrNone,
kIOErrFileNotFound,
kIOErrFileOpen,
kIOErrEOF,
kIOErrRead,
kIOErrWrite,
kIOErrSyntax,
kIOErrFormat,
kIOErrNotValue,
kIOErrNullPointer,
kIOErrParameter,
};
const char* FaceIOErrorStringFromCode(FaceIOErr err);
/********************************************************************************
********************************************************************************
********************************************************************************
***** IO Adapter *****
********************************************************************************
********************************************************************************
********************************************************************************/
/********************************************************************************
* FaceIOAdapter.
* Subclass from this and supply the accessors.
********************************************************************************/
class FaceIOAdapter {
public:
virtual uint32_t getShapeMeanSize() const { return 0; } /* The size of the mean shape mean, in elements. */
virtual uint32_t getShapeModesSize() const { return 0; } /* The total size of all shape modes (numModes*modeSize) */
virtual uint32_t getShapeNumModes() const { return 0; } /* The number of shape modes. */
virtual uint32_t getShapeEigenvaluesSize() const { return 0; } /* The number of shape eigenvalues
(should equal the number of modes) */
virtual float* getShapeMean(uint32_t /*size*/) { return nullptr; } /* Get a pointer to the shape mean.
If a nonzero size if supplied, it is resized first. */
virtual float* getShapeModes(uint32_t /*modeSize*/, uint32_t /*numModes*/) { return nullptr; } /* Get a pointer to the
shape modes, resizing first, if the parameters are nonzero. */
virtual float* getShapeEigenvalues(uint32_t /*numModes*/) { return nullptr; } /* Get a pointer to the shape eigenvalues,
resizing first if numModes is nonzero. */
virtual uint32_t getColorMeanSize() const { return 0; } /* The color mean ... */
virtual uint32_t getColorModesSize() const { return 0; } /* ... and modes */
virtual uint32_t getColorNumModes() const { return 0; }
virtual uint32_t getColorEigenvaluesSize() const { return 0; }
virtual float* getColorMean(uint32_t /*size*/) { return nullptr; }
virtual float* getColorModes(uint32_t /*modeSize*/, uint32_t /*numModes*/) { return nullptr; }
virtual float* getColorEigenvalues(uint32_t /*numModes*/) { return nullptr; }
virtual void setTriangleListSize(uint32_t /*size*/) {} /* The triangle list */
virtual uint32_t getTriangleListSize() const = 0;
virtual uint16_t* getTriangleList(uint32_t /*size*/) { return nullptr; }
virtual void setTextureCoordinatesSize(uint32_t /*size*/) {} /* The texture coordinates */
virtual uint32_t getTextureCoordinatesSize() const { return 0; }
virtual float* getTextureCoordinates(uint32_t /*size*/) { return nullptr; }
virtual void setNumBlendShapes(uint32_t /*numShapes*/) {} /* The blend shapes */
virtual void setBlendShapeName(uint32_t /*i*/, const char* /*name*/) {}
virtual uint32_t getNumBlendShapes() const { return 0; }
virtual const char* getBlendShapeName(uint32_t /*i*/) const { return nullptr; }
virtual uint32_t getBlendShapeSize(uint32_t /*i*/) const { return 0; }
virtual float* getBlendShape(uint32_t /*i*/, uint32_t /*size*/) { return nullptr; }
virtual void setIbugLandmarkMappingsSize(uint32_t /*n*/) {} /* The mappings from IBUG landmarks to vertex index */
virtual uint32_t getIbugLandmarkMappingsSize() const { return 0; }
virtual uint16_t* getIbugLandmarkMappings(uint32_t /*size*/) { return nullptr; }
virtual void appendIbugLandmarkMapping(uint16_t /*i*/) {}
virtual void appendIbugLandmarkMapping(uint16_t /*i*/, uint16_t /*j*/) {}
virtual void setIbugRightContourSize(uint32_t /*n*/) {} /* The IBUG contour on the right side of the face */
virtual uint32_t getIbugRightContourSize() const { return 0; }
virtual uint16_t* getIbugRightContour(uint32_t /*size*/) { return nullptr; }
virtual void appendIbugRightContour(uint16_t /*i*/) {}
virtual void setIbugLeftContourSize(uint32_t /*n*/) {} /* The IBUG contour on the left side of the face */
virtual uint32_t getIbugLeftContourSize() const { return 0; }
virtual uint16_t* getIbugLeftContour(uint32_t /*size*/) { return nullptr; }
virtual void appendIbugLeftContour(uint16_t /*i*/) {}
virtual void setModelRightContourSize(uint32_t /*n*/) {} /* The right contour of our model */
virtual uint32_t getModelRightContourSize() const { return 0; }
virtual uint16_t* getModelRightContour(uint32_t /*size*/) { return nullptr; }
virtual void appendModelRightContour(uint16_t /*i*/) {}
virtual void setModelLeftContourSize(uint32_t /*n*/) {} /* The left contour of our model */
virtual uint32_t getModelLeftContourSize() const { return 0; }
virtual uint16_t* getModelLeftContour(uint32_t /*size*/) { return nullptr; }
virtual void appendModelLeftContour(uint16_t /*i*/) {}
virtual void setAdjacentFacesSize(uint32_t /*n*/) {} /* The topology of adjacent faces to each edge */
virtual uint32_t getAdjacentFacesSize() const { return 0; }
virtual uint16_t* getAdjacentFaces(uint32_t /*size*/) { return nullptr; }
virtual void appendAdjacentFace(uint16_t /*i*/) {}
virtual void appendAdjacentFaces(uint16_t /*i*/, uint16_t /*j*/) {}
virtual void setAdjacentVerticesSize(uint32_t /*n*/) {} /* The topology of adjacent vertices to each edge */
virtual uint32_t getAdjacentVerticesSize() const { return 0; }
virtual uint16_t* getAdjacentVertices(uint32_t /*size*/) { return nullptr; }
virtual void appendAdjacentVertex(uint16_t /*i*/) {}
virtual void appendAdjacentVertices(uint16_t /*i*/, uint16_t /*j*/) {}
virtual void setNvlmLandmarksSize(uint32_t /*n*/) {} /* The tracked landmarks */
virtual uint32_t getNvlmLandmarksSize() const { return 0; }
virtual uint16_t* getNvlmLandmarks(uint32_t /*size*/) { return nullptr; }
virtual void appendNvlmLandmark(uint16_t /*i*/) {}
virtual void setNvlmRightContourSize(uint32_t /*n*/) {} /* The tracked right jawline contour */
virtual uint32_t getNvlmRightContourSize() const { return 0; }
virtual uint16_t* getNvlmRightContour(uint32_t /*size*/) { return nullptr; }
virtual void appendNvlmRightContour(uint16_t /*i*/) {}
virtual void setNvlmLeftContourSize(uint32_t /*n*/) {}; /* The tracked left jawline contour */
virtual uint32_t getNvlmLeftContourSize() const { return 0; }
virtual uint16_t* getNvlmLeftContour(uint32_t /*size*/) { return nullptr; }
virtual void appendNvlmLeftContour(uint16_t /*i*/) {}
virtual void setNumPartitions(uint32_t /*n*/) {}
virtual void setPartitionName(uint32_t /*i*/, const char* /*name*/) {}
virtual void setPartitionMaterialName(uint32_t /*i*/, const char* /*name*/) {}
virtual void setPartition(uint32_t /*i*/, uint32_t /*faceIndex*/, uint32_t /*numFaces*/,
uint32_t /*vertexIndex*/, uint32_t /*numVertices*/, int32_t /*smoothingGroup*/) {}
virtual uint32_t getNumPartitions() const { return 0; }
virtual const char* getPartitionName(uint32_t /*i*/) const { return nullptr; }
virtual const char* getPartitionMaterialName(uint32_t /*i*/) const { return nullptr; }
virtual int16_t getPartition(uint32_t /*i*/, uint32_t* faceIndex, uint32_t* numFaces, uint32_t* vertexIndex,
uint32_t* numVertices, int32_t* smoothingGroup) const
{ if (faceIndex) *faceIndex = 0u; if (numFaces) *numFaces = 0u; if (vertexIndex) *vertexIndex = 0u;
if (numVertices) *numVertices = 0u; if (smoothingGroup) *smoothingGroup = -1; return /*partitionIndex*/-1;
}
/* Const accessors do not have the ability to resize. */
const float* getShapeMean() const { return const_cast<FaceIOAdapter*>(this)->getShapeMean(0); }
const float* getShapeModes() const { return const_cast<FaceIOAdapter*>(this)->getShapeModes(0, 0); }
const float* getShapeEigenvalues() const { return const_cast<FaceIOAdapter*>(this)->getShapeEigenvalues(0); }
const float* getColorMean() const { return const_cast<FaceIOAdapter*>(this)->getColorMean(0); }
const float* getColorModes() const { return const_cast<FaceIOAdapter*>(this)->getColorModes(0, 0); }
const float* getColorEigenvalues() const { return const_cast<FaceIOAdapter*>(this)->getColorEigenvalues(0); }
const float* getTextureCoordinates() const { return const_cast<FaceIOAdapter*>(this)->getTextureCoordinates(0); }
const uint16_t* getTriangleList() const { return const_cast<FaceIOAdapter*>(this)->getTriangleList(0); }
const float* getBlendShape(uint32_t i) const { return const_cast<FaceIOAdapter*>(this)->getBlendShape(i, 0); }
const uint16_t* getIbugLandmarkMappings() const { return const_cast<FaceIOAdapter*>(this)->getIbugLandmarkMappings(0); }
const uint16_t* getIbugRightContour() const { return const_cast<FaceIOAdapter*>(this)->getIbugRightContour(0); }
const uint16_t* getIbugLeftContour() const { return const_cast<FaceIOAdapter*>(this)->getIbugLeftContour(0); }
const uint16_t* getModelRightContour() const { return const_cast<FaceIOAdapter*>(this)->getModelRightContour(0); }
const uint16_t* getModelLeftContour() const { return const_cast<FaceIOAdapter*>(this)->getModelLeftContour(0); }
const uint16_t* getAdjacentFaces() const { return const_cast<FaceIOAdapter*>(this)->getAdjacentFaces(0); }
const uint16_t* getAdjacentVertices() const { return const_cast<FaceIOAdapter*>(this)->getAdjacentVertices(0); }
const uint16_t* getNvlmLandmarks() const { return const_cast<FaceIOAdapter*>(this)->getNvlmLandmarks(0); }
const uint16_t* getNvlmRightContour() const { return const_cast<FaceIOAdapter*>(this)->getNvlmRightContour(0); }
const uint16_t* getNvlmLeftContour() const { return const_cast<FaceIOAdapter*>(this)->getNvlmLeftContour(0); }
};
/** Write the face model as an NVF model.
* @param[in] fac the face I/O adapter for the target data structure.
* @param[in] fileName the desired name of the output file.
* @return kIOErrNone if the file was written completed successfully.
* @return kIOErrFileOpen if the file could not be opened.
* @return kIOErrWrite if an error occurred while writing the file.
*/
FaceIOErr WriteNVFFaceModel(FaceIOAdapter* fac, const char* fileName);
/** Read a face model from an NVF file.
* @param[in] fileName the name of the file to be read.
* @param[in,out] fac the face I/O adapter for the target data structure.
* @return kIOErrNone if the file was read successfully.
* @return kIOErrFileNotFound if the file was not found .
* @return kIOErrFileOpen if the file could not be opened.
* @return kIOErrRead if an error occurred while reading the file.
* @return kIOErrSyntax if a syntax error has been encountered while reading the file.
*/
FaceIOErr ReadNVFFaceModel(const char* fileName, FaceIOAdapter* fac);
/** Read a face model from five OES files.
* @param[in] shape the name of the shape file to be read.
* @param[in] ibugNumandmarks the number of Ibug landmarks.
* @param[in] blendShapes the name of the blend shapes file to be read.
* @param[in] contours the name of the contours file to be read.
* @param[in] topology the name of the topology file to be read.
* @param[in,out] fac the face I/O adapter for the target data structure.
* @return kIOErrNone if the file was read successfully.
* @return kIOErrFileNotFound if the file was not found .
* @return kIOErrFileOpen if the file could not be opened.
* @return kIOErrRead if an error occurred while reading the file.
* @return kIOErrSyntax if a syntax error has been encountered while reading the file.
*/
FaceIOErr ReadEOSFaceModel(const char* shape, const unsigned ibugNumLandmarks, const char* blendShapes,
const char* contours, const char* topology, FaceIOAdapter* fac);
/** Write the face model as a JSON model.
* @param[in] fac the face I/O adapter for the target data structure.
* @param[in] fileName the desired name of the output file.
* If NULL is supplied, it is written to the standard output.
* @return kIOErrNone if the file was written completed successfully.
* @return kIOErrFileOpen if the file could not be opened.
* @return kIOErrWrite if an error occurred while writing the file.
*/
FaceIOErr PrintJSONFaceModel(FaceIOAdapter* fac, const char* fileName);
#endif /* __FACE_IO__ */

View File

@@ -0,0 +1,314 @@
/*###############################################################################
#
# Copyright 2016-2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#include "GLMaterial.h"
#include <stdlib.h>
#include <string.h>
#include <fstream>
#include <sstream>
#include <vector>
#ifdef _MSC_VER
#define strcasecmp _stricmp
#endif // _MSC_VER
////////////////////////////////////////////////////////////////////////////////
///// UTILITY FUNCTIONS /////
////////////////////////////////////////////////////////////////////////////////
static void SplitString(const std::string &s, std::vector<std::string>&tokens) {
tokens.clear();
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, ' ')) {
if (0 == token.size())
continue;
tokens.push_back(token);
}
}
static void SetColorFromStringArray(GLSpectrum3f& color, std::string* strs) {
color.r = strtof(strs[0].c_str(), nullptr);
color.g = strtof(strs[1].c_str(), nullptr);
color.b = strtof(strs[2].c_str(), nullptr);
}
static bool StrToBool(const char* str) {
return strcasecmp(str, "true") == 0 ||
strcasecmp(str, "on") == 0 ||
strcasecmp(str, "yes") == 0 ||
strcasecmp(str, "1") == 0;
}
////////////////////////////////////////////////////////////////////////////////
///// GLMaterial /////
////////////////////////////////////////////////////////////////////////////////
GLMaterial::~GLMaterial() {
//if (diffuseTexture)
// delete diffuseTexture;
}
GLMaterial::GLMaterial() {
clear();
}
GLMaterial::GLMaterial(const GLMaterial& mtl) {
*this = mtl;
}
void GLMaterial::setTextureFile(const char* file) {
if (file) diffuseTextureFile = file;
else diffuseTextureFile.clear();
}
void GLMaterial::clear() {
diffuseColor.set(1.f, 1.f, 1.f);
ambientColor.set(0.f, 0.f, 0.f);
specularColor.set(0.f, 0.f, 0.f);
transmissionColor.set(0.f, 0.f, 0.f);
specularExponent = 0.f;
opacity = 1.f;
diffuseTexture = nullptr;
diffuseTextureFile.clear();
illuminationModel = kUnspecifiedIlluminationModel;
}
////////////////////////////////////////////////////////////////////////////////
///// GLMaterialLibrary /////
////////////////////////////////////////////////////////////////////////////////
struct GLMaterialName {
GLMaterial mtl;
std::string name;
GLMaterialName() {}
GLMaterialName(const GLMaterial& matParam, const char* nameParam) {
mtl = matParam;
name = nameParam;
}
};
struct GLMaterialLibrary::Impl {
std::vector<GLMaterialName> lib;
};
GLMaterialLibrary::GLMaterialLibrary() {
pimpl = new Impl;
}
GLMaterialLibrary::~GLMaterialLibrary() {
delete pimpl;
}
void GLMaterialLibrary::clear() {
pimpl->lib.clear();
}
unsigned GLMaterialLibrary::numMaterials() const {
return unsigned(pimpl->lib.size());
}
NvCV_Status GLMaterialLibrary::addMaterial(const GLMaterial& mtrl, const char* name) {
if (getMaterial(name))
return NVCV_ERR_SELECTOR;
pimpl->lib.emplace_back(mtrl, name);
return NVCV_SUCCESS;
}
NvCV_Status GLMaterialLibrary::addDiffuseMaterial(const GLSpectrum3f& color, const char* name) {
GLMaterial mtrl;
if (getMaterial(name))
return NVCV_ERR_SELECTOR;
mtrl.diffuseColor = color;
pimpl->lib.emplace_back(mtrl, name);
return NVCV_SUCCESS;
}
NvCV_Status GLMaterialLibrary::removeMaterial(const char* name) {
unsigned i, n;
for (i = 0, n = unsigned(pimpl->lib.size()); i < n; ++i) {
if (name == pimpl->lib[i].name) {
pimpl->lib.erase(pimpl->lib.begin() + i);
return NVCV_SUCCESS;
}
}
return NVCV_ERR_FEATURENOTFOUND;
}
GLMaterial* GLMaterialLibrary::newMaterial(const char* name) {
if (getMaterial(name))
return nullptr;
size_t z = pimpl->lib.size();
pimpl->lib.resize(z + 1);
GLMaterialName* mtn = &pimpl->lib[z];
mtn->name = name;
return &mtn->mtl;
}
const GLMaterial* GLMaterialLibrary::getMaterial(const char* name) const {
GLMaterialName *mp, *mEnd;
for (mEnd = (mp = pimpl->lib.data()) + pimpl->lib.size(); mp < mEnd; ++mp)
if (name == mp->name)
return &mp->mtl;
return nullptr;
}
const GLMaterial* GLMaterialLibrary::getMaterial(unsigned i, const char** name) const {
if (i < pimpl->lib.size()) {
const GLMaterialName& matn = pimpl->lib[i];
if (name)
*name = matn.name.c_str();
return &matn.mtl;
}
if (name)
name = nullptr;
return nullptr;
}
NvCV_Status GLMaterialLibrary::read(const char* name) {
unsigned lineNum;
std::vector<std::string> tokens;
GLMaterial *mtl = nullptr;
clear();
std::ifstream fd(name);
if (!fd.is_open())
return NVCV_ERR_READ;
std::string line;
for (lineNum = 1; std::getline(fd, line); ++lineNum) {
SplitString(line, tokens);
if (!tokens.size())
continue;
if (tokens[0][0] == '#') {
continue;
}
if (tokens[0] == "newmtl" && 2 == tokens.size()) {
mtl = newMaterial(tokens[1].c_str());
continue;
}
if (tokens[0] == "Ka" && 4 == tokens.size()) {
SetColorFromStringArray(mtl->ambientColor, &tokens[1]);
continue;
}
if (tokens[0] == "Kd" && 4 == tokens.size()) {
SetColorFromStringArray(mtl->diffuseColor, &tokens[1]);
continue;
}
if (tokens[0] == "Ks" && 4 == tokens.size()) {
SetColorFromStringArray(mtl->specularColor, &tokens[1]);
continue;
}
if (tokens[0] == "Tf" && 4 == tokens.size()) {
SetColorFromStringArray(mtl->transmissionColor, &tokens[1]);
continue;
}
if (tokens[0] == "illum" && 2 == tokens.size()) {
mtl->illuminationModel = (unsigned char)strtol(tokens[1].c_str(), nullptr, 10);
continue;
}
if (tokens[0] == "d" && 2 == tokens.size()) {
/* We don't support "_d" */
mtl->opacity = strtof(tokens[1].c_str(), nullptr);
continue;
}
if (tokens[0] == "Ns" && 2 == tokens.size()) { /* We don't support "-d" */
mtl->specularExponent = strtof(tokens[1].c_str(), nullptr);
continue;
}
if (tokens[0] == "sharpness") { /* We don't support sharpness */
continue;
}
if (tokens[0] == "Ni") { /* We don't support index of refraction */
continue;
}
if (tokens[0] == "map_Kd") {
for (unsigned i = 1; i < tokens.size(); ++i) {
if (tokens[i] == "-blendu") {
++i;
}
else if (tokens[i] == "-blendv") {
++i;
}
else if (tokens[i] == "-cc") {
++i;
}
else if (tokens[i] == "-clamp") {
++i;
}
else if (tokens[i] == "-mm") {
++i;
}
else if (tokens[i] == "-o") {
i += 3;
}
else if (tokens[i] == "-s") {
i += 3;
}
else if (tokens[i] == "-t") {
i += 3;
}
else if (tokens[i] == "-texres") {
++i;
}
else if (tokens[i][0] == '-') {
printf("Unknown option: \"%s\"\n", tokens[i].c_str());
}
else {
mtl->diffuseTextureFile = tokens[i];
}
}
if (mtl->diffuseTextureFile.empty())
printf("No diffuse texture given on line %u\n", lineNum);
continue;
}
}
return NVCV_SUCCESS;
}

View File

@@ -0,0 +1,143 @@
/*###############################################################################
#
# Copyright 2016-2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifndef __GLMATERIAL_H
#define __GLMATERIAL_H
#include <string>
#include "GLSpectrum.h"
#include "nvCVStatus.h"
////////////////////////////////////////////////////////////////////////////////
/// Specification for light transport on the surfaces of objects.
/// @todo Store the provenance of the material?
/// @todo Should we store the name, too?
////////////////////////////////////////////////////////////////////////////////
class GLMaterial {
public:
/// Default constructor.
GLMaterial();
/// Copy constructor.
/// @param[in] mtl the material to copy.
/// @note a copy is made of the diffuseTextureFile, if not NULL.
GLMaterial(const GLMaterial& mtl);
/// Destructor.
/// @note the diffuseTextureFile string is disposed.
/// @note the opaque diffuseTexture is *not* disposed.
~GLMaterial();
/// Assignment.
/// This copies the diffuseTextureFile, if not NULL>
/// @param[in] mtl the material to copy (RHS).
/// @return a reference to the LHS of the assignment.
//GLMaterial& operator=(const GLMaterial& mtl); // default implementation
/// Reset as it was in the constructor: 0 materials.
void clear();
/// Use this to set the diffuseTextureFile, by making a copy of the specified string.
/// @param[in] fileName the file name of the texture file. A copy of the string is made.
void setTextureFile(const char* fileName);
GLSpectrum3f ambientColor; ///< The ambient color, in [0,1].
GLSpectrum3f diffuseColor; ///< The diffuse color, in [0,1].
GLSpectrum3f specularColor; ///< The specular color, in [0,1].
GLSpectrum3f transmissionColor; ///< The transmission color, in [0,1].
float specularExponent; ///< The specular exponent, in [1, 10000]
float opacity; ///< The opacity, in [0,1].
std::string diffuseTextureFile; ///< The name of the diffuse texture file. Set through setTextureFile().
void *diffuseTexture; ///< User-defined texture representation -- unmanaged.
unsigned char illuminationModel; ///< The illumination model, in [0,10], or kUnspecifiedIlluminationModel.
static const int kUnspecifiedIlluminationModel = 255; ///< The value to be used for an unspecified illumination model.
};
////////////////////////////////////////////////////////////////////////////////
/// Library of material specifications for surface light transport.
////////////////////////////////////////////////////////////////////////////////
class GLMaterialLibrary {
public:
/// Constructor.
GLMaterialLibrary();
/// Destructor.
~GLMaterialLibrary();
/// Reset as it was in the constructor: 0 materials.
void clear();
/// Read from a file
NvCV_Status read(const char* name);
/// Add a new material to the library. A copy is made both of material and name.
/// @param[in] mtrl the material to be added to the library.
/// @param[in] name the name of the material, for future access.
/// @return keErrNone if the operation was completed successfully.
/// @return keErrDuplicate if a material of the same name is already found in the library.
NvCV_Status addMaterial(const GLMaterial& mtrl, const char* name);
/// Add a new diffuse material to the library. A copy is made both of the name.
/// @param[in] color the diffuse color to be added to the library.
/// @param[in] name the name of the material, for future access.
/// @return keErrNone if the operation was completed successfully.
/// @return keErrDuplicate if a material of the same name is already found in the library.
NvCV_Status addDiffuseMaterial(const GLSpectrum3f& color, const char* name);
/// Remove a material.
/// @param[in] name the name of the material to remove.
/// @return keErrNone if the operation was completed successfully.
NvCV_Status removeMaterial(const char* name);
/// Create a new material with the given name.
/// @param[in] name the name of the material, for future access.
/// @return a pointer to the new material in the database, if the operation was completed successfully.
/// @return NULL, if a material of the same name is already found in the library.
GLMaterial* newMaterial(const char* name);
/// Get the number of materials in the material library.
/// @return the number of materials.
unsigned numMaterials() const;
/// Get the material with the specified name.
/// @param[in] name the name of the material to get.
/// @return the specified material, or NULL if the material was not found.
const GLMaterial* getMaterial(const char* name) const;
/// Get the material with the specified index.
/// @param[in] i the index of the material to get.
/// @param[out] name the name of the material with the specified index (can be NULL).
/// @return the specified material, or NULL if the material was not found.
const GLMaterial* getMaterial(unsigned i, const char** name = nullptr) const;
private:
struct Impl;
Impl *pimpl;
};
#endif /* __GLMATERIAL_H */

View File

@@ -0,0 +1,624 @@
/*###############################################################################
#
# Copyright 2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#include "GLMesh.h"
#include <string.h>
#include <algorithm>
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//// ////
//// GLMesh ////
//// ////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Note: we assume that the transformation is affine, i.e. that M[3] = M[7] = M[11] = 0 and M[15] = 1.
static void TransformPoints(const glm::mat4x4& M, unsigned numPts, const glm::vec3 *pts, glm::vec3 *xPts) {
for (; numPts--; ++pts, ++xPts) { // NB it is better to do the dot products in double precision
glm::vec3 q; // Use an intermediate variable to allow transformation in-place.
q.x = M[0][0] * pts->x + M[1][0] * pts->y + M[2][0] * pts->z + M[3][0];
q.y = M[0][1] * pts->x + M[1][1] * pts->y + M[2][1] * pts->z + M[3][1];
q.z = M[0][2] * pts->x + M[1][2] * pts->y + M[2][2] * pts->z + M[3][2];
*xPts = q;
}
}
// Note: We assume here that the transformation is isotropic;
// otherwise we would need to transform by the inverse transpose of the upper left.
static void TransformNormals(const glm::mat4x4& M, unsigned numPts, const glm::vec3 *pts, glm::vec3 *xPts) {
for (; numPts--; ++pts, ++xPts) { // NB it is better to do the dot products in double precision
glm::vec3 q; // Use an intermediate variable to allow transformation in-place.
q.x = M[0][0] * pts->x + M[1][0] * pts->y + M[2][0] * pts->z;
q.y = M[0][1] * pts->x + M[1][1] * pts->y + M[2][1] * pts->z;
q.z = M[0][2] * pts->x + M[1][2] * pts->y + M[2][2] * pts->z;
*xPts = glm::normalize(q);
}
}
////////////////////////////////////////////////////////////////////////////////
// GLMesh API
////////////////////////////////////////////////////////////////////////////////
GLMesh::GLMesh(const GLMesh& mesh) {
m_faceVertexCount = mesh.m_faceVertexCount;
m_vertices = mesh.m_vertices;
m_vertexIndices = mesh.m_vertexIndices;
m_texCoords = mesh.m_texCoords;
m_textureIndices = mesh.m_textureIndices;
m_normals = mesh.m_normals;
m_normalIndices = mesh.m_normalIndices;
m_faceNormals = mesh.m_faceNormals;
}
GLMesh::GLMesh() { }
GLMesh::~GLMesh() { }
void GLMesh::resizeVertices(unsigned n) { m_vertices.resize(n); }
void GLMesh::resizeTexCoords(unsigned n) {
m_texCoords.resize(n);
m_textureIndices.resize(n ? unsigned(m_vertexIndices.size()) : 0);
}
void GLMesh::resizeNormals(unsigned n) {
m_normals.resize(n);
m_normalIndices.resize(n ? unsigned(m_vertexIndices.size()) : 0);
}
void GLMesh::resizeFaces(unsigned n) { m_faceVertexCount.resize(n); }
void GLMesh::resizeTriangles(unsigned n) { m_faceVertexCount.clear(); m_faceVertexCount.resize(n, 3); }
void GLMesh::resizeVertexIndices(unsigned n) {
m_vertexIndices.resize(n);
m_textureIndices.resize(m_texCoords.size() ? n : 0);
m_normalIndices.resize(m_normals.size() ? n : 0);
}
void GLMesh::resizeDualIndices(unsigned n) {
m_dualIndices.resize(n);
m_vertexFaceCount.resize(m_vertices.size());
}
void GLMesh::useFaceNormals(bool yes) { m_faceNormals.resize(yes ? numFaces() : 0); }
unsigned GLMesh::numVertices() const { return unsigned(m_vertices.size()); }
unsigned GLMesh::numTexCoords() const { return unsigned(m_texCoords.size()); }
unsigned GLMesh::numNormals() const { return unsigned(m_normals.size()); }
unsigned GLMesh::numFaces() const { return unsigned(m_faceVertexCount.size()); }
unsigned GLMesh::numIndices() const { return unsigned(m_vertexIndices.size()); }
void GLMesh::initPartitions() {
m_partitions.resize(1);
Partition& pt = m_partitions[0];
pt.faceIndex = 0;
pt.vertexIndex = 0;
pt.name.clear();
pt.materialName.clear();
}
NvCV_Status GLMesh::startPartition(const char *name, const char *material, int smooth) {
if (name)
for (const GLMesh::Partition& p : m_partitions)
if (p.name == name)
return NVCV_ERR_SELECTOR;
unsigned i = unsigned(m_partitions.size());
GLMesh::Partition *pt = &m_partitions[i - 1];
if (m_faceVertexCount.size() != pt->faceIndex) {
m_partitions.resize(i + 1);
pt = &m_partitions[i];
pt->faceIndex = unsigned(m_faceVertexCount.size());
pt->vertexIndex = unsigned(m_vertexIndices.size());
}
if (name) pt->name = name;
if (material) pt->materialName = material;
if (smooth >= 0) pt->smooth = smooth;
return NVCV_SUCCESS;
}
NvCV_Status GLMesh::partitionMesh(unsigned numPartitions, const GLMesh::Partition *srcPartition) {
m_partitions.resize(numPartitions);
for (unsigned i = 0; i < numPartitions; ++i, ++srcPartition) {
if (srcPartition->faceIndex >= m_faceVertexCount.size()) {
initPartitions();
return NVCV_ERR_MISMATCH;
}
GLMesh::Partition& pt = m_partitions[i];
pt.faceIndex = srcPartition->faceIndex;
pt.vertexIndex = srcPartition->vertexIndex;
pt.numFaces = srcPartition->numFaces;
pt.numVertexIndices = srcPartition->numVertexIndices;
pt.name = srcPartition->name;
pt.materialName = srcPartition->materialName;
}
//computeStartingVertexIndices();
return NVCV_SUCCESS;
}
NvCV_Status GLMesh::updatePartition(unsigned i, const GLMesh::Partition& update) {
if (i >= m_partitions.size())
return NVCV_ERR_FEATURENOTFOUND;
GLMesh::Partition& pt = m_partitions[i];
pt.faceIndex = update.faceIndex;
pt.vertexIndex = update.vertexIndex;
if (update.name.empty()) pt.name.clear();
else pt.name = update.name;
if (update.materialName.empty()) pt.materialName.clear();
else pt.materialName = update.materialName;
return NVCV_SUCCESS;
}
void GLMesh::computeStartingVertexIndices() {
unsigned vertIx;
std::sort(m_partitions.begin(), m_partitions.end());
const unsigned short *faceCount = m_faceVertexCount.data(), *lastFace;
GLMesh::Partition *pt = m_partitions.data(), *lastPt = pt + m_partitions.size() - 1;
for (vertIx = 0; pt != lastPt; ++pt) {
pt->vertexIndex = vertIx;
for (lastFace = faceCount + (pt[1].faceIndex - pt[0].faceIndex); faceCount != lastFace; ++faceCount)
vertIx += *faceCount;
}
pt->vertexIndex = vertIx;
}
NvCV_Status GLMesh::getPartition(unsigned i, GLMesh::Partition& pt) const {
if (i > m_partitions.size())
return NVCV_ERR_FEATURENOTFOUND;
pt = m_partitions[i];
return NVCV_SUCCESS;
}
bool GLMesh::indicesMatch(const std::vector<unsigned short>& ivecA, const std::vector<unsigned short>& ivecB) {
size_t n = ivecA.size();
const unsigned short *a = ivecA.data(),
*b = ivecB.data();
if (ivecB.size() != n)
return false;
for (; n--; ++a, ++b)
if (*a != *b)
return false;
return true;
}
void GLMesh::assureConsistency() {
if (m_texCoords.size() && (m_textureIndices.size() != m_vertexIndices.size()))
m_textureIndices.resize(m_vertexIndices.size());
if (m_normals.size() && (m_normalIndices.size() != m_vertexIndices.size()))
m_normalIndices.resize(m_vertexIndices.size());
if (m_faceNormals.size() && (m_faceNormals.size() != (m_vertexIndices.size() / 3)))
m_faceNormals.resize(m_vertexIndices.size() / 3);
}
void GLMesh::clear() {
m_faceVertexCount.resize(0);
m_vertices.resize(0);
m_texCoords.resize(0);
m_normals.resize(0);
m_faceNormals.resize(0);
m_vertexIndices.resize(0);
m_textureIndices.resize(0);
m_normalIndices.resize(0);
initPartitions();
}
glm::vec3* GLMesh::getVertices() {
return m_vertices.data();
}
glm::vec2* GLMesh::getTexCoords() {
assureConsistency();
return m_texCoords.size() ? m_texCoords.data() : nullptr;
}
glm::vec3* GLMesh::getNormals() {
assureConsistency();
return m_normals.size() ? m_normals.data() : nullptr;
}
glm::vec3* GLMesh::getFaceNormals() {
assureConsistency();
return m_faceNormals.size() ? m_faceNormals.data() : nullptr;
}
const glm::vec3* GLMesh::getVertices() const { return const_cast<GLMesh*>(this)->getVertices(); }
const glm::vec2* GLMesh::getTexCoords() const { return const_cast<GLMesh*>(this)->getTexCoords(); }
const glm::vec3* GLMesh::getNormals() const { return const_cast<GLMesh*>(this)->getNormals(); }
const glm::vec3* GLMesh::getFaceNormals() const { return const_cast<GLMesh*>(this)->getFaceNormals(); }
unsigned short* GLMesh::getFaceVertexCounts() { return m_faceVertexCount.data(); }
unsigned short* GLMesh::getVertexIndices() { return m_vertexIndices.data(); }
unsigned short* GLMesh::getTextureIndices() {
return m_textureIndices.size() ? m_textureIndices.data() : nullptr;
}
unsigned short* GLMesh::getNormalIndices() {
return m_normalIndices.size() ? m_normalIndices.data() : nullptr;
}
const unsigned short* GLMesh::getFaceVertexCounts() const { return m_faceVertexCount.data(); }
const unsigned short* GLMesh::getVertexIndices() const { return m_vertexIndices.data(); }
const unsigned short* GLMesh::getTextureIndices() const { return const_cast<GLMesh*>(this)->getTextureIndices(); }
const unsigned short* GLMesh::getNormalIndices() const { return const_cast<GLMesh*>(this)->getNormalIndices(); }
unsigned short* GLMesh::getVertexFaceCounts() { return m_vertexFaceCount.data(); }
unsigned short* GLMesh::getDualIndices() {
return m_dualIndices.size() ? m_dualIndices.data() : nullptr;
}
const unsigned short* GLMesh::getVertexFaceCounts() const { return const_cast<GLMesh*>(this)->getVertexFaceCounts(); }
const unsigned short* GLMesh::getDualIndices() const { return const_cast<GLMesh*>(this)->getDualIndices(); }
void GLMesh::addVertex(float x, float y, float z) { m_vertices.emplace_back(glm::vec3{ x, y, z }); }
void GLMesh::addTexCoord(float u, float v) { m_texCoords.emplace_back(glm::vec2{ u, v }); }
void GLMesh::addNormal(float x, float y, float z) { m_normals.emplace_back(glm::vec3{ x, y, z }); }
void GLMesh::addVertices(unsigned numVertices, const float *vertices) {
size_t preVertices = m_vertices.size();
m_vertices.resize(preVertices + numVertices);
memcpy(m_vertices.data() + preVertices, vertices, numVertices * sizeof(*m_vertices.data()));
}
void GLMesh::addTexCoords(unsigned numTexCoords, const float *texCoords) {
size_t preTexCoords = m_texCoords.size();
m_texCoords.resize(preTexCoords + numTexCoords);
memcpy(m_texCoords.data() + preTexCoords, texCoords, numTexCoords * sizeof(*m_texCoords.data()));
}
void GLMesh::addNormals(unsigned numNormals, const float *normals) {
size_t preNormals = m_normals.size();
m_normals.resize(preNormals + numNormals);
memcpy(m_normals.data() + preNormals, normals, numNormals * sizeof(*m_normals.data()));
}
void GLMesh::addFace(unsigned numVertices, const unsigned short *vertexIndices,
const unsigned short *textureIndices, const unsigned short *normalIndices)
{
size_t n;
m_faceVertexCount.push_back((unsigned short)numVertices);
if (vertexIndices) {
n = m_vertexIndices.size();
m_vertexIndices.resize(n + numVertices);
memcpy(m_vertexIndices.data() + n, vertexIndices, numVertices * sizeof(*vertexIndices));
}
if (textureIndices) {
n = m_textureIndices.size();
m_textureIndices.resize(n + numVertices);
memcpy(m_textureIndices.data() + n, textureIndices, numVertices * sizeof(*textureIndices));
}
if (normalIndices) {
n = m_normalIndices.size();
m_normalIndices.resize(n + numVertices);
memcpy(m_normalIndices.data() + n, normalIndices, numVertices * sizeof(*normalIndices));
}
}
void GLMesh::addFaces(unsigned numFaces, unsigned numVerticesPerFace, const unsigned short *vertexIndices,
const unsigned short *textureIndices, const unsigned short *normalIndices)
{
size_t numIndices = numFaces * numVerticesPerFace,
indexBytes = numIndices * sizeof(*vertexIndices);
size_t n;
n = m_faceVertexCount.size();
m_faceVertexCount.resize(n + numFaces, (unsigned short)numVerticesPerFace);
if (vertexIndices) {
n = m_vertexIndices.size();
m_vertexIndices.resize(n + numIndices);
memcpy(m_vertexIndices.data() + n, vertexIndices, indexBytes);
}
if (textureIndices) {
n = m_textureIndices.size();
m_textureIndices.resize(n + numIndices);
memcpy(m_textureIndices.data() + n, textureIndices, indexBytes);
}
if (normalIndices) {
n = m_normalIndices.size();
m_normalIndices.resize(n + numIndices);
memcpy(m_normalIndices.data() + n, normalIndices, indexBytes);
}
}
bool GLMesh::isTriMesh() const {
unsigned n;
const unsigned short *ix;
for (n = numFaces(), ix = getFaceVertexCounts(); n--; ++ix)
if (*ix != 3)
return false;
return true;
}
bool GLMesh::isQuadMesh() const {
unsigned n;
const unsigned short *ix;
for (n = numFaces(), ix = getFaceVertexCounts(); n--; ++ix)
if (*ix != 4)
return false;
return true;
}
bool GLMesh::isTriQuadMesh() const {
unsigned n;
const unsigned short *ix;
for (n = numFaces(), ix = getFaceVertexCounts(); n--; ++ix)
if (*ix > 4)
return false;
return true;
}
void GLMesh::transform(const glm::mat4x4& M) {
TransformPoints (M, unsigned(m_vertices.size()), m_vertices.data(), m_vertices.data());
TransformNormals(M, unsigned(m_normals.size()), m_normals.data(), m_normals.data());
TransformNormals(M, unsigned(m_faceNormals.size()), m_faceNormals.data(), m_faceNormals.data());
}
unsigned GLMesh::numPartitions() const {
return unsigned(m_partitions.size());
}
void GLMesh::finishPartitioning() {
computeStartingVertexIndices();
}
NvCV_Status GLMesh::setMaterial(const char *name) {
Partition pt{};
pt.materialName = name;
return partitionMesh(1, &pt);
}
void GLMesh::computeFaceNormals(int weighted) {
const glm::vec3 *vertices = m_vertices.data();
const unsigned short *numVertices = m_faceVertexCount.data();
glm::vec3 *nrm, *nrmEnd;
const unsigned short *ix;
const glm::vec3 *p0, *p1, *p2;
glm::vec3 n;
float mag;
useFaceNormals(true);
nrm = getFaceNormals();
nrmEnd = nrm + numFaces();
ix = m_vertexIndices.data();
for (; nrm != nrmEnd; ++nrm, ix += *numVertices++) {
if (3 == *numVertices) {
p0 = &vertices[ix[0]];
p1 = &vertices[ix[1]];
p2 = &vertices[ix[2]];
n = glm::cross((*p1 - *p0), (*p2 - *p0));
}
else {
unsigned numPts = *numVertices;
unsigned i;
p0 = &vertices[ix[numPts - 1]];
n = { 0.f, 0.f, 0.f };
for (i = 0, p0 = &vertices[ix[numPts - 1]]; i < numPts; ++i, p0 = p1) {
p1 = &vertices[ix[i]];
n.x -= (p1->y - p0->y) * (p1->z + p0->z);
n.y -= (p1->z - p0->z) * (p1->x + p0->x);
n.z -= (p1->x - p0->x) * (p1->y + p0->y);
}
}
mag = glm::length(n);
if (weighted == 0) { if (mag) n /= mag; } // Unit vector
else if (weighted > 0) { n *= 0.5f; } // Area-weighted normal
else /* weighted < 0 */ { if (mag) n /= mag * mag * 0.25f; } // Inverse-area-weighted vector
*nrm = n;
}
}
void GLMesh::computeVertexNormals(int weighted) {
glm::vec3 nrm;
computeFaceNormals(weighted);
if (m_normals.size() != m_vertices.size()) {
m_normals.resize(m_vertices.size());
m_normalIndices = m_vertexIndices;
}
if (m_vertexFaceCount.size() == m_vertices.size()) { // We already have the dual topology
glm::vec3 *n, *nEnd;
unsigned short *numPolys, *ix, *ixEnd;
for (nEnd = (n = m_normals.data()) + m_vertexFaceCount.size(), numPolys = m_vertexFaceCount.data(), ix = m_dualIndices.data(); n != nEnd; ++n, ++numPolys) {
for (ixEnd = ix + *numPolys, nrm = { 0.f, 0.f, 0.f }; ix != ixEnd; ++ix)
nrm += m_faceNormals[*ix];
*n = glm::normalize(nrm);
}
}
}
void GLMesh::BoundingBox::unionPoint(const glm::vec3& pt) { /* This works with NaN's */
if (!(_box[0].x < pt.x)) _box[0].x = pt.x; if (!(_box[1].x > pt.x)) _box[1].x = pt.x;
if (!(_box[0].y < pt.y)) _box[0].y = pt.y; if (!(_box[1].y > pt.y)) _box[1].y = pt.y;
if (!(_box[0].z < pt.z)) _box[0].z = pt.z; if (!(_box[1].z > pt.z)) _box[1].z = pt.z;
}
void GLMesh::BoundingBox::set(unsigned numPts, const glm::vec3 *pts) {
_box[0] = pts[0];
_box[1] = pts[0];
for (++pts; --numPts; ++pts)
unionPoint(*pts);
}
void GLMesh::BoundingBox::set(unsigned numPts, const glm::vec3 *pts, const glm::mat4x4& M) {
memset(this, -1, sizeof(*this)); // Set to NaN
for (; numPts--; ++pts) {
glm::vec3 q;
TransformPoints(M, 1, pts, &q);
unionPoint(q);
}
}
void GLMesh::BoundingSphere::set(unsigned numPts, const glm::vec3 *pts) {
/* Ritter algorithm */
float d, d0;
glm::vec3 p0, p1;
const glm::vec3 *pp, *pEnd = pts + numPts;
p0 = p1 = pts[0]; // Choose one point
for (pp = pts + 1, d0 = 0; pp != pEnd; ++pp) {
if (!(d0 > (d = glm::distance(p0, *pp)))) {
d0 = d;
p1 = *pp; // Find the furthest point
}
}
p0 = p1; // Choose that furthest point
for (pp = pts, d0 = 0; pp != pEnd; ++pp) {
if (!(d0 > (d = glm::distance(p0, *pp)))) {
d0 = d;
p1 = *pp; // Find the furthest point from that
}
}
_radius = d0 * .5f; // Make a sphere ...
_center = (p1 - p0) * .5f + p0; // ... from these furthest points
bool done;
// Accommodate every outlier as we encounter them
do {
done = true;
for (pp = pts; pp != pEnd; ++pp) { // Check that all points are in this sphere
glm::vec3 v = *pp - _center;
d0 = glm::length(v);
if (d0 > _radius) { // If not, ...
d = (d0 - _radius) * .5f;
_center += v * (d / d0); // ... adjust the sphere center ...
_radius += d; // ... and radius to accommodate this new point
done = false;
}
}
} while (!done);
}
void GLMesh::BoundingSphere::set(unsigned numPts, const glm::vec3 *pts, const glm::mat4x4& M) {
std::vector<glm::vec3> xPts(numPts);
TransformPoints(M, numPts, pts, xPts.data());
set(numPts, xPts.data());
}
void GLMesh::getBoundingBox(BoundingBox *bbox, const glm::mat4x4 *M) const {
if (M) bbox->set(unsigned(m_vertices.size()), m_vertices.data(), *M);
else bbox->set(unsigned(m_vertices.size()), m_vertices.data());
}
void GLMesh::getBoundingSphere(BoundingSphere *bsph, const glm::mat4x4 *M) const {
if (M) bsph->set(unsigned(m_vertices.size()), m_vertices.data(), *M);
else bsph->set(unsigned(m_vertices.size()), m_vertices.data());
}
unsigned GLMesh::notRenderable(unsigned /*options*/) const {
unsigned result = RENDERABLE;
if (!isTriMesh())
result |= NOT_TRIMESH;
if (0 != m_textureIndices.size() && !indicesMatch(m_vertexIndices, m_textureIndices))
result |= COMPLEX_TOPOLOGY;
if (0 != m_normalIndices.size() && !indicesMatch(m_vertexIndices, m_normalIndices))
result |= COMPLEX_TOPOLOGY;
return result;
}
NvCV_Status GLMesh::append(const GLMesh& other, const glm::mat4x4 *M) {
if ((!numTexCoords() != !other.numTexCoords()) || (!numNormals() != !other.numNormals()))
return NVCV_ERR_MISMATCH;
unsigned indexOffset = numVertices(),
thisCount = numIndices(),
otherCount = other.numIndices(),
i;
// Add vertices
addVertices(other.numVertices(), &other.getVertices()->x);
if (M)
TransformPoints(*M, other.numVertices(), getVertices() + indexOffset, getVertices() + indexOffset);
if (0 != (i = other.numTexCoords()))
addTexCoords(i, &other.getTexCoords()->x);
if (0 != (i = other.numNormals())) {
addNormals(i, &other.getNormals()->x);
if (M)
TransformNormals(*M, other.numNormals(), getNormals() + indexOffset, getNormals() + indexOffset);
}
{ // Add indices
const unsigned short *nvx = other.getFaceVertexCounts(),
*vix = other.getVertexIndices(),
*tix = other.getTextureIndices(),
*nix = other.getNormalIndices();
for (i = other.numFaces(); i--; ++nvx) {
addFace(*nvx, vix, tix, nix);
vix += *nvx;
if (tix) tix += *nvx;
if (nix) nix += *nvx;
}
}
{ // Offset the new indices
unsigned short *ix;
for (i = otherCount, ix = getVertexIndices() + thisCount; i--; ++ix)
*ix += (unsigned short)indexOffset;
if (nullptr != (ix = getTextureIndices()))
for (i = otherCount, ix += thisCount; i--; ++ix)
*ix += (unsigned short)indexOffset;
if (nullptr != (ix = getNormalIndices()))
for (i = otherCount, ix += thisCount; i--; ++ix)
*ix += (unsigned short)indexOffset;
}
return NVCV_SUCCESS;
}

View File

@@ -0,0 +1,280 @@
/*###############################################################################
#
# Copyright 2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifndef __GLMESH_H
#define __GLMESH_H
#include <string>
#include <vector>
#include "glm/glm.hpp"
#include "nvCVStatus.h"
class GLMesh {
public:
struct Partition {
unsigned faceIndex; ///< The index of the first face in the partition.
unsigned numFaces; ///< The number of faces in the partition.
unsigned vertexIndex; ///< The index of the first topological vertex in the partition.
unsigned numVertexIndices; ///< The number of topological vertices in the partition.
std::string name; ///< The name of the partition.
std::string materialName; ///< The name of the material assigned to the partition.
int smooth; ///< The smoothing group > 0; no smoothing == 0; unassigned < 0.
Partition() { smooth = -1; }
bool operator<(const Partition& pt) const { return faceIndex < pt.faceIndex; }
void finishPartitioning();
};
class BoundingBox {
public:
void unionPoint(const glm::vec3& pt);
void set(unsigned numPts, const glm::vec3* pts);
void set(unsigned numPts, const glm::vec3* pts, const glm::mat4x4& M);
glm::vec3& min() { return _box[0]; }
glm::vec3& max() { return _box[1]; }
const glm::vec3& min() const { return _box[0]; }
const glm::vec3& max() const { return _box[1]; }
glm::vec3 center() const { return (_box[0] + _box[1]) * 0.5f; }
private:
glm::vec3 _box[2];
};
class BoundingSphere {
public:
void set(unsigned numPts, const glm::vec3* pts);
void set(unsigned numPts, const glm::vec3* pts, const glm::mat4x4& M);
glm::vec3& center() { return _center; }
const glm::vec3& center() const { return _center; }
float& radius() { return _radius; }
float radius() const { return _radius; }
private:
glm::vec3 _center;
float _radius;
};
enum { BOUNDARY = 0xFFFFu }; ///< An index that indicates the boundary
GLMesh();
GLMesh(const GLMesh& mesh);
~GLMesh();
/// Get the number of faces.
/// @return the number of faces.
unsigned numFaces() const;
/// Get the number of XYZ vertices.
/// @return the number of XYZ vertices.
unsigned numVertices() const;
/// Get the number of UV texture coordinates.
/// @return the number of UV texture coordinates.
unsigned numTexCoords() const;
/// Get the number of XYZ normals.
/// @return the number of XYZ normals.
unsigned numNormals() const;
/// Get the number of vertex indices.
/// @return the number of vertex indices.
unsigned numIndices() const;
/// Evaluate whether the mesh is composed only of triangles.
/// @return true if all faces have 3 vertices; false otherwise.
bool isTriMesh() const;
/// Evaluate whether the mesh is composed only of quadrilaterals.
/// @return true if all faces have 4 vertices; false otherwise.
bool isQuadMesh() const;
/// Evaluate whether the mesh is composed only of triangles and quadrilaterals.
/// @return true if no face has greater than 4 vertices; false otherwise.
bool isTriQuadMesh() const;
void resizeVertices(unsigned numVert);
void resizeTexCoords(unsigned numTexCoord);
void resizeNormals(unsigned numNorm);
void resizeFaces(unsigned numFace);
void resizeTriangles(unsigned numTriangles);
void resizeVertexIndices(unsigned numIndices);
void resizeDualIndices(unsigned numIndices);
void clear();
glm::vec3* getVertices(); ///< Get the vertices. @return a pointer to the vertices.
const glm::vec3* getVertices() const; ///< Get the vertices. @return a pointer to the vertices.
glm::vec2* getTexCoords(); ///< Get the texture coordinates. @return a pointer to the texture coordinates.
const glm::vec2* getTexCoords() const; ///< Get the texture coordinates. @return a pointer to the texture coordinates.
glm::vec3* getNormals(); ///< Get the vertex normals. @return a pointer to the vertex normals.
const glm::vec3* getNormals() const; ///< Get the vertex normals. @return a pointer to the vertex normals.
glm::vec3* getFaceNormals(); ///< Get the face normals, computed with computeFaceNormals(). @return a pointer to the vertex normals.
const glm::vec3* getFaceNormals() const; ///< Get the face normals, computed with computeFaceNormals(). @return a pointer to the vertex normals.
unsigned short* getFaceVertexCounts(); ///< Get the vertex counts for each face, in the primal topology. @return an array of vertex counts, one per face.
const unsigned short* getFaceVertexCounts() const; ///< Get the vertex counts for each face, in the primal topology. @return an array of vertex counts, one per face.
unsigned short* getVertexIndices(); ///< Get the vertex indices for each face: the primal topology. @return a pointer to the vertex indices.
const unsigned short* getVertexIndices() const; ///< Get the vertex indices for each face: the primal topology. @return a pointer to the vertex indices.
unsigned short* getTextureIndices(); ///< Get the texture indices for each face: the primal topology. @return a pointer to the texture indices.
const unsigned short* getTextureIndices() const; ///< Get the texture indices for each face: the primal topology. @return a pointer to the texture indices.
unsigned short* getNormalIndices(); ///< Get the normal indices for each face: the primal topology. @return a pointer to the normal indices.
const unsigned short* getNormalIndices() const; ///< Get the normal indices for each face: the primal topology. @return a pointer to the normal indices.
unsigned short* getVertexFaceCounts(); ///< Get the face counts for each vertex, in the dual topology. @return an array of face counts, one per vertex.
const unsigned short* getVertexFaceCounts() const; ///< Get the face counts for each vertex, in the dual topology. @return an array of face counts, one per vertex.
unsigned short* getDualIndices(); ///< Get the face indices for each vertex: the dual topology. @return a pointer to the dual face indices.
const unsigned short* getDualIndices() const; ///< Get the face indices for each vertex: the dual topology. @return a pointer to the dual face indices.
void addVertex(float x, float y, float z);
void addTexCoord(float u, float v);
void addNormal(float x, float y, float z);
void addVertices(unsigned numVertices, const float* vertices);
void addTexCoords(unsigned numTexCoords, const float* texCoords);
void addNormals(unsigned numNormals, const float* normals);
void addFace(unsigned numVertices, const unsigned short* vertexIndices,
const unsigned short* textureIndices, const unsigned short* normalIndices);
void addFaces(unsigned numFaces, unsigned numVerticesPerFace, const unsigned short* vertexIndices,
const unsigned short* textureIndices, const unsigned short* normalIndices);
/// Compute the normals per face.
/// @param[in] specify the weighing for the normals. In all cases, the zero vector will
/// be returned for faces with zero area.
/// 0: unit vectors.
/// +1: vectors weighted by the area.
/// -1: vectors weighted by the reciprocal of the area.
void computeFaceNormals(int weighted = 0);
/// Compute the vertex normals. The face normals will be computed in the process.
/// @param[in] weighted Determines the weighting used to combine the face normals:
/// 0: all incident faces normals will have the same weight.
/// -1: the normals will be weighted by inverse area of the face.
void computeVertexNormals(int weighted = 0);
void transform(const glm::mat4x4& M);
/// Get the number of partitions.
/// There is always at least one, which may neither have a name nor a material.
/// @return the number of partitions.
unsigned numPartitions() const;
/// The easiest way to partition a mesh: call this after all vertices and attributes
/// are recorded, and before the first face of each partition is recorded.
/// @param[in] name the name of the new partition.
/// @param[in] material the name of the material to be used in the new partition.
/// @param[in] smooth {-1, 0, 1} means {unspecified, not smooth, smooth}.
/// @return NvCV_StatusNone if the partition was retrieved successfully.
/// @return NvCV_StatusDuplicate if a partition with the same name already exists.
NvCV_Status startPartition(const char* name, const char* material, int smooth = -1);
/// Get the specified partition.
/// @param[in] i the index of the partition to retrieve.
/// @param[out] pt a place to store the specified partition.
/// @return NvCV_StatusNone if the partition was retrieved successfully.
/// @return NvCV_StatusTooBig if the face index was >= the number of faces.
NvCV_Status getPartition(unsigned i, Partition& pt) const;
/// Update the specified partition.
/// The function finishPartitioning() should be called
/// after the last updatePartition() has been called.
/// @param[in] i the index of the partition.
/// @param[in] partition the desired value for the specified partition.
/// @return NvCV_StatusNone if the partition was updated successfully.
/// @return NvCV_StatusTooBig if the face index was >= the number of faces.
NvCV_Status updatePartition(unsigned i, const Partition& partition);
/// Partition the mesh.
/// @param[in] numPartitions the number of partitions.
/// @param[in] partitions the array of partitions. Only { faceIndex, name, and
/// materialName need be supplied}; the rest are computed.
/// @return NvCV_StatusNone if the partition was executed successfully.
/// @return NvCV_StatusTooBig if any faceIndex was >= the number of faces.
NvCV_Status partitionMesh(unsigned numPartitions, const Partition* partitions);
/// The last step after partitioning with updatePartition().
/// This is not needed if the partitions were created solely with the use of
/// startPartition() or PartitionMesh().
/// @note The partitions may be reordered (sorted) after calling finishPartitioning().
void finishPartitioning();
/// Set a single material for the whole mesh.
/// @param[in] name the name of the material.
NvCV_Status setMaterial(const char* name);
/// Get the bounding box, optionally with an affine transformation.
/// @param[out] bbox a place to store the bounding box.
/// @param[in] M pointer to a modeling matrix; NULL implies the identity.
void getBoundingBox(BoundingBox* bbox, const glm::mat4x4* M = nullptr) const;
/// Get the bounding box, optionally with an affine transformation.
/// @param[out] bbox a place to store the bounding box.
/// @param[in] M pointer to a modeling matrix; NULL implies the identity.
void getBoundingSphere(BoundingSphere* bsph, const glm::mat4x4* M = nullptr) const;
/// Query whether the PolyMesh is not renderable easily by Open GL.
/// Since the more typical query would be whether it is renderable instead,
/// this seems like negative logic, but this choice was made to return a bit vector
/// indicating the reason that the Polymesh is not renderable.
/// @param[in] options Rendering options; currently ignored.
/// @return RENDERABLE if the PolyMesh is renderable. Otherwise a bit vector of:
/// NOT_TRIMESH if some faces are not triangular;
/// COMPLEX_TOPOLOGY if the vertex attribute topology is inconsistent;
unsigned notRenderable(unsigned options) const;
/// Append another mesh.
/// @param[in] mesh the other mesh.
/// @param[in] M an optional affine transform
NvCV_Status append(const GLMesh& mesh, const glm::mat4x4* M = nullptr);
/// Bit vector components indicating non-renderability.
enum {
RENDERABLE = 0x0, ///< The PolyMesh is renderable.
NOT_TRIMESH = 0x1, ///< Some faces are not triangular.
COMPLEX_TOPOLOGY = 0x2 ///< The vertex topology is not consistent.
};
private:
void initPartitions();
void computeStartingVertexIndices();
static bool indicesMatch(const std::vector<unsigned short>& ivecA, const std::vector<unsigned short>& ivecB);
void assureConsistency();
void useFaceNormals(bool yes);
std::vector<unsigned short> m_faceVertexCount;
std::vector<glm::vec3> m_vertices;
std::vector<unsigned short> m_vertexIndices;
std::vector<glm::vec2> m_texCoords;
std::vector<unsigned short> m_textureIndices;
std::vector<glm::vec3> m_normals;
std::vector<unsigned short> m_normalIndices;
std::vector<glm::vec3> m_faceNormals;
std::vector<Partition> m_partitions;
std::vector<unsigned short> m_vertexFaceCount; // the number of faces surrounding each vertex
std::vector<unsigned short> m_dualIndices; // the face indices for each vertex
};
#endif // __GLMESH_H

View File

@@ -0,0 +1,632 @@
/*###############################################################################
#
# Copyright 2016-2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifdef _MSC_VER
#include "glad/glad.h"
#else
#include <GLES3/gl3.h>
#endif // _MSC_VER
#include <string>
#include <stdint.h>
#include "GLShaders.h"
enum {
myErrNone = 0,
myErrShader = -1,
myErrProgram = -2,
myErrTexture = -3,
};
#define BAIL_IF_ERR(err) do { if ((err)) { goto bail; } } while(0)
#define _STRINGIFY_(token) #token
#define STRINGIFY(token) _STRINGIFY_(token)
#define MAYBE_UNUSED(token) if (token){}
/****************************************************************************//**
* Print Shader Log.
* \param[in] id the ID of the shader.
* \param[in] type either GL_VERTEX_SHADER or GL_FRAGMENT_SHADER.
* \param[in] shader the shader source code.
********************************************************************************/
static void PrintShaderLog(GLuint id, GLenum type, const char *shader) {
GLsizei msgLength;
std::string errMsg;
glGetShaderiv(id, GL_INFO_LOG_LENGTH, &msgLength);
errMsg.resize(msgLength);
glGetShaderInfoLog(id, msgLength, &msgLength, &errMsg[0]);
fprintf(stderr, "\nShader Log:\n%sfor %s Shader:\n%s\n", errMsg.c_str(),
((type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment"), shader);
}
/****************************************************************************//**
* Print Program Log.
* \param[in] id the id of the program.
********************************************************************************/
static void PrintProgramLog(GLuint id) {
GLsizei msgLength;
std::string errMsg;
glGetProgramiv(id, GL_INFO_LOG_LENGTH, &msgLength);
errMsg.resize(msgLength);
glGetProgramInfoLog(id, msgLength, &msgLength, &errMsg[0]);
fprintf(stderr, "\nProgram Log:\n%s\n", errMsg.c_str());
}
/****************************************************************************//**
* NewShader
********************************************************************************/
static int NewShader(const char *shaderStr, GLenum type, GLuint *shaderID) {
GLuint id;
GLint result;
*shaderID = 0;
id = glCreateShader(type);
glShaderSource(id, 1, &shaderStr, NULL);
glCompileShader(id);
glGetShaderiv(id, GL_COMPILE_STATUS, &result);
if (result) {
*shaderID = id;
return myErrNone;
}
else {
PrintShaderLog(id, type, shaderStr);
glDeleteShader(id);
return myErrShader;
}
}
/****************************************************************************//**
* NewProgram
********************************************************************************/
static int NewProgram(GLuint vertexShader, GLuint fragmentShader, GLuint *progID) {
GLint result;
GLuint id;
*progID = 0;
id = glCreateProgram();
glAttachShader(id, vertexShader);
glAttachShader(id, fragmentShader);
glLinkProgram(id);
glGetProgramiv(id, GL_LINK_STATUS, &result);
if (result) {
*progID = id;
return myErrNone;
}
else {
PrintProgramLog(id);
glDeleteProgram(id);
return myErrProgram;
}
}
/****************************************************************************//**
* IndexTypeFromSize
********************************************************************************/
static GLenum IndexTypeFromSize(unsigned indexSize) {
return (indexSize < 2) ? GL_UNSIGNED_BYTE
: (indexSize == 2) ? GL_UNSIGNED_SHORT
: GL_UNSIGNED_INT;
}
/********************************************************************************
********************************************************************************
***** SMOOTH RENDERER *****
********************************************************************************
********************************************************************************/
/********************************************************************************
* Shaders
********************************************************************************/
const char SmoothRenderer::_vertexShader[] =
"uniform mat4 MVP;\n"
"attribute vec3 vCol;\n"
"attribute vec3 vPos;\n"
"varying vec3 color;\n"
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(vPos, 1.0);\n"
" color = vCol;\n"
"}\n";
const char SmoothRenderer::_fragmentShader[] =
"varying vec3 color;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(color, 1.0);\n"
"}\n";
/********************************************************************************
* startup
********************************************************************************/
int SmoothRenderer::startup() {
int err = myErrNone;
GLuint vertexShader = 0, fragmentShader = 0;
_programID = 0;
BAIL_IF_ERR(err = NewShader(_vertexShader, GL_VERTEX_SHADER, &vertexShader));
BAIL_IF_ERR(err = NewShader(_fragmentShader, GL_FRAGMENT_SHADER, &fragmentShader));
BAIL_IF_ERR(err = NewProgram(vertexShader, fragmentShader, &_programID));
_maxtrixID = glGetUniformLocation(_programID, "MVP");
_vtxPosID = glGetAttribLocation(_programID, "vPos");
_vtxColID = glGetAttribLocation(_programID, "vCol");
err = (-1 == _maxtrixID || -1 == _vtxPosID || -1 == _vtxColID) ? myErrShader : myErrNone;
bail:
if (myErrNone != err) shutdown();
if (fragmentShader) glDeleteShader(fragmentShader);
if (vertexShader) glDeleteShader(vertexShader);
return err;
}
/********************************************************************************
* use
********************************************************************************/
int SmoothRenderer::use() {
if (0 == _programID)
return myErrProgram;
glUseProgram(_programID);
return myErrNone;
}
/********************************************************************************
* drawElements, from user memory
********************************************************************************/
void SmoothRenderer::drawElements(GLsizei numVertices, const GLfloat *positions, const GLfloat *colors,
GLenum graphicsMode, GLsizei indexCount, GLenum indexType, const GLvoid *indices, const GLfloat *M
) {
MAYBE_UNUSED(numVertices);
glUseProgram(_programID);
if (M)
glUniformMatrix4fv(_maxtrixID, 1, GL_FALSE, M);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if (colors) { /* Separate array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(*positions), positions);
glEnableVertexAttribArray(_vtxColID);
glVertexAttribPointer(_vtxColID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(*colors), colors);
}
else { /* One contiguous array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*positions), positions);
glEnableVertexAttribArray(_vtxColID);
glVertexAttribPointer(_vtxColID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*positions), positions + 3);
}
glDrawElements(graphicsMode, indexCount, indexType, indices);
}
/********************************************************************************
* drawElements, from buffer objects
********************************************************************************/
void SmoothRenderer::drawElements(GLuint vtxBuf, unsigned posOff, unsigned colOff,
GLenum graphicsMode, GLsizei numIndices, unsigned indexSize, GLuint topoBuf, const GLfloat *M
) {
GLenum err;
glUseProgram(_programID);
if (M)
glUniformMatrix4fv(_maxtrixID, 1, GL_FALSE, M);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuf);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, topoBuf);
if (!(colOff == 12 || colOff == 0)) { /* Separate array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(intptr_t)posOff);
glEnableVertexAttribArray(_vtxColID);
glVertexAttribPointer(_vtxColID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(intptr_t)colOff);
}
else { /* One contiguous array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(intptr_t)(posOff + 0 * sizeof(float)));
glEnableVertexAttribArray(_vtxColID);
glVertexAttribPointer(_vtxColID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(intptr_t)(posOff + 3 * sizeof(float)));
err = glGetError(); if (err) printf("glVertexAttribPointer returns %d\n", err);
}
glDrawElements(graphicsMode, numIndices, IndexTypeFromSize(indexSize), (void*)0);
}
/********************************************************************************
********************************************************************************
***** TEXTURE RENDERER *****
********************************************************************************
********************************************************************************/
/********************************************************************************
* shaders
********************************************************************************/
const char TextureRenderer::_vertexShader[] =
"uniform mat4 MVP;\n" // Model, view, projection matrices, concatenated.
"attribute vec3 vPos;\n" // Vertex position
"attribute vec2 vTex;\n" // Vertex texture coordinate
"varying vec2 texCoord;\n" // Interpolated texture coordinate
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(vPos, 1.0);\n"
" texCoord = vTex;\n"
"}\n";
const char TextureRenderer::_fragmentShader[] =
"uniform sampler2D tex;\n"
"varying vec2 texCoord;\n" // Interpolated texture coordinate
"void main()\n"
"{\n"
" gl_FragColor = texture2D(tex, texCoord);\n"
"}\n";
/********************************************************************************
* startup
********************************************************************************/
int TextureRenderer::startup() {
int err = myErrNone;
GLuint vertexShader = 0, fragmentShader = 0;
if (_programID)
return myErrNone;
_programID = 0;
BAIL_IF_ERR(err = NewShader(_vertexShader, GL_VERTEX_SHADER, &vertexShader));
BAIL_IF_ERR(err = NewShader(_fragmentShader, GL_FRAGMENT_SHADER, &fragmentShader));
BAIL_IF_ERR(err = NewProgram(vertexShader, fragmentShader, &_programID));
_maxtrixID = _vtxPosID = _vtxTexID = -1;
_maxtrixID = glGetUniformLocation(_programID, "MVP");
_vtxPosID = glGetAttribLocation(_programID, "vPos");
_vtxTexID = glGetAttribLocation(_programID, "vTex");
err = (-1 == _maxtrixID || -1 == _vtxPosID || -1 == _vtxTexID) ? myErrShader : myErrNone;
bail:
if (myErrNone != err) shutdown();
if (fragmentShader) glDeleteShader(fragmentShader);
if (vertexShader) glDeleteShader(vertexShader);
return err;
}
/********************************************************************************
* use
********************************************************************************/
int TextureRenderer::use() {
if (0 == _programID)
return myErrProgram;
glUseProgram(_programID);
return myErrNone;
}
/********************************************************************************
* drawElements, from user buffers
********************************************************************************/
void TextureRenderer::drawElements(GLsizei numVertices, const GLfloat *xyz, const GLfloat *uv,
GLenum graphicsMode, GLsizei indexCount, GLenum indexType, const GLvoid *indices, GLuint texID ,const GLfloat *M
) {
MAYBE_UNUSED(numVertices);
glUseProgram(_programID);
if (M)
glUniformMatrix4fv(_maxtrixID, 1, GL_FALSE, M);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
glBindTexture(GL_TEXTURE_2D, texID);
if (uv) { /* Separate array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(*xyz), xyz);
glEnableVertexAttribArray(_vtxTexID);
glVertexAttribPointer(_vtxTexID, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*uv), uv);
}
else { /* One contiguous array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(*xyz), xyz);
glEnableVertexAttribArray(_vtxTexID);
glVertexAttribPointer(_vtxTexID, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(*xyz), xyz + 3);
}
glDrawElements(graphicsMode, indexCount, indexType, indices);
}
/********************************************************************************
* drawElements, from buffer objects
********************************************************************************/
void TextureRenderer::drawElements(
GLuint vtxBuf, unsigned xyzOff, unsigned uvOff, GLenum graphicsMode,
GLsizei numIndices, GLenum indexSize, GLuint indexBuf, GLuint texID, const GLfloat *M
) {
GLenum err;
glUseProgram(_programID);
if (M)
glUniformMatrix4fv(_maxtrixID, 1, GL_FALSE, M);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuf);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
glBindTexture(GL_TEXTURE_2D, texID);
if (!(uvOff == 12 || uvOff == 0)) { /* Separate array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(intptr_t)xyzOff);
glEnableVertexAttribArray(_vtxTexID);
glVertexAttribPointer(_vtxTexID, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)(intptr_t)uvOff);
}
else { /* One contiguous array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(intptr_t)(xyzOff + 0 * sizeof(float)));
glEnableVertexAttribArray(_vtxTexID);
glVertexAttribPointer(_vtxTexID, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(intptr_t)(xyzOff + 3 * sizeof(float)));
err = glGetError(); if (err) printf("glVertexAttribPointer returns %d\n", err);
}
glDrawElements(graphicsMode, numIndices, IndexTypeFromSize(indexSize), (void*)0);
}
/********************************************************************************
* drawQuad, from user buffers
********************************************************************************/
void TextureRenderer::drawQuad(const float xyz[4*3], const float uv[4*2], GLuint texID, const float *M) {
static const unsigned char indices[4] = { 0, 1, 2, 3 }; // These need to be static, because GL is asynchronous
drawElements(4, xyz, uv, GL_TRIANGLE_FAN, 4, GL_UNSIGNED_BYTE, indices, texID, M);
}
/********************************************************************************
* Mesh shader
********************************************************************************/
class MeshShader {
static const char _vertexShader[], _fragmentShader[];
};
// The ambient and diffuse coefficients are rolled into the ambCol and litCol, respectively.
const char MeshShader::_vertexShader[] =
"uniform mat4 MVP;\n" // Model, view, projection matrices, concatenated.
"uniform mat3 N;\n" // Normal matrix.
"uniform vec3 litDir;\n" // Light direction
"uniform vec3 litCol;\n" // Light color multiplied by the diffuse coefficient
"uniform vec3 ambCol;\n" // Ambient color multiplied by the ambient coefficient
"attribute vec3 vtxPos;\n" // Vertex position
"attribute vec3 vtxNor;\n" // Vertex normal
"attribute vec2 vtxTex;\n" // Vertex texture coordinate
"varying vec2 texCoord;\n" // Interpolated texture coordinate
"varying vec3 illum;\n" // Interpolated illumination
"void main()\n"
"{\n"
" gl_Position = MVP * vec4(vtxPos, 1.0);\n"
" texCoord = vtxTex;\n"
" illum = max(dot(N * vtxNor, litDir) * litCol + ambCol;\n"
"}\n";
/********************************************************************************
* UpdateTexture
********************************************************************************/
GLenum UpdateTexture(GLint texID, GLsizei width, GLsizei height, GLsizei rowBytes, GLenum glFormat, const GLvoid *pixels) {
glBindTexture(GL_TEXTURE_2D, texID);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH, rowBytes / 4);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, glFormat, GL_UNSIGNED_BYTE, pixels);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); // restore to default
return glGetError();
}
/********************************************************************************
********************************************************************************
***** LAMBERTIAN RENDERER *****
********************************************************************************
********************************************************************************/
/********************************************************************************
* Shaders
********************************************************************************/
const char LambertianRenderer::_vertexShader[] =
"#version 120\n"
"uniform mat4 M;\n"
"uniform mat4 VP;\n"
"uniform vec4 lightLoc[" STRINGIFY(LAMBERTIAN_NUM_LIGHTS) "];\n"
"uniform vec3 lightColor[" STRINGIFY(LAMBERTIAN_NUM_LIGHTS) "];\n"
"uniform vec3 Ka;\n"
"uniform vec3 Kd;\n"
"attribute vec3 vPos;\n"
"attribute vec3 vNrm;\n"
"varying vec3 color;\n"
"void main()\n"
"{\n"
" vec4 loc = M * vec4(vPos, 1.);\n" // Transform points into world space ...
" gl_Position = VP * loc;\n" // ... and screen space
" vec3 N = normalize(mat3(M) * vNrm);\n" // Transform normal into world space, assuming isotropic scaling
" color = Ka;\n" // Initialize color to ambient
" for (int i = 0; i < " STRINGIFY(LAMBERTIAN_NUM_LIGHTS) "; ++i)\n"
" {\n"
" vec3 L = normalize(lightLoc[i].xyz - lightLoc[i].w * loc.xyz);\n" // Compute vector to light: w must be either 1 or 0
" float d = dot(L, N);\n" // Lambertian lighting
" if (d > 0.)\n" // If the light hits the outside surface, ...
" color += d * lightColor[i] * Kd;\n" // ... accumulate color from the light source
" }\n"
"}\n";
const char LambertianRenderer::_fragmentShader[] =
"varying vec3 color;\n"
"void main()\n"
"{\n"
" gl_FragColor = vec4(color, 1.);\n" // Interpolate the color
"}\n";
/********************************************************************************
* startup
********************************************************************************/
int LambertianRenderer::startup() {
int err = myErrNone;
GLuint vertexShader = 0, fragmentShader = 0;
_programID = 0;
BAIL_IF_ERR(err = NewShader(_vertexShader, GL_VERTEX_SHADER, &vertexShader));
BAIL_IF_ERR(err = NewShader(_fragmentShader, GL_FRAGMENT_SHADER, &fragmentShader));
BAIL_IF_ERR(err = NewProgram(vertexShader, fragmentShader, &_programID));
_lightLoc = glGetUniformLocation(_programID, "lightLoc"); // light locations
_lightColor = glGetUniformLocation(_programID, "lightColor"); // light diffuse colors
_MmatrixID = glGetUniformLocation(_programID, "M"); // M matrix; require UL 3x3 to be orthogonal
_VPmatrixID = glGetUniformLocation(_programID, "VP"); // VP matrix
_ambientColorID = glGetUniformLocation(_programID, "Ka"); // ambient color
_diffuseColorID = glGetUniformLocation(_programID, "Kd"); // diffuse color
_vtxPosID = glGetAttribLocation(_programID, "vPos"); // vertex positions
_vtxNrmID = glGetAttribLocation(_programID, "vNrm"); // vertex normals
err = (-1 == _lightLoc || -1 == _lightColor || -1 == _MmatrixID || -1 == _VPmatrixID || -1 == _ambientColorID
|| -1 == _diffuseColorID || -1 == _vtxPosID || -1 == _vtxNrmID) ? myErrShader : myErrNone;
BAIL_IF_ERR(err);
bail:
if (myErrNone != err) shutdown();
if (fragmentShader) glDeleteShader(fragmentShader);
if (vertexShader) glDeleteShader(vertexShader);
return err;
}
/********************************************************************************
* use
********************************************************************************/
int LambertianRenderer::use() {
if (0 == _programID)
return myErrProgram;
glUseProgram(_programID);
return myErrNone;
}
/********************************************************************************
* set lights
********************************************************************************/
void LambertianRenderer::setLights(const float locXYZW[4*LAMBERTIAN_NUM_LIGHTS], const float colorRGB[3*LAMBERTIAN_NUM_LIGHTS]) {
glUseProgram(_programID);
glUniform4fv(_lightLoc, LAMBERTIAN_NUM_LIGHTS, locXYZW);
glUniform3fv(_lightColor, LAMBERTIAN_NUM_LIGHTS, colorRGB);
}
/********************************************************************************
* drawElements, from user memory
********************************************************************************/
void LambertianRenderer::drawElements(GLsizei numVertices, const GLfloat *positions, const GLfloat *normals,
GLenum graphicsMode, GLsizei indexCount, GLenum indexType, const GLvoid *indices,
const GLfloat M[4*4], const GLfloat VP[4*4], const float Ka[3], const float Kd[3]
) {
MAYBE_UNUSED(numVertices);
glUseProgram(_programID);
if (M) glUniformMatrix4fv(_MmatrixID, 1, GL_FALSE, M);
if (VP) glUniformMatrix4fv(_VPmatrixID, 1, GL_FALSE, VP);
if (Ka) glUniform3fv(_ambientColorID, 1, Ka);
if (Kd) glUniform3fv(_diffuseColorID, 1, Kd);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
if (normals) { /* Separate array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(*positions), positions);
glEnableVertexAttribArray(_vtxNrmID);
glVertexAttribPointer(_vtxNrmID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(*normals), normals);
}
else { /* One contiguous array for position and color */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*positions), positions);
glEnableVertexAttribArray(_vtxNrmID);
glVertexAttribPointer(_vtxNrmID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(*normals), normals + 3);
}
glDrawElements(graphicsMode, indexCount, indexType, indices);
}
/********************************************************************************
* drawElements, from buffer objects
********************************************************************************/
void LambertianRenderer::drawElements(GLuint vtxBuf, unsigned posOff, unsigned nrmOff,
GLenum graphicsMode, GLsizei numIndices, unsigned indexSize, GLuint indexBuf,
const GLfloat M[4*4], const GLfloat VP[4*4], const float Ka[3], const float Kd[3]
) {
GLenum err;
glUseProgram(_programID);
if (M) glUniformMatrix4fv(_MmatrixID, 1, GL_FALSE, M);
if (VP) glUniformMatrix4fv(_VPmatrixID, 1, GL_FALSE, VP);
if (Ka) glUniform3fv(_ambientColorID, 1, Ka);
if (Kd) glUniform3fv(_diffuseColorID, 1, Kd);
glBindBuffer(GL_ARRAY_BUFFER, vtxBuf);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuf);
if (!(nrmOff == 12 || nrmOff == 0)) { /* Separate array for position and normal */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(intptr_t)posOff);
glEnableVertexAttribArray(_vtxNrmID);
glVertexAttribPointer(_vtxNrmID, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)(intptr_t)nrmOff);
}
else { /* One contiguous array for position and normal */
glEnableVertexAttribArray(_vtxPosID);
glVertexAttribPointer(_vtxPosID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(intptr_t)(posOff + 0 * sizeof(float)));
glEnableVertexAttribArray(_vtxNrmID);
glVertexAttribPointer(_vtxNrmID, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(intptr_t)(posOff + 3 * sizeof(float)));
err = glGetError(); if (err) printf("glVertexAttribPointer returns %d\n", err);
}
glDrawElements(graphicsMode, numIndices, IndexTypeFromSize(indexSize), (void*)0);
}

View File

@@ -0,0 +1,366 @@
/*###############################################################################
#
# Copyright 2016-2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifndef __ARSHADERS_H__
#define __ARSHADERS_H__
#ifdef _MSC_VER
#include "glad/glad.h"
#else
#include <GLES3/gl3.h>
#endif // _MSC_VER
/********************************************************************************
********************************************************************************
***** SMOOTH RENDERER *****
********************************************************************************
********************************************************************************/
class SmoothRenderer {
public:
SmoothRenderer() { _programID = 0; }
~SmoothRenderer() { shutdown(); }
int startup();
void shutdown() { if (_programID) glDeleteProgram(_programID); _programID = 0; }
int use();
int activate() { return startup(); } // DEPRECATED
void deactivate() { shutdown(); } // DEPRECATED
/** These take vertex and topology data in user-space buffers.
* @param[in] numPts The number of points in xyz or rgb.
* @param[in] xyz The vertex locations {x, y, z }.
* @param[in] rgb The vertex colors { r, g, b }, in [0, 1].
* @param[in] numIndices The number of indices.
* @param[in] indices The indices. Note that three versions are given, where indices can be 1, 2, or 4 bytes.
* @param[in] M the matrix.
*/
void drawTriMesh(unsigned numPts, const float* xyz, const float* rgb,
unsigned numIndices, const unsigned char* indices, const float* M = nullptr) {
drawElements(numPts, xyz, rgb, GL_TRIANGLES, numIndices, GL_UNSIGNED_BYTE, indices, M);
}
void drawTriMesh(unsigned numPts, const float* xyz, const float* rgb,
unsigned numIndices, const unsigned short* indices, const float* M = nullptr) {
drawElements(numPts, xyz, rgb, GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices, M);
}
void drawTriMesh(unsigned numPts, const float* xyz, const float* rgb,
unsigned numIndices, const unsigned int* indices, const float* M = nullptr) {
drawElements(numPts, xyz, rgb, GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, indices, M);
}
/** These take vertex and topology data in GL buffer objects
* @param[in] vtxBuf the vertex buffer object identifier.
* @param[in] xyzOff the offset, in bytes, of the xyz positions in the vertex buffer.
* @param[in] rgbOff the offset, in bytes, of the rgb color in the vertex buffer.
* @param[in] numIndices the number of indices.
* @param[in] indexBuf the index buffer object identifier.
* @param[in] indexSize the byte size of the indices: 1, 2, or 4.
* @param[in] M the matrix.
*/
void drawTriMesh(GLuint vtxBuf, unsigned xyzOff, unsigned rgbOff,
unsigned numIndices, GLuint indexBuf, GLenum indexSize, const float* M) {
drawElements(vtxBuf, xyzOff, rgbOff, GL_TRIANGLES, numIndices, indexSize, indexBuf, M);
}
private:
/** Render geometry from user buffers, pre-shaded at vertices.
* @param[in] numVertices The number of 3D vertices.
* @param[in] positions The array of 3D positions -- one for every vertex.
* @param[in] colors The array of RGB colors -- one for every vertex.
* @param[in] graphicsMode One of { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }.
* GL_QUADS is not supported.
* @param[in] indexCount The number of 0-based vertex indices that define the geometry
* from the vertices and graphics mode.
* @param[in] indexType The type of index { GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT }.
* @param[in] indices The array of vertices.
* @param[in] M The modeling-viewing-projection matrix.
*/
void drawElements(GLsizei numVertices, const GLfloat* positions, const GLfloat* colors,
GLenum graphicsMode, GLsizei indexCount, GLenum indexType, const GLvoid* indices, const GLfloat* M);
/** Render geometry from GL buffer objects, pre-shaded at vertices.
* @param[in] vtxBuf The ID of the GL buffer used to store the vertices.
* @param[in] posOff The offset of the positions in the vertex buffer. This is typically 0,
* but is not restricted so.
* @param[in] colOff The offset of the colors in the vertex buffer. Both planar (homogeneous, separate)
* and chunky (nonhomogeneous, interleaved) representations are accommodated.
* @param[in] graphicsMode One of { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }.
* GL_QUADS is not supported.
* @param[in] indexCount The number of 0-based vertex indices that define the geometry
* from the vertices and graphics mode.
* @param[in] indexType The type of index { GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT }.
* @param[in] indexBuf The ID of the GL buffer used to store the indices.
* @param[in] M The modeling-viewing-projection matrix.
*/
void drawElements(GLuint vtxBuf, unsigned posOff, unsigned colOff,
GLenum graphicsMode, GLsizei indexCount, GLenum indexType, GLuint indexBuf, const GLfloat* M);
GLuint _programID;
GLint _maxtrixID, _vtxPosID, _vtxColID;
static const char _vertexShader[], _fragmentShader[];
};
/********************************************************************************
********************************************************************************
***** TEXTURE RENDERER *****
********************************************************************************
********************************************************************************/
class TextureRenderer {
public:
TextureRenderer() { _programID = 0; }
~TextureRenderer() { shutdown(); }
int startup();
void shutdown() { if (_programID) glDeleteProgram(_programID); _programID = 0; }
int use();
int activate() { return startup(); } // DEPRECATED
void deactivate() { shutdown(); } // DEPRECATED
/** These take vertex and topology data in user-space buffers.
* @param[in] numPts The number of points in xyz or uv.
* @param[in] xyz The vertex locations {x, y, z }.
* @param[in] uv The vertex texture coordinates { u, v }, in [0, 1].
* @param[in] numIndices The number of indices.
* @param[in] indices The indices. Note that three versions are given, where indices can be 1, 2, or 4 bytes.
* @param[in] texID The texture ID.
* @param[in] M the matrix. NULL keeps the matrix as it was in the last invocation.
*/
void drawTriMesh(unsigned numPts, const float* xyz, const float* uv,
unsigned numIndices, const unsigned char* indices, GLuint texID, const float* M = nullptr) {
drawElements(numPts, xyz, uv, GL_TRIANGLES, numIndices, GL_UNSIGNED_BYTE, indices, texID, M);
}
void drawTriMesh(unsigned numPts, const float* xyz, const float* uv,
unsigned numIndices, const unsigned short* indices, GLuint texID, const float* M = nullptr) {
drawElements(numPts, xyz, uv, GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices, texID, M);
}
void drawTriMesh(unsigned numPts, const float* xyz, const float* uv,
unsigned numIndices, const unsigned int* indices, GLuint texID, const float* M = nullptr) {
drawElements(numPts, xyz, uv, GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, indices, texID, M);
}
/** Draw a texture-mapped quadrilateral.
* @param[in] xyz The vertex locations {x, y, z }.
* @param[in] uv The vertex texture coordinates { u, v }, in [0, 1].
* @param[in] texID The texture ID.
* @param[in] M the matrix. NULL keeps the matrix as it was in the last invocation.
*/
void drawQuad(const float xyz[4 * 3], const float uv[4 * 2], GLuint texID, const float* M = nullptr);
/** These take vertex and topology data in GL buffer objects
* @param[in] vtxBuf the vertex buffer object identifier.
* @param[in] xyzOff the offset, in bytes, of the xyz positions in the vertex buffer.
* @param[in] uvOff the offset, in bytes, of the texture coordinates in the vertex buffer.
* @param[in] numIndices the number of indices.
* @param[in] indexBuf the index buffer object identifier.
* @param[in] indexSize the byte size of the indices: 1, 2, or 4.
* @param[in] texID The texture ID.
* @param[in] M the matrix. NULL keeps the matrix as it was in the last invocation.
*/
void drawTriMesh(GLuint vtxBuf, unsigned xyzOff, unsigned rgbOff,
unsigned numIndices, GLuint indexBuf, GLenum indexSize, GLuint texID, const float* M) {
drawElements(vtxBuf, xyzOff, rgbOff, GL_TRIANGLES, numIndices, indexSize, indexBuf, texID, M);
}
private:
/** Render geometry from user buffers.
* @param[in] numVertices The number of 3D vertices.
* @param[in] positions The array of 3D positions -- one for every vertex.
* @param[in] uv The array of texture coordinates -- one for every vertex.
* @param[in] graphicsMode One of { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }.
* GL_QUADS is not supported.
* @param[in] indexCount The number of 0-based vertex indices that define the geometry
* from the vertices and graphics mode.
* @param[in] indexType The type of index { GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT }.
* @param[in] indices The array of vertices.
* @param[in] texID The ID of the texture to be used.
* @param[in] M The modeling-viewing-projection matrix.
*/
void drawElements(GLsizei numVertices, const GLfloat* positions, const GLfloat* uv, GLenum graphicsMode,
GLsizei indexCount, GLenum indexType, const GLvoid* indices, GLuint texID, const GLfloat* M);
/** Render geometry from GL buffer objects.
* @param[in] vtxBuf The ID of the GL buffer used to store the vertices.
* @param[in] posOff The offset of the positions in the vertex buffer. This is typically 0,
* but is not restricted so.
* @param[in] uvOff The offset of the texture coordinates in the vertex buffer. Both planar
* (homogeneous, separate) and chunky (nonhomogeneous, interleaved)
* representations are accommodated.
* @param[in] graphicsMode One of { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }.
* GL_QUADS is not supported.
* @param[in] indexCount The number of 0-based vertex indices that define the geometry
* from the vertices and graphics mode.
* @param[in] indexSize The size of index { 1, 2, 4 } in bytes.
* @param[in] indexBuf The ID of the GL buffer used to store the indices.
* @param[in] texID The ID of the texture to be used.
* @param[in] M The modeling-viewing-projection matrix.
*/
void drawElements(GLuint vtxBuf, unsigned posOff, unsigned uvOff, GLenum graphicsMode,
GLsizei indexCount, GLenum indexSize, GLuint indexBuf, GLuint texID, const GLfloat* M);
GLuint _programID;
GLint _maxtrixID, _vtxPosID, _vtxTexID;
static const char _vertexShader[], _fragmentShader[];
};
/** Update the specified texture.
* @param[in] texID The ID of the texture to be updated.
* @param[in] width The width of the source image.
* @param[in] height The height of the source image.
* @param[in] rowBytes The byte stride between pixels vertically in the source image (must be positive).
* @param[in] glFormat The format of the source image. One of { GL_RGBA, GL_BGRA, GL_RGB, GL_BGR, GL_RG, GL_R }.
* @param[in] pixels A pointer to pixel(0,0) of the source image.
* @return GL_NO_ERROR if the update was successful.
*/
GLenum UpdateTexture(GLint texID, GLsizei width, GLsizei height, GLsizei rowBytes, GLenum glFormat,
const GLvoid* pixels);
/********************************************************************************
********************************************************************************
***** LAMBERTIAN SHADER *****
********************************************************************************
********************************************************************************/
/** The camera is assumed to be at the origin. Lights are represented in the camera coordinate system.
* The model coordinates are transformed M * pt;
* the normal transformed by (M{3x3})^(-1)^(T), or simply M{3x3} assuming the scaling is isotropic.
* We further assume that M{3x3} is orthonormal.
*/
class LambertianRenderer {
public:
#define LAMBERTIAN_NUM_LIGHTS 2
LambertianRenderer() { _programID = 0; }
~LambertianRenderer() { shutdown(); }
int startup();
void shutdown() { if (_programID) glDeleteProgram(_programID); _programID = 0; }
int use();
/** Set all lights. We accommodate point lights or directional lights.
* These are specified in camera space, which we assume is fixed while the objects move.
* @param[in] locXYZW The location of the lights -- in camera space.
* The homogeneous coordinate W is used to choose between
* directional lights (W=0) and point lights (W=1).
* The result is undefined for other values of W.
* @param[in] colorRGB the emissive color of the light source, RGB in [0, 1].
* To turn a light off, set its emissive color to (0,0,0).
*/
void setLights(const float locXYZW[4 * LAMBERTIAN_NUM_LIGHTS], const float colorRGB[3 * LAMBERTIAN_NUM_LIGHTS]);
/* These take vertex and topology data in user-space buffers.
* @param[in] numPts The number of points in xyz or normals.
* @param[in] xyz The vertex locations {x, y, z}.
* @param[in] nrm The vertex normals {nx, ny, nz}.
* @param[in] numIndices The number of indices.
* @param[in] indices The indices. Note that three versions are given, where indices can be 1, 2, or 4 bytes.
* @param[in] M The modeling matrix. If NULL, the previous matrix will be used.
* @param[in] VP The viewing+projection matrix. If NULL, the previous matrix will be used.
* @param[in] Ka The ambient color {r, g, b}. If NULL, the previous ambient color will be used.
* @param[in] Kd The diffuse color {r, g, b}. If NULL, the previous diffuse color will be used.
*/
void drawTriMesh(unsigned numPts, const float* xyz, const float* nrm, unsigned numIndices, const unsigned char* indices,
const float* M = nullptr, const float* VP = nullptr, const float* Ka = nullptr, const float* Kd = nullptr) {
drawElements(numPts, xyz, nrm, GL_TRIANGLES, numIndices, GL_UNSIGNED_BYTE, indices, M, VP, Ka, Kd);
}
void drawTriMesh(unsigned numPts, const float* xyz, const float* nrm, unsigned numIndices, const unsigned short* indices,
const float* M = nullptr, const float* VP = nullptr, const float* Ka = nullptr, const float* Kd = nullptr) {
drawElements(numPts, xyz, nrm, GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices, M, VP, Ka, Kd);
}
void drawTriMesh(unsigned numPts, const float* xyz, const float* nrm, unsigned numIndices, const unsigned int* indices,
const float* M = nullptr, const float* VP = nullptr, const float* Ka = nullptr, const float* Kd = nullptr) {
drawElements(numPts, xyz, nrm, GL_TRIANGLES, numIndices, GL_UNSIGNED_INT, indices, M, VP, Ka, Kd);
}
/* These take vertex and topology data in GL buffer objects
* @param[in] vtxBuf the vertex buffer object identifier.
* @param[in] xyzOff the offset, in bytes, of the xyz positions in the vertex buffer.
* @param[in] nrmOff the offset, in bytes, of the normals in the vertex buffer.
* @param[in] numIndices the number of indices.
* @param[in] indexBuf the index buffer object identifier.
* @param[in] indexSize the byte size of the indices: 1, 2, or 4.
* @param[in] M The modeling matrix. If NULL, the previous matrix will be used.
* @param[in] VP The viewing+projection matrix. If NULL, the previous matrix will be used.
* @param[in] Ka The ambient color {r, g, b}. If NULL, the previous ambient color will be used.
* @param[in] Kd The diffuse color {r, g, b}. If NULL, the previous diffuse color will be used.
*/
void drawTriMesh(GLuint vtxBuf, unsigned xyzOff, unsigned rgbOff, unsigned numIndices, GLuint indexBuf, GLenum indexSize,
const float* M = nullptr, const float* VP = nullptr, const float* Ka = nullptr, const float* Kd = nullptr) {
drawElements(vtxBuf, xyzOff, rgbOff, GL_TRIANGLES, numIndices, indexSize, indexBuf, M, VP, Ka, Kd);
}
private:
/** Render geometry from user buffers.
* @param[in] numVertices The number of 3D vertices.
* @param[in] positions The array of 3D positions -- one for every vertex.
* @param[in] normals The array of normals -- one for every vertex.
* @param[in] graphicsMode One of { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }.
* GL_QUADS is not supported.
* @param[in] indexCount The number of 0-based vertex indices that define the geometry
* from the vertices and graphics mode.
* @param[in] indexType The type of index { GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, GL_UNSIGNED_INT }.
* @param[in] indices The array of vertices.
* @param[in] MV The modeling-viewing matrix.
* @param[in] P The projection matrix.
* @param[in] Ka The ambient color.
* @param[in] Kd The diffuse color.
*/
void drawElements(GLsizei numVertices, const GLfloat* positions, const GLfloat* normals,
GLenum graphicsMode, GLsizei indexCount, GLenum indexType, const GLvoid* indices,
const GLfloat M[4 * 4], const GLfloat VP[4 * 4], const float Ka[3], const float Kd[3]);
/** Render geometry from GL buffer objects.
* @param[in] vtxBuf The ID of the GL buffer used to store the vertices.
* @param[in] posOff The offset of the positions in the vertex buffer. This is typically 0,
* but is not restricted so.
* @param[in] nrmOff The offset of the normals in the vertex buffer. Both planar (homogeneous, separate)
* and chunky (nonhomogeneous, interleaved) representations are accommodated.
* @param[in] graphicsMode One of { GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN }.
* GL_QUADS is not supported.
* @param[in] numIndices The number of 0-based vertex indices that define the geometry
* from the vertices and graphics mode.
* @param[in] indexSize The size of index { 1, 2, 4 } in bytes.
* @param[in] indexBuf The ID of the GL buffer used to store the indices.
* @param[in] M The modeling matrix.
* @param[in] VP The viewing+projection matrix.
* @param[in] Ka The ambient color.
* @param[in] Kd The diffuse color.
*/
void drawElements(GLuint vtxBuf, unsigned posOff, unsigned nrmOff,
GLenum graphicsMode, GLsizei numIndices, unsigned indexSize, GLuint indexBuf,
const GLfloat M[4 * 4], const GLfloat VP[4 * 4], const float Ka[3], const float Kd[3]);
GLuint _programID;
GLint _MmatrixID, _VPmatrixID, _lightLoc, _lightColor, _ambientColorID, _diffuseColorID;
GLint _vtxPosID, _vtxNrmID;
static const char _vertexShader[], _fragmentShader[];
};
#endif /* __ARSHADERS_H__ */

View File

@@ -0,0 +1,106 @@
/*###############################################################################
#
# Copyright 2016-2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifndef __GLSPECTRUM_H
#define __GLSPECTRUM_H
////////////////////////////////////////////////////////////////////////////////
/// The representation used for spectral parameters in surface illumination transport.
////////////////////////////////////////////////////////////////////////////////
struct GLSpectrum3f {
float r, g, b; ///< Red, green and blue components of the color spectrum.
/// Default constructor.
GLSpectrum3f() {}
/// Initialization constructor.
/// @param[in] R the red component.
/// @param[in] G the green component.
/// @param[in] B the blue component.
GLSpectrum3f(float R, float G, float B) { set(R, G, B); }
/// Access to the array of spectral components.
/// @return a pointer to the array of spectral components.
const float* data() const { return &r; }
/// Access to the array of spectral components.
/// @return a pointer to the array of spectral components.
float* data() { return &r; }
/// Set the spectral components.
/// @param[in] R the red component.
/// @param[in] G the green component.
/// @param[in] B the blue component.
void set(float R, float G, float B) { r = R; g = G; b = B; };
/// Componentwise scaling of the spectrum.
/// @param[in] the scaling spectrum (RHS).
/// @return The LHS, scaled by the RHS.
GLSpectrum3f& operator*=(const GLSpectrum3f& k) { r *= k.r; g *= k.g; b *= k.b; return *this; }
/// Componentwise augmentation of the spectrum.
/// @param[in] the delta spectrum (RHS).
/// @return The LHS, augmented by the RHS.
GLSpectrum3f& operator+=(const GLSpectrum3f& k) { r += k.r; g += k.g; b += k.b; return *this; }
/// Scalar scaling of the spectrum.
/// @param[in] the scalar (RHS).
/// @return The LHS, scaled by the RHS.
GLSpectrum3f& operator*=(float s) { r *= s; g *= s; b *= s; return *this; }
/// Scalar scaling of the spectrum.
/// @param[in] the scalar (RHS).
/// @return The LHS, scaled by the RHS.
GLSpectrum3f& operator/=(float s) { r /= s; g /= s; b /= s; return *this; }
/// Componentwise scaling of the spectrum.
/// @param[in] k the scaling spectrum (RHS).
/// @return The componentwise product of the LHS and RHS.
GLSpectrum3f operator*(const GLSpectrum3f& k) const { return GLSpectrum3f(r * k.r, g * k.g, b * k.b); }
/// Componentwise augmentation of the spectrum.
/// @param[in] k the scale vector (RHS).
/// @return The componentwise sum of the LHS and RHS.
GLSpectrum3f operator+(const GLSpectrum3f& k) const { return GLSpectrum3f(r + k.r, g + k.g, b + k.b); }
/// Scalar scaling of the spectrum.
/// @param[in] s the scalar (RHS).
/// @return The product of the LHS and the RHS scalar.
GLSpectrum3f operator*(float s) const { return GLSpectrum3f(r * s, g * s, b * s); }
/// Scalar scaling of the spectrum.
/// @param[in] s the scalar (RHS).
/// @return The product of the LHS and the RHS scalar.
GLSpectrum3f operator/(float s) const { return GLSpectrum3f(r / s, g / s, b / s); }
};
/// Scalar scaling of the spectrum.
/// @param[in] s the scalar (LHS).
/// @param[in] k the spectrum to be scaled (RHS).
/// @return The product of the RHS and the scalar LHS.
inline GLSpectrum3f operator*(float s, const GLSpectrum3f& k) { return GLSpectrum3f(s * k.r, s * k.g, s * k.b); }
#endif // __GLSPECTRUM_H

View File

@@ -0,0 +1,645 @@
/*###############################################################################
#
# Copyright 2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#include "OpenGLMeshRenderer.h"
#include <cmath>
#include <cstring>
#include <string>
#include <vector>
#ifdef _MSC_VER
#include "glad/glad.h"
#define strcasecmp _stricmp
#else
#include <GLES3/gl3.h>
#endif // _MSC_VER
#include "FaceIO.h"
#include "GLFW/glfw3.h"
#include "glm/glm.hpp"
#include "glm/gtc/matrix_transform.hpp"
#include "glm/gtc/quaternion.hpp"
#include "GLMaterial.h"
#include "GLMesh.h"
#include "GLShaders.h"
#include "GLSpectrum.h"
#include "nvAR_defs.h"
#include "nvCVOpenCV.h"
#include "opencv2/highgui/highgui.hpp"
#include "SimpleFaceModel.h"
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
///// SUPPORT MACROS AND FUNCTIONS /////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#define BAIL_IF_ERR(err) do { if ((err) != 0) { goto bail; } } while(0)
#define BAIL(err, code) do { err = code; goto bail; } while(0)
#ifndef __BYTE_ORDER__ /* How bytes are packed into a 32 bit word */
#define __ORDER_LITTLE_ENDIAN__ 3210 /* First byte in the least significant position */
#define __ORDER_BIG_ENDIAN__ 0123 /* First byte in the most significant position */
#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_AMD64) || _MSC_VER
#define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__
#endif /* _MSC_VER */
#endif /* __BYTE_ORDER__ */
/********************************************************************************
* glfwErrorCallback
********************************************************************************/
static void glfwErrorCallback(int error, const char *description) {
fprintf(stderr, "Error %d: %s\n", error, description);
}
/********************************************************************************
* MakeGLContext
********************************************************************************/
static NvCV_Status MakeGLContext(int width, int height, const char *title, GLFWwindow **pWindow) {
NvCV_Status nvErr = NVCV_SUCCESS;
GLFWwindow *window;
/* Get a context */
glfwSetErrorCallback(glfwErrorCallback);
if (!glfwInit()) {
// Initialization failed
fprintf(stderr, "Unable to initialize glfw\n");
return NVCV_ERR_INITIALIZATION;
}
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
window = glfwCreateWindow(width, height, title, /*GLFWmonitor */NULL, /*GLFWwindow*/NULL);
if (!window) {
// Window or OpenGL context creation failed
fprintf(stderr, "Unable to create glfw window\n");
BAIL(nvErr, NVCV_ERR_INITIALIZATION);
}
int winWidth, winHeight;
glfwGetWindowSize(window, &winWidth, &winHeight);
if (winWidth != width || winHeight != height) {
fprintf(stderr, "getWindowSize(%u x %u) != (%u x %u)\n", winWidth, winHeight, width, height);
}
glfwMakeContextCurrent(window);
#ifdef _MSC_VER
if (!gladLoadGL()) {
fprintf(stderr, "Unable to load GL\n");
BAIL(nvErr, NVCV_ERR_INITIALIZATION);
}
fprintf(stderr, "OpenGL Version %d.%d loaded\n", GLVersion.major, GLVersion.minor);
#endif // _MSC_VER
*pWindow = window;
bail:
return nvErr;
}
/********************************************************************************
* CloseGLContext
********************************************************************************/
static void CloseGLContext(GLFWwindow *window) {
if (window)
glfwDestroyWindow(window);
glfwTerminate();
}
/********************************************************************************
* ComputeDualTopologyFromAdjacencies
********************************************************************************/
static NvCV_Status ComputeDualTopologyFromAdjacencies(const SimpleFaceModelAdapter *fma, GLMesh *mesh) {
union IVF {
unsigned i;
struct VF {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
unsigned short face, vertex; // Vertex in most significant position
#else // __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
unsigned short vertex, face; // Vertex in most significant position
#endif // __BYTE_ORDER__
} vf;
bool operator<( const IVF& other) { return i < other.i; }
bool operator==(const IVF& other) { return i == other.i; }
};
std::vector<IVF> topo;
topo.reserve(mesh->numVertices() * 6 * 2); // Assume valence-6, duplicated
const unsigned short *adjVertices = const_cast<SimpleFaceModelAdapter*>(fma)->getAdjacentVertices(0),
*adjFaces = const_cast<SimpleFaceModelAdapter*>(fma)->getAdjacentFaces(0);
unsigned n = fma->getAdjacentVerticesSize();
if (n != fma->getAdjacentFacesSize()) return NVCV_ERR_MISMATCH;
for (unsigned ej = 0; ej < n; ej += 2) { // 2 adjacencies per edge
for (unsigned vx = 0; vx < 2; ++vx) { // for every vertex on the edge
for (unsigned fc = 0; fc < 2; ++fc) { // and every face on the edge
IVF vf;
vf.vf.vertex = adjVertices[ej + vx];
vf.vf.face = adjFaces[ej + fc];
if (vf.vf.vertex && vf.vf.face) { // if a real vertex and a real face
--vf.vf.vertex; // convert from 1-based index ...
--vf.vf.face; // ... to 0-based index
topo.push_back(vf);
}
}
}
}
std::sort(topo.begin(), topo.end());
topo.erase(std::unique(topo.begin(), topo.end()), topo.end());
mesh->resizeDualIndices(unsigned(topo.size()));
unsigned short *dual = mesh->getDualIndices(),
*numFaces = mesh->getVertexFaceCounts();
memset(numFaces, 0, mesh->numVertices() * sizeof(*numFaces));
for (unsigned i = 0; i < topo.size(); ++i) {
numFaces[topo[i].vf.vertex]++;
dual[i] = topo[i].vf.face;
}
return NVCV_SUCCESS;
}
/********************************************************************************
* MakeMesh
********************************************************************************/
NvCV_Status MakeMesh(const SimpleFaceModelAdapter *fma, GLMesh *mesh) {
mesh->clear();
mesh->addVertices(fma->getShapeMeanSize() / 3, const_cast<SimpleFaceModelAdapter*>(fma)->getShapeMean(0));
mesh->addFaces(fma->getTriangleListSize() / 3, 3, const_cast<SimpleFaceModelAdapter*>(fma)->getTriangleList(0), 0, 0);
NvCV_Status err = ComputeDualTopologyFromAdjacencies(fma, mesh); // This make vertex normal computation lightning fast
if (NVCV_SUCCESS != err) return err;
mesh->computeVertexNormals();
if (fma->fm.partitions.size()) {
std::vector<GLMesh::Partition> parts(fma->fm.partitions.size());
for (unsigned i = unsigned(parts.size()); i--;) {
const SimpleFaceModel::Partition& fr = fma->fm.partitions[i];
GLMesh::Partition& to = parts[fr.partitionIndex];
//to.partitionIndex = fr.partitionIndex; // to doesn't have a partitionIndex
to.faceIndex = fr.faceIndex;
to.numFaces = fr.numFaces;
to.vertexIndex = fr.vertexIndex;
to.numVertexIndices = fr.numVertexIndices;
to.name = fr.name;
to.materialName = fr.materialName;
to.smooth = fr.smoothingGroup;
}
mesh->partitionMesh(unsigned(parts.size()), parts.data());
}
return NVCV_SUCCESS;
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
///// RENDER CONTEXT /////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
#define LAMBERTIAN_NUM_LIGHTS 2
class RenderContext {
public:
RenderContext() {
m_win = nullptr;
}
~RenderContext() {
if (m_win) CloseGLContext(m_win);
m_lam.shutdown();
m_txr.shutdown();
}
NvCV_Status init() {
if (0 != m_lam.startup())
return NVCV_ERR_OPENGL;
if (0 != m_txr.startup())
return NVCV_ERR_OPENGL;
return NVCV_SUCCESS;
}
void setClearColor(float r, float g, float b, float a = 1.f) { glClearColor(r, g, b, a); }
void setClearColor(unsigned char r, unsigned char g, unsigned char b) {
glClearColor(r * (1.f / 255.f), g * (1.f / 255.f), b * (1.f / 255.f), 1.f);
}
NvCV_Status makeWindowContext(int wd, int ht, const char *title) {
NvCV_Status err = MakeGLContext(wd, ht, title, &m_win);
if (NVCV_SUCCESS == err) {
m_width = wd;
m_height = ht;
glfwMakeContextCurrent(m_win);
glViewport(0, 0, wd, ht);
glEnable(GL_DEPTH_TEST);
glClearColor(0.f, 0.f, 0.f, 1.f);
if (/*FLAG_orientation*/0) {
glEnable(GL_CULL_FACE);
glCullFace((/*FLAG_orientation*/0 > 0) ? GL_BACK : GL_FRONT);
}
else {
glDisable(GL_CULL_FACE);
}
}
return err;
}
void computeInverseViewMatrix() {
#if 0
Vinv = glm::inverse(V);
#else // Inversion is simple because we know that it is a rigid transform
m_Vinv = glm::transpose(m_V);
m_Vinv[3][0] = -(m_Vinv[0][0] * m_V[3][0] + m_Vinv[1][0] * m_V[3][1] + m_Vinv[2][0] * m_V[3][2]);
m_Vinv[3][1] = -(m_Vinv[0][1] * m_V[3][0] + m_Vinv[1][1] * m_V[3][1] + m_Vinv[2][1] * m_V[3][2]);
m_Vinv[3][2] = -(m_Vinv[0][2] * m_V[3][0] + m_Vinv[1][2] * m_V[3][1] + m_Vinv[2][2] * m_V[3][2]);
m_Vinv[0][3] = 0.f; m_Vinv[1][3] = 0.f; m_Vinv[2][3] = 0.f; m_Vinv[3][3] = 1.f;
#endif
}
void setViewMatrix(const glm::mat4x4& viewMatrix) {
m_V = viewMatrix;
computeInverseViewMatrix();
}
void setViewMatrix(const glm::vec3& fromPoint, const glm::vec3& toPoint, const glm::vec3& upVector) {
m_V = glm::lookAt(fromPoint, toPoint, upVector);
computeInverseViewMatrix();
}
void setOrthoCamera(float hither, float yon) {
m_P = glm::orthoLH_NO(m_width * -.5f, m_width * +.5f, m_height * -.5f, m_height * +.5f, hither, yon);
}
void setOrthoCamera(float wd, float ht, float hither, float yon) {
m_P = glm::orthoLH_NO(wd * -.5f, wd * +.5f, ht * -.5f, ht * +.5f, hither, yon);
}
void setLights(const glm::vec4 locs[LAMBERTIAN_NUM_LIGHTS], const GLSpectrum3f colors[LAMBERTIAN_NUM_LIGHTS]) {
memcpy(m_lightLoc, locs, sizeof(m_lightLoc));
memcpy(m_lightColor, colors, sizeof(m_lightColor));
}
void setViewOfBound(const GLMesh::BoundingSphere& bsph, const glm::vec3& lookAt, const glm::vec3& up, float vfov,
float fracFill, float yDir = 1.f) {
float r = bsph.radius() / fracFill,
aspect = (float)m_width / (float)m_height,
signZ = -yDir,
dist;
if (vfov > 0) { // Perspective
dist = r * .5f / tanf(vfov * .5f);
m_P = glm::perspective(vfov, aspect, (dist - r) * 0.2f, (dist + r) * 2.0f);
}
else { // Orthographic
float w = r,
h = r;
if (aspect < 1.f) h /= aspect; // Wide
else w *= aspect; // Tall
dist = r * 2.f;
m_P = glm::orthoLH_NO(-w, +w, -h, +h, (dist - r) * signZ, (dist + r) * signZ);
}
m_V = glm::lookAt(bsph.center() - glm::normalize(lookAt) * dist, bsph.center(), up);
computeInverseViewMatrix();
}
void setViewOfBound(const GLMesh::BoundingBox& bbox, const glm::vec3& lookAt, const glm::vec3& up, float vfov,
float fracFill, float yDir = 1.f, float yOff = 0.f) {
glm::vec3 boxSize = bbox.max() - bbox.min();
glm::vec3 boxCenter = bbox.center();
float borderFrac = ((1.f - fracFill) / fracFill),
dx = boxSize.x * (1.f + borderFrac), // border on left and right
dy = boxSize.y * (1.f + borderFrac) * (1.f - fabsf(yOff)),
r = ((dx > dy) ? dx : dy), // radius of bounding sphere
aspectGeom = dx / dy,
aspectWind = (float)m_width / (float)m_height,
signZ = -yDir,
dist;
if (vfov > 0) { // Perspective
dist = r * .5f / tanf(vfov * .5f);
m_P = glm::perspective(vfov, aspectWind, dist - r, dist + r);
}
else { // Orthographic
if (aspectGeom > aspectWind) dy *= aspectGeom / aspectWind;
else dx *= aspectWind / aspectGeom;
dist = r * 2.f;
dx *= .5f;
dy *= .5f;
m_P = glm::orthoLH_NO(-dx, +dx, -dy, +dy, (dist - r) * signZ, (dist + r) * signZ);
}
boxCenter.y += boxSize.y * yOff;
m_V = glm::lookAt(boxCenter - glm::normalize(lookAt) * dist, boxCenter, up);
computeInverseViewMatrix();
}
NvCV_Status renderPolyMesh(const GLMesh& mesh, const glm::mat4x4& M, const char *materialOverride = nullptr) {
NvCV_Status nvErr = NVCV_SUCCESS;
glm::mat4x4 VP = m_P * m_V;
const GLSpectrum3f defaultDiffuse = { 0.77f, 0.63f, 0.55f },
defaultAmbient = defaultDiffuse * 0.3f;
#ifdef DEBUG_RENDERING
unsigned why = mesh.notRenderable(0);
if (why) {
if (FLAG_debug)
printf("Mesh %p is not renderable: %s: %s\n", &mesh,
((why & GLMesh::NOT_TRIMESH) ? "not a TriMesh" : ""),
((why & GLMesh::COMPLEX_TOPOLOGY) ? "complex topology" : "")
);
return keErrGeometry;
}
#endif // DEBUG_RENDERING
// Set lights for all shaders
m_lam.setLights(&m_lightLoc[0].x, m_lightColor[0].data()); // TODO: set this elsewhere
for (unsigned ix = 0, numPartitions = mesh.numPartitions(); ix < numPartitions; ++ix) {
GLMesh::Partition pt;
nvErr = mesh.getPartition(ix, pt);
BAIL_IF_ERR(nvErr);
const GLMaterial *mtl = m_mtlLib.getMaterial(materialOverride ? materialOverride : pt.materialName.c_str());
if (mesh.numNormals()) { // We should check for textures, too
const GLSpectrum3f *difColor, *ambColor;
if (mtl) {
difColor = &mtl->diffuseColor;
ambColor = &mtl->ambientColor;
}
else {
difColor = &defaultDiffuse;
ambColor = &defaultAmbient;
}
m_lam.drawTriMesh(mesh.numVertices(), &mesh.getVertices()->x, &mesh.getNormals()->x,
pt.numVertexIndices, mesh.getVertexIndices() + pt.vertexIndex, &M[0][0], &VP[0][0],
ambColor->data(), difColor->data());
}
}
bail:
return nvErr;
}
unsigned m_width, m_height; ///< The dimensions of the viewport.
GLFWwindow *m_win; ///< The window context.
GLMaterialLibrary m_mtlLib; ///< The material library.
glm::mat4x4 m_V, m_Vinv; ///< The viewing matrix and its inverse.
glm::mat4x4 m_P; ///< The projection matrix.
glm::vec4 m_lightLoc[LAMBERTIAN_NUM_LIGHTS]; ///< The light locations.
GLSpectrum3f m_lightColor[LAMBERTIAN_NUM_LIGHTS]; ///< The light colors.
LambertianRenderer m_lam; ///< The Lambertian renderer.
TextureRenderer m_txr; ///< The texture renderer.
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
///// OPENGL MESH RENDERER /////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
class OpenGLMeshRenderer : public MeshRenderer {
public:
~OpenGLMeshRenderer();
static NvCV_Status initDispatch(MeshRenderer::Dispatch *dispatch);
static NvCV_Status unload();
private:
OpenGLMeshRenderer();
SimpleFaceModelAdapter _sfma;
RenderContext _ctx;
GLMesh _mesh;
glm::vec3 _ctrRot;
// C-style object-oriented member functions that are usually loaded from DLL, although in this implementation
// the OpenGLMeshRenderer is compiled directly into the ExpressionApp, and the MeshRendererBroker is
// automatically adding it to its portfolio of renderers without creating a separate DLL.
static NvCV_Status create(MeshRenderer **han);
static void destroy(MeshRenderer *han);
static NvCV_Status name(const char **str);
static NvCV_Status info(const char **str);
static NvCV_Status read(MeshRenderer *han, const char *modelFile);
static NvCV_Status init(MeshRenderer *han, unsigned width, unsigned height, const char *windowName);
static NvCV_Status setCamera(MeshRenderer *han,
const float locPt[3], const float lookVec[3], const float upVec[3], float vfov);
static NvCV_Status render(MeshRenderer *han,
const float exprs[53], const float qrot[4], const float tran[3], NvCVImage *result);
NvCV_Status setFOV(float radians);
};
NvCV_Status OpenGLMeshRenderer_InitDispatch(MeshRenderer::Dispatch *dispatch) {
return OpenGLMeshRenderer::initDispatch(dispatch);
}
NvCV_Status OpenGLMeshRenderer_Unload() {
return OpenGLMeshRenderer::unload();
}
/********************************************************************************
* DeformModel
********************************************************************************/
static NvCV_Status DeformModel(const SimpleFaceModel& model, const float *identCoeffs, const float *exprCoeffs, GLMesh *mesh) {
unsigned size = unsigned(model.shapeMean.size()) * 3, // the number of floats in the mesh vector
numCoeffs, i;
float *const dst0 = &mesh->getVertices()->x, // begin
*const dst1 = dst0 + size; // end
float const *src;
float *dst, c;
memcpy(dst0, model.shapeMean.data(), size * sizeof(*dst0)); // Initialize
if (identCoeffs) {
for (i = 0, numCoeffs = unsigned(model.shapeEigenValues.size()), src = model.shapeModes.data()->vec; i < numCoeffs; ++i, ++identCoeffs) {
if ((c = *identCoeffs) != 0.f) {
for (dst = dst0; dst != dst1;)
*dst++ += *src++ * c;
}
else {
src += size;
}
}
}
for (i = 0, numCoeffs = unsigned(model.blendShapes.size()); i < numCoeffs; ++i, ++exprCoeffs) {
if ((c = *exprCoeffs) != 0.f) {
for (dst = dst0, src = model.blendShapes[i].shape.data()->vec; dst != dst1;)
*dst++ += *src++ * c;
}
}
mesh->computeVertexNormals();
return NVCV_SUCCESS;
}
OpenGLMeshRenderer::OpenGLMeshRenderer() {
/*NvCV_Status err =*/ (void)initDispatch(&this->m_dispatch);
}
OpenGLMeshRenderer::~OpenGLMeshRenderer() {
}
NvCV_Status OpenGLMeshRenderer::name(const char **str) {
static const char name[] = "OpenGL";
*str = name;
return NVCV_SUCCESS;
}
NvCV_Status OpenGLMeshRenderer::info(const char **str) {
static const char info[] = "OpenGL renderer using local illumination";
*str = info;
return NVCV_SUCCESS;
}
NvCV_Status OpenGLMeshRenderer::create(MeshRenderer **han) {
*han = new OpenGLMeshRenderer();
return NVCV_SUCCESS;
}
void OpenGLMeshRenderer::destroy(MeshRenderer* /*han*/) {
}
NvCV_Status OpenGLMeshRenderer::read(MeshRenderer *han, const char *modelFile) {
OpenGLMeshRenderer *ren = static_cast<OpenGLMeshRenderer*>(han);
size_t z = strlen(modelFile);
if (z < 5) return NVCV_ERR_FILE;
if (!strcasecmp(".nvf", modelFile + z - 4)) {
FaceIOErr ioErr = ReadNVFFaceModel(modelFile, &ren->_sfma); // TODO clear _sfma first
if (kIOErrNone != ioErr) {
printf("Error: \"%s\": %s\n", modelFile, FaceIOErrorStringFromCode(ioErr));
return NVCV_ERR_READ;
}
NvCV_Status nvErr;
nvErr = MakeMesh(&ren->_sfma, &ren->_mesh);
if (NVCV_SUCCESS != nvErr) return nvErr;
std::string mtlFile;
mtlFile.assign(modelFile, 0, strlen(modelFile) - 3);
mtlFile += "mtl";
nvErr = ren->_ctx.m_mtlLib.read(mtlFile.c_str());
unsigned why = ren->_mesh.notRenderable(0);
if (why) {
printf("Mesh \"%s\" is not renderable: %s: %s\n", modelFile,
((why & GLMesh::NOT_TRIMESH) ? "not a TriMesh" : ""),
((why & GLMesh::COMPLEX_TOPOLOGY) ? "complex topology" : "")
);
return NVCV_ERR_MISMATCH;
}
return NVCV_SUCCESS;
}
// else if (!strcasecmp(".obj", file + z - 4)) { read obj files }
else {
return NVCV_ERR_FILE;
}
}
NvCV_Status OpenGLMeshRenderer::init(MeshRenderer *han,
unsigned width, unsigned height, const char *windowName) {
OpenGLMeshRenderer *ren = static_cast<OpenGLMeshRenderer*>(han);
static const GLSpectrum3f lightColor[LAMBERTIAN_NUM_LIGHTS] = { { 1.f, 1.f, 1.f }, { .8f, .1f, .1f } };
static const glm::vec4 lightLoc[LAMBERTIAN_NUM_LIGHTS] = { { 0, 0, +1000, 0}, { 100, -200, -500, 0 } };
NvCV_Status nvErr;
nvErr = ren->_ctx.makeWindowContext(width, height, windowName);
nvErr = ren->_ctx.init();
ren->_ctx.setClearColor(0.2f, 0.2f, 0.2f, 1.f);
ren->_ctx.setLights(lightLoc, lightColor);
ren->setFOV(0.f); // Default orthographic
return NVCV_SUCCESS;
}
NvCV_Status OpenGLMeshRenderer::setFOV(float fov) {
if (0 == _mesh.numVertices())
return NVCV_ERR_MODEL;
GLMesh::BoundingBox bbox;
_mesh.getBoundingBox(&bbox);
_ctrRot = bbox.center();
_ctrRot.y = bbox.min().y; // Assume that assets are designed with Y-up.
float vShift = (_mesh.numVertices() > 10000) ? 0.15f : 0.0f; // Heuristic to determine whether there is a neck
_ctx.setViewOfBound(bbox, glm::vec3(0.f, 0.f, -1.f), glm::vec3(0.f, +1.f, 0.f), fov, .9f, +1, vShift);
return NVCV_SUCCESS;
}
NvCV_Status OpenGLMeshRenderer::setCamera(MeshRenderer *han,
const float locPt[3], const float lookVec[3], const float upVec[3], float vfov) {
if (locPt || lookVec || upVec) {} // We don't accommodate these yet
return static_cast<OpenGLMeshRenderer*>(han)->setFOV(vfov);
}
NvCV_Status OpenGLMeshRenderer::render(MeshRenderer *han,
const float exprs[53], const float qrot[4], const float* /*tran*/, NvCVImage *result) {
OpenGLMeshRenderer *ren = static_cast<OpenGLMeshRenderer*>(han);
NvCV_Status nvErr;
glm::mat4x4 M;
glm::quat q; // Convert quaternion from {x,y,z,w} --> GLM's {w,x,y,z}
if (NVCV_RGBA != result->pixelFormat)
return NVCV_ERR_PIXELFORMAT;
if (qrot) { q.x = qrot[0]; q.y = qrot[1]; q.z = qrot[2]; q.w = qrot[3]; }
else { q.x = 0.0f; q.y = 0.0f; q.z = 0.0f; q.w = 1.0f; }
#ifndef TRANSLATE_POSE
M = glm::translate(glm::mat4x4(1.f), -ren->_ctrRot);
M = glm::mat4_cast(q) * M;
M = glm::translate(M, ren->_ctrRot);
#else // TRANSLATE_POSE
M = glm::mat4_cast(q);
if (tran)
M = glm::translate(M, *((const glm::vec3*)(trans)));
#endif // TRANSLATE_POSE
nvErr = DeformModel(ren->_sfma.fm, nullptr, exprs, &ren->_mesh);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
nvErr = ren->_ctx.renderPolyMesh(ren->_mesh, M, NULL);
glReadPixels(0, 0, result->width, result->height, GL_RGBA, GL_UNSIGNED_BYTE, result->pixels);
GLenum glErr = glGetError();
if (glErr)
return NVCV_ERR_OPENGL;
// GL returns an image upside-down, but we can use the NvCVImage_FlipY in the caller to flip it with no overhead
return NVCV_SUCCESS;
}
NvCV_Status OpenGLMeshRenderer::initDispatch(MeshRenderer::Dispatch *dispatch) {
dispatch->name = &OpenGLMeshRenderer::name;
dispatch->info = &OpenGLMeshRenderer::info;
dispatch->create = &OpenGLMeshRenderer::create;
dispatch->destroy = &OpenGLMeshRenderer::destroy;
dispatch->read = &OpenGLMeshRenderer::read;
dispatch->init = &OpenGLMeshRenderer::init;
dispatch->setCamera = &OpenGLMeshRenderer::setCamera;
dispatch->render = &OpenGLMeshRenderer::render;
return NVCV_SUCCESS;
}
NvCV_Status OpenGLMeshRenderer::unload() {
return NVCV_SUCCESS;
}

View File

@@ -0,0 +1,41 @@
/*###############################################################################
#
# Copyright 2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifndef __OPENGL_MESH_RENDERER__
#define __OPENGL_MESH_RENDERER__
#include "MeshRenderer.h"
/// Initialize the renderer dispatch table.
/// @param[out] dispatch the dispatch table.
/// @return NVCV_SUCCESS if successful.
NvCV_Status OpenGLMeshRenderer_InitDispatch(MeshRenderer::Dispatch *dispatch);
/// Unload the OpenGL Mesh Renderer from memory.
/// @note Any previously initialized dispatch tables will be invalid.
/// @return NVCV_SUCCESS if successful.
NvCV_Status OpenGLMeshRenderer_Unload();
#endif // __OPENGL_MESH_RENDERER__

View File

@@ -0,0 +1,245 @@
/*###############################################################################
#
# Copyright 2021 NVIDIA Corporation
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of
# this software and associated documentation files (the "Software"), to deal in
# the Software without restriction, including without limitation the rights to
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
# the Software, and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
###############################################################################*/
#ifndef __SIMPLE_FACE_MODEL__
#define __SIMPLE_FACE_MODEL__
#include <stdint.h>
#include <vector>
#include <string>
#include "FaceIO.h"
#include "nvAR_defs.h"
/********************************************************************************
* SimpleFaceModel
********************************************************************************/
struct SimpleFaceModel {
std::vector<NvAR_Point3f> shapeMean;
std::vector<NvAR_Vector3f> shapeModes; /* shapeMean.size() * numModes */
std::vector<float> shapeEigenValues;
std::vector<NvAR_Vector3u16> triangles;
struct BlendShape {
std::string name;
std::vector<NvAR_Vector3f> shape;
};
std::vector<BlendShape> blendShapes;
struct Partition {
unsigned partitionIndex; ///< The index of the partition.
unsigned faceIndex; ///< The index of the first face in the partition.
unsigned numFaces; ///< The number of faces in the partition.
unsigned vertexIndex; ///< The index of the first topological vertex in the partition.
unsigned numVertexIndices; ///< The number of topological vertices in the partition.
int smoothingGroup; ///< Smoothing group > 0; no smoothing == 0; undefined < 0.
std::string name; ///< The name of the partition.
std::string materialName; ///< The name of the material assigned to the partition.
void set(unsigned partIx, unsigned firstFaceIndex, unsigned lastFaceIndex,
unsigned firstVertexIndex, unsigned lastVertexIndex, int smooth,
const char* partName = nullptr, const char* mtrlName = nullptr) {
partitionIndex = partIx;
smoothingGroup = smooth;
faceIndex = firstFaceIndex;
numFaces = lastFaceIndex - firstFaceIndex + 1;
vertexIndex = firstVertexIndex;
numVertexIndices = lastVertexIndex - firstVertexIndex + 1;
if (partName) name = partName;
if (mtrlName) materialName = mtrlName;
}
Partition(unsigned partIx, unsigned firstFaceIndex, unsigned lastFaceIndex,
unsigned firstVertexIndex, unsigned lastVertexIndex, int smooth,
const char* partName, const char* mtrlName) {
set(partIx, firstFaceIndex, lastFaceIndex, firstVertexIndex, lastVertexIndex, smooth, partName, mtrlName);
}
Partition() { set(0, 0, 0, 0, 0, -1, nullptr, nullptr); }
};
std::vector<Partition> partitions;
std::vector<unsigned short> ibugLandmarkMappings; /* 68 */
const unsigned short ibugRightContour[8] = { 1, 2, 3, 4, 5, 6, 7, 8 };
const unsigned short ibugLeftContour[8] = { 10, 11, 12, 13, 14, 15, 16, 17 };
std::vector<unsigned short> modelRightContour;
std::vector<unsigned short> modelLeftContour;
std::vector<unsigned short> adjacentFaces;
std::vector<unsigned short> adjacentVertices;
std::vector<unsigned short> nvlmLandmarks;
std::vector<unsigned short> nvlmRightContour;
std::vector<unsigned short> nvlmLeftContour;
void appendMode(const NvAR_Point3f* pts) {
size_t n = shapeMean.size(),
off = shapeModes.size();
shapeModes.resize(off + n);
float *to = shapeModes[off].vec; // Delta mode vector
const float *fr = &pts->x; // Mode points
const float *mn = &shapeMean[0].x; // Mean points
for (n *= 3; n--;) // 3D points
*to++ = *fr++ - *mn++; // Delta shape
}
void setBlendShape(unsigned i, const std::string& name, const NvAR_Point3f* pts) {
size_t n = shapeMean.size();
blendShapes[i].name = name;
blendShapes[i].shape.resize(n);
float *to = blendShapes[i].shape.data()->vec; // Delta mode vector
const float *fr = &pts->x; // Blendshape points
const float *mn = &shapeMean[0].x; // Mean points
for (n *= 3; n--;) // 3D points
*to++ = *fr++ - *mn++; // Delta shape
}
};
/********************************************************************************
* SimpleFaceModelAdapter
********************************************************************************/
class SimpleFaceModelAdapter : public FaceIOAdapter {
public:
SimpleFaceModel fm;
uint32_t getShapeMeanSize() const override { return unsigned(fm.shapeMean.size()) * 3; }
uint32_t getShapeModesSize() const override { return unsigned(fm.shapeModes.size()) * 3; }
uint32_t getShapeNumModes() const override { return unsigned(fm.shapeModes.size() / fm.shapeMean.size()); }
uint32_t getShapeEigenvaluesSize()const override { return unsigned(fm.shapeEigenValues.size()); }
float* getShapeMean(uint32_t size) override { if (size) fm.shapeMean.resize(size / 3);
return &fm.shapeMean.data()->x; };
float* getShapeModes(uint32_t modeSize, uint32_t numModes) override {
if (modeSize) fm.shapeModes.resize(modeSize / 3 * numModes); return fm.shapeModes.data()->vec; }
float* getShapeEigenvalues(uint32_t numModes) override { if (numModes) fm.shapeEigenValues.resize(numModes);
return fm.shapeEigenValues.data(); }
uint32_t getColorMeanSize() const override { return 0; }
uint32_t getColorModesSize() const override { return 0; }
uint32_t getColorNumModes() const override { return 0; }
uint32_t getColorEigenvaluesSize() const override { return 0; }
float* getColorMean(uint32_t /*size*/) override { return nullptr; }
float* getColorModes(uint32_t /*modeSize*/, uint32_t /*numModes*/) override { return nullptr; }
float* getColorEigenvalues(uint32_t /*numModes*/) override { return nullptr; }
void setTriangleListSize(uint32_t size) override { fm.triangles.resize(size / 3); }
uint32_t getTriangleListSize() const override { return unsigned(fm.triangles.size()) * 3; }
uint16_t* getTriangleList(uint32_t size) override { if (size) fm.triangles.resize(size / 3);
return fm.triangles.data()->vec; }
void setTextureCoordinatesSize(uint32_t /*size*/) override {}
uint32_t getTextureCoordinatesSize() const override { return 0; }
float* getTextureCoordinates(uint32_t /*size*/) override { return nullptr; }
void setNumBlendShapes(uint32_t n) override { fm.blendShapes.resize(n); }
void setBlendShapeName(uint32_t i, const char* name) override { fm.blendShapes[i].name = name; }
uint32_t getNumBlendShapes() const override { return unsigned(fm.blendShapes.size()); }
const char* getBlendShapeName(uint32_t i) const override { return fm.blendShapes[i].name.c_str(); }
uint32_t getBlendShapeSize(uint32_t i) const override { return unsigned((fm.blendShapes[i].shape.size()) * 3); }
float* getBlendShape(uint32_t i, uint32_t size) override { if (size) fm.blendShapes[i].shape.resize(size / 3);
return fm.blendShapes[i].shape.data()->vec; }
void setIbugLandmarkMappingsSize(uint32_t n) override { fm.ibugLandmarkMappings.resize(n); }
uint32_t getIbugLandmarkMappingsSize() const override { return unsigned(fm.ibugLandmarkMappings.size()); }
uint16_t* getIbugLandmarkMappings(uint32_t size) override { if (size) fm.ibugLandmarkMappings.resize(size);
return fm.ibugLandmarkMappings.data(); }
void appendIbugLandmarkMapping(uint16_t i) override { fm.ibugLandmarkMappings.push_back(i); }
void appendIbugLandmarkMapping(uint16_t i, uint16_t j) override { fm.ibugLandmarkMappings.push_back(i);
fm.ibugLandmarkMappings.push_back(j); }
void setIbugRightContourSize(uint32_t /*n*/) override {}
uint32_t getIbugRightContourSize() const override {
return sizeof(fm.ibugRightContour) / sizeof(fm.ibugRightContour[0]); }
uint16_t* getIbugRightContour(uint32_t /*size*/) override { return const_cast<uint16_t*>(fm.ibugRightContour); }
void appendIbugRightContour(uint16_t /*i*/) override {}
void setIbugLeftContourSize(uint32_t /*n*/) override {}
uint32_t getIbugLeftContourSize() const override { return sizeof(fm.ibugLeftContour)/sizeof(fm.ibugLeftContour[0]);}
uint16_t* getIbugLeftContour(uint32_t /*size*/) override { return const_cast<uint16_t*>(fm.ibugLeftContour); }
void appendIbugLeftContour(uint16_t /*i*/) override {}
void setModelRightContourSize(uint32_t n) override { fm.modelRightContour.resize(n); }
uint32_t getModelRightContourSize() const override { return unsigned(fm.modelRightContour.size()); }
uint16_t* getModelRightContour(uint32_t size) override { if (size) fm.modelRightContour.resize(size);
return fm.modelRightContour.data(); }
void appendModelRightContour(uint16_t i) override { fm.modelRightContour.push_back(i); }
void setModelLeftContourSize(uint32_t n) override { fm.modelLeftContour.resize(n); }
uint32_t getModelLeftContourSize() const override { return unsigned(fm.modelLeftContour.size()); }
uint16_t* getModelLeftContour(uint32_t size) override { if (size) fm.modelLeftContour.resize(size);
return fm.modelLeftContour.data(); }
void appendModelLeftContour(uint16_t i) override { fm.modelLeftContour.push_back(i); }
void setAdjacentFacesSize(uint32_t n) override { fm.adjacentFaces.resize(n); }
uint32_t getAdjacentFacesSize() const override { return unsigned(fm.adjacentFaces.size()); }
uint16_t* getAdjacentFaces(uint32_t size) override { if (size) fm.adjacentFaces.resize(size);
return fm.adjacentFaces.data(); }
void appendAdjacentFace(uint16_t i) override { fm.adjacentFaces.push_back(i); }
void appendAdjacentFaces(uint16_t i, uint16_t j) override { fm.adjacentFaces.push_back(i);
fm.adjacentFaces.push_back(j); }
void setAdjacentVerticesSize(uint32_t n) override { fm.adjacentVertices.resize(n); }
uint32_t getAdjacentVerticesSize() const override { return unsigned(fm.adjacentVertices.size()); }
uint16_t* getAdjacentVertices(uint32_t size) override { if (size) fm.adjacentVertices.resize(size);
return fm.adjacentVertices.data(); }
void appendAdjacentVertex(uint16_t i) override { fm.adjacentVertices.push_back(i); }
void appendAdjacentVertices(uint16_t i, uint16_t j) override { fm.adjacentVertices.push_back(i);
fm.adjacentVertices.push_back(j); }
void setNvlmLandmarksSize(uint32_t n) override { fm.nvlmLandmarks.resize(n); }
uint32_t getNvlmLandmarksSize() const override { return (uint32_t)fm.nvlmLandmarks.size(); }
uint16_t* getNvlmLandmarks(uint32_t size) override { if (size) fm.nvlmLandmarks.resize(size);
return fm.nvlmLandmarks.data(); }
void appendNvlmLandmark(uint16_t i) override { fm.nvlmLandmarks.push_back(i); }
void setNvlmRightContourSize(uint32_t n) override { fm.nvlmRightContour.resize(n); }
uint32_t getNvlmRightContourSize() const override { return (uint32_t)fm.nvlmRightContour.size(); }
uint16_t* getNvlmRightContour(uint32_t size) override { if (size) fm.nvlmRightContour.resize(size);
return fm.nvlmRightContour.data(); }
void appendNvlmRightContour(uint16_t i) override { fm.nvlmRightContour.push_back(i); }
void setNvlmLeftContourSize(uint32_t n) override { fm.nvlmLeftContour.resize(n); }
uint32_t getNvlmLeftContourSize() const override { return (uint32_t)fm.nvlmLeftContour.size(); }
uint16_t* getNvlmLeftContour(uint32_t size) override { if (size) fm.nvlmLeftContour.resize(size);
return fm.nvlmLeftContour.data(); }
void appendNvlmLeftContour(uint16_t i) override { fm.nvlmLeftContour.push_back(i); }
void setNumPartitions(uint32_t n) override { fm.partitions.resize(n); }
void setPartitionName(uint32_t i, const char* name) override { fm.partitions.at(i).name = name; }
void setPartitionMaterialName(uint32_t i, const char* name) override { fm.partitions.at(i).materialName = name;}
void setPartition(uint32_t i, uint32_t faceIndex, uint32_t numFaces, uint32_t vertexIndex, uint32_t numVertices,
int32_t smoothingGroup) override {
fm.partitions.at(i).set(i, faceIndex, faceIndex + numFaces - 1, vertexIndex,
vertexIndex + numVertices - 1, smoothingGroup);
}
uint32_t getNumPartitions() const override { return (uint32_t)fm.partitions.size(); }
const char* getPartitionName(uint32_t i) const override { return fm.partitions.at(i).name.c_str(); }
const char* getPartitionMaterialName(uint32_t i) const override { return fm.partitions.at(i).materialName.c_str(); }
int16_t getPartition(uint32_t i, uint32_t* faceIndex, uint32_t* numFaces, uint32_t* vertexIndex,
uint32_t* numVertices, int32_t* smoothingGroup) const override {
const SimpleFaceModel::Partition& pt = fm.partitions.at(i);
if (faceIndex) *faceIndex = pt.faceIndex;
if (numFaces) *numFaces = pt.numFaces;
if (vertexIndex) *vertexIndex = pt.vertexIndex;
if (numVertices) *numVertices = pt.numVertexIndices;
if (smoothingGroup) *smoothingGroup = pt.smoothingGroup;
return (int16_t)pt.partitionIndex;
}
};
#endif // __SIMPLE_FACE_MODEL__