Merge branch 'master' of https://github.com/highfidelity/hifi into gpuStreamizing

This commit is contained in:
ZappoMan 2014-12-24 10:25:41 -08:00
commit 02a1721185
36 changed files with 2254 additions and 251 deletions

View file

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

View file

@ -274,19 +274,11 @@ modelUploader = (function () {
}
}
if (view.string(0, 18) === "Kaydara FBX Binary") {
previousNodeFilename = "";
index = 27;
while (index < view.byteLength - 39 && !EOF) {
parseBinaryFBX();
}
} else {
readTextFBX();
}
}
function readModel() {

View file

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

View file

@ -3626,16 +3626,6 @@ void Application::setMenuShortcutsEnabled(bool enabled) {
setShortcutsEnabled(_window->menuBar(), enabled);
}
void Application::uploadModel(ModelType modelType) {
ModelUploader* uploader = new ModelUploader(modelType);
QThread* thread = new QThread();
thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit()));
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
uploader->connect(thread, SIGNAL(started()), SLOT(send()));
thread->start();
}
void Application::updateWindowTitle(){
QString buildVersion = " (build " + applicationVersion() + ")";
@ -4191,15 +4181,19 @@ void Application::toggleRunningScriptsWidget() {
}
void Application::uploadHead() {
uploadModel(HEAD_MODEL);
ModelUploader::uploadHead();
}
void Application::uploadSkeleton() {
uploadModel(SKELETON_MODEL);
ModelUploader::uploadSkeleton();
}
void Application::uploadAttachment() {
uploadModel(ATTACHMENT_MODEL);
ModelUploader::uploadAttachment();
}
void Application::uploadEntity() {
ModelUploader::uploadEntity();
}
void Application::openUrl(const QUrl& url) {

View file

@ -374,6 +374,7 @@ public slots:
void uploadHead();
void uploadSkeleton();
void uploadAttachment();
void uploadEntity();
void openUrl(const QUrl& url);
@ -459,8 +460,6 @@ private:
void setMenuShortcutsEnabled(bool enabled);
void uploadModel(ModelType modelType);
static void attachNewHeadToNode(Node *newNode);
static void* networkReceive(void* args); // network receive thread

View file

@ -189,10 +189,14 @@ Menu::Menu() :
SLOT(toggleAddressBar()));
addDisabledActionAndSeparator(fileMenu, "Upload Avatar Model");
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0, Application::getInstance(), SLOT(uploadHead()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0, Application::getInstance(), SLOT(uploadSkeleton()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadHead, 0,
Application::getInstance(), SLOT(uploadHead()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadSkeleton, 0,
Application::getInstance(), SLOT(uploadSkeleton()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadAttachment, 0,
Application::getInstance(), SLOT(uploadAttachment()));
Application::getInstance(), SLOT(uploadAttachment()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::UploadEntity, 0,
Application::getInstance(), SLOT(uploadEntity()));
addDisabledActionAndSeparator(fileMenu, "Settings");
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsImport, 0, this, SLOT(importSettings()));
addActionToQMenuAndActionHash(fileMenu, MenuOption::SettingsExport, 0, this, SLOT(exportSettings()));

View file

@ -482,6 +482,7 @@ namespace MenuOption {
const QString TransmitterDrive = "Transmitter Drive";
const QString TurnWithHead = "Turn using Head";
const QString UploadAttachment = "Upload Attachment Model";
const QString UploadEntity = "Upload Entity Model";
const QString UploadHead = "Upload Head Model";
const QString UploadSkeleton = "Upload Skeleton Model";
const QString UserInterface = "User Interface";

View file

@ -55,7 +55,8 @@ static const QString MODEL_URL = "/api/v1/models";
static const QString SETTING_NAME = "LastModelUploadLocation";
static const int MAX_SIZE = 10 * 1024 * 1024; // 10 MB
static const int BYTES_PER_MEGABYTES = 1024 * 1024;
static const unsigned long MAX_SIZE = 50 * 1024 * BYTES_PER_MEGABYTES; // 50 GB (Virtually remove limit)
static const int MAX_TEXTURE_SIZE = 1024;
static const int TIMEOUT = 1000;
static const int MAX_CHECK = 30;
@ -63,6 +64,32 @@ static const int MAX_CHECK = 30;
static const int QCOMPRESS_HEADER_POSITION = 0;
static const int QCOMPRESS_HEADER_SIZE = 4;
void ModelUploader::uploadModel(ModelType modelType) {
ModelUploader* uploader = new ModelUploader(modelType);
QThread* thread = new QThread();
thread->connect(uploader, SIGNAL(destroyed()), SLOT(quit()));
thread->connect(thread, SIGNAL(finished()), SLOT(deleteLater()));
uploader->connect(thread, SIGNAL(started()), SLOT(send()));
thread->start();
}
void ModelUploader::uploadHead() {
uploadModel(HEAD_MODEL);
}
void ModelUploader::uploadSkeleton() {
uploadModel(SKELETON_MODEL);
}
void ModelUploader::uploadAttachment() {
uploadModel(ATTACHMENT_MODEL);
}
void ModelUploader::uploadEntity() {
uploadModel(ENTITY_MODEL);
}
ModelUploader::ModelUploader(ModelType modelType) :
_lodCount(-1),
_texturesCount(-1),
@ -148,6 +175,91 @@ bool ModelUploader::zip() {
FBXGeometry geometry = readFBX(fbxContents, QVariantHash());
// make sure we have some basic mappings
populateBasicMapping(mapping, filename, geometry);
// open the dialog to configure the rest
ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry);
if (properties.exec() == QDialog::Rejected) {
return false;
}
mapping = properties.getMapping();
QByteArray nameField = mapping.value(NAME_FIELD).toByteArray();
QString urlBase;
if (!nameField.isEmpty()) {
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
textPart.setBody(nameField);
_dataMultiPart->append(textPart);
urlBase = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField;
_url = urlBase + ".fst";
} else {
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("Model name is missing in the .fst file."),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Model name is missing in the .fst file.");
return false;
}
QByteArray texdirField = mapping.value(TEXDIR_FIELD).toByteArray();
QString texDir;
_textureBase = urlBase + "/textures/";
if (!texdirField.isEmpty()) {
texDir = basePath + "/" + texdirField;
QFileInfo texInfo(texDir);
if (!texInfo.exists() || !texInfo.isDir()) {
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("Texture directory could not be found."),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Texture directory could not be found.");
return false;
}
}
QVariantHash lodField = mapping.value(LOD_FIELD).toHash();
for (QVariantHash::const_iterator it = lodField.constBegin(); it != lodField.constEnd(); it++) {
QFileInfo lod(basePath + "/" + it.key());
if (!lod.exists() || !lod.isFile()) { // Check existence
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("LOD file %1 could not be found.").arg(lod.fileName()),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
}
// Compress and copy
if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
return false;
}
}
// Write out, compress and copy the fst
if (!addPart(*fst, writeMapping(mapping), QString("fst"))) {
return false;
}
// Compress and copy the fbx
if (!addPart(fbx, fbxContents, "fbx")) {
return false;
}
if (!addTextures(texDir, geometry)) {
return false;
}
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
" name=\"model_category\"");
textPart.setBody(MODEL_TYPE_NAMES[_modelType]);
_dataMultiPart->append(textPart);
_readyToSend = true;
return true;
}
void ModelUploader::populateBasicMapping(QVariantHash& mapping, QString filename, FBXGeometry geometry) {
if (!mapping.contains(NAME_FIELD)) {
mapping.insert(NAME_FIELD, QFileInfo(filename).baseName());
}
@ -162,11 +274,11 @@ bool ModelUploader::zip() {
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
if (!joints.contains("jointEyeLeft")) {
joints.insert("jointEyeLeft", geometry.jointIndices.contains("jointEyeLeft") ? "jointEyeLeft" :
(geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye"));
(geometry.jointIndices.contains("EyeLeft") ? "EyeLeft" : "LeftEye"));
}
if (!joints.contains("jointEyeRight")) {
joints.insert("jointEyeRight", geometry.jointIndices.contains("jointEyeRight") ? "jointEyeRight" :
geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
geometry.jointIndices.contains("EyeRight") ? "EyeRight" : "RightEye");
}
if (!joints.contains("jointNeck")) {
joints.insert("jointNeck", geometry.jointIndices.contains("jointNeck") ? "jointNeck" : "Neck");
@ -250,87 +362,6 @@ bool ModelUploader::zip() {
blendshapes.insertMulti("Sneer", QVariantList() << "Squint_Right" << 0.5);
mapping.insert(BLENDSHAPE_FIELD, blendshapes);
}
// open the dialog to configure the rest
ModelPropertiesDialog properties(_modelType, mapping, basePath, geometry);
if (properties.exec() == QDialog::Rejected) {
return false;
}
mapping = properties.getMapping();
QByteArray nameField = mapping.value(NAME_FIELD).toByteArray();
QString urlBase;
if (!nameField.isEmpty()) {
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"model_name\"");
textPart.setBody(nameField);
_dataMultiPart->append(textPart);
urlBase = S3_URL + "/models/" + MODEL_TYPE_NAMES[_modelType] + "/" + nameField;
_url = urlBase + ".fst";
} else {
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("Model name is missing in the .fst file."),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Model name is missing in the .fst file.");
return false;
}
QByteArray texdirField = mapping.value(TEXDIR_FIELD).toByteArray();
QString texDir;
_textureBase = urlBase + "/textures/";
if (!texdirField.isEmpty()) {
texDir = basePath + "/" + texdirField;
QFileInfo texInfo(texDir);
if (!texInfo.exists() || !texInfo.isDir()) {
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("Texture directory could not be found."),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Texture directory could not be found.");
return false;
}
}
QVariantHash lodField = mapping.value(LOD_FIELD).toHash();
for (QVariantHash::const_iterator it = lodField.constBegin(); it != lodField.constEnd(); it++) {
QFileInfo lod(basePath + "/" + it.key());
if (!lod.exists() || !lod.isFile()) { // Check existence
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("LOD file %1 could not be found.").arg(lod.fileName()),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("FBX file %1 could not be found.").arg(lod.fileName());
}
// Compress and copy
if (!addPart(lod.filePath(), QString("lod%1").arg(++_lodCount))) {
return false;
}
}
// Write out, compress and copy the fst
if (!addPart(*fst, writeMapping(mapping), QString("fst"))) {
return false;
}
// Compress and copy the fbx
if (!addPart(fbx, fbxContents, "fbx")) {
return false;
}
if (!addTextures(texDir, geometry)) {
return false;
}
QHttpPart textPart;
textPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;"
" name=\"model_category\"");
textPart.setBody(MODEL_TYPE_NAMES[_modelType]);
_dataMultiPart->append(textPart);
_readyToSend = true;
return true;
}
void ModelUploader::send() {
@ -590,9 +621,9 @@ bool ModelUploader::addPart(const QFile& file, const QByteArray& contents, const
if (_totalSize > MAX_SIZE) {
QMessageBox::warning(NULL,
QString("ModelUploader::zip()"),
QString("Model too big, over %1 Bytes.").arg(MAX_SIZE),
QString("Model too big, over %1 MB.").arg(MAX_SIZE / BYTES_PER_MEGABYTES),
QMessageBox::Ok);
qDebug() << "[Warning] " << QString("Model too big, over %1 Bytes.").arg(MAX_SIZE);
qDebug() << "[Warning] " << QString("Model too big, over %1 MB.").arg(MAX_SIZE / BYTES_PER_MEGABYTES);
return false;
}
qDebug() << "Current model size: " << _totalSize;
@ -613,8 +644,8 @@ ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariant
_modelType(modelType),
_originalMapping(originalMapping),
_basePath(basePath),
_geometry(geometry) {
_geometry(geometry)
{
setWindowTitle("Set Model Properties");
QFormLayout* form = new QFormLayout();
@ -629,33 +660,35 @@ ModelPropertiesDialog::ModelPropertiesDialog(ModelType modelType, const QVariant
_scale->setMaximum(FLT_MAX);
_scale->setSingleStep(0.01);
if (_modelType == ATTACHMENT_MODEL) {
QHBoxLayout* translation = new QHBoxLayout();
form->addRow("Translation:", translation);
translation->addWidget(_translationX = createTranslationBox());
translation->addWidget(_translationY = createTranslationBox());
translation->addWidget(_translationZ = createTranslationBox());
form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox());
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
_pivotAboutCenter->setChecked(true);
} else {
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
form->addRow("Neck Joint:", _neckJoint = createJointBox());
}
if (_modelType == SKELETON_MODEL) {
form->addRow("Root Joint:", _rootJoint = createJointBox());
form->addRow("Lean Joint:", _leanJoint = createJointBox());
form->addRow("Head Joint:", _headJoint = createJointBox());
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
_freeJoints->addWidget(newFreeJoint);
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
if (_modelType != ENTITY_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
QHBoxLayout* translation = new QHBoxLayout();
form->addRow("Translation:", translation);
translation->addWidget(_translationX = createTranslationBox());
translation->addWidget(_translationY = createTranslationBox());
translation->addWidget(_translationZ = createTranslationBox());
form->addRow("Pivot About Center:", _pivotAboutCenter = new QCheckBox());
form->addRow("Pivot Joint:", _pivotJoint = createJointBox());
connect(_pivotAboutCenter, SIGNAL(toggled(bool)), SLOT(updatePivotJoint()));
_pivotAboutCenter->setChecked(true);
} else {
form->addRow("Left Eye Joint:", _leftEyeJoint = createJointBox());
form->addRow("Right Eye Joint:", _rightEyeJoint = createJointBox());
form->addRow("Neck Joint:", _neckJoint = createJointBox());
}
if (_modelType == SKELETON_MODEL) {
form->addRow("Root Joint:", _rootJoint = createJointBox());
form->addRow("Lean Joint:", _leanJoint = createJointBox());
form->addRow("Head Joint:", _headJoint = createJointBox());
form->addRow("Left Hand Joint:", _leftHandJoint = createJointBox());
form->addRow("Right Hand Joint:", _rightHandJoint = createJointBox());
form->addRow("Free Joints:", _freeJoints = new QVBoxLayout());
QPushButton* newFreeJoint = new QPushButton("New Free Joint");
_freeJoints->addWidget(newFreeJoint);
connect(newFreeJoint, SIGNAL(clicked(bool)), SLOT(createNewFreeJoint()));
}
}
QDialogButtonBox* buttons = new QDialogButtonBox(QDialogButtonBox::Ok |
@ -683,38 +716,40 @@ QVariantHash ModelPropertiesDialog::getMapping() const {
}
mapping.insert(JOINT_INDEX_FIELD, jointIndices);
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
if (_modelType == ATTACHMENT_MODEL) {
glm::vec3 pivot;
if (_pivotAboutCenter->isChecked()) {
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
} else if (_pivotJoint->currentIndex() != 0) {
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
if (_modelType != ENTITY_MODEL) {
QVariantHash joints = mapping.value(JOINT_FIELD).toHash();
if (_modelType == ATTACHMENT_MODEL) {
glm::vec3 pivot;
if (_pivotAboutCenter->isChecked()) {
pivot = (_geometry.meshExtents.minimum + _geometry.meshExtents.maximum) * 0.5f;
} else if (_pivotJoint->currentIndex() != 0) {
pivot = extractTranslation(_geometry.joints.at(_pivotJoint->currentIndex() - 1).transform);
}
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
} else {
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
}
mapping.insert(TRANSLATION_X_FIELD, -pivot.x * _scale->value() + _translationX->value());
mapping.insert(TRANSLATION_Y_FIELD, -pivot.y * _scale->value() + _translationY->value());
mapping.insert(TRANSLATION_Z_FIELD, -pivot.z * _scale->value() + _translationZ->value());
} else {
insertJointMapping(joints, "jointEyeLeft", _leftEyeJoint->currentText());
insertJointMapping(joints, "jointEyeRight", _rightEyeJoint->currentText());
insertJointMapping(joints, "jointNeck", _neckJoint->currentText());
}
if (_modelType == SKELETON_MODEL) {
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
insertJointMapping(joints, "jointHead", _headJoint->currentText());
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
mapping.remove(FREE_JOINT_FIELD);
for (int i = 0; i < _freeJoints->count() - 1; i++) {
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
if (_modelType == SKELETON_MODEL) {
insertJointMapping(joints, "jointRoot", _rootJoint->currentText());
insertJointMapping(joints, "jointLean", _leanJoint->currentText());
insertJointMapping(joints, "jointHead", _headJoint->currentText());
insertJointMapping(joints, "jointLeftHand", _leftHandJoint->currentText());
insertJointMapping(joints, "jointRightHand", _rightHandJoint->currentText());
mapping.remove(FREE_JOINT_FIELD);
for (int i = 0; i < _freeJoints->count() - 1; i++) {
QComboBox* box = static_cast<QComboBox*>(_freeJoints->itemAt(i)->widget()->layout()->itemAt(0)->widget());
mapping.insertMulti(FREE_JOINT_FIELD, box->currentText());
}
}
mapping.insert(JOINT_FIELD, joints);
}
mapping.insert(JOINT_FIELD, joints);
return mapping;
}
@ -729,32 +764,35 @@ void ModelPropertiesDialog::reset() {
_scale->setValue(_originalMapping.value(SCALE_FIELD).toDouble());
QVariantHash jointHash = _originalMapping.value(JOINT_FIELD).toHash();
if (_modelType == ATTACHMENT_MODEL) {
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
_translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble());
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
_pivotAboutCenter->setChecked(true);
_pivotJoint->setCurrentIndex(0);
} else {
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
}
if (_modelType == SKELETON_MODEL) {
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
setJointText(_leanJoint, jointHash.value("jointLean").toString());
setJointText(_headJoint, jointHash.value("jointHead").toString());
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
while (_freeJoints->count() > 1) {
delete _freeJoints->itemAt(0)->widget();
if (_modelType != ENTITY_MODEL) {
if (_modelType == ATTACHMENT_MODEL) {
_translationX->setValue(_originalMapping.value(TRANSLATION_X_FIELD).toDouble());
_translationY->setValue(_originalMapping.value(TRANSLATION_Y_FIELD).toDouble());
_translationZ->setValue(_originalMapping.value(TRANSLATION_Z_FIELD).toDouble());
_pivotAboutCenter->setChecked(true);
_pivotJoint->setCurrentIndex(0);
} else {
setJointText(_leftEyeJoint, jointHash.value("jointEyeLeft").toString());
setJointText(_rightEyeJoint, jointHash.value("jointEyeRight").toString());
setJointText(_neckJoint, jointHash.value("jointNeck").toString());
}
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
QString jointName = joint.toString();
if (_geometry.jointIndices.contains(jointName)) {
createNewFreeJoint(jointName);
if (_modelType == SKELETON_MODEL) {
setJointText(_rootJoint, jointHash.value("jointRoot").toString());
setJointText(_leanJoint, jointHash.value("jointLean").toString());
setJointText(_headJoint, jointHash.value("jointHead").toString());
setJointText(_leftHandJoint, jointHash.value("jointLeftHand").toString());
setJointText(_rightHandJoint, jointHash.value("jointRightHand").toString());
while (_freeJoints->count() > 1) {
delete _freeJoints->itemAt(0)->widget();
}
foreach (const QVariant& joint, _originalMapping.values(FREE_JOINT_FIELD)) {
QString jointName = joint.toString();
if (_geometry.jointIndices.contains(jointName)) {
createNewFreeJoint(jointName);
}
}
}
}

View file

@ -33,13 +33,15 @@ class ModelUploader : public QObject {
Q_OBJECT
public:
ModelUploader(ModelType type);
~ModelUploader();
static void uploadModel(ModelType modelType);
public slots:
void send();
static void uploadHead();
static void uploadSkeleton();
static void uploadAttachment();
static void uploadEntity();
private slots:
void send();
void checkJSON(QNetworkReply& requestReply);
void uploadUpdate(qint64 bytesSent, qint64 bytesTotal);
void uploadSuccess(QNetworkReply& requestReply);
@ -48,12 +50,21 @@ private slots:
void processCheck();
private:
ModelUploader(ModelType type);
~ModelUploader();
void populateBasicMapping(QVariantHash& mapping, QString filename, FBXGeometry geometry);
bool zip();
bool addTextures(const QString& texdir, const FBXGeometry& geometry);
bool addPart(const QString& path, const QString& name, bool isTexture = false);
bool addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture = false);
QString _url;
QString _textureBase;
QSet<QByteArray> _textureFilenames;
int _lodCount;
int _texturesCount;
int _totalSize;
unsigned long _totalSize;
ModelType _modelType;
bool _readyToSend;
@ -64,12 +75,6 @@ private:
QDialog* _progressDialog;
QProgressBar* _progressBar;
bool zip();
bool addTextures(const QString& texdir, const FBXGeometry& geometry);
bool addPart(const QString& path, const QString& name, bool isTexture = false);
bool addPart(const QFile& file, const QByteArray& contents, const QString& name, bool isTexture = false);
};
/// A dialog that allows customization of various model properties.

View file

@ -116,9 +116,8 @@ void AddressBarDialog::setupUI() {
void AddressBarDialog::showEvent(QShowEvent* event) {
_goButton->setIcon(QIcon(PathUtils::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON));
_addressLineEdit->setText(AddressManager::getInstance().currentAddress().toString());
_addressLineEdit->setText(QString());
_addressLineEdit->setFocus();
_addressLineEdit->selectAll();
FramelessDialog::showEvent(event);
}

View file

@ -12,16 +12,19 @@
#include <QDialog>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QFileInfo>
#include <QHeaderView>
#include <QLineEdit>
#include <QMessageBox>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QThread>
#include <QUrl>
#include <qurlquery.h>
#include <QUrlQuery>
#include <QXmlStreamReader>
#include <NetworkAccessManager.h>
#include "Application.h"
#include "ModelsBrowser.h"
const char* MODEL_TYPE_NAMES[] = { "entities", "heads", "skeletons", "attachments" };

View file

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

View file

@ -5,7 +5,7 @@ setup_hifi_library(Network Script)
include_glm()
link_hifi_libraries(audio shared octree voxels networking physics fbx)
link_hifi_libraries(audio shared octree voxels networking physics gpu model fbx)
# call macro to include our dependency includes and bubble them up via a property on our target
include_dependency_includes()

View file

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

View file

@ -144,6 +144,13 @@ void EntityCollisionSystem::updateCollisionWithEntities(EntityItem* entityA) {
penetration = collision->_penetration;
entityB = static_cast<EntityItem*>(collision->_extraData);
// The collision _extraData should be a valid entity, but if for some reason
// it's NULL then continue with a warning.
if (!entityB) {
qDebug() << "UNEXPECTED - we have a collision with missing _extraData. Something went wrong down below!";
continue; // skip this loop pass if the entity is NULL
}
// don't collide entities with unknown IDs,
if (!entityB->isKnownID()) {
continue; // skip this loop pass if the entity has an unknown ID

View file

@ -585,8 +585,12 @@ bool EntityTreeElement::findShapeCollisions(const Shape* shape, CollisionList& c
if (shape != otherCollisionShape && !ignoreForCollisions) {
if (ShapeCollider::collideShapes(shape, otherCollisionShape, collisions)) {
CollisionInfo* lastCollision = collisions.getLastCollision();
lastCollision->_extraData = entity;
atLeastOneCollision = true;
if (lastCollision) {
lastCollision->_extraData = entity;
atLeastOneCollision = true;
} else {
qDebug() << "UNEXPECTED - ShapeCollider::collideShapes() returned true, but no lastCollision.";
}
}
}
++entityItr;

View file

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

View file

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

View file

@ -95,7 +95,7 @@ Resource::Sysmem::~Sysmem() {
Resource::Size Resource::Sysmem::allocate(Size size) {
if (size != _size) {
Byte* newData = 0;
Byte* newData = NULL;
Size newSize = 0;
if (size > 0) {
Size allocated = allocateMemory(&newData, size);
@ -116,7 +116,7 @@ Resource::Size Resource::Sysmem::allocate(Size size) {
Resource::Size Resource::Sysmem::resize(Size size) {
if (size != _size) {
Byte* newData = 0;
Byte* newData = NULL;
Size newSize = 0;
if (size > 0) {
Size allocated = allocateMemory(&newData, size);
@ -196,11 +196,11 @@ Buffer& Buffer::operator=(const Buffer& buf) {
Buffer::~Buffer() {
if (_sysmem) {
delete _sysmem;
_sysmem = 0;
_sysmem = NULL;
}
if (_gpuObject) {
delete _gpuObject;
_gpuObject = 0;
_gpuObject = NULL;
}
}

View file

@ -82,10 +82,8 @@ protected:
inline const Byte* readData() const { return _data; }
inline Byte* editData() { _stamp++; return _data; }
template< typename T >
const T* read() const { return reinterpret_cast< T* > ( _data ); }
template< typename T >
T* edit() { _stamp++; return reinterpret_cast< T* > ( _data ); }
template< typename T > const T* read() const { return reinterpret_cast< T* > ( _data ); }
template< typename T > T* edit() { _stamp++; return reinterpret_cast< T* > ( _data ); }
// Access the current version of the sysmem, used to compare if copies are in sync
inline Stamp getStamp() const { return _stamp; }
@ -210,11 +208,7 @@ public:
//Template iterator with random access on the buffer sysmem
template<typename T>
class Iterator : public std::iterator<std::random_access_iterator_tag,
T,
Index,
T*,
T&>
class Iterator : public std::iterator<std::random_access_iterator_tag, T, Index, T*, T&>
{
public:

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

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

View file

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

View file

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

View file

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

View file

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

View file

@ -189,6 +189,9 @@ void OctreePersistThread::backup() {
quint64 intervalToBackup = _backupInterval * MSECS_TO_USECS;
if (sinceLastBackup > intervalToBackup) {
qDebug() << "Time since last backup [" << sinceLastBackup << "] exceeds backup interval ["
<< intervalToBackup << "] doing backup now...";
struct tm* localTime = localtime(&_lastPersistTime);
QString backupFileName;
@ -213,6 +216,8 @@ void OctreePersistThread::backup() {
} else {
qDebug() << "ERROR in backing up persist file...";
}
_lastBackup = now;
}
}
}

View file

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

View file

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

View file

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

View file

@ -63,20 +63,20 @@ public:
void setIdentity();
const Vec3& getTranslation() const;
void setTranslation(const Vec3& translation);
void preTranslate(const Vec3& translation);
void postTranslate(const Vec3& translation);
void setTranslation(const Vec3& translation); // [new this] = [translation] * [this.rotation] * [this.scale]
void preTranslate(const Vec3& translation); // [new this] = [translation] * [this]
void postTranslate(const Vec3& translation); // [new this] = [this] * [translation] equivalent to glTranslate
const Quat& getRotation() const;
void setRotation(const Quat& rotation);
void preRotate(const Quat& rotation);
void postRotate(const Quat& rotation);
void setRotation(const Quat& rotation); // [new this] = [this.translation] * [rotation] * [this.scale]
void preRotate(const Quat& rotation); // [new this] = [rotation] * [this]
void postRotate(const Quat& rotation); // [new this] = [this] * [rotation] equivalent to glRotate
const Vec3& getScale() const;
void setScale(float scale);
void setScale(const Vec3& scale);
void postScale(float scale);
void postScale(const Vec3& scale);
void setScale(const Vec3& scale); // [new this] = [this.translation] * [this.rotation] * [scale]
void postScale(float scale); // [new this] = [this] * [scale] equivalent to glScale
void postScale(const Vec3& scale); // [new this] = [this] * [scale] equivalent to glScale
bool isIdentity() const { return (_flags & ~Flags(FLAG_CACHE_INVALID_BITSET)).none(); }
bool isTranslating() const { return _flags[FLAG_TRANSLATION]; }

View file

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

View file

@ -2,3 +2,4 @@
add_subdirectory(bitstream2json)
add_subdirectory(json2bitstream)
add_subdirectory(mtc)
add_subdirectory(scribe)

5
tools/scribe/CMakeLists.txt Executable file
View file

@ -0,0 +1,5 @@
set(TARGET_NAME scribe)
setup_hifi_project()
# call macro to include our dependency includes and bubble them up via a property on our target
include_dependency_includes()

989
tools/scribe/src/TextTemplate.cpp Executable file
View file

@ -0,0 +1,989 @@
//
// TextTemplate.cpp
// tools/shaderScribe/src
//
// Created by Sam Gateau on 12/15/2014.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#include "TextTemplate.h"
#include <stdarg.h>
#include <fstream>
#include <sstream>
#include <algorithm>
typedef TextTemplate::Block::Pointer BlockPointer;
typedef TextTemplate::Config::Pointer ConfigPointer;
typedef TextTemplate::Pointer TextTemplatePointer;
//-----------------------------------------------------------------------------
TextTemplate::Config::Config() :
_includes(),
_funcs(),
_logStream(&std::cout),
_numErrors(0),
_includerCallback(TextTemplate::loadFile) {
_paths.push_back("");
}
const TextTemplatePointer TextTemplate::Config::addInclude(const ConfigPointer& config, const char* include) {
if (!config) {
return TextTemplatePointer();
}
TextTemplatePointer included = config->findInclude(include);
if (included) {
return included;
}
// INcluded doest exist yet so let's try to create it
String includeStream;
if (config->_includerCallback(config, include, includeStream)) {
// ok, then create a new Template on the include file with this as lib
included = TextTemplatePointer(new TextTemplate(include, config));
std::stringstream src(includeStream);
int nbErrors = included->parse(src);
if (nbErrors > 0) {
included->logError(included->_root, "File failed to parse, not included");
return TextTemplatePointer();
}
config->_includes.insert(Includes::value_type(include, included));
return included;
}
return TextTemplatePointer();
}
const TextTemplatePointer TextTemplate::Config::findInclude(const char* include) {
Includes::iterator includeIt = _includes.find(String(include));
if (includeIt != _includes.end()) {
return (*includeIt).second;
} else {
return TextTemplatePointer();
}
}
void TextTemplate::Config::addIncludePath(const char* path) {
_paths.push_back(String(path));
}
bool TextTemplate::loadFile(const ConfigPointer& config, const char* filename, String& source) {
String sourceFile(filename);
String fullfilename;
for (unsigned int i = 0; i < config->_paths.size(); i++) {
fullfilename = config->_paths[i] + sourceFile;
std::ifstream ifs;
ifs.open(fullfilename.c_str());
if (ifs.is_open()) {
std::string str((std::istreambuf_iterator<char>(ifs)), std::istreambuf_iterator<char>());
source = str;
ifs.close();
return (source.length() > 0);
}
}
return false;
}
TextTemplate::Funcs::Funcs() :
_funcs() {
}
TextTemplate::Funcs::~Funcs() {
}
const BlockPointer TextTemplate::Funcs::findFunc(const char* func) {
map::iterator it = _funcs.find(String(func));
if (it != _funcs.end()) {
return (*it).second;
} else {
return BlockPointer();
}
}
const BlockPointer TextTemplate::Funcs::addFunc(const char* func, const BlockPointer& funcBlock) {
BlockPointer included = findFunc(func);
if (! included) {
_funcs.insert(map::value_type(func, funcBlock));
}
return included;
}
void TextTemplate::Block::addNewBlock(const Pointer& parent, const Pointer& block) {
if (parent) {
parent->blocks.push_back(block);
block->parent = parent;
}
}
const BlockPointer& TextTemplate::Block::getCurrentBlock(const Pointer& block) {
if (block && block->command.isBlockEnd()) {
return block->parent;
} else {
return block;
}
}
void TextTemplate::logError(const Block::Pointer& block, const char* fmt, ...) {
va_list argp;
va_start(argp, fmt);
char buff[256];
sprintf(buff, fmt, argp);
_numErrors++;
log() << block->sourceName << " Error >>" << buff << std::endl;
int level = 1;
displayTree(std::cerr, level);
}
bool TextTemplate::grabUntilBeginTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) {
std::stringstream dst;
while (!str->eof()) {
std::string datatoken;
getline((*str), datatoken, Tag::BEGIN);
dst << datatoken;
char next = str->peek();
if (next == Tag::VAR) {
tagType = Tag::VARIABLE;
grabbed = dst.str();
str->get(); // skip tag char
return true;
} else if (next == Tag::COM) {
tagType = Tag::COMMAND;
grabbed = dst.str();
str->get(); // skip tag char
return true;
} else if (next == Tag::REM) {
tagType = Tag::REMARK;
grabbed = dst.str();
str->get(); // skip tag char
return true;
} else {
if (!str->eof()) {
// false positive, just found the Tag::BEGIN without consequence
dst << Tag::BEGIN;
// keep searching
} else {
// end of the file finishing with no tag
tagType = Tag::INVALID;
grabbed = dst.str();
return true;
}
}
}
return false;
}
bool TextTemplate::grabUntilEndTag(std::istream* str, std::string& grabbed, Tag::Type& tagType) {
std::stringstream dst;
// preEnd char depends on tag type
char preEnd = Tag::COM;
if (tagType == Tag::VARIABLE) {
preEnd = Tag::VAR;
} else if (tagType == Tag::REMARK) {
preEnd = Tag::REM;
}
while (!str->eof()) {
// looking for the end of the tag means find the next preEnd
std::string dataToken;
getline((*str), dataToken, preEnd);
char end = str->peek();
dst << dataToken;
// and if the next char is Tag::END then that's it
if (end == Tag::END) {
grabbed = dst.str();
str->get(); // eat the Tag::END
return true;
} else {
// false positive, keep on searching
dst << preEnd;
}
}
grabbed = dst.str();
return false;
}
bool TextTemplate::stepForward(std::istream* str, std::string& grabbed, std::string& tag, Tag::Type& tagType,
Tag::Type& nextTagType) {
if (str->eof()) {
return false;
}
if (!_steppingStarted) {
_steppingStarted = true;
return grabUntilBeginTag(str, grabbed, nextTagType);
}
// Read from the last opening Tag::BEGIN captured to the next Tag::END
if (grabUntilEndTag(str, tag, tagType)) {
// skip trailing space and new lines only after Command or Remark tag block
if ((tagType == Tag::COMMAND) || (tagType == Tag::REMARK)) {
while (!str->eof()) {
char c = str->peek();
if ((c == ' ') || (c == '\t') || (c == '\n')) {
str->get();
} else {
break;
}
}
}
grabUntilBeginTag(str, grabbed, nextTagType);
}
return true; //hasElement;
}
const BlockPointer TextTemplate::processStep(const BlockPointer& block, std::string& grabbed, std::string& tag,
Tag::Type& tagType) {
switch (tagType) {
case Tag::INVALID:
block->ostr << grabbed;
return block;
break;
case Tag::VARIABLE:
return processStepVar(block, grabbed, tag);
break;
case Tag::COMMAND:
return processStepCommand(block, grabbed, tag);
break;
case Tag::REMARK:
return processStepRemark(block, grabbed, tag);
break;
}
logError(block, "Invalid tag");
return block;
}
bool TextTemplate::grabFirstToken(String& src, String& token, String& reminder) {
bool goOn = true;
std::string::size_type i = 0;
while (goOn && (i < src.length())) {
char c = src[i];
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == '[') || (c == ']')) {
token += c;
} else {
if (!token.empty()) {
reminder = src.substr(i);
return true;
}
}
i++;
}
return (!token.empty());
}
bool TextTemplate::convertExpressionToArguments(String& src, std::vector< String >& arguments) {
std::stringstream str(src);
String token;
while (!str.eof()) {
str >> token;
if (!str.fail()) {
arguments.push_back(token);
}
}
return true;
}
bool TextTemplate::convertExpressionToDefArguments(String& src, std::vector< String >& arguments) {
if (src.empty()) {
return false;
}
std::stringstream argstr(src);
std::stringstream dest;
bool inVar = false;
while (!argstr.eof()) {
// parse the value of a var, try to find a VAR, so look for the pattern BEGIN,VAR ... VAR,END
String token;
char tag;
if (!inVar) {
getline(argstr, token, Tag::BEGIN);
dest << token;
tag = argstr.peek();
} else {
getline(argstr, token, Tag::END);
dest << token;
tag = token.back();
}
if (tag == Tag::VAR) {
if (!inVar) {
// real var coming:
arguments.push_back(dest.str());
inVar = true;
} else {
// real var over
arguments.push_back(dest.str());
inVar = false;
}
} else {
if (argstr.eof()) {
arguments.push_back(dest.str());
} else {
// put back the tag char stolen
dest << (!inVar ? Tag::BEGIN : Tag::END);
}
}
}
return true;
}
bool TextTemplate::convertExpressionToFuncArguments(String& src, std::vector< String >& arguments) {
if (src.empty()) {
return false;
}
std::stringstream streamSrc(src);
String params;
getline(streamSrc, params, '(');
getline(streamSrc, params, ')');
if (params.empty()) {
return false;
}
std::stringstream str(params);
String token;
int nbTokens = 0;
while (!str.eof()) {
char c = str.peek();
if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || (c == '_') || (c == '.') || (c == Tag::VAR) || (c == '[') || (c == ']')) {
token += c;
} else if (c == ',') {
if (!token.empty()) {
arguments.push_back(token);
nbTokens++;
}
token.clear();
}
str.get();
}
if (token.size()) {
arguments.push_back(token);
nbTokens++;
}
return (nbTokens > 0);
}
const BlockPointer TextTemplate::processStepVar(const BlockPointer& block, String& grabbed, String& tag) {
// then look at the define
String var = tag;
String varName;
String val;
if (grabFirstToken(var, varName, val)) {
if (!varName.empty()) {
BlockPointer parent = Block::getCurrentBlock(block);
// Add a new BLock
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::VAR;
newBlock->command.arguments.push_back(varName);
if (!val.empty()) {
convertExpressionToFuncArguments(val, newBlock->command.arguments);
}
Block::addNewBlock(parent, newBlock);
// dive in the new block
return newBlock;
}
}
return block;
}
const BlockPointer TextTemplate::processStepCommand(const BlockPointer& block, String& grabbed, String& tag) {
// Grab the command name
String command = tag;
String commandName;
if (grabFirstToken(command, commandName, command)) {
if (commandName.compare("def") == 0) {
return processStepDef(block, grabbed, command);
} else if (commandName.compare("if") == 0) {
return processStepCommandIf(block, grabbed, command);
} else if (commandName.compare("endif") == 0) {
return processStepCommandEndIf(block, grabbed, command);
} else if (commandName.compare("else") == 0) {
return processStepCommandElse(block, grabbed, command);
} else if (commandName.compare("elif") == 0) {
return processStepCommandElif(block, grabbed, command);
} else if (commandName.compare("include") == 0) {
return processStepInclude(block, grabbed, command);
} else if (commandName.compare("func") == 0) {
return processStepFunc(block, grabbed, command);
} else if (commandName.compare("endfunc") == 0) {
return processStepEndFunc(block, grabbed, command);
}
}
return block;
}
const BlockPointer TextTemplate::processStepDef(const BlockPointer& block, String& grabbed, String& def) {
// then look at the define
String varName;
String val;
if (!grabFirstToken(def, varName, val)) {
logError(block, "Invalid Var name in a <def> tag");
return block;
}
BlockPointer parent = Block::getCurrentBlock(block);
// Add a new BLock
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::DEF;
newBlock->command.arguments.push_back(varName);
if (!val.empty()) {
// loose first character which should be a white space
val = val.substr(val.find_first_not_of(' '));
convertExpressionToDefArguments(val, newBlock->command.arguments);
}
Block::addNewBlock(parent, newBlock);
// dive in the new block
return newBlock;
}
const BlockPointer TextTemplate::processStepCommandIf(const BlockPointer& block, String& grabbed, String& expression) {
BlockPointer parent = Block::getCurrentBlock(block);
// Add a new BLock depth
BlockPointer newIfBlock = BlockPointer(new Block(_root->sourceName));
newIfBlock->command.type = Command::IFBLOCK;
Block::addNewBlock(parent, newIfBlock);
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::IF;
convertExpressionToArguments(expression, newBlock->command.arguments);
Block::addNewBlock(newIfBlock, newBlock);
// dive in the new If branch
return newBlock;
}
const BlockPointer TextTemplate::processStepCommandEndIf(const BlockPointer& block, String& grabbed, String& expression) {
BlockPointer parent = Block::getCurrentBlock(block);
// are we in a if block ?
if ((parent->command.type == Command::IF)
|| (parent->command.type == Command::ELIF)
|| (parent->command.type == Command::ELSE)) {
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::ENDIF;
newBlock->command.arguments.push_back(expression);
Block::addNewBlock(parent->parent->parent, newBlock);
return newBlock;
} else {
logError(block, "Invalid <endif> block, not in a <if> block");
return block;
}
return block;
}
const BlockPointer TextTemplate::processStepCommandElse(const BlockPointer& block, String& grabbed, String& expression) {
BlockPointer parent = Block::getCurrentBlock(block);
// are we in a if block ?
if ((parent->command.type == Command::IF)
|| (parent->command.type == Command::ELIF)) {
// All good go back to the IfBlock
parent = parent->parent;
// Add a new BLock depth
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
newBlock->ostr << grabbed;
newBlock->command.type = Command::ELSE;
newBlock->command.arguments.push_back(expression);
Block::addNewBlock(parent, newBlock);
// dive in the new block
return newBlock;
} else if ((block)->command.type == Command::ELSE) {
logError(block, "Invalid <elif> block, in a <if> block but after a <else> block");
return block;
} else {
logError(block, "Invalid <elif> block, not in a <if> block");
return block;
}
}
const BlockPointer TextTemplate::processStepCommandElif(const BlockPointer& block, String& grabbed, String& expression) {
BlockPointer parent = Block::getCurrentBlock(block);
// are we in a if block ?
if ((parent->command.type == Command::IF)
|| (parent->command.type == Command::ELIF)) {
// All good go back to the IfBlock
parent = parent->parent;
// Add a new BLock depth
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::ELIF;
convertExpressionToArguments(expression, newBlock->command.arguments);
Block::addNewBlock(parent, newBlock);
// dive in the new block
return newBlock;
} else if (parent->command.type == Command::ELSE) {
logError(block, "Invalid <elif> block, in a <if> block but after a <else> block");
return block;
} else {
logError(block, "Invalid <elif> block, not in a <if> block");
return block;
}
}
const BlockPointer TextTemplate::processStepRemark(const BlockPointer& block, String& grabbed, String& tag) {
// nothing to do :)
// no need to think, let's just add the grabbed text
(block->ostr) << grabbed;
return block;
}
const BlockPointer TextTemplate::processStepInclude(const BlockPointer& block, String& grabbed, String& include) {
BlockPointer parent = Block::getCurrentBlock(block);
// Add a new BLock
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::INCLUDE;
convertExpressionToArguments(include, newBlock->command.arguments);
Block::addNewBlock(parent, newBlock);
TextTemplatePointer inc = Config::addInclude(_config, newBlock->command.arguments.front().c_str());
if (!inc) {
logError(newBlock, "Failed to include %s", newBlock->command.arguments.front().c_str());
}
// dive in the new block
return newBlock;
}
const BlockPointer TextTemplate::processStepFunc(const BlockPointer& block, String& grabbed, String& func) {
// then look at the define
String varName;
String var;
if (!grabFirstToken(func, varName, var)) {
logError(block, "Invalid func name <%s> in a <func> block", func.c_str());
return block;
}
// DOes the func already exists ?
if (_config->_funcs.findFunc(varName.c_str())) {
logError(block, "Declaring a new func named <%s> already exists", func.c_str());
return block;
}
BlockPointer parent = Block::getCurrentBlock(block);
// Add a new BLock
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::FUNC;
newBlock->command.arguments.push_back(varName);
convertExpressionToFuncArguments(var, newBlock->command.arguments);
_config->_funcs.addFunc(varName.c_str(), newBlock);
Block::addNewBlock(parent, newBlock);
// dive in the new block
return newBlock;
}
const BlockPointer TextTemplate::processStepEndFunc(const BlockPointer& block, String& grabbed, String& tag) {
BlockPointer parent = Block::getCurrentBlock(block);
// are we in a func block ?
if (parent->command.type == Command::FUNC) {
// Make sure the FUnc has been declared properly
BlockPointer funcBlock = _config->_funcs.findFunc(parent->command.arguments.front().c_str());
if (funcBlock != parent) {
logError(block, "Mismatching <func> blocks");
return BlockPointer();
}
// Everything is cool , so let's unplugg the FUnc block from this tree and just put the EndFunc block
BlockPointer newBlock = BlockPointer(new Block(_root->sourceName));
(newBlock->ostr) << grabbed;
newBlock->command.type = Command::ENDFUNC;
convertExpressionToArguments(tag, newBlock->command.arguments);
newBlock->parent = parent->parent;
parent->parent->blocks.back() = newBlock;
parent->parent = 0;
// dive in the new block
return newBlock;
} else {
logError(block, "Invalid <endfunc> block, not in a <func> block");
return BlockPointer();
}
return block;
}
TextTemplate::TextTemplate(const String& name, const ConfigPointer& externalConfig) :
_config(externalConfig),
_root(new Block(name)),
_numErrors(0),
_steppingStarted(false) {
}
TextTemplate::~TextTemplate() {
}
int TextTemplate::scribe(std::ostream& dst, std::istream& src, Vars& vars) {
int nbErrors = parse(src);
if (nbErrors == 0) {
return generate(dst, vars);
}
return nbErrors;
}
int TextTemplate::generateTree(std::ostream& dst, const BlockPointer& block, Vars& vars) {
BlockPointer newCurrentBlock;
int numPasses = evalBlockGeneration(dst, block, vars, newCurrentBlock);
for (int passNum= 0; passNum < numPasses; passNum++) {
dst << newCurrentBlock->ostr.str();
for (auto child : newCurrentBlock->blocks) {
generateTree(dst, child, vars);
}
}
return _numErrors;
}
int TextTemplate::evalBlockGeneration(std::ostream& dst, const BlockPointer& block, Vars& vars, BlockPointer& branch) {
switch (block->command.type) {
case Command::BLOCK: {
branch = block;
return 1;
}
break;
case Command::VAR: {
Vars::iterator it = vars.find(block->command.arguments.front());
if (it != vars.end()) {
dst << (*it).second;
} else {
BlockPointer funcBlock = _config->_funcs.findFunc(block->command.arguments.front().c_str());
if (funcBlock) {
// before diving in the func tree, let's modify the vars with the local defs:
int nbParams = std::min(block->command.arguments.size(), funcBlock->command.arguments.size());
std::vector< String > paramCache;
paramCache.push_back("");
String val;
for (int i = 1; i < nbParams; i++) {
val = block->command.arguments[i];
if ((val[0] == Tag::VAR) && (val[val.length()-1] == Tag::VAR)) {
val = val.substr(1, val.length()-2);
Vars::iterator it = vars.find(val);
if (it != vars.end()) {
val = (*it).second;
}
}
Vars::iterator it = vars.find(funcBlock->command.arguments[i]);
if (it != vars.end()) {
paramCache.push_back((*it).second);
(*it).second = val;
} else {
vars.insert(Vars::value_type(funcBlock->command.arguments[i], val));
paramCache.push_back("");
}
}
generateTree(dst, funcBlock, vars);
for (int i = 1; i < nbParams; i++) {
vars[ funcBlock->command.arguments[i] ] = paramCache[i];
}
}
}
branch = block;
return 1;
}
break;
case Command::IFBLOCK: {
// ok, go through the branches and pick the first one that goes
for (auto child: block->blocks) {
int numPasses = evalBlockGeneration(dst, child, vars, branch);
if (numPasses > 0) {
return numPasses;
}
}
}
break;
case Command::IF:
case Command::ELIF: {
if (!block->command.arguments.empty()) {
// Just one argument means check for the var beeing defined
if (block->command.arguments.size() == 1) {
Vars::iterator it = vars.find(block->command.arguments.front());
if (it != vars.end()) {
branch = block;
return 1;
}
} else if (block->command.arguments.size() == 2) {
if (block->command.arguments[0].compare("not") == 0) {
Vars::iterator it = vars.find(block->command.arguments[1]);
if (it == vars.end()) {
branch = block;
return 1;
}
}
} else if (block->command.arguments.size() == 3) {
if (block->command.arguments[1].compare("and") == 0) {
Vars::iterator itL = vars.find(block->command.arguments[0]);
Vars::iterator itR = vars.find(block->command.arguments[2]);
if ((itL != vars.end()) && (itR != vars.end())) {
branch = block;
return 1;
}
} else if (block->command.arguments[1].compare("or") == 0) {
Vars::iterator itL = vars.find(block->command.arguments[0]);
Vars::iterator itR = vars.find(block->command.arguments[2]);
if ((itL != vars.end()) || (itR != vars.end())) {
branch = block;
return 1;
}
} else if (block->command.arguments[1].compare("==") == 0) {
Vars::iterator itL = vars.find(block->command.arguments[0]);
if (itL != vars.end()) {
if ((*itL).second.compare(block->command.arguments[2]) == 0) {
branch = block;
return 1;
}
}
}
}
}
return 0;
}
break;
case Command::ELSE: {
branch = block;
return 1;
}
break;
case Command::ENDIF: {
branch = block;
return 1;
}
break;
case Command::DEF: {
if (block->command.arguments.size()) {
// THe actual value of the var defined sneeds to be evaluated:
String val;
for (int t = 1; t < block->command.arguments.size(); t++) {
// detect if a param is a var
int len = block->command.arguments[t].length();
if ((block->command.arguments[t][0] == Tag::VAR)
&& (block->command.arguments[t][len - 1] == Tag::VAR)) {
String var = block->command.arguments[t].substr(1, len - 2);
Vars::iterator it = vars.find(var);
if (it != vars.end()) {
val += (*it).second;
}
} else {
val += block->command.arguments[t];
}
}
Vars::iterator it = vars.find(block->command.arguments.front());
if (it == vars.end()) {
vars.insert(Vars::value_type(block->command.arguments.front(), val));
} else {
(*it).second = val;
}
branch = block;
return 1;
} else {
branch = block;
return 0;
}
}
break;
case Command::INCLUDE: {
TextTemplatePointer include = _config->findInclude(block->command.arguments.front().c_str());
if (include && !include->_root->blocks.empty()) {
if (&include->_root) {
generateTree(dst, include->_root, vars);
}
}
branch = block;
return 1;
}
break;
case Command::FUNC: {
branch = block;
return 1;
}
break;
case Command::ENDFUNC: {
branch = block;
return 1;
}
break;
default: {
}
}
return 0;
}
int TextTemplate::parse(std::istream& src) {
_root->command.type = Command::BLOCK;
BlockPointer currentBlock = _root;
_numErrors = 0;
// First Parse the input file
int nbLoops = 0;
bool goOn = true;
Tag::Type tagType = Tag::INVALID;
Tag::Type nextTagType = Tag::INVALID;
while (goOn) {
std::string data;
std::string tag;
if (stepForward(&src, data, tag, tagType, nextTagType)) {
currentBlock = processStep(currentBlock, data, tag, tagType);
} else {
goOn = false;
}
tagType = nextTagType;
nbLoops++;
}
return _numErrors;
}
int TextTemplate::generate(std::ostream& dst, Vars& vars) {
return generateTree(dst, _root, vars);
}
void TextTemplate::displayTree(std::ostream& dst, int& level) const {
Block::displayTree(_root, dst, level);
}
void TextTemplate::Block::displayTree(const BlockPointer& block, std::ostream& dst, int& level) {
String tab(level * 2, ' ');
const String BLOCK_TYPE_NAMES[] = {
"VAR",
"BLOCK",
"FUNC",
"ENDFUNC",
"IFBLOCK",
"IF",
"ELIF",
"ELSE",
"ENDIF",
"FOR",
"ENDFOR",
"INCLUDE",
"DEF"
};
dst << tab << "{ " << BLOCK_TYPE_NAMES[block->command.type] << ":";
if (!block->command.arguments.empty()) {
for (auto arg: block->command.arguments) {
dst << " " << arg;
}
}
dst << std::endl;
level++;
for (auto sub: block->blocks) {
displayTree(sub, dst, level);
}
level--;
dst << tab << "}" << std::endl;
}
void TextTemplate::Config::displayTree(std::ostream& dst, int& level) const {
String tab(level * 2, ' ');
level++;
dst << tab << "Includes:" << std::endl;
for (auto inc: _includes) {
dst << tab << tab << inc.first << std::endl;
inc.second->displayTree(dst, level);
}
dst << tab << "Funcs:" << std::endl;
for (auto func: _funcs._funcs) {
dst << tab << tab << func.first << std::endl;
TextTemplate::Block::displayTree( func.second, dst, level);
}
level--;
}

204
tools/scribe/src/TextTemplate.h Executable file
View file

@ -0,0 +1,204 @@
//
// TextTemplate.cpp
// tools/shaderScribe/src
//
// Created by Sam Gateau on 12/15/2014.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#ifndef hifi_TEXT_TEMPLATE_H
#define hifi_TEXT_TEMPLATE_H
#include <list>
#include <vector>
#include <map>
#include <string>
#include <iostream>
#include <sstream>
#include <memory>
class TextTemplate {
public:
typedef std::shared_ptr< TextTemplate > Pointer;
typedef std::string String;
typedef std::vector< String > StringVector;
typedef std::map< String, String > Vars;
typedef std::map< String, TextTemplate::Pointer > Includes;
class Tag {
public:
enum Type {
VARIABLE = 0,
COMMAND,
REMARK,
INVALID = -1,
};
static const char BEGIN = '<';
static const char END = '>';
static const char VAR = '$';
static const char COM = '@';
static const char REM = '!';
};
class Command {
public:
typedef std::vector< Command > vector;
enum Type {
VAR = 0,
BLOCK,
FUNC,
ENDFUNC,
IFBLOCK,
IF,
ELIF,
ELSE,
ENDIF,
FOR,
ENDFOR,
INCLUDE,
DEF,
};
Type type;
std::vector< String > arguments;
bool isBlockEnd() {
switch (type) {
case ENDFUNC:
case ENDIF:
case ENDFOR:
case INCLUDE:
case DEF:
case VAR:
return true;
default:
return false;
}
}
};
class Block {
public:
typedef std::shared_ptr<Block> Pointer;
typedef std::vector< Block::Pointer > Vector;
Block::Pointer parent;
Command command;
Vector blocks;
std::stringstream ostr;
String sourceName;
Block(const String& sourceFilename) :
sourceName(sourceFilename) {}
static void addNewBlock(const Block::Pointer& parent, const Block::Pointer& block);
static const Block::Pointer& getCurrentBlock(const Block::Pointer& block);
static void displayTree(const Block::Pointer& block, std::ostream& dst, int& level);
};
class Funcs {
public:
typedef std::map< String, Block::Pointer > map;
Funcs();
~Funcs();
const Block::Pointer findFunc(const char* func);
const Block::Pointer addFunc(const char* func, const Block::Pointer& root);
map _funcs;
protected:
};
class Config {
public:
typedef std::shared_ptr< Config > Pointer;
typedef bool (*IncluderCallback) (const Config::Pointer& config, const char* filename, String& source);
Includes _includes;
Funcs _funcs;
std::ostream* _logStream;
int _numErrors;
IncluderCallback _includerCallback;
StringVector _paths;
Config();
static const TextTemplate::Pointer addInclude(const Config::Pointer& config, const char* include);
const TextTemplate::Pointer findInclude(const char* include);
void addIncludePath(const char* path);
void displayTree(std::ostream& dst, int& level) const;
};
static bool loadFile(const Config::Pointer& config, const char* filename, String& source);
TextTemplate(const String& name, const Config::Pointer& config = Config::Pointer(new Config()));
~TextTemplate();
// Scibe does all the job of parsing an inout template stream and then gneerating theresulting stream using the vars
int scribe(std::ostream& dst, std::istream& src, Vars& vars);
int parse(std::istream& src);
int generate(std::ostream& dst, Vars& vars);
const Config::Pointer config() { return _config; }
void displayTree(std::ostream& dst, int& level) const;
protected:
Config::Pointer _config;
Block::Pointer _root;
int _numErrors;
bool _steppingStarted;
bool grabUntilBeginTag(std::istream* str, String& grabbed, Tag::Type& tagType);
bool grabUntilEndTag(std::istream* str, String& grabbed, Tag::Type& tagType);
bool stepForward(std::istream* str, String& grabbed, String& tag, Tag::Type& tagType, Tag::Type& nextTagType);
bool grabFirstToken(String& src, String& token, String& reminder);
bool convertExpressionToArguments(String& src, std::vector< String >& arguments);
bool convertExpressionToDefArguments(String& src, std::vector< String >& arguments);
bool convertExpressionToFuncArguments(String& src, std::vector< String >& arguments);
// Filter between var, command or comments
const Block::Pointer processStep(const Block::Pointer& block, String& grabbed, String& tag, Tag::Type& tagType);
const Block::Pointer processStepVar(const Block::Pointer& block, String& grabbed, String& tag);
const Block::Pointer processStepCommand(const Block::Pointer& block, String& grabbed, String& tag);
const Block::Pointer processStepRemark(const Block::Pointer& block, String& grabbed, String& tag);
// Define command
const Block::Pointer processStepDef(const Block::Pointer& block, String& grabbed, String& tag);
// If commands
const Block::Pointer processStepCommandIf(const Block::Pointer& block, String& grabbed, String& expression);
const Block::Pointer processStepCommandEndIf(const Block::Pointer& block, String& grabbed, String& expression);
const Block::Pointer processStepCommandElif(const Block::Pointer& block, String& grabbed, String& expression);
const Block::Pointer processStepCommandElse(const Block::Pointer& block, String& grabbed, String& expression);
// Include command
const Block::Pointer processStepInclude(const Block::Pointer& block, String& grabbed, String& tag);
// Function command
const Block::Pointer processStepFunc(const Block::Pointer& block, String& grabbed, String& tag);
const Block::Pointer processStepEndFunc(const Block::Pointer& block, String& grabbed, String& tag);
// Generation
int generateTree(std::ostream& dst, const Block::Pointer& block, Vars& vars);
int evalBlockGeneration(std::ostream& dst, const Block::Pointer& block, Vars& vars, Block::Pointer& branch);
// Errors
std::ostream& log() { return (* _config->_logStream); }
void logError(const Block::Pointer& block, const char* error, ...);
};
#endif

225
tools/scribe/src/main.cpp Executable file
View file

@ -0,0 +1,225 @@
//
// main.cpp
// tools/shaderScribe/src
//
// Created by Sam Gateau on 12/15/2014.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
#include "TextTemplate.h"
#include <fstream>
#include <ctime>
#include <chrono>
using namespace std;
int main (int argc, char** argv) {
// process the command line arguments
std::vector< std::string > inputs;
std::string srcFilename;
std::string destFilename;
std::string targetName;
TextTemplate::Vars vars;
std::string lastVarName;
bool listVars = false;
bool showParseTree = false;
bool makeCPlusPlus = false;
TextTemplate::Config::Pointer config(new TextTemplate::Config());
enum Mode {
READY = 0,
GRAB_OUTPUT,
GRAB_VAR_NAME,
GRAB_VAR_VALUE,
GRAB_INCLUDE_PATH,
GRAB_TARGET_NAME,
EXIT,
} mode = READY;
for (int ii = 1; (mode != EXIT) && (ii < argc); ii++) {
inputs.push_back(argv[ii]);
switch (mode) {
case READY: {
if (inputs.back() == "-o") {
mode = GRAB_OUTPUT;
} else if (inputs.back() == "-tn") {
mode = GRAB_TARGET_NAME;
} else if (inputs.back() == "-D") {
mode = GRAB_VAR_NAME;
} else if (inputs.back() == "-I") {
mode = GRAB_INCLUDE_PATH;
} else if (inputs.back() == "-listVars") {
listVars = true;
mode = READY;
} else if (inputs.back() == "-showParseTree") {
showParseTree = true;
mode = READY;
} else if (inputs.back() == "-c++") {
makeCPlusPlus = true;
mode = READY;
} else {
// just grabbed the source filename, stop parameter parsing
srcFilename = inputs.back();
mode = EXIT;
}
}
break;
case GRAB_OUTPUT: {
destFilename = inputs.back();
mode = READY;
}
break;
case GRAB_TARGET_NAME: {
targetName = inputs.back();
mode = READY;
}
break;
case GRAB_VAR_NAME: {
// grab first the name of the var
lastVarName = inputs.back();
mode = GRAB_VAR_VALUE;
}
break;
case GRAB_VAR_VALUE: {
// and then the value
vars.insert(TextTemplate::Vars::value_type(lastVarName, inputs.back()));
mode = READY;
}
break;
case GRAB_INCLUDE_PATH: {
config->addIncludePath(inputs.back().c_str());
mode = READY;
}
break;
case EXIT: {
// THis shouldn't happen
}
break;
}
}
if (srcFilename.empty()) {
cerr << "Usage: shaderScribe [OPTION]... inputFilename" << endl;
cerr << "Where options include:" << endl;
cerr << " -o filename: Send output to filename rather than standard output." << endl;
cerr << " -t targetName: Set the targetName used, if not defined use the output filename 'name' and if not defined use the inputFilename 'name'" << endl;
cerr << " -I include_directory: Declare a directory to be added to the includes search pool." << endl;
cerr << " -D varname varvalue: Declare a var used to generate the output file." << endl;
cerr << " varname and varvalue must be made of alpha numerical characters with no spaces." << endl;
cerr << " -listVars : Will list the vars name and value in the standard output." << endl;
cerr << " -showParseTree : Draw the tree obtained while parsing the source" << endl;
cerr << " -c++ : Generate a c++ header file containing the output file stream stored as a char[] variable" << endl;
return 0;
}
// Define targetName: if none, get destFilenmae, if none get srcFilename
if (targetName.empty()) {
if (destFilename.empty()) {
targetName = srcFilename;
} else {
targetName = destFilename;
}
}
// no clean it to have just a descent c var name
if (!targetName.empty()) {
// trim anything before '/' or '\'
targetName = targetName.substr(targetName.find_last_of('/') + 1);
targetName = targetName.substr(targetName.find_last_of('\\') + 1);
// trim anything after '.'
targetName = targetName.substr(0, targetName.find_first_of('.'));
}
// Add built in vars
time_t endTime = chrono::system_clock::to_time_t(chrono::system_clock::now());
std::string endTimStr(ctime(&endTime));
vars["_SCRIBE_DATE"] = endTimStr.substr(0, endTimStr.length() - 1);
// List vars?
if (listVars) {
cerr << "Vars:" << endl;
for (auto v : vars) {
cerr << " " << v.first << " = " << v.second << endl;
}
}
// Open up source
std::fstream srcStream;
srcStream.open(srcFilename, std::fstream::in);
if (!srcStream.is_open()) {
cerr << "Failed to open source file <" << srcFilename << ">" << endl;
return 0;
}
TextTemplate::Pointer scribe(new TextTemplate(srcFilename, config));
// ready to parse and generate
std::ostringstream destStringStream;
int numErrors = scribe->scribe(destStringStream, srcStream, vars);
if (numErrors) {
cerr << "Scribe " << srcFilename << "> failed: " << numErrors << " errors." << endl;
return 0;
};
if (showParseTree) {
int level = 1;
cerr << "Config trees:" << std::endl;
config->displayTree(cerr, level);
cerr << srcFilename << " tree:" << std::endl;
scribe->displayTree(cerr, level);
}
std::ostringstream targetStringStream;
if (makeCPlusPlus) {
targetStringStream << "// File generated by Scribe " << vars["_SCRIBE_DATE"] << std::endl;
targetStringStream << "#ifndef scribe_" << targetName << "_h" << std::endl;
targetStringStream << "#define scribe_" << targetName << "_h" << std::endl << std::endl;
targetStringStream << "const char " << targetName << "[] = {\n\"";
std::stringstream destStringStreamAgain(destStringStream.str());
while (!destStringStreamAgain.eof()) {
std::string line;
std::getline(destStringStreamAgain, line);
targetStringStream << line << " \\n\\\n";
}
targetStringStream << "\"};" << std::endl << std::endl;
targetStringStream << "#endif" << std::endl;
} else {
targetStringStream << destStringStream.str();
}
// Destination stream
if (!destFilename.empty()) {
std::fstream destFileStream;
destFileStream.open(destFilename, std::fstream::out);
if (!destFileStream.is_open()) {
cerr << "Scribe output file " << destFilename << "> failed to open." << endl;
return 0;
}
destFileStream << targetStringStream.str();
} else {
cerr << targetStringStream.str();
}
return 0;
}