diff --git a/CMakeLists.txt b/CMakeLists.txt index 16eed7b0fc..81d532fbe2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,10 @@ if (WIN32) message (WINDOW_SDK_PATH= ${WINDOW_SDK_PATH}) set(CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH} ${WINDOW_SDK_PATH}) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /MP") + # /LARGEADDRESSAWARE enables 32-bit apps to use more than 2GB of memory. + # Caveats: http://stackoverflow.com/questions/2288728/drawbacks-of-using-largeaddressaware-for-32-bit-windows-executables + # TODO: Remove when building 64-bit. + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /LARGEADDRESSAWARE") elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic") #SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unknown-pragmas") diff --git a/examples/entityScripts/alternativeLightController.js b/examples/entityScripts/alternativeLightController.js new file mode 100644 index 0000000000..c3ea56ff77 --- /dev/null +++ b/examples/entityScripts/alternativeLightController.js @@ -0,0 +1,100 @@ +(function() { + this.preload = function(entityId) { + var soundURLs = ["https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_1.wav", + "https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_2.wav", + "https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_3.wav"]; + this.entityId = entityId; + this.properties = Entities.getEntityProperties(this.entityId); + this.previousPosition = this.properties.position; + this.previousRotation = this.properties.rotation; + this.getUserData() + if (!this.userData) { + this.userData = {}; + this.userData.lightOn = false; + this.userData.soundIndex = Math.floor(Math.random() * soundURLs.length); + this.updateUserData(); + } + this.sound = SoundCache.getSound(soundURLs[this.userData.soundIndex]); + } + + + this.getUserData = function() { + if (this.properties.userData) { + this.userData = JSON.parse(this.properties.userData); + } + return false; + } + + this.updateUserData = function() { + Entities.editEntity(this.entityId, { + userData: JSON.stringify(this.userData) + }); + } + + this.clickReleaseOnEntity = function(entityId, mouseEvent) { + if (!mouseEvent.isLeftButton) { + return; + } + //first find closest light + this.entityId = entityId + this.playSound(); + this.properties = Entities.getEntityProperties(this.entityId) + this.light = this.findClosestLight(); + if (this.light) { + this.lightProperties = Entities.getEntityProperties(this.light); + this.getUserData(); + Entities.editEntity(this.light, { + visible: !this.userData.lightOn + }); + + this.userData.lightOn = !this.userData.lightOn; + this.updateUserData(); + this.tryMoveLight(); + } + } + + this.playSound = function() { + if (this.sound && this.sound.downloaded) { + Audio.playSound(this.sound, { + position: this.properties.position, + volume: 0.3 + }); + } else { + print("Warning: Couldn't play sound."); + } + } + + this.tryMoveLight = function() { + if (this.light) { + //compute offset position + var offsetPosition = Quat.multiply(Quat.inverse(this.previousRotation), Vec3.subtract(this.lightProperties.position, this.previousPosition)); + var newPosition = Vec3.sum(this.properties.position, Vec3.multiplyQbyV(this.properties.rotation, offsetPosition)); + if (!Vec3.equal(newPosition, this.lightProperties.position)) { + Entities.editEntity(this.light, {position: newPosition}); + } + this.previousPosition = this.properties.position; + this.previousRotation = this.properties.rotation; + } + + } + + this.findClosestLight = function() { + var entities = Entities.findEntities(this.properties.position, 10); + var lightEntities = []; + var closestLight = null; + var nearestDistance = 20 + + for (var i = 0; i < entities.length; i++) { + var props = Entities.getEntityProperties(entities[i]); + if (props.type === "Light") { + var distance = Vec3.distance(props.position, this.properties.position) + if (distance < nearestDistance) { + closestLight = entities[i]; + nearestDistance = distance + } + } + } + return closestLight; + } + +}); \ No newline at end of file diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index d0be4144c9..bfb84c68a5 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -211,7 +211,14 @@ disableChildren(document.getElementById("properties-list"), 'input'); } else { var activeElement = document.activeElement; - var selected = activeElement.selectionStart == 0 && activeElement.selectionEnd == activeElement.value.length; + + try { + var selected = (activeElement + && activeElement.selectionStart == 0 + && activeElement.selectionEnd == activeElement.value.length); + } catch (e) { + var selected = false; + } var properties = data.selections[0].properties; diff --git a/examples/users.js b/examples/users.js index d6ebe86d4e..d9fe76418d 100644 --- a/examples/users.js +++ b/examples/users.js @@ -201,11 +201,11 @@ var usersWindow = (function () { WINDOW_BASE_MARGIN = 6, // A little less is needed in order look correct WINDOW_FONT = { size: 12 }, WINDOW_FOREGROUND_COLOR = { red: 240, green: 240, blue: 240 }, - WINDOW_FOREGROUND_ALPHA = 0.9, + WINDOW_FOREGROUND_ALPHA = 0.95, WINDOW_HEADING_COLOR = { red: 180, green: 180, blue: 180 }, - WINDOW_HEADING_ALPHA = 0.9, + WINDOW_HEADING_ALPHA = 0.95, WINDOW_BACKGROUND_COLOR = { red: 80, green: 80, blue: 80 }, - WINDOW_BACKGROUND_ALPHA = 0.7, + WINDOW_BACKGROUND_ALPHA = 0.8, windowPane, windowHeading, MIN_MAX_BUTTON_SVG = HIFI_PUBLIC_BUCKET + "images/tools/min-max-toggle.svg", @@ -223,7 +223,7 @@ var usersWindow = (function () { SCROLLBAR_BAR_MIN_HEIGHT = 5, SCROLLBAR_BAR_COLOR = { red: 170, green: 170, blue: 170 }, SCROLLBAR_BAR_ALPHA = 0.8, - SCROLLBAR_BAR_SELECTED_ALPHA = 0.9, + SCROLLBAR_BAR_SELECTED_ALPHA = 0.95, scrollbarBar, scrollbarBackgroundHeight, scrollbarBarHeight, @@ -233,12 +233,12 @@ var usersWindow = (function () { FRIENDS_BUTTON_SVG_HEIGHT = 27, FRIENDS_BUTTON_WIDTH = FRIENDS_BUTTON_SVG_WIDTH, FRIENDS_BUTTON_HEIGHT = FRIENDS_BUTTON_SVG_HEIGHT, - FRIENDS_BUTTON_COLOR = { red: 255, green: 255, blue: 255 }, - FRIENDS_BUTTON_ALPHA = 0.9, + FRIENDS_BUTTON_COLOR = { red: 225, green: 225, blue: 225 }, + FRIENDS_BUTTON_ALPHA = 0.95, friendsButton, OPTION_BACKGROUND_COLOR = { red: 60, green: 60, blue: 60 }, - OPTION_BACKGROUND_ALPHA = 0.8, + OPTION_BACKGROUND_ALPHA = 0.1, DISPLAY_SPACER = 12, // Space before display control DISPLAY_PROMPT = "Show me:", @@ -247,8 +247,8 @@ var usersWindow = (function () { DISPLAY_FRIENDS = "friends", DISPLAY_VALUES = [DISPLAY_EVERYONE, DISPLAY_FRIENDS], DISPLAY_DISPLAY_VALUES = DISPLAY_VALUES, - DISPLAY_OPTIONS_BACKGROUND_COLOR = { red: 40, green: 40, blue: 40 }, - DISPLAY_OPTIONS_BACKGROUND_ALPHA = 0.95, + DISPLAY_OPTIONS_BACKGROUND_COLOR = { red: 120, green: 120, blue: 120 }, + DISPLAY_OPTIONS_BACKGROUND_ALPHA = 0.9, displayControl, VISIBILITY_SPACER = 6, // Space before visibility control diff --git a/interface/src/starfield/renderer/Renderer.cpp b/interface/src/starfield/renderer/Renderer.cpp index 034a571814..1ebb4245e2 100644 --- a/interface/src/starfield/renderer/Renderer.cpp +++ b/interface/src/starfield/renderer/Renderer.cpp @@ -212,6 +212,7 @@ void Renderer::glUpload(GLsizei numStars) { void Renderer::glBatch(GLfloat const* matrix, GLsizei n_ranges, float alpha) { glDisable(GL_DEPTH_TEST); glDisable(GL_LIGHTING); + glEnable(GL_BLEND); // setup modelview matrix glPushMatrix(); diff --git a/libraries/entities/src/EntityItemPropertiesMacros.h b/libraries/entities/src/EntityItemPropertiesMacros.h index 6a8f52719e..e3e54f5bc8 100644 --- a/libraries/entities/src/EntityItemPropertiesMacros.h +++ b/libraries/entities/src/EntityItemPropertiesMacros.h @@ -201,7 +201,7 @@ #define COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(P, S)\ QScriptValue P = object.property(#P); \ if (P.isValid()) { \ - QString newValue = P.toVariant().toString();\ + QString newValue = P.toVariant().toString().trimmed();\ if (_defaultSettings || newValue != _##P) { \ S(newValue); \ } \ diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index ddb2c6ba31..0deb241343 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -116,6 +116,24 @@ bool OBJTokenizer::isNextTokenFloat() { return ok; } +void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID) { + meshPart.diffuseColor = glm::vec3(1, 1, 1); + meshPart.specularColor = glm::vec3(1, 1, 1); + meshPart.emissiveColor = glm::vec3(0, 0, 0); + meshPart.emissiveParams = glm::vec2(0, 1); + meshPart.shininess = 40; + meshPart.opacity = 1; + + meshPart.materialID = materialID; + meshPart.opacity = 1.0; + meshPart._material = model::MaterialPointer(new model::Material()); + meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0)); + meshPart._material->setOpacity(1.0); + meshPart._material->setSpecular(glm::vec3(1.0, 1.0, 1.0)); + meshPart._material->setShininess(96.0); + meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0)); +} + bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, FBXGeometry &geometry, QVector& faceNormals, QVector& faceNormalIndexes, float& scaleGuess) { @@ -125,21 +143,7 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, bool sawG = false; bool result = true; - meshPart.diffuseColor = glm::vec3(1, 1, 1); - meshPart.specularColor = glm::vec3(1, 1, 1); - meshPart.emissiveColor = glm::vec3(0, 0, 0); - meshPart.emissiveParams = glm::vec2(0, 1); - meshPart.shininess = 40; - meshPart.opacity = 1; - - meshPart.materialID = QString("dontknow") + QString::number(mesh.parts.count()); - meshPart.opacity = 1.0; - meshPart._material = model::MaterialPointer(new model::Material()); - meshPart._material->setDiffuse(glm::vec3(1.0, 1.0, 1.0)); - meshPart._material->setOpacity(1.0); - meshPart._material->setSpecular(glm::vec3(1.0, 1.0, 1.0)); - meshPart._material->setShininess(96.0); - meshPart._material->setEmissive(glm::vec3(0.0, 0.0, 0.0)); + setMeshPartDefaults(meshPart, QString("dontknow") + QString::number(mesh.parts.count())); while (true) { int tokenType = tokenizer.nextToken(); diff --git a/libraries/fbx/src/OBJReader.h b/libraries/fbx/src/OBJReader.h index 8c7aa1aba6..a272e46f2d 100644 --- a/libraries/fbx/src/OBJReader.h +++ b/libraries/fbx/src/OBJReader.h @@ -5,3 +5,4 @@ FBXGeometry readOBJ(const QByteArray& model, const QVariantHash& mapping); FBXGeometry readOBJ(QIODevice* device, const QVariantHash& mapping); void fbxDebugDump(const FBXGeometry& fbxgeo); +void setMeshPartDefaults(FBXMeshPart &meshPart, QString materialID); diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp index f1956727f8..708ca0f627 100644 --- a/libraries/gpu/src/gpu/Resource.cpp +++ b/libraries/gpu/src/gpu/Resource.cpp @@ -27,7 +27,7 @@ Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) if (size > 0) { // Try allocating as much as the required size + one block of memory newSize = size; - (*dataAllocated) = new Byte[newSize]; + (*dataAllocated) = new (std::nothrow) Byte[newSize]; // Failed? if (!(*dataAllocated)) { qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index f4121d051f..b1e92e2140 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -1024,7 +1024,7 @@ int Octree::encodeTreeBitstream(OctreeElement* element, roomForOctalCode = packetData->startSubTree(newCode); if (newCode) { - delete newCode; + delete[] newCode; codeLength = numberOfThreeBitSectionsInCode(newCode); } else { codeLength = 1; @@ -2064,7 +2064,7 @@ bool Octree::readJSONFromStream(unsigned long streamLength, QDataStream& inputSt QVariant asVariant = asDocument.toVariant(); QVariantMap asMap = asVariant.toMap(); readFromMap(asMap); - delete rawData; + delete[] rawData; return true; } diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index bed7b473da..2a73f5d28f 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -36,7 +36,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { float diagonal = 4.0f * glm::length2(info.getHalfExtents()); const float MIN_SHAPE_DIAGONAL_SQUARED = 3.0e-4f; // 1 cm cube const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e6f; // 1000 m cube - if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) { + if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED /* || diagonal > MAX_SHAPE_DIAGONAL_SQUARED*/ ) { // qCDebug(physics) << "ShapeManager::getShape -- not making shape due to size" << diagonal; return NULL; } diff --git a/tools/vhacd/src/VHACDUtil.cpp b/tools/vhacd/src/VHACDUtil.cpp index c05f5327ca..92ae62db13 100644 --- a/tools/vhacd/src/VHACDUtil.cpp +++ b/tools/vhacd/src/VHACDUtil.cpp @@ -50,42 +50,92 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, FBXGeometry& result) { } +unsigned int getTrianglesInMeshPart(const FBXMeshPart &meshPart, std::vector& triangles) { + // append all the triangles (and converted quads) from this mesh-part to triangles + std::vector meshPartTriangles = meshPart.triangleIndices.toStdVector(); + triangles.insert(triangles.end(), meshPartTriangles.begin(), meshPartTriangles.end()); -// void vhacd::VHACDUtil::fattenMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFBXResults *results) const { + // convert quads to triangles + unsigned int triangleCount = meshPart.triangleIndices.size() / 3; + unsigned int quadCount = meshPart.quadIndices.size() / 4; + for (unsigned int i = 0; i < quadCount; i++) { + unsigned int p0Index = meshPart.quadIndices[i * 4]; + unsigned int p1Index = meshPart.quadIndices[i * 4 + 1]; + unsigned int p2Index = meshPart.quadIndices[i * 4 + 2]; + unsigned int p3Index = meshPart.quadIndices[i * 4 + 3]; + // split each quad into two triangles + triangles.push_back(p0Index); + triangles.push_back(p1Index); + triangles.push_back(p2Index); + triangles.push_back(p0Index); + triangles.push_back(p2Index); + triangles.push_back(p3Index); + triangleCount += 2; + } -// for (int i = 0; i < meshes->meshCount; i++) { -// QVector vertices = meshes->perMeshVertices.at(i); -// QVector triangles = meshes->perMeshTriangleIndices.at(i); -// const float largestDimension = meshes->perMeshLargestDimension.at(i); + return triangleCount; +} -// results->perMeshVertices.append(vertices); -// results->perMeshTriangleIndices.append(triangles); -// results->perMeshLargestDimension.append(largestDimension); -// for (int j = 0; j < triangles.size(); j += 3) { -// auto p0 = vertices[triangles[j]]; -// auto p1 = vertices[triangles[j+1]]; -// auto p2 = vertices[triangles[j+2]]; +void vhacd::VHACDUtil::fattenMeshes(const FBXMesh& mesh, FBXMesh& result, + unsigned int& meshPartCount, + unsigned int startMeshIndex, unsigned int endMeshIndex) const { + // this is used to make meshes generated from a highfield collidable. each triangle + // is converted into a tetrahedron and made into its own mesh-part. -// auto d0 = p1 - p0; -// auto d1 = p2 - p0; + std::vector triangles; + foreach (const FBXMeshPart &meshPart, mesh.parts) { + if (meshPartCount < startMeshIndex || meshPartCount >= endMeshIndex) { + meshPartCount++; + continue; + } + getTrianglesInMeshPart(meshPart, triangles); + } -// auto cp = glm::cross(d0, d1); -// cp = -2.0f * glm::normalize(cp); + unsigned int triangleCount = triangles.size() / 3; + if (triangleCount == 0) { + return; + } -// auto p3 = p0 + cp; - -// auto n = results->perMeshVertices.size(); -// results->perMeshVertices[i] << p3; + int indexStartOffset = result.vertices.size(); -// results->perMeshTriangleIndices[i] << triangles[j] << n << triangles[j + 1]; -// results->perMeshTriangleIndices[i] << triangles[j + 1] << n << triangles[j + 2]; -// results->perMeshTriangleIndices[i] << triangles[j + 2] << n << triangles[j]; -// } + // new mesh gets the transformed points from the original + for (int i = 0; i < mesh.vertices.size(); i++) { + // apply the source mesh's transform to the points + glm::vec4 v = mesh.modelTransform * glm::vec4(mesh.vertices[i], 1.0f); + result.vertices += glm::vec3(v); + } -// results->meshCount++; -// } -// } + // turn each triangle into a tetrahedron + + for (unsigned int i = 0; i < triangleCount; i++) { + int index0 = triangles[i * 3] + indexStartOffset; + int index1 = triangles[i * 3 + 1] + indexStartOffset; + int index2 = triangles[i * 3 + 2] + indexStartOffset; + + glm::vec3 p0 = result.vertices[index0]; + glm::vec3 p1 = result.vertices[index1]; + glm::vec3 p2 = result.vertices[index2]; + glm::vec3 av = (p0 + p1 + p2) / 3.0f; // center of the triangular face + + float dropAmount = 0; + dropAmount = glm::max(glm::length(p1 - p0), dropAmount); + dropAmount = glm::max(glm::length(p2 - p1), dropAmount); + dropAmount = glm::max(glm::length(p0 - p2), dropAmount); + + glm::vec3 p3 = av - glm::vec3(0, dropAmount, 0); // a point 1 meter below the average of this triangle's points + int index3 = result.vertices.size(); + result.vertices << p3; // add the new point to the result mesh + + FBXMeshPart newMeshPart; + setMeshPartDefaults(newMeshPart, "unknown"); + newMeshPart.triangleIndices << index0 << index1 << index2; + newMeshPart.triangleIndices << index0 << index3 << index1; + newMeshPart.triangleIndices << index1 << index3 << index2; + newMeshPart.triangleIndices << index2 << index3 << index0; + result.parts.append(newMeshPart); + } +} @@ -127,8 +177,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, FBXGeometry& result, int startMeshIndex, int endMeshIndex, - float minimumMeshSize, float maximumMeshSize, - bool fattenFaces) { + float minimumMeshSize, float maximumMeshSize) { // count the mesh-parts int meshCount = 0; foreach (const FBXMesh& mesh, geometry.meshes) { @@ -168,27 +217,8 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, qDebug() << "--------------------"; - std::vector triangles = meshPart.triangleIndices.toStdVector(); - - AABox aaBox = getAABoxForMeshPart(mesh, meshPart); - - // convert quads to triangles - unsigned int triangleCount = meshPart.triangleIndices.size() / 3; - unsigned int quadCount = meshPart.quadIndices.size() / 4; - for (unsigned int i = 0; i < quadCount; i++) { - unsigned int p0Index = meshPart.quadIndices[i * 4]; - unsigned int p1Index = meshPart.quadIndices[i * 4 + 1]; - unsigned int p2Index = meshPart.quadIndices[i * 4 + 2]; - unsigned int p3Index = meshPart.quadIndices[i * 4 + 3]; - // split each quad into two triangles - triangles.push_back(p0Index); - triangles.push_back(p1Index); - triangles.push_back(p2Index); - triangles.push_back(p0Index); - triangles.push_back(p2Index); - triangles.push_back(p3Index); - triangleCount += 2; - } + std::vector triangles; + unsigned int triangleCount = getTrianglesInMeshPart(meshPart, triangles); // only process meshes with triangles if (triangles.size() <= 0) { @@ -198,6 +228,7 @@ bool vhacd::VHACDUtil::computeVHACD(FBXGeometry& geometry, } int nPoints = vertices.size(); + AABox aaBox = getAABoxForMeshPart(mesh, meshPart); const float largestDimension = aaBox.getLargestDimension(); qDebug() << "Mesh " << count << " -- " << nPoints << " points, " << triangleCount << " triangles, " diff --git a/tools/vhacd/src/VHACDUtil.h b/tools/vhacd/src/VHACDUtil.h index 8d0c6acc32..34ad39a6f6 100644 --- a/tools/vhacd/src/VHACDUtil.h +++ b/tools/vhacd/src/VHACDUtil.h @@ -26,14 +26,16 @@ namespace vhacd { class VHACDUtil { public: bool loadFBX(const QString filename, FBXGeometry& result); - // void combineMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFBXResults *results) const; - // void fattenMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFBXResults *results) const; + + void fattenMeshes(const FBXMesh& mesh, FBXMesh& result, + unsigned int& meshPartCount, + unsigned int startMeshIndex, unsigned int endMeshIndex) const; + bool computeVHACD(FBXGeometry& geometry, VHACD::IVHACD::Parameters params, FBXGeometry& result, int startMeshIndex, int endMeshIndex, - float minimumMeshSize, float maximumMeshSize, - bool fattenFaces); + float minimumMeshSize, float maximumMeshSize); ~VHACDUtil(); }; diff --git a/tools/vhacd/src/VHACDUtilApp.cpp b/tools/vhacd/src/VHACDUtilApp.cpp index ba3fbb94bd..242e69c363 100644 --- a/tools/vhacd/src/VHACDUtilApp.cpp +++ b/tools/vhacd/src/VHACDUtilApp.cpp @@ -33,7 +33,7 @@ QString formatFloat(double n) { } -bool writeOBJ(QString outFileName, FBXGeometry& geometry, int whichMeshPart = -1) { +bool writeOBJ(QString outFileName, FBXGeometry& geometry, bool outputCentimeters, int whichMeshPart = -1) { QFile file(outFileName); if (!file.open(QIODevice::WriteOnly)) { qDebug() << "Unable to write to " << outFileName; @@ -41,6 +41,10 @@ bool writeOBJ(QString outFileName, FBXGeometry& geometry, int whichMeshPart = -1 } QTextStream out(&file); + if (outputCentimeters) { + out << "# This file uses centimeters as units\n\n"; + } + unsigned int nth = 0; // vertex indexes in obj files span the entire file @@ -116,6 +120,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : const QCommandLineOption outputFilenameOption("o", "output file", "filename.obj"); parser.addOption(outputFilenameOption); + const QCommandLineOption outputCentimetersOption("c", "output units are centimeters"); + parser.addOption(outputCentimetersOption); + const QCommandLineOption startMeshIndexOption("s", "start-mesh index", "0"); parser.addOption(startMeshIndexOption); @@ -137,7 +144,21 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : "according the \"best\" clipping plane (range=1-32)", "20"); parser.addOption(vHacdDepthOption); - const QCommandLineOption vHacdDeltaOption("delta", "Controls the bias toward maximaxing local concavity (range=0.0-1.0)", "0.05"); + + const QCommandLineOption vHacdAlphaOption("alpha", "Controls the bias toward clipping along symmetry " + "planes (range=0.0-1.0)", "0.05"); + parser.addOption(vHacdAlphaOption); + + const QCommandLineOption vHacdBetaOption("beta", "Controls the bias toward clipping along revolution " + "axes (range=0.0-1.0)", "0.05"); + parser.addOption(vHacdBetaOption); + + const QCommandLineOption vHacdGammaOption("gamma", "Controls the maximum allowed concavity during the " + "merge stage (range=0.0-1.0)", "0.00125"); + parser.addOption(vHacdGammaOption); + + const QCommandLineOption vHacdDeltaOption("delta", "Controls the bias toward maximaxing local " + "concavity (range=0.0-1.0)", "0.05"); parser.addOption(vHacdDeltaOption); const QCommandLineOption vHacdConcavityOption("concavity", "Maximum allowed concavity (range=0.0-1.0)", "0.0025"); @@ -152,10 +173,6 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : "plane selection stage (range=1-16)", "4"); parser.addOption(vHacdConvexhulldownsamplingOption); - // alpha - // beta - // gamma - // delta // pca // mode @@ -178,9 +195,11 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : Q_UNREACHABLE(); } - bool fattenFaces = parser.isSet(fattenFacesOption); + bool outputCentimeters = parser.isSet(outputCentimetersOption); + bool fattenFaces = parser.isSet(fattenFacesOption); bool generateHulls = parser.isSet(generateHullsOption); + bool splitModel = parser.isSet(splitOption); QString inputFilename; @@ -236,6 +255,21 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : vHacdDepth = parser.value(vHacdDepthOption).toInt(); } + float vHacdAlpha = 0.05; + if (parser.isSet(vHacdAlphaOption)) { + vHacdAlpha = parser.value(vHacdAlphaOption).toFloat(); + } + + float vHacdBeta = 0.05; + if (parser.isSet(vHacdBetaOption)) { + vHacdBeta = parser.value(vHacdBetaOption).toFloat(); + } + + float vHacdGamma = 0.00125; + if (parser.isSet(vHacdGammaOption)) { + vHacdGamma = parser.value(vHacdGammaOption).toFloat(); + } + float vHacdDelta = 0.05; if (parser.isSet(vHacdDeltaOption)) { vHacdDelta = parser.value(vHacdDeltaOption).toFloat(); @@ -261,8 +295,8 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : vHacdMaxVerticesPerCH = parser.value(vHacdMaxVerticesPerCHOption).toInt(); } - if (!parser.isSet(splitOption) && !generateHulls) { - cerr << "\nNothing to do! Use -g or --split\n\n"; + if (!splitModel && !generateHulls && !fattenFaces) { + cerr << "\nNothing to do! Use -g or -f or --split\n\n"; parser.showHelp(); Q_UNREACHABLE(); } @@ -279,14 +313,14 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : auto loadDuration = std::chrono::duration_cast(end - begin).count(); - if (parser.isSet(splitOption)) { + if (splitModel) { QVector infileExtensions = {"fbx", "obj"}; QString baseFileName = fileNameWithoutExtension(inputFilename, infileExtensions); int count = 0; foreach (const FBXMesh& mesh, fbx.meshes) { foreach (const FBXMeshPart &meshPart, mesh.parts) { QString outputFileName = baseFileName + "-" + QString::number(count) + ".obj"; - writeOBJ(outputFileName, fbx, count); + writeOBJ(outputFileName, fbx, outputCentimeters, count); count++; } } @@ -304,9 +338,9 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : params.m_delta = vHacdDelta; params.m_planeDownsampling = vHacdPlanedownsampling; params.m_convexhullDownsampling = vHacdConvexhulldownsampling; - params.m_alpha = 0.05; // 0.05 // controls the bias toward clipping along symmetry planes - params.m_beta = 0.05; // 0.05 - params.m_gamma = 0.0005; // 0.0005 + params.m_alpha = vHacdAlpha; + params.m_beta = vHacdBeta; + params.m_gamma = vHacdGamma; params.m_pca = 0; // 0 enable/disable normalizing the mesh before applying the convex decomposition params.m_mode = 0; // 0: voxel-based (recommended), 1: tetrahedron-based params.m_maxNumVerticesPerCH = vHacdMaxVerticesPerCH; @@ -321,7 +355,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : FBXGeometry result; if (!vUtil.computeVHACD(fbx, params, result, startMeshIndex, endMeshIndex, - minimumMeshSize, maximumMeshSize, fattenFaces)) { + minimumMeshSize, maximumMeshSize)) { cout << "Compute Failed..."; } end = std::chrono::high_resolution_clock::now(); @@ -350,7 +384,34 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : cout << "Total FBX load time: " << (double)loadDuration / 1000000000.00 << " seconds" << endl; cout << "V-HACD Compute time: " << (double)computeDuration / 1000000000.00 << " seconds" << endl; - writeOBJ(outputFilename, result); + writeOBJ(outputFilename, result, outputCentimeters); + } + + if (fattenFaces) { + FBXGeometry newFbx; + FBXMesh result; + + // count the mesh-parts + unsigned int meshCount = 0; + foreach (const FBXMesh& mesh, fbx.meshes) { + meshCount += mesh.parts.size(); + } + + if (startMeshIndex < 0) { + startMeshIndex = 0; + } + if (endMeshIndex < 0) { + endMeshIndex = meshCount; + } + + unsigned int meshPartCount = 0; + result.modelTransform = glm::mat4(); // Identity matrix + foreach (const FBXMesh& mesh, fbx.meshes) { + vUtil.fattenMeshes(mesh, result, meshPartCount, startMeshIndex, endMeshIndex); + } + + newFbx.meshes.append(result); + writeOBJ(outputFilename, newFbx, outputCentimeters); } }