From 7d2fa8d7ce40c5b73107210c91193b3917a9ab61 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Tue, 7 Apr 2015 10:20:19 -0700 Subject: [PATCH 01/19] Updated lightcontroller.js logic to find the nearest light when user clicks on light switch entity- this will allow lights to work even when a user has disabled other users from spawning items in his domain --- examples/entityScripts/lightController.js | 306 +++++++--------------- 1 file changed, 88 insertions(+), 218 deletions(-) diff --git a/examples/entityScripts/lightController.js b/examples/entityScripts/lightController.js index 6d6c0a59bd..de338b8d33 100644 --- a/examples/entityScripts/lightController.js +++ b/examples/entityScripts/lightController.js @@ -1,228 +1,98 @@ (function() { - this.entityID = null; - this.lightID = null; - this.sound = null; - this.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"] - var DEFAULT_USER_DATA = { - creatingLight: false, - lightID: null, - lightDefaultProperties: { - type: "Light", - position: { x: 0, y: 0, z: 0 }, - dimensions: { x: 5, y: 5, z: 5 }, - isSpotlight: false, - color: { red: 255, green: 48, blue: 0 }, - diffuseColor: { red: 255, green: 255, blue: 255 }, - ambientColor: { red: 255, green: 255, blue: 255 }, - specularColor: { red: 0, green: 0, blue: 0 }, - constantAttenuation: 1, - linearAttenuation: 0, - quadraticAttenuation: 0, - intensity: 10, - exponent: 0, - cutoff: 180, // in degrees - }, - soundIndex: Math.floor(Math.random() * this.soundURLs.length) - }; - - function copyObject(object) { - return JSON.parse(JSON.stringify(object)); - } - function didEntityExist(entityID) { - return entityID && entityID.isKnownID; - } - function doesEntityExistNow(entityID) { - return entityID && getTrueID(entityID).isKnownID; - } - function getTrueID(entityID) { - var properties = Entities.getEntityProperties(entityID); - return { id: properties.id, creatorTokenID: properties.creatorTokenID, isKnownID: properties.isKnownID }; - } - function getUserData(entityID) { - var properties = Entities.getEntityProperties(entityID); - if (properties.userData) { - return JSON.parse(properties.userData); - } else { - print("Warning: light controller has no user data."); - return null; - } - } - function updateUserData(entityID, userData) { - Entities.editEntity(entityID, { userData: JSON.stringify(userData) }); - } - - // Download sound if needed - this.maybeDownloadSound = function() { - if (this.sound === null) { - var soundIndex = getUserData(this.entityID).soundIndex; - this.sound = SoundCache.getSound(this.soundURLs[soundIndex]); - } - } - // Play switch sound - this.playSound = function() { - if (this.sound && this.sound.downloaded) { - Audio.playSound(this.sound, { - position: Entities.getEntityProperties(this.entityID).position, - volume: 0.2 - }); - } else { - print("Warning: Couldn't play sound."); - } - } - - // Checks whether the userData is well-formed and updates it if not - this.checkUserData = function() { - var userData = getUserData(this.entityID); - if (!userData) { - userData = DEFAULT_USER_DATA; - } else if (!userData.lightDefaultProperties) { - userData.lightDefaultProperties = DEFAULT_USER_DATA.lightDefaultProperties; - } else if (!userData.soundIndex) { - userData.soundIndex = DEFAULT_USER_DATA.soundIndex; - } - updateUserData(this.entityID, userData); - } - - // Create a Light entity - this.createLight = function(userData) { - var lightProperties = copyObject(userData.lightDefaultProperties); - if (lightProperties) { - var entityProperties = Entities.getEntityProperties(this.entityID); - - lightProperties.visible = false; - lightProperties.position = Vec3.sum(entityProperties.position, - Vec3.multiplyQbyV(entityProperties.rotation, - lightProperties.position)); - return Entities.addEntity(lightProperties); - } else { - print("Warning: light controller has no default light."); - return null; - } - } - - // Tries to find a valid light, creates one otherwise - this.updateLightID = function() { - // Find valid light - if (doesEntityExistNow(this.lightID)) { - return; - } - - var userData = getUserData(this.entityID); - if (doesEntityExistNow(userData.lightID)) { - this.lightID = userData.lightID; - return; - } - - if (!userData.creatingLight) { - // No valid light, create one - userData.creatingLight = true; - updateUserData(this.entityID, userData); - this.lightID = this.createLight(userData); - this.maybeUpdateLightIDInUserData(); - print("Created new light entity"); - } - } - - this.maybeUpdateLightIDInUserData = function() { - if (getTrueID(this.lightID).isKnownID) { - this.lightID = getTrueID(this.lightID); - this.updateLightIDInUserData(); - } else { - var that = this; - Script.setTimeout(function() { that.maybeUpdateLightIDInUserData() }, 500); - } - } - - // Update user data with new lightID - this.updateLightIDInUserData = function() { - var userData = getUserData(this.entityID); - userData.lightID = this.lightID; - userData.creatingLight = false; - updateUserData(this.entityID, userData); - } - - // Moves light entity if the lamp entity moved - this.maybeMoveLight = function() { - var entityProperties = Entities.getEntityProperties(this.entityID); - var lightProperties = Entities.getEntityProperties(this.lightID); - var lightDefaultProperties = getUserData(this.entityID).lightDefaultProperties; - - var position = Vec3.sum(entityProperties.position, - Vec3.multiplyQbyV(entityProperties.rotation, - lightDefaultProperties.position)); - - if (!Vec3.equal(position, lightProperties.position)) { - print("Lamp entity moved, moving light entity as well"); - Entities.editEntity(this.lightID, { position: position }); - } + this.preload = function(entityId) { + this.sound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_1.wav"); + this.entityId = entityId; + this.properties = Entities.getEntityProperties(this.entityId); + this.previousPosition = this.properties.position; + this.getUserData() + if (!this.userData) { + this.userData = {}; + this.userData.lightOn = false; } - // Stores light entity relative position in the lamp metadata - this.updateRelativeLightPosition = function() { - if (!doesEntityExistNow(this.lightID)) { - print("Warning: ID invalid, couldn't save relative position."); - return; - } + } - var userData = getUserData(this.entityID); - var entityProperties = Entities.getEntityProperties(this.entityID); - var lightProperties = Entities.getEntityProperties(this.lightID); - var newProperties = {}; - - // Copy only meaningful properties (trying to save space in userData here) - for (var key in userData.lightDefaultProperties) { - if (userData.lightDefaultProperties.hasOwnProperty(key)) { - newProperties[key] = lightProperties[key]; - } - } - - // Compute new relative position - newProperties.position = Quat.multiply(Quat.inverse(entityProperties.rotation), - Vec3.subtract(lightProperties.position, - entityProperties.position)); - // inverse "visible" because right after we loaded the properties, the light entity is toggled. - newProperties.visible = !lightProperties.visible; - - userData.lightDefaultProperties = copyObject(newProperties); - updateUserData(this.entityID, userData); - print("Relative properties of light entity saved."); + this.getUserData = function() { + if (this.properties.userData) { + this.userData = JSON.parse(this.properties.userData); } - - // This function should be called before any callback is executed - this.preOperation = function(entityID) { - this.entityID = entityID; - - this.checkUserData(); - this.maybeDownloadSound(); + 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) { + if (!Vec3.equal(this.properties.position, this.previousPosition)) { + //just get new offset + var offset = Vec3.subtract(this.properties.position, this.previousPosition); + var newWorldLightPosition = Vec3.sum(this.lightProperties.position, offset); + Entities.editEntity(this.light, { + position: newWorldLightPosition + }) + this.previousPosition = this.properties.position; + + } } - // Toggles the associated light entity - this.toggleLight = function() { - if (this.lightID) { - var lightProperties = Entities.getEntityProperties(this.lightID); - Entities.editEntity(this.lightID, { visible: !lightProperties.visible }); - this.playSound(); - } else { - print("Warning: No light to turn on/off"); - } - } + } - this.preload = function(entityID) { - this.preOperation(entityID); - } - - this.clickReleaseOnEntity = function(entityID, mouseEvent) { - this.preOperation(entityID); - - if (mouseEvent.isLeftButton) { - this.updateLightID(); - this.maybeMoveLight(); - this.toggleLight(); - } else if (mouseEvent.isRightButton) { - this.updateRelativeLightPosition(); + + 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 } - }; -}) \ No newline at end of file + } + } + return closestLight; + } + +}); \ No newline at end of file From 9c220b3e9b1ffbe5ab145c82823e35f1cfd55fff Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Tue, 7 Apr 2015 19:25:37 -0700 Subject: [PATCH 02/19] light container now assigned 1 of 3 random light switch sounds. Also light now changes position based on container's change in position AND rotation. --- examples/entityScripts/lightController.js | 24 +++++++++++------------ 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/examples/entityScripts/lightController.js b/examples/entityScripts/lightController.js index de338b8d33..93ce720728 100644 --- a/examples/entityScripts/lightController.js +++ b/examples/entityScripts/lightController.js @@ -1,7 +1,10 @@ (function() { - this.preload = function(entityId) { - this.sound = SoundCache.getSound("https://hifi-public.s3.amazonaws.com/sounds/Switches%20and%20sliders/lamp_switch_1.wav"); + 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"]; + var soundURL = soundURLs[Math.floor(Math.random() * soundURLs.length)]; + this.sound = SoundCache.getSound(soundURL); this.entityId = entityId; this.properties = Entities.getEntityProperties(this.entityId); this.previousPosition = this.properties.position; @@ -10,7 +13,6 @@ this.userData = {}; this.userData.lightOn = false; } - } this.getUserData = function() { @@ -27,6 +29,7 @@ } this.clickReleaseOnEntity = function(entityId, mouseEvent) { + print("ENTITY CLICK") if (!mouseEvent.isLeftButton) { return; } @@ -61,21 +64,16 @@ this.tryMoveLight = function() { if (this.light) { - if (!Vec3.equal(this.properties.position, this.previousPosition)) { - //just get new offset - var offset = Vec3.subtract(this.properties.position, this.previousPosition); - var newWorldLightPosition = Vec3.sum(this.lightProperties.position, offset); - Entities.editEntity(this.light, { - position: newWorldLightPosition - }) - this.previousPosition = this.properties.position; - + //compute offset position + var offsetPosition = Quat.multiply(Quat.inverse(this.properties.rotation), Vec3.subtract(this.lightProperties.position, this.properties.position)); + 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.findClosestLight = function() { var entities = Entities.findEntities(this.properties.position, 10); var lightEntities = []; From a16f6c2ce467e3de913b553b97c8f8a4fecf537c Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Tue, 7 Apr 2015 19:35:41 -0700 Subject: [PATCH 03/19] remove unnecessary print statement --- examples/entityScripts/lightController.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/entityScripts/lightController.js b/examples/entityScripts/lightController.js index 93ce720728..f04b3fed07 100644 --- a/examples/entityScripts/lightController.js +++ b/examples/entityScripts/lightController.js @@ -29,7 +29,6 @@ } this.clickReleaseOnEntity = function(entityId, mouseEvent) { - print("ENTITY CLICK") if (!mouseEvent.isLeftButton) { return; } From 6de4cec836ea67f19c03a0da4132e49b137cac68 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Wed, 8 Apr 2015 09:20:29 -0700 Subject: [PATCH 04/19] made randomly chosen sound persistent between sessions by storing sound index in user data. Now using previous rotation and position to compute offset position --- examples/entityScripts/lightController.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/examples/entityScripts/lightController.js b/examples/entityScripts/lightController.js index f04b3fed07..3621f5347d 100644 --- a/examples/entityScripts/lightController.js +++ b/examples/entityScripts/lightController.js @@ -3,18 +3,22 @@ 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"]; - var soundURL = soundURLs[Math.floor(Math.random() * soundURLs.length)]; - this.sound = SoundCache.getSound(soundURL); + this.sound = ; 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); @@ -64,11 +68,13 @@ this.tryMoveLight = function() { if (this.light) { //compute offset position - var offsetPosition = Quat.multiply(Quat.inverse(this.properties.rotation), Vec3.subtract(this.lightProperties.position, this.properties.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; } } From b8719f6e5f283eefd7bdc3e0b47749f4e6d5fee9 Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Wed, 8 Apr 2015 09:21:24 -0700 Subject: [PATCH 05/19] made new alternative light script --- .../{lightController.js => alternativeLightController.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename examples/entityScripts/{lightController.js => alternativeLightController.js} (100%) diff --git a/examples/entityScripts/lightController.js b/examples/entityScripts/alternativeLightController.js similarity index 100% rename from examples/entityScripts/lightController.js rename to examples/entityScripts/alternativeLightController.js From 62fe9870e3420c7175c01da2e582ea5fb3433ee2 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 8 Apr 2015 10:48:13 -0700 Subject: [PATCH 06/19] more command-line arguments --- tools/vhacd/src/VHACDUtilApp.cpp | 41 +++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/tools/vhacd/src/VHACDUtilApp.cpp b/tools/vhacd/src/VHACDUtilApp.cpp index ba3fbb94bd..f421f0245f 100644 --- a/tools/vhacd/src/VHACDUtilApp.cpp +++ b/tools/vhacd/src/VHACDUtilApp.cpp @@ -137,7 +137,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 +166,6 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : "plane selection stage (range=1-16)", "4"); parser.addOption(vHacdConvexhulldownsamplingOption); - // alpha - // beta - // gamma - // delta // pca // mode @@ -236,6 +246,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(); @@ -304,9 +329,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 = vHacdBeta; 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; From 12f5b992c99da424b47b74d9986d79dcdd0490d6 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 8 Apr 2015 20:01:01 -0700 Subject: [PATCH 07/19] Add /LARGEADDRESSAWARE linker flag for Windows builds Enables Interface to use more than 2GB of memory. Reduces the incidence of bad_alloc crashes. --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) 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") From e7688526456bba02c88ffdd2e42ae9341e709f79 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 8 Apr 2015 20:02:22 -0700 Subject: [PATCH 08/19] Fix catching bad_alloc Non-allocation wasn't caught on Windows; it just crashed. --- libraries/gpu/src/gpu/Resource.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp index f1956727f8..e9be897170 100644 --- a/libraries/gpu/src/gpu/Resource.cpp +++ b/libraries/gpu/src/gpu/Resource.cpp @@ -27,9 +27,10 @@ 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]; - // Failed? - if (!(*dataAllocated)) { + try { + (*dataAllocated) = new Byte[newSize]; + } + catch (const std::bad_alloc&) { qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; return NOT_ALLOCATED; } From 76d6c6cac5b3a097301a16d9f554ff2591e66d93 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 8 Apr 2015 20:03:42 -0700 Subject: [PATCH 09/19] Delete char* items properly --- libraries/octree/src/Octree.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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; } From 298a81cea905ae142d682b7d17fe3023cefd61a1 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 9 Apr 2015 14:42:26 +0200 Subject: [PATCH 10/19] Trim entity properties set from JS that are strings --- libraries/entities/src/EntityItemPropertiesMacros.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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); \ } \ From b2dd53ac431f2d76c41b6d35d47910f18439edee Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 9 Apr 2015 08:37:24 -0700 Subject: [PATCH 11/19] Coding standard --- libraries/gpu/src/gpu/Resource.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp index e9be897170..a13a5cc444 100644 --- a/libraries/gpu/src/gpu/Resource.cpp +++ b/libraries/gpu/src/gpu/Resource.cpp @@ -29,8 +29,7 @@ Resource::Size Resource::Sysmem::allocateMemory(Byte** dataAllocated, Size size) newSize = size; try { (*dataAllocated) = new Byte[newSize]; - } - catch (const std::bad_alloc&) { + } catch (const std::bad_alloc&) { qWarning() << "Buffer::Sysmem::allocate() : Can't allocate a system memory buffer of " << newSize << "bytes. Fails to create the buffer Sysmem."; return NOT_ALLOCATED; } From 5efb9606459c8241f4fa685223d2a8bf7e738164 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Thu, 9 Apr 2015 09:18:53 -0700 Subject: [PATCH 12/19] Ensure blending is enabled during starfield rendering --- interface/src/starfield/renderer/Renderer.cpp | 1 + 1 file changed, 1 insertion(+) 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(); From c0e7cd1f6afbcc898faa8a7aa43f2602cb28ef3a Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 9 Apr 2015 09:50:12 -0700 Subject: [PATCH 13/19] deleted unnecessary line with typo --- examples/entityScripts/alternativeLightController.js | 1 - 1 file changed, 1 deletion(-) diff --git a/examples/entityScripts/alternativeLightController.js b/examples/entityScripts/alternativeLightController.js index 3621f5347d..c3ea56ff77 100644 --- a/examples/entityScripts/alternativeLightController.js +++ b/examples/entityScripts/alternativeLightController.js @@ -3,7 +3,6 @@ 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.sound = ; this.entityId = entityId; this.properties = Entities.getEntityProperties(this.entityId); this.previousPosition = this.properties.position; From ae3d606e951574070ca7890b14703d9ccf4afc57 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 9 Apr 2015 10:01:31 -0700 Subject: [PATCH 14/19] Replace try with nothrow --- libraries/gpu/src/gpu/Resource.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/gpu/src/gpu/Resource.cpp b/libraries/gpu/src/gpu/Resource.cpp index a13a5cc444..708ca0f627 100644 --- a/libraries/gpu/src/gpu/Resource.cpp +++ b/libraries/gpu/src/gpu/Resource.cpp @@ -27,9 +27,9 @@ 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; - try { - (*dataAllocated) = new Byte[newSize]; - } catch (const std::bad_alloc&) { + (*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."; return NOT_ALLOCATED; } From 396325629efbf0c6b571dd107f18af16e2125bca Mon Sep 17 00:00:00 2001 From: Eric Levin Date: Thu, 9 Apr 2015 10:43:34 -0700 Subject: [PATCH 15/19] added back original light controller script --- examples/entityScripts/lightController.js | 228 ++++++++++++++++++++++ 1 file changed, 228 insertions(+) create mode 100644 examples/entityScripts/lightController.js diff --git a/examples/entityScripts/lightController.js b/examples/entityScripts/lightController.js new file mode 100644 index 0000000000..6d6c0a59bd --- /dev/null +++ b/examples/entityScripts/lightController.js @@ -0,0 +1,228 @@ +(function() { + this.entityID = null; + this.lightID = null; + this.sound = null; + this.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"] + + var DEFAULT_USER_DATA = { + creatingLight: false, + lightID: null, + lightDefaultProperties: { + type: "Light", + position: { x: 0, y: 0, z: 0 }, + dimensions: { x: 5, y: 5, z: 5 }, + isSpotlight: false, + color: { red: 255, green: 48, blue: 0 }, + diffuseColor: { red: 255, green: 255, blue: 255 }, + ambientColor: { red: 255, green: 255, blue: 255 }, + specularColor: { red: 0, green: 0, blue: 0 }, + constantAttenuation: 1, + linearAttenuation: 0, + quadraticAttenuation: 0, + intensity: 10, + exponent: 0, + cutoff: 180, // in degrees + }, + soundIndex: Math.floor(Math.random() * this.soundURLs.length) + }; + + function copyObject(object) { + return JSON.parse(JSON.stringify(object)); + } + function didEntityExist(entityID) { + return entityID && entityID.isKnownID; + } + function doesEntityExistNow(entityID) { + return entityID && getTrueID(entityID).isKnownID; + } + function getTrueID(entityID) { + var properties = Entities.getEntityProperties(entityID); + return { id: properties.id, creatorTokenID: properties.creatorTokenID, isKnownID: properties.isKnownID }; + } + function getUserData(entityID) { + var properties = Entities.getEntityProperties(entityID); + if (properties.userData) { + return JSON.parse(properties.userData); + } else { + print("Warning: light controller has no user data."); + return null; + } + } + function updateUserData(entityID, userData) { + Entities.editEntity(entityID, { userData: JSON.stringify(userData) }); + } + + // Download sound if needed + this.maybeDownloadSound = function() { + if (this.sound === null) { + var soundIndex = getUserData(this.entityID).soundIndex; + this.sound = SoundCache.getSound(this.soundURLs[soundIndex]); + } + } + // Play switch sound + this.playSound = function() { + if (this.sound && this.sound.downloaded) { + Audio.playSound(this.sound, { + position: Entities.getEntityProperties(this.entityID).position, + volume: 0.2 + }); + } else { + print("Warning: Couldn't play sound."); + } + } + + // Checks whether the userData is well-formed and updates it if not + this.checkUserData = function() { + var userData = getUserData(this.entityID); + if (!userData) { + userData = DEFAULT_USER_DATA; + } else if (!userData.lightDefaultProperties) { + userData.lightDefaultProperties = DEFAULT_USER_DATA.lightDefaultProperties; + } else if (!userData.soundIndex) { + userData.soundIndex = DEFAULT_USER_DATA.soundIndex; + } + updateUserData(this.entityID, userData); + } + + // Create a Light entity + this.createLight = function(userData) { + var lightProperties = copyObject(userData.lightDefaultProperties); + if (lightProperties) { + var entityProperties = Entities.getEntityProperties(this.entityID); + + lightProperties.visible = false; + lightProperties.position = Vec3.sum(entityProperties.position, + Vec3.multiplyQbyV(entityProperties.rotation, + lightProperties.position)); + return Entities.addEntity(lightProperties); + } else { + print("Warning: light controller has no default light."); + return null; + } + } + + // Tries to find a valid light, creates one otherwise + this.updateLightID = function() { + // Find valid light + if (doesEntityExistNow(this.lightID)) { + return; + } + + var userData = getUserData(this.entityID); + if (doesEntityExistNow(userData.lightID)) { + this.lightID = userData.lightID; + return; + } + + if (!userData.creatingLight) { + // No valid light, create one + userData.creatingLight = true; + updateUserData(this.entityID, userData); + this.lightID = this.createLight(userData); + this.maybeUpdateLightIDInUserData(); + print("Created new light entity"); + } + } + + this.maybeUpdateLightIDInUserData = function() { + if (getTrueID(this.lightID).isKnownID) { + this.lightID = getTrueID(this.lightID); + this.updateLightIDInUserData(); + } else { + var that = this; + Script.setTimeout(function() { that.maybeUpdateLightIDInUserData() }, 500); + } + } + + // Update user data with new lightID + this.updateLightIDInUserData = function() { + var userData = getUserData(this.entityID); + userData.lightID = this.lightID; + userData.creatingLight = false; + updateUserData(this.entityID, userData); + } + + // Moves light entity if the lamp entity moved + this.maybeMoveLight = function() { + var entityProperties = Entities.getEntityProperties(this.entityID); + var lightProperties = Entities.getEntityProperties(this.lightID); + var lightDefaultProperties = getUserData(this.entityID).lightDefaultProperties; + + var position = Vec3.sum(entityProperties.position, + Vec3.multiplyQbyV(entityProperties.rotation, + lightDefaultProperties.position)); + + if (!Vec3.equal(position, lightProperties.position)) { + print("Lamp entity moved, moving light entity as well"); + Entities.editEntity(this.lightID, { position: position }); + } + } + + // Stores light entity relative position in the lamp metadata + this.updateRelativeLightPosition = function() { + if (!doesEntityExistNow(this.lightID)) { + print("Warning: ID invalid, couldn't save relative position."); + return; + } + + var userData = getUserData(this.entityID); + var entityProperties = Entities.getEntityProperties(this.entityID); + var lightProperties = Entities.getEntityProperties(this.lightID); + var newProperties = {}; + + // Copy only meaningful properties (trying to save space in userData here) + for (var key in userData.lightDefaultProperties) { + if (userData.lightDefaultProperties.hasOwnProperty(key)) { + newProperties[key] = lightProperties[key]; + } + } + + // Compute new relative position + newProperties.position = Quat.multiply(Quat.inverse(entityProperties.rotation), + Vec3.subtract(lightProperties.position, + entityProperties.position)); + // inverse "visible" because right after we loaded the properties, the light entity is toggled. + newProperties.visible = !lightProperties.visible; + + userData.lightDefaultProperties = copyObject(newProperties); + updateUserData(this.entityID, userData); + print("Relative properties of light entity saved."); + } + + // This function should be called before any callback is executed + this.preOperation = function(entityID) { + this.entityID = entityID; + + this.checkUserData(); + this.maybeDownloadSound(); + } + + // Toggles the associated light entity + this.toggleLight = function() { + if (this.lightID) { + var lightProperties = Entities.getEntityProperties(this.lightID); + Entities.editEntity(this.lightID, { visible: !lightProperties.visible }); + this.playSound(); + } else { + print("Warning: No light to turn on/off"); + } + } + + this.preload = function(entityID) { + this.preOperation(entityID); + } + + this.clickReleaseOnEntity = function(entityID, mouseEvent) { + this.preOperation(entityID); + + if (mouseEvent.isLeftButton) { + this.updateLightID(); + this.maybeMoveLight(); + this.toggleLight(); + } else if (mouseEvent.isRightButton) { + this.updateRelativeLightPosition(); + } + }; +}) \ No newline at end of file From d6c15549e4744d1f27c414fd78526fbeb61448da Mon Sep 17 00:00:00 2001 From: David Rowe Date: Thu, 9 Apr 2015 13:03:12 -0700 Subject: [PATCH 16/19] Tweak users display to work better with variety of backgrounds --- examples/users.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) 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 From efa5c473d2a4a5f154e49d60ac692f078a9f2992 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 9 Apr 2015 14:29:17 -0700 Subject: [PATCH 17/19] add fatten-faces mode to convex-hull tool for use on meshes which were derived from height-fields --- libraries/fbx/src/OBJReader.cpp | 34 ++++---- libraries/fbx/src/OBJReader.h | 1 + tools/vhacd/src/VHACDUtil.cpp | 131 +++++++++++++++++++------------ tools/vhacd/src/VHACDUtil.h | 10 ++- tools/vhacd/src/VHACDUtilApp.cpp | 54 ++++++++++--- 5 files changed, 152 insertions(+), 78 deletions(-) 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/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 f421f0245f..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); @@ -188,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; @@ -286,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(); } @@ -304,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++; } } @@ -331,7 +340,7 @@ VHACDUtilApp::VHACDUtilApp(int argc, char* argv[]) : params.m_convexhullDownsampling = vHacdConvexhulldownsampling; params.m_alpha = vHacdAlpha; params.m_beta = vHacdBeta; - params.m_gamma = 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; @@ -346,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(); @@ -375,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); } } From 4ada3fc9a4acfc1d491e51c1daa93b370dcb9102 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Thu, 9 Apr 2015 15:12:51 -0700 Subject: [PATCH 18/19] don't keep large things out of physics engine --- libraries/physics/src/ShapeManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; } From 5a9ee734764e467c424806a30fe5f7330cf4d148 Mon Sep 17 00:00:00 2001 From: Ryan Huffman Date: Thu, 9 Apr 2015 20:10:00 -0700 Subject: [PATCH 19/19] Fix error in entity properties not showing selections --- examples/html/entityProperties.html | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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;