diff --git a/examples/controllers/hydra/gun.js b/examples/controllers/hydra/gun.js index 8bea5d623e..f2ed50733d 100644 --- a/examples/controllers/hydra/gun.js +++ b/examples/controllers/hydra/gun.js @@ -4,6 +4,7 @@ // // Created by Brad Hefta-Gaub on 12/31/13. // Modified by Philip on 3/3/14 +// Modified by Thijs Wenker on 3/31/15 // Copyright 2013 High Fidelity, Inc. // // This is an example script that turns the hydra controllers and mouse into a entity gun. @@ -66,7 +67,7 @@ var impactSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Guns/BulletIm var targetHitSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/hit.raw"); var targetLaunchSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/Space%20Invaders/shoot.raw"); -var gunModel = "http://public.highfidelity.io/models/attachments/HaloGun.fst"; +var gunModel = "https://s3.amazonaws.com/hifi-public/cozza13/gun/m1911-handgun+1.fbx?v=4"; var audioOptions = { volume: 0.9 @@ -90,44 +91,49 @@ var score = 0; var bulletID = false; var targetID = false; -// Create a reticle image in center of screen +// Create overlay buttons and reticle + +var BUTTON_SIZE = 32; +var PADDING = 3; +var NUM_BUTTONS = 3; + var screenSize = Controller.getViewportDimensions(); +var startX = screenSize.x / 2 - (NUM_BUTTONS * (BUTTON_SIZE + PADDING)) / 2; var reticle = Overlays.addOverlay("image", { - x: screenSize.x / 2 - 16, - y: screenSize.y / 2 - 16, - width: 32, - height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/billiardsReticle.png", - color: { red: 255, green: 255, blue: 255}, + x: screenSize.x / 2 - (BUTTON_SIZE / 2), + y: screenSize.y / 2 - (BUTTON_SIZE / 2), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/crosshairs.svg", alpha: 1 }); var offButton = Overlays.addOverlay("image", { - x: screenSize.x - 48, - y: 96, - width: 32, - height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", - color: { red: 255, green: 255, blue: 255}, + x: startX, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/close.svg", alpha: 1 }); +startX += BUTTON_SIZE + PADDING; var platformButton = Overlays.addOverlay("image", { - x: screenSize.x - 48, - y: 130, - width: 32, - height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/city.png", - color: { red: 255, green: 255, blue: 255}, + x: startX, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/platform-targets.svg", alpha: 1 }); + +startX += BUTTON_SIZE + PADDING; var gridButton = Overlays.addOverlay("image", { - x: screenSize.x - 48, - y: 164, - width: 32, - height: 32, - imageURL: HIFI_PUBLIC_BUCKET + "images/blocks.png", - color: { red: 255, green: 255, blue: 255}, + x: startX, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, + imageURL: HIFI_PUBLIC_BUCKET + "images/gun/floating-targets.svg", alpha: 1 }); @@ -163,7 +169,7 @@ function shootBullet(position, velocity, grenade) { { type: "Sphere", position: position, dimensions: { x: bSize, y: bSize, z: bSize }, - color: { red: 255, green: 0, blue: 0 }, + color: { red: 0, green: 0, blue: 0 }, velocity: bVelocity, lifetime: BULLET_LIFETIME, gravity: { x: 0, y: bGravity, z: 0 }, @@ -260,6 +266,7 @@ function makeGrid(type, scale, size) { } } } + function makePlatform(gravity, scale, size) { var separation = scale * 2; var pos = Vec3.sum(Camera.getPosition(), Vec3.multiply(10.0 * scale * separation, Quat.getFront(Camera.getOrientation()))); @@ -282,7 +289,7 @@ function makePlatform(gravity, scale, size) { z: pos.z - (separation * size / 2.0) + z * separation }, dimensions: dimensions, color: { red: Math.random() * 255, green: Math.random() * 255, blue: Math.random() * 255 }, - velocity: { x: 0, y: 0, z: 0 }, + velocity: { x: 0, y: 0.05, z: 0 }, gravity: { x: 0, y: gravity, z: 0 }, lifetime: TARGET_LIFE, damping: 0.1, @@ -297,7 +304,7 @@ function makePlatform(gravity, scale, size) { type: "Box", position: { x: pos.x, y: pos.y - separation / 2.0, z: pos.z }, dimensions: { x: 2.0 * separation * size, y: separation / 2.0, z: 2.0 * separation * size }, - color: { red: 128, green: 128, blue: 128 }, + color: { red: 100, green: 100, blue: 100 }, lifetime: TARGET_LIFE }); @@ -372,8 +379,8 @@ function takeFiringPose() { } } -MyAvatar.attach(gunModel, "RightHand", {x:0.02, y: 0.11, z: 0.04}, Quat.fromPitchYawRollDegrees(-0, -160, -79), 0.20); -MyAvatar.attach(gunModel, "LeftHand", {x:-0.02, y: 0.11, z: 0.04}, Quat.fromPitchYawRollDegrees(0, 0, 79), 0.20); +MyAvatar.attach(gunModel, "RightHand", {x:0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, -85, 79), 0.40); +MyAvatar.attach(gunModel, "LeftHand", {x:-0.04, y: 0.22, z: 0.02}, Quat.fromPitchYawRollDegrees(-172, 85, -79), 0.40); // Give a bit of time to load before playing sound Script.setTimeout(playLoadSound, 2000); diff --git a/examples/defaultScripts.js b/examples/defaultScripts.js index 05ffb0bd3f..9d2a74fe56 100644 --- a/examples/defaultScripts.js +++ b/examples/defaultScripts.js @@ -14,7 +14,6 @@ Script.load("selectAudioDevice.js"); Script.load("controllers/hydra/hydraMove.js"); Script.load("headMove.js"); Script.load("inspect.js"); -Script.load("lobby.js"); Script.load("notifications.js"); Script.load("look.js"); Script.load("users.js"); diff --git a/examples/dice.js b/examples/dice.js index 6943c926ae..b118a6b289 100644 --- a/examples/dice.js +++ b/examples/dice.js @@ -26,20 +26,24 @@ var rollSound = SoundCache.getSound(HIFI_PUBLIC_BUCKET + "sounds/dice/diceRoll.w var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to create new objects." var screenSize = Controller.getViewportDimensions(); + +var BUTTON_SIZE = 32; +var PADDING = 3; + var offButton = Overlays.addOverlay("image", { - x: screenSize.x - 48, - y: 96, - width: 32, - height: 32, + x: screenSize.x / 2 - BUTTON_SIZE, + y: screenSize.y- (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/close.png", color: { red: 255, green: 255, blue: 255}, alpha: 1 }); var diceButton = Overlays.addOverlay("image", { - x: screenSize.x - 48, - y: 130, - width: 32, - height: 32, + x: screenSize.x / 2 + PADDING, + y: screenSize.y - (BUTTON_SIZE + PADDING), + width: BUTTON_SIZE, + height: BUTTON_SIZE, imageURL: HIFI_PUBLIC_BUCKET + "images/die.png", color: { red: 255, green: 255, blue: 255}, alpha: 1 @@ -50,6 +54,7 @@ var LIFETIME = 300; // NOTE: angularVelocity is in radians/sec var MAX_ANGULAR_SPEED = Math.PI; + function shootDice(position, velocity) { if (!Entities.canRez()) { Window.alert(INSUFFICIENT_PERMISSIONS_ERROR_MSG); @@ -100,7 +105,7 @@ function mousePressEvent(event) { var clickedText = false; var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); if (clickedOverlay == offButton) { - deleteDice(); + Script.stop(); } else if (clickedOverlay == diceButton) { var HOW_HARD = 2.0; var position = Vec3.sum(Camera.getPosition(), Quat.getFront(Camera.getOrientation())); diff --git a/examples/edit.js b/examples/edit.js index 08a2c4f3f7..156cf44fa0 100644 --- a/examples/edit.js +++ b/examples/edit.js @@ -16,8 +16,6 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; Script.include([ "libraries/stringHelpers.js", "libraries/dataviewHelpers.js", - "libraries/httpMultiPart.js", - "libraries/modelUploader.js", "libraries/toolBars.js", "libraries/progressDialog.js", diff --git a/examples/example/misc/sunLightExample.js b/examples/example/misc/sunLightExample.js index 42837c6836..e6c06bb1ae 100644 --- a/examples/example/misc/sunLightExample.js +++ b/examples/example/misc/sunLightExample.js @@ -8,27 +8,82 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -var intensity = 1.0; -var day = 0.0; -var hour = 12.0; -var longitude = 115.0; -var latitude = 31.0; -var stageOrientation = Quat.fromPitchYawRollDegrees(0.0, 180.0, 0.0); +Script.include("../../utilities/tools/cookies.js"); -Scene.setStageDayTime(hour); -Scene.setStageOrientation(stageOrientation); -Scene.setStageLocation(longitude, latitude, 0.0); -/* -function ticktack() { - hour += 0.1; - //Scene.setSunIntensity(Math.cos(time)); - if (hour > 24.0) { - hour = 0.0; - day++; - Scene.setStageYearTime(day); +var panel = new Panel(10, 400); + +panel.newSlider("Origin Longitude", -180, 180, + function(value) { Scene.setStageLocation(value, Scene.getStageLocationLatitude(), Scene.getStageLocationAltitude()); }, + function() { return Scene.getStageLocationLongitude(); }, + function(value) { return value.toFixed(0) + " deg"; } +); + +panel.newSlider("Origin Latitude", -90, 90, + function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), value, Scene.getStageLocationAltitude()); }, + function() { return Scene.getStageLocationLatitude(); }, + function(value) { return value.toFixed(0) + " deg"; } +); + +panel.newSlider("Origin Altitude", 0, 1000, + function(value) { Scene.setStageLocation(Scene.getStageLocationLongitude(), Scene.getStageLocationLatitude(), value); }, + function() { return Scene.getStageLocationAltitude(); }, + function(value) { return (value).toFixed(0) + " km"; } +); + +panel.newSlider("Year Time", 0, 364, + function(value) { Scene.setStageYearTime(value); }, + function() { return Scene.getStageYearTime(); }, + function(value) { + var numDaysPerMonth = 365.0 / 12.0; + var monthly = (value / numDaysPerMonth); + var month = Math.floor(monthly); + return (month + 1).toFixed(0) + "/" + Math.ceil(0.5 + (monthly - month)*Math.ceil(numDaysPerMonth)).toFixed(0); } +); + +panel.newSlider("Day Time", 0, 24, + function(value) { Scene.setStageDayTime(value); }, + function() { return Scene.getStageDayTime(); }, + function(value) { + var hour = Math.floor(value); + return (hour).toFixed(0) + ":" + ((value - hour)*60.0).toFixed(0); } - Scene.setStageDayTime(hour); -} +); -Script.setInterval(ticktack, 41); -*/ \ No newline at end of file +var tickTackPeriod = 50; +var tickTackSpeed = 0.0; +panel.newSlider("Tick tack time", -1.0, 1.0, + function(value) { tickTackSpeed = value; }, + function() { return tickTackSpeed; }, + function(value) { return (value).toFixed(2); } +); + +function runStageTime() { + if (tickTackSpeed != 0.0) { + var hour = panel.get("Day Time"); + hour += tickTackSpeed; + panel.set("Day Time", hour); + + if (hour >= 24.0) { + panel.set("Year Time", panel.get("Year Time") + 1); + } else if (hour < 0.0) { + panel.set("Year Time", panel.get("Year Time") - 1); + } + } +} +Script.setInterval(runStageTime, tickTackPeriod); + +panel.newSlider("Light Intensity", 0.0, 5, + function(value) { Scene.setSunIntensity(value); }, + function() { return Scene.getSunIntensity(); }, + function(value) { return (value).toFixed(2); } +); + +Controller.mouseMoveEvent.connect(function panelMouseMoveEvent(event) { return panel.mouseMoveEvent(event); }); +Controller.mousePressEvent.connect( function panelMousePressEvent(event) { return panel.mousePressEvent(event); }); +Controller.mouseReleaseEvent.connect(function(event) { return panel.mouseReleaseEvent(event); }); + +function scriptEnding() { + Menu.removeMenu("Developer > Scene"); + panel.destroy(); +} +Script.scriptEnding.connect(scriptEnding); diff --git a/examples/html/entityProperties.html b/examples/html/entityProperties.html index 1c5fd58084..0c2e09ef16 100644 --- a/examples/html/entityProperties.html +++ b/examples/html/entityProperties.html @@ -312,7 +312,7 @@ } elTextText.value = properties.text; - elTextLineHeight.value = properties.lineHeight; + elTextLineHeight.value = properties.lineHeight.toFixed(4); elTextTextColorRed.value = properties.textColor.red; elTextTextColorGreen.value = properties.textColor.green; elTextTextColorBlue.value = properties.textColor.blue; @@ -477,6 +477,28 @@ ev.initEvent("change", true, true); document.activeElement.dispatchEvent(ev); } + + // For input and textarea elements, select all of the text on focus + // WebKit-based browsers, such as is used with QWebView, have a quirk + // where the mouseup event comes after the focus event, causing the + // text to be deselected immediately after selecting all of the text. + // To make this work we block the first mouseup event after the elements + // received focus. If we block all mouseup events the user will not + // be able to click within the selected text. + var els = document.querySelectorAll("input, textarea"); + for (var i = 0; i < els.length; i++) { + var clicked = false; + els[i].onfocus = function() { + this.select(); + clicked = false; + }; + els[i].onmouseup = function(e) { + if (!clicked) { + e.preventDefault(); + clicked = true; + } + }; + } } @@ -723,7 +745,7 @@
Line Height
- +
diff --git a/examples/libraries/modelUploader.js b/examples/libraries/modelUploader.js deleted file mode 100644 index 64a9e91203..0000000000 --- a/examples/libraries/modelUploader.js +++ /dev/null @@ -1,693 +0,0 @@ -// -// modelUploader.js -// examples/libraries -// -// 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 -// - - -modelUploader = (function () { - var that = {}, - modelFile, - modelName, - modelURL, - modelCallback, - isProcessing, - fstBuffer, - fbxBuffer, - //svoBuffer, - mapping, - geometry, - API_URL = "https://metaverse.highfidelity.com/api/v1/models", - MODEL_URL = "http://public.highfidelity.com/models/content", - NAME_FIELD = "name", - SCALE_FIELD = "scale", - FILENAME_FIELD = "filename", - TEXDIR_FIELD = "texdir", - MAX_TEXTURE_SIZE = 1024; - - function info(message) { - if (progressDialog.isOpen()) { - progressDialog.update(message); - } else { - progressDialog.open(message); - } - print(message); - } - - function error(message) { - if (progressDialog.isOpen()) { - progressDialog.close(); - } - print(message); - Window.alert(message); - } - - function randomChar(length) { - var characters = "0123457689abcdefghijklmnopqrstuvwxyz", - string = "", - i; - - for (i = 0; i < length; i += 1) { - string += characters[Math.floor(Math.random() * 36)]; - } - - return string; - } - - function resetDataObjects() { - fstBuffer = null; - fbxBuffer = null; - //svoBuffer = null; - mapping = {}; - geometry = {}; - geometry.textures = []; - geometry.embedded = []; - } - - function readFile(filename) { - var url = "file:///" + filename, - req = new XMLHttpRequest(); - - req.open("GET", url, false); - req.responseType = "arraybuffer"; - req.send(); - if (req.status !== 200) { - error("Could not read file: " + filename + " : " + req.statusText); - return null; - } - - return { - filename: filename.fileName(), - buffer: req.response - }; - } - - function readMapping(buffer) { - var dv = new DataView(buffer.buffer), - lines, - line, - tokens, - i, - name, - value, - remainder, - existing; - - mapping = {}; // { name : value | name : { value : [remainder] } } - lines = dv.string(0, dv.byteLength).split(/\r\n|\r|\n/); - for (i = 0; i < lines.length; i += 1) { - line = lines[i].trim(); - if (line.length > 0 && line[0] !== "#") { - tokens = line.split(/\s*=\s*/); - if (tokens.length > 1) { - name = tokens[0]; - value = tokens[1]; - if (tokens.length > 2) { - remainder = tokens.slice(2, tokens.length).join(" = "); - } else { - remainder = null; - } - if (tokens.length === 2 && mapping[name] === undefined) { - mapping[name] = value; - } else { - if (mapping[name] === undefined) { - mapping[name] = {}; - - } else if (typeof mapping[name] !== "object") { - existing = mapping[name]; - mapping[name] = { existing : null }; - } - - if (mapping[name][value] === undefined) { - mapping[name][value] = []; - } - mapping[name][value].push(remainder); - } - } - } - } - } - - function writeMapping(buffer) { - var name, - value, - remainder, - i, - string = ""; - - for (name in mapping) { - if (mapping.hasOwnProperty(name)) { - if (typeof mapping[name] === "object") { - for (value in mapping[name]) { - if (mapping[name].hasOwnProperty(value)) { - remainder = mapping[name][value]; - if (remainder === null) { - string += (name + " = " + value + "\n"); - } else { - for (i = 0; i < remainder.length; i += 1) { - string += (name + " = " + value + " = " + remainder[i] + "\n"); - } - } - } - } - } else { - string += (name + " = " + mapping[name] + "\n"); - } - } - } - - buffer.buffer = string.toArrayBuffer(); - } - - function readGeometry(fbxBuffer) { - var textures, - view, - index, - EOF, - previousNodeFilename; - - // Reference: - // http://code.blender.org/index.php/2013/08/fbx-binary-file-format-specification/ - - textures = {}; - view = new DataView(fbxBuffer.buffer); - EOF = false; - - function parseBinaryFBX() { - var endOffset, - numProperties, - propertyListLength, - nameLength, - name, - filename; - - endOffset = view.getUint32(index, true); - numProperties = view.getUint32(index + 4, true); - propertyListLength = view.getUint32(index + 8, true); - nameLength = view.getUint8(index + 12); - index += 13; - - if (endOffset === 0) { - return; - } - if (endOffset < index || endOffset > view.byteLength) { - EOF = true; - return; - } - - name = view.string(index, nameLength).toLowerCase(); - index += nameLength; - - if (name === "content" && previousNodeFilename !== "") { - // Blender 2.71 exporter "embeds" external textures as empty binary blobs so ignore these - if (propertyListLength > 5) { - geometry.embedded.push(previousNodeFilename); - } - } - - if (name === "relativefilename") { - filename = view.string(index + 5, view.getUint32(index + 1, true)).fileName(); - if (!textures.hasOwnProperty(filename)) { - textures[filename] = ""; - geometry.textures.push(filename); - } - previousNodeFilename = filename; - } else { - previousNodeFilename = ""; - } - - index += (propertyListLength); - - while (index < endOffset && !EOF) { - parseBinaryFBX(); - } - } - - function readTextFBX() { - var line, - view, - viewLength, - charCode, - charCodes, - numCharCodes, - filename, - relativeFilename = "", - MAX_CHAR_CODES = 250; - - view = new Uint8Array(fbxBuffer.buffer); - viewLength = view.byteLength; - charCodes = []; - numCharCodes = 0; - - for (index = 0; index < viewLength; index += 1) { - charCode = view[index]; - if (charCode !== 9 && charCode !== 32) { - if (charCode === 10) { // EOL. Can ignore EOF. - line = String.fromCharCode.apply(String, charCodes).toLowerCase(); - // For embedded textures, "Content:" line immediately follows "RelativeFilename:" line. - if (line.slice(0, 8) === "content:" && relativeFilename !== "") { - geometry.embedded.push(relativeFilename); - } - if (line.slice(0, 17) === "relativefilename:") { - filename = line.slice(line.indexOf("\""), line.lastIndexOf("\"") - line.length).fileName(); - if (!textures.hasOwnProperty(filename)) { - textures[filename] = ""; - geometry.textures.push(filename); - } - relativeFilename = filename; - } else { - relativeFilename = ""; - } - charCodes = []; - numCharCodes = 0; - } else { - if (numCharCodes < MAX_CHAR_CODES) { // Only interested in start of line - charCodes.push(charCode); - numCharCodes += 1; - } - } - } - } - } - - - - readTextFBX(); - - - } - - function readModel() { - var fbxFilename, - //svoFilename, - fileType; - - info("Reading model file"); - print("Model file: " + modelFile); - - if (modelFile.toLowerCase().fileType() === "fst") { - fstBuffer = readFile(modelFile); - if (fstBuffer === null) { - return false; - } - readMapping(fstBuffer); - fileType = mapping[FILENAME_FIELD].toLowerCase().fileType(); - if (mapping.hasOwnProperty(FILENAME_FIELD)) { - if (fileType === "fbx") { - fbxFilename = modelFile.path() + "\\" + mapping[FILENAME_FIELD]; - //} else if (fileType === "svo") { - // svoFilename = modelFile.path() + "\\" + mapping[FILENAME_FIELD]; - } else { - error("Unrecognized model type in FST file!"); - return false; - } - } else { - error("Model file name not found in FST file!"); - return false; - } - } else { - fstBuffer = { - filename: "Interface." + randomChar(6), // Simulate avatar model uploading behaviour - buffer: null - }; - - if (modelFile.toLowerCase().fileType() === "fbx") { - fbxFilename = modelFile; - mapping[FILENAME_FIELD] = modelFile.fileName(); - - //} else if (modelFile.toLowerCase().fileType() === "svo") { - // svoFilename = modelFile; - // mapping[FILENAME_FIELD] = modelFile.fileName(); - - } else { - error("Unrecognized file type: " + modelFile); - return false; - } - } - - if (!isProcessing) { return false; } - - if (fbxFilename) { - fbxBuffer = readFile(fbxFilename); - if (fbxBuffer === null) { - return false; - } - - if (!isProcessing) { return false; } - - readGeometry(fbxBuffer); - } - - //if (svoFilename) { - // svoBuffer = readFile(svoFilename); - // if (svoBuffer === null) { - // return false; - // } - //} - - // Add any missing basic mappings - if (!mapping.hasOwnProperty(NAME_FIELD)) { - mapping[NAME_FIELD] = modelFile.fileName().fileBase(); - } - if (!mapping.hasOwnProperty(TEXDIR_FIELD)) { - mapping[TEXDIR_FIELD] = "."; - } - if (!mapping.hasOwnProperty(SCALE_FIELD)) { - mapping[SCALE_FIELD] = 1.0; - } - - return true; - } - - function setProperties() { - var form = [], - directory, - displayAs, - validateAs; - - progressDialog.close(); - print("Setting model properties"); - - form.push({ label: "Name:", value: mapping[NAME_FIELD] }); - - directory = modelFile.path() + "/" + mapping[TEXDIR_FIELD]; - displayAs = new RegExp("^" + modelFile.path().regExpEscape() + "[\\\\\\\/](.*)"); - validateAs = new RegExp("^" + modelFile.path().regExpEscape() + "([\\\\\\\/].*)?"); - - form.push({ - label: "Texture directory:", - directory: modelFile.path() + "/" + mapping[TEXDIR_FIELD], - title: "Choose Texture Directory", - displayAs: displayAs, - validateAs: validateAs, - errorMessage: "Texture directory must be subdirectory of the model directory." - }); - - form.push({ button: "Cancel" }); - - if (!Window.form("Set Model Properties", form)) { - print("User cancelled uploading model"); - return false; - } - - mapping[NAME_FIELD] = form[0].value; - mapping[TEXDIR_FIELD] = form[1].directory.slice(modelFile.path().length + 1); - if (mapping[TEXDIR_FIELD] === "") { - mapping[TEXDIR_FIELD] = "."; - } - - writeMapping(fstBuffer); - - return true; - } - - function createHttpMessage(callback) { - var multiparts = [], - lodCount, - lodFile, - lodBuffer, - textureBuffer, - textureSourceFormat, - textureTargetFormat, - embeddedTextures, - i; - - info("Preparing to send model"); - - // Model name - if (mapping.hasOwnProperty(NAME_FIELD)) { - multiparts.push({ - name : "model_name", - string : mapping[NAME_FIELD] - }); - } else { - error("Model name is missing"); - httpMultiPart.clear(); - return; - } - - // FST file - if (fstBuffer) { - multiparts.push({ - name : "fst", - buffer: fstBuffer - }); - } - - // FBX file - if (fbxBuffer) { - multiparts.push({ - name : "fbx", - buffer: fbxBuffer - }); - } - - // SVO file - //if (svoBuffer) { - // multiparts.push({ - // name : "svo", - // buffer: svoBuffer - // }); - //} - - // LOD files - lodCount = 0; - for (lodFile in mapping.lod) { - if (mapping.lod.hasOwnProperty(lodFile)) { - lodBuffer = readFile(modelFile.path() + "\/" + lodFile); - if (lodBuffer === null) { - return; - } - multiparts.push({ - name: "lod" + lodCount, - buffer: lodBuffer - }); - lodCount += 1; - } - if (!isProcessing) { return; } - } - - // Textures - embeddedTextures = "|" + geometry.embedded.join("|") + "|"; - for (i = 0; i < geometry.textures.length; i += 1) { - if (embeddedTextures.indexOf("|" + geometry.textures[i].fileName() + "|") === -1) { - textureBuffer = readFile(modelFile.path() + "\/" - + (mapping[TEXDIR_FIELD] !== "." ? mapping[TEXDIR_FIELD] + "\/" : "") - + geometry.textures[i]); - if (textureBuffer === null) { - return; - } - - textureSourceFormat = geometry.textures[i].fileType().toLowerCase(); - textureTargetFormat = (textureSourceFormat === "jpg" ? "jpg" : "png"); - textureBuffer.buffer = - textureBuffer.buffer.recodeImage(textureSourceFormat, textureTargetFormat, MAX_TEXTURE_SIZE); - textureBuffer.filename = textureBuffer.filename.slice(0, -textureSourceFormat.length) + textureTargetFormat; - - multiparts.push({ - name: "texture" + i, - buffer: textureBuffer - }); - } - - if (!isProcessing) { return; } - } - - // Model category - multiparts.push({ - name : "model_category", - string : "content" - }); - - // Create HTTP message - httpMultiPart.clear(); - Script.setTimeout(function addMultipart() { - var multipart = multiparts.shift(); - httpMultiPart.add(multipart); - - if (!isProcessing) { return; } - - if (multiparts.length > 0) { - Script.setTimeout(addMultipart, 25); - } else { - callback(); - } - }, 25); - } - - function sendToHighFidelity() { - var req, - uploadedChecks, - HTTP_GET_TIMEOUT = 60, // 1 minute - HTTP_SEND_TIMEOUT = 900, // 15 minutes - UPLOADED_CHECKS = 30, - CHECK_UPLOADED_TIMEOUT = 1, // 1 second - handleCheckUploadedResponses, - handleUploadModelResponses, - handleRequestUploadResponses; - - function uploadTimedOut() { - error("Model upload failed: Internet request timed out!"); - } - - function debugResponse() { - print("req.errorCode = " + req.errorCode); - print("req.readyState = " + req.readyState); - print("req.status = " + req.status); - print("req.statusText = " + req.statusText); - print("req.responseType = " + req.responseType); - print("req.responseText = " + req.responseText); - print("req.response = " + req.response); - print("req.getAllResponseHeaders() = " + req.getAllResponseHeaders()); - } - - function checkUploaded() { - if (!isProcessing) { return; } - - info("Checking uploaded model"); - - req = new XMLHttpRequest(); - req.open("HEAD", modelURL, true); - req.timeout = HTTP_GET_TIMEOUT * 1000; - req.onreadystatechange = handleCheckUploadedResponses; - req.ontimeout = uploadTimedOut; - req.send(); - } - - handleCheckUploadedResponses = function () { - //debugResponse(); - if (req.readyState === req.DONE) { - if (req.status === 200) { - // Note: Unlike avatar models, for content models we don't need to refresh texture cache. - print("Model uploaded: " + modelURL); - progressDialog.close(); - if (Window.confirm("Your model has been uploaded as: " + modelURL + "\nDo you want to rez it?")) { - modelCallback(modelURL); - } - } else if (req.status === 404) { - if (uploadedChecks > 0) { - uploadedChecks -= 1; - Script.setTimeout(checkUploaded, CHECK_UPLOADED_TIMEOUT * 1000); - } else { - print("Error: " + req.status + " " + req.statusText); - error("We could not verify that your model was successfully uploaded but it may have been at: " - + modelURL); - } - } else { - print("Error: " + req.status + " " + req.statusText); - error("There was a problem with your upload, please try again later."); - } - } - }; - - function uploadModel(method) { - var url; - - if (!isProcessing) { return; } - - req = new XMLHttpRequest(); - if (method === "PUT") { - url = API_URL + "\/" + modelName; - req.open("PUT", url, true); //print("PUT " + url); - } else { - url = API_URL; - req.open("POST", url, true); //print("POST " + url); - } - req.setRequestHeader("Content-Type", "multipart/form-data; boundary=\"" + httpMultiPart.boundary() + "\""); - req.timeout = HTTP_SEND_TIMEOUT * 1000; - req.onreadystatechange = handleUploadModelResponses; - req.ontimeout = uploadTimedOut; - req.send(httpMultiPart.response().buffer); - } - - handleUploadModelResponses = function () { - //debugResponse(); - if (req.readyState === req.DONE) { - if (req.status === 200) { - uploadedChecks = UPLOADED_CHECKS; - checkUploaded(); - } else { - print("Error: " + req.status + " " + req.statusText); - error("There was a problem with your upload, please try again later."); - } - } - }; - - function requestUpload() { - var url; - - if (!isProcessing) { return; } - - url = API_URL + "\/" + modelName; // XMLHttpRequest automatically handles authorization of API requests. - req = new XMLHttpRequest(); - req.open("GET", url, true); //print("GET " + url); - req.responseType = "json"; - req.timeout = HTTP_GET_TIMEOUT * 1000; - req.onreadystatechange = handleRequestUploadResponses; - req.ontimeout = uploadTimedOut; - req.send(); - } - - handleRequestUploadResponses = function () { - var response; - - //debugResponse(); - if (req.readyState === req.DONE) { - if (req.status === 200) { - if (req.responseType === "json") { - response = JSON.parse(req.responseText); - if (response.status === "success") { - if (response.exists === false) { - uploadModel("POST"); - } else if (response.can_update === true) { - uploadModel("PUT"); - } else { - error("This model file already exists and is owned by someone else!"); - } - return; - } - } - } else { - print("Error: " + req.status + " " + req.statusText); - } - error("Model upload failed! Something went wrong at the data server."); - } - }; - - info("Sending model to High Fidelity"); - - requestUpload(); - } - - that.upload = function (file, callback) { - - modelFile = file; - modelCallback = callback; - - isProcessing = true; - - progressDialog.onCancel = function () { - print("User cancelled uploading model"); - isProcessing = false; - }; - - resetDataObjects(); - - if (readModel()) { - if (setProperties()) { - modelName = mapping[NAME_FIELD]; - modelURL = MODEL_URL + "\/" + mapping[NAME_FIELD] + ".fst"; // All models are uploaded as an FST - - createHttpMessage(sendToHighFidelity); - } - } - - resetDataObjects(); - }; - - return that; -}()); diff --git a/examples/utilities/diagnostics/XMLHttpRequest.js b/examples/utilities/diagnostics/XMLHttpRequest.js index fb25cb4fad..76374749ac 100644 --- a/examples/utilities/diagnostics/XMLHttpRequest.js +++ b/examples/utilities/diagnostics/XMLHttpRequest.js @@ -145,98 +145,3 @@ test("Test timeout", function() { this.assertEquals(0, req.status, "status should be `0`"); this.assertEquals(4, req.errorCode, "4 is the timeout error code for QNetworkReply::NetworkError"); }); - - -var localFile = Window.browse("Find defaultScripts.js file ...", "", "defaultScripts.js (defaultScripts.js)"); - -if (localFile !== null) { - - localFile = "file:///" + localFile; - - test("Test GET local file synchronously", function () { - var req = new XMLHttpRequest(); - - var statesVisited = [true, false, false, false, false] - req.onreadystatechange = function () { - statesVisited[req.readyState] = true; - }; - - req.open("GET", localFile, false); - req.send(); - - this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); - this.assertEquals(200, req.status, "status should be `200`"); - this.assertEquals("OK", req.statusText, "statusText should be `OK`"); - this.assertEquals(0, req.errorCode); - this.assertNotEquals("", req.getAllResponseHeaders(), "headers should not be null"); - this.assertContains("High Fidelity", req.response.substring(0, 100), "expected text not found in response") - - for (var i = 0; i <= req.DONE; i++) { - this.assertEquals(true, statesVisited[i], i + " should be set"); - } - }); - - test("Test GET nonexistent local file", function () { - var nonexistentFile = localFile.replace(".js", "NoExist.js"); - - var req = new XMLHttpRequest(); - req.open("GET", nonexistentFile, false); - req.send(); - - this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); - this.assertEquals(404, req.status, "status should be `404`"); - this.assertEquals("Not Found", req.statusText, "statusText should be `Not Found`"); - this.assertNotEquals(0, req.errorCode); - }); - - test("Test GET local file already open", function () { - // Can't open file exclusively in order to test. - }); - - test("Test GET local file with data not implemented", function () { - var req = new XMLHttpRequest(); - req.open("GET", localFile, true); - req.send("data"); - - this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); - this.assertEquals(501, req.status, "status should be `501`"); - this.assertEquals("Not Implemented", req.statusText, "statusText should be `Not Implemented`"); - this.assertNotEquals(0, req.errorCode); - }); - - test("Test GET local file asynchronously not implemented", function () { - var req = new XMLHttpRequest(); - req.open("GET", localFile, true); - req.send(); - - this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); - this.assertEquals(501, req.status, "status should be `501`"); - this.assertEquals("Not Implemented", req.statusText, "statusText should be `Not Implemented`"); - this.assertNotEquals(0, req.errorCode); - }); - - test("Test POST local file not implemented", function () { - var req = new XMLHttpRequest(); - req.open("POST", localFile, false); - req.send(); - - this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); - this.assertEquals(501, req.status, "status should be `501`"); - this.assertEquals("Not Implemented", req.statusText, "statusText should be `Not Implemented`"); - this.assertNotEquals(0, req.errorCode); - }); - - test("Test local file username and password not implemented", function () { - var req = new XMLHttpRequest(); - req.open("GET", localFile, false, "username", "password"); - req.send(); - - this.assertEquals(req.DONE, req.readyState, "readyState should be DONE"); - this.assertEquals(501, req.status, "status should be `501`"); - this.assertEquals("Not Implemented", req.statusText, "statusText should be `Not Implemented`"); - this.assertNotEquals(0, req.errorCode); - }); - -} else { - print("Local file operation not tested"); -} diff --git a/examples/utilities/tools/cookies.js b/examples/utilities/tools/cookies.js new file mode 100755 index 0000000000..205b19c9f9 --- /dev/null +++ b/examples/utilities/tools/cookies.js @@ -0,0 +1,278 @@ +// +// cookies.js +// +// version 1.0 +// +// Created by Sam Gateau, 4/1/2015 +// A simple ui panel that present a list of porperties and the proper widget to edit it +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// The Slider class +Slider = function(x,y,width,thumbSize) { + + this.thumb = Overlays.addOverlay("text", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + x: x, + y: y, + width: thumbSize, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 1.0, + visible: true + }); + this.background = Overlays.addOverlay("text", { + backgroundColor: { red: 125, green: 125, blue: 255 }, + x: x, + y: y, + width: width, + height: thumbSize, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true + }); + + this.thumbSize = thumbSize; + this.thumbHalfSize = 0.5 * thumbSize; + + this.minThumbX = x + this.thumbHalfSize; + this.maxThumbX = x + width - this.thumbHalfSize; + this.thumbX = this.minThumbX; + + this.minValue = 0.0; + this.maxValue = 1.0; + + this.clickOffsetX = 0; + this.isMoving = false; + + this.updateThumb = function() { + thumbTruePos = this.thumbX - 0.5 * this.thumbSize; + Overlays.editOverlay(this.thumb, { x: thumbTruePos } ); + }; + + this.onMouseMoveEvent = function(event) { + if (this.isMoving) { + newThumbX = event.x - this.clickOffsetX; + if (newThumbX < this.minThumbX) { + newThumbX = this.minThumbX; + } + if (newThumbX > this.maxThumbX) { + newThumbX = this.maxThumbX; + } + this.thumbX = newThumbX; + this.updateThumb(); + this.onValueChanged(this.getValue()); + } + }; + + this.onMousePressEvent = function(event) { + this.isMoving = true; + var clickOffset = event.x - this.thumbX; + if ((clickOffset > -this.thumbHalfSize) && (clickOffset < this.thumbHalfSize)) { + this.clickOffsetX = clickOffset; + } else { + this.clickOffsetX = 0; + this.thumbX = event.x; + this.updateThumb(); + this.onValueChanged(this.getValue()); + } + + }; + + this.onMouseReleaseEvent = function(event) { + this.isMoving = false; + }; + + // Public members: + + this.setNormalizedValue = function(value) { + if (value < 0.0) { + this.thumbX = this.minThumbX; + } else if (value > 1.0) { + this.thumbX = this.maxThumbX; + } else { + this.thumbX = value * (this.maxThumbX - this.minThumbX) + this.minThumbX; + } + this.updateThumb(); + }; + this.getNormalizedValue = function() { + return (this.thumbX - this.minThumbX) / (this.maxThumbX - this.minThumbX); + }; + + this.setValue = function(value) { + var normValue = (value - this.minValue) / (this.maxValue - this.minValue); + this.setNormalizedValue(normValue); + }; + + this.getValue = function() { + return this.getNormalizedValue() * (this.maxValue - this.minValue) + this.minValue; + }; + + this.onValueChanged = function(value) {}; + + this.destroy = function() { + Overlays.deleteOverlay(this.background); + Overlays.deleteOverlay(this.thumb); + }; +} + + +var textFontSize = 16; + +function PanelItem(name, setter, getter, displayer, x, y, textWidth, valueWidth, height) { + this.name = name; + + + this.displayer = typeof displayer !== 'undefined' ? displayer : function(value) { return value.toFixed(2); }; + + var topMargin = (height - textFontSize); + this.title = Overlays.addOverlay("text", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + x: x, + y: y, + width: textWidth, + height: height, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true, + text: name, + font: {size: textFontSize}, + topMargin: topMargin, + }); + + this.value = Overlays.addOverlay("text", { + backgroundColor: { red: 255, green: 255, blue: 255 }, + x: x + textWidth, + y: y, + width: valueWidth, + height: height, + alpha: 1.0, + backgroundAlpha: 0.5, + visible: true, + text: this.displayer(getter()), + font: {size: textFontSize}, + topMargin: topMargin + + }); + this.getter = getter; + + this.setter = function(value) { + setter(value); + Overlays.editOverlay(this.value, {text: this.displayer(getter())}); + if (this.widget) { + this.widget.setValue(value); + } + }; + this.setterFromWidget = function(value) { + setter(value); + Overlays.editOverlay(this.value, {text: this.displayer(getter())}); + }; + + + this.widget = null; + + this.destroy = function() { + Overlays.deleteOverlay(this.title); + Overlays.deleteOverlay(this.value); + if (this.widget != null) { + this.widget.destroy(); + } + } +} + +var textWidth = 180; +var valueWidth = 100; +var widgetWidth = 300; +var rawHeight = 20; +var rawYDelta = rawHeight * 1.5; + +Panel = function(x, y) { + + this.x = x; + this.y = y; + this.nextY = y; + + this.widgetX = x + textWidth + valueWidth; + + this.items = new Array(); + this.activeWidget = null; + + this.mouseMoveEvent = function(event) { + if (this.activeWidget) { + this.activeWidget.onMouseMoveEvent(event); + } + }; + + // we also handle click detection in our mousePressEvent() + this.mousePressEvent = function(event) { + // Make sure we quitted previous widget + if (this.activeWidget) { + this.activeWidget.onMouseReleaseEvent(event); + } + this.activeWidget = null; + + var clickedOverlay = Overlays.getOverlayAtPoint({x: event.x, y: event.y}); + + // If the user clicked any of the slider background then... + for (var i in this.items) { + var widget = this.items[i].widget; + + if (clickedOverlay == widget.background) { + this.activeWidget = widget; + this.activeWidget.onMousePressEvent(event); + // print("clicked... widget=" + i); + break; + } + } + }; + + this.mouseReleaseEvent = function(event) { + if (this.activeWidget) { + this.activeWidget.onMouseReleaseEvent(event); + } + this.activeWidget = null; + }; + + this.newSlider = function(name, minValue, maxValue, setValue, getValue, displayValue) { + + var sliderItem = new PanelItem(name, setValue, getValue, displayValue, this.x, this.nextY, textWidth, valueWidth, rawHeight); + + var slider = new Slider(this.widgetX, this.nextY, widgetWidth, rawHeight); + slider.minValue = minValue; + slider.maxValue = maxValue; + slider.onValueChanged = function(value) { sliderItem.setterFromWidget(value); }; + + + sliderItem.widget = slider; + sliderItem.setter(getValue()); + this.items[name] = sliderItem; + this.nextY += rawYDelta; + // print("created Item... slider=" + name); + }; + + this.destroy = function() { + for (var i in this.items) { + this.items[i].destroy(); + } + } + + this.set = function(name, value) { + var item = this.items[name]; + if (item != null) { + return item.setter(value); + } + return null; + } + + this.get = function(name) { + var item = this.items[name]; + if (item != null) { + return item.getter(); + } + return null; + } +}; + + diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 875b0ef8c6..3a835d4f9e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -256,7 +256,8 @@ bool setupEssentials(int& argc, char** argv) { auto speechRecognizer = DependencyManager::set(); #endif auto discoverabilityManager = DependencyManager::set(); - + auto sceneScriptingInterface = DependencyManager::set(); + return true; } @@ -3587,6 +3588,8 @@ void Application::registerScriptEngineWithApplicationServices(ScriptEngine* scri scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition2D", HMDScriptingInterface::getHUDLookAtPosition2D, 0); scriptEngine->registerFunction(hmdInterface, "getHUDLookAtPosition3D", HMDScriptingInterface::getHUDLookAtPosition3D, 0); + scriptEngine->registerGlobalObject("Scene", DependencyManager::get().data()); + #ifdef HAVE_RTMIDI scriptEngine->registerGlobalObject("MIDI", &MIDIManager::getInstance()); #endif @@ -3738,22 +3741,27 @@ bool Application::askToSetAvatarUrl(const QString& url) { _myAvatar->setFaceModelURL(url); UserActivityLogger::getInstance().changedModel("head", url); _myAvatar->sendIdentityPacket(); + emit faceURLChanged(url); } else if (msgBox.clickedButton() == bodyButton) { qDebug() << "Chose to use for body: " << url; _myAvatar->setSkeletonModelURL(url); // if the head is empty, reset it to the default head. if (_myAvatar->getFaceModelURLString().isEmpty()) { _myAvatar->setFaceModelURL(DEFAULT_HEAD_MODEL_URL); + emit faceURLChanged(DEFAULT_HEAD_MODEL_URL.toString()); UserActivityLogger::getInstance().changedModel("head", DEFAULT_HEAD_MODEL_URL.toString()); } UserActivityLogger::getInstance().changedModel("skeleton", url); _myAvatar->sendIdentityPacket(); + emit skeletonURLChanged(url); } else if (msgBox.clickedButton() == bodyAndHeadButton) { qDebug() << "Chose to use for body + head: " << url; _myAvatar->setFaceModelURL(QString()); _myAvatar->setSkeletonModelURL(url); UserActivityLogger::getInstance().changedModel("skeleton", url); _myAvatar->sendIdentityPacket(); + emit faceURLChanged(QString()); + emit skeletonURLChanged(url); } else { qDebug() << "Declined to use the avatar: " << url; } diff --git a/interface/src/Application.h b/interface/src/Application.h index 1c281a6a13..a92031d570 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -336,6 +336,9 @@ signals: void checkBackgroundDownloads(); void domainConnectionRefused(const QString& reason); + void faceURLChanged(const QString& newValue); + void skeletonURLChanged(const QString& newValue); + public slots: void domainChanged(const QString& domainHostname); void updateWindowTitle(); diff --git a/interface/src/scripting/WebWindowClass.cpp b/interface/src/scripting/WebWindowClass.cpp index 61b3ce225f..31bd4029ac 100644 --- a/interface/src/scripting/WebWindowClass.cpp +++ b/interface/src/scripting/WebWindowClass.cpp @@ -54,6 +54,7 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid _windowWidget = dockWidget; } else { + _windowWidget = new QWidget(Application::getInstance()->getWindow(), Qt::Window); _windowWidget->setWindowTitle(title); _windowWidget->setMinimumSize(width, height); @@ -89,24 +90,24 @@ void WebWindowClass::setVisible(bool visible) { if (visible) { if (_isToolWindow) { QMetaObject::invokeMethod( - Application::getInstance()->getToolWindow(), "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible)); + Application::getInstance()->getToolWindow(), "setVisible", Qt::AutoConnection, Q_ARG(bool, visible)); } else { - QMetaObject::invokeMethod(_windowWidget, "raise", Qt::BlockingQueuedConnection); + QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection); } } - QMetaObject::invokeMethod(_windowWidget, "setVisible", Qt::BlockingQueuedConnection, Q_ARG(bool, visible)); + QMetaObject::invokeMethod(_windowWidget, "setVisible", Qt::AutoConnection, Q_ARG(bool, visible)); } void WebWindowClass::setURL(const QString& url) { if (QThread::currentThread() != thread()) { - QMetaObject::invokeMethod(this, "setURL", Qt::BlockingQueuedConnection, Q_ARG(QString, url)); + QMetaObject::invokeMethod(this, "setURL", Qt::AutoConnection, Q_ARG(QString, url)); return; } _webView->setUrl(url); } void WebWindowClass::raise() { - QMetaObject::invokeMethod(_windowWidget, "raise", Qt::BlockingQueuedConnection); + QMetaObject::invokeMethod(_windowWidget, "raise", Qt::AutoConnection); } QScriptValue WebWindowClass::constructor(QScriptContext* context, QScriptEngine* engine) { diff --git a/interface/src/ui/LoginDialog.cpp b/interface/src/ui/LoginDialog.cpp index 690489fdb2..b134f7f230 100644 --- a/interface/src/ui/LoginDialog.cpp +++ b/interface/src/ui/LoginDialog.cpp @@ -24,6 +24,7 @@ #include "LoginDialog.h" #include "UIUtil.h" +const QString CREATE_ACCOUNT_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/signup"; const QString FORGOT_PASSWORD_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/users/password/new"; LoginDialog::LoginDialog(QWidget* parent) : @@ -45,6 +46,7 @@ LoginDialog::LoginDialog(QWidget* parent) : this, &LoginDialog::close); UIUtil::scaleWidgetFontSizes(this); + _ui->accountLabel->setText(_ui->accountLabel->text().arg(CREATE_ACCOUNT_URL, FORGOT_PASSWORD_URL)); // Initialize toggle connection toggleQAction(); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 4a9165ae44..bd32fc7c34 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -16,17 +16,18 @@ #include #include #include +#include #include "Application.h" #include "MainWindow.h" #include "LODManager.h" #include "Menu.h" -#include "ModelsBrowser.h" #include "PreferencesDialog.h" #include "Snapshot.h" #include "UserActivityLogger.h" #include "UIUtil.h" + const int PREFERENCES_HEIGHT_PADDING = 20; PreferencesDialog::PreferencesDialog(QWidget* parent) : @@ -46,6 +47,11 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) : connect(ui.buttonBrowseScriptsLocation, &QPushButton::clicked, this, &PreferencesDialog::openScriptsLocationBrowser); connect(ui.buttonReloadDefaultScripts, &QPushButton::clicked, Application::getInstance(), &Application::loadDefaultScripts); + + + connect(Application::getInstance(), &Application::faceURLChanged, this, &PreferencesDialog::faceURLChanged); + connect(Application::getInstance(), &Application::skeletonURLChanged, this, &PreferencesDialog::skeletonURLChanged); + // move dialog to left side move(parentWidget()->geometry().topLeft()); setFixedHeight(parentWidget()->size().height() - PREFERENCES_HEIGHT_PADDING); @@ -53,9 +59,19 @@ PreferencesDialog::PreferencesDialog(QWidget* parent) : UIUtil::scaleWidgetFontSizes(this); } +void PreferencesDialog::faceURLChanged(const QString& newValue) { + ui.faceURLEdit->setText(newValue); +} + +void PreferencesDialog::skeletonURLChanged(const QString& newValue) { + ui.skeletonURLEdit->setText(newValue); +} + void PreferencesDialog::accept() { savePreferences(); close(); + delete _marketplaceWindow; + _marketplaceWindow = NULL; } void PreferencesDialog::setHeadUrl(QString modelUrl) { @@ -67,15 +83,23 @@ void PreferencesDialog::setSkeletonUrl(QString modelUrl) { } void PreferencesDialog::openHeadModelBrowser() { - ModelsBrowser modelBrowser(FSTReader::HEAD_MODEL); - connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setHeadUrl); - modelBrowser.browse(); + auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars"; + auto WIDTH = 900; + auto HEIGHT = 700; + if (!_marketplaceWindow) { + _marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false); + } + _marketplaceWindow->setVisible(true); } void PreferencesDialog::openBodyModelBrowser() { - ModelsBrowser modelBrowser(FSTReader::HEAD_AND_BODY_MODEL); - connect(&modelBrowser, &ModelsBrowser::selected, this, &PreferencesDialog::setSkeletonUrl); - modelBrowser.browse(); + auto MARKETPLACE_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/marketplace?category=avatars"; + auto WIDTH = 900; + auto HEIGHT = 700; + if (!_marketplaceWindow) { + _marketplaceWindow = new WebWindowClass("Marketplace", MARKETPLACE_URL, WIDTH, HEIGHT, false); + } + _marketplaceWindow->setVisible(true); } void PreferencesDialog::openSnapshotLocationBrowser() { @@ -192,11 +216,13 @@ void PreferencesDialog::savePreferences() { UserActivityLogger::getInstance().changedDisplayName(displayNameStr); shouldDispatchIdentityPacket = true; } + + auto AVATAR_FILE_EXTENSION = ".fst"; QUrl faceModelURL(ui.faceURLEdit->text()); QString faceModelURLString = faceModelURL.toString(); if (faceModelURLString != _faceURLString) { - if (faceModelURLString.isEmpty() || faceModelURLString.toLower().endsWith(".fst")) { + if (faceModelURLString.isEmpty() || faceModelURLString.toLower().contains(AVATAR_FILE_EXTENSION)) { // change the faceModelURL in the profile, it will also update this user's BlendFace myAvatar->setFaceModelURL(faceModelURL); UserActivityLogger::getInstance().changedModel("head", faceModelURLString); @@ -209,7 +235,7 @@ void PreferencesDialog::savePreferences() { QUrl skeletonModelURL(ui.skeletonURLEdit->text()); QString skeletonModelURLString = skeletonModelURL.toString(); if (skeletonModelURLString != _skeletonURLString) { - if (skeletonModelURLString.isEmpty() || skeletonModelURLString.toLower().endsWith(".fst")) { + if (skeletonModelURLString.isEmpty() || skeletonModelURLString.toLower().contains(AVATAR_FILE_EXTENSION)) { // change the skeletonModelURL in the profile, it will also update this user's Body myAvatar->setSkeletonModelURL(skeletonModelURL); UserActivityLogger::getInstance().changedModel("skeleton", skeletonModelURLString); diff --git a/interface/src/ui/PreferencesDialog.h b/interface/src/ui/PreferencesDialog.h index ddacaa7127..4daa2d9696 100644 --- a/interface/src/ui/PreferencesDialog.h +++ b/interface/src/ui/PreferencesDialog.h @@ -17,6 +17,8 @@ #include #include +#include "scripting/WebWindowClass.h" + class PreferencesDialog : public QDialog { Q_OBJECT @@ -36,6 +38,8 @@ private: QString _faceURLString; QString _skeletonURLString; QString _displayNameString; + + WebWindowClass* _marketplaceWindow = NULL; private slots: void accept(); @@ -43,6 +47,8 @@ private slots: void setSkeletonUrl(QString modelUrl); void openSnapshotLocationBrowser(); void openScriptsLocationBrowser(); + void faceURLChanged(const QString& newValue); + void skeletonURLChanged(const QString& newValue); }; diff --git a/interface/ui/loginDialog.ui b/interface/ui/loginDialog.ui index 58ff353a16..e203699155 100644 --- a/interface/ui/loginDialog.ui +++ b/interface/ui/loginDialog.ui @@ -447,7 +447,7 @@ border-radius: 4px; padding-top: 1px; - + Helvetica,Arial,sans-serif @@ -456,9 +456,12 @@ border-radius: 4px; padding-top: 1px; <style type="text/css"> - a { text-decoration: none; color: #267077;} +a { text-decoration: none; color: #267077; margin:0;padding:0;} +#create {font-weight:bold;} +p {margin:5px 0;} </style> -<a href="https://metaverse.highfidelity.com/password/new">Recover password?</a> +<p><a id="create" href="%1">Create account</a></p> +<p><a href="%2">Recover password</a></p> true diff --git a/libraries/fbx/src/OBJReader.cpp b/libraries/fbx/src/OBJReader.cpp index c87a942baa..7e71ba07f7 100644 --- a/libraries/fbx/src/OBJReader.cpp +++ b/libraries/fbx/src/OBJReader.cpp @@ -21,9 +21,12 @@ #include "Shape.h" +QHash COMMENT_SCALE_HINTS; + + class OBJTokenizer { public: - OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { } + OBJTokenizer(QIODevice* device); enum SpecialToken { NO_TOKEN = -1, NO_PUSHBACKED_TOKEN = -1, @@ -46,6 +49,15 @@ private: }; +OBJTokenizer::OBJTokenizer(QIODevice* device) : _device(device), _pushedBackToken(-1) { + // This is a list of comments that exports use to hint at scaling + if (COMMENT_SCALE_HINTS.isEmpty()) { + COMMENT_SCALE_HINTS["This file uses centimeters as units"] = 1.0f / 100.0f; + COMMENT_SCALE_HINTS["This file uses millimeters as units"] = 1.0f / 1000.0f; + } +} + + int OBJTokenizer::nextToken() { if (_pushedBackToken != NO_PUSHBACKED_TOKEN) { int token = _pushedBackToken; @@ -60,7 +72,7 @@ int OBJTokenizer::nextToken() { } switch (ch) { case '#': { - _comment = _device->readLine(); // skip the comment + _comment = _device->readLine(); // stash comment for a future call to getComment qDebug() << "COMMENT:" << _comment; return COMMENT_TOKEN; } @@ -136,11 +148,15 @@ bool parseOBJGroup(OBJTokenizer &tokenizer, const QVariantHash& mapping, while (true) { int tokenType = tokenizer.nextToken(); if (tokenType == OBJTokenizer::COMMENT_TOKEN) { - if (tokenizer.getComment().contains("This file uses centimeters as units")) { - scaleGuess = 1.0f / 100.0f; - } - if (tokenizer.getComment().contains("This file uses millimeters as units")) { - scaleGuess = 1.0f / 1000.0f; + // loop through the list of known comments which suggest a scaling factor. + // if we find one, save the scaling hint into scaleGuess + QString comment = tokenizer.getComment(); + QHashIterator i(COMMENT_SCALE_HINTS); + while (i.hasNext()) { + i.next(); + if (comment.contains(i.key())) { + scaleGuess = i.value(); + } } continue; } diff --git a/libraries/model/src/model/Stage.cpp b/libraries/model/src/model/Stage.cpp index 1c171eee76..096ec273eb 100644 --- a/libraries/model/src/model/Stage.cpp +++ b/libraries/model/src/model/Stage.cpp @@ -92,8 +92,8 @@ void EarthSunModel::setSurfaceOrientation(const Quat& orientation) { double moduloRange(double val, double minVal, double maxVal) { double range = maxVal - minVal; double rval = (val - minVal) / range; - double intval; - return modf(rval, &intval) * range + minVal; + rval = rval - floor(rval); + return rval * range + minVal; } const float MAX_LONGITUDE = 180.0f; diff --git a/libraries/physics/src/CharacterController.cpp b/libraries/physics/src/CharacterController.cpp index d714194b2e..f8c8e31ece 100755 --- a/libraries/physics/src/CharacterController.cpp +++ b/libraries/physics/src/CharacterController.cpp @@ -212,6 +212,9 @@ btVector3 CharacterController::perpindicularComponent(const btVector3& direction } const btVector3 LOCAL_UP_AXIS(0.0f, 1.0f, 0.0f); +const float DEFAULT_GRAVITY = 5.0f; +const float TERMINAL_VELOCITY = 55.0f; +const float JUMP_SPEED = 5.0f; CharacterController::CharacterController(AvatarData* avatarData) { assert(avatarData); @@ -226,9 +229,9 @@ CharacterController::CharacterController(AvatarData* avatarData) { _velocityTimeInterval = 0.0f; _verticalVelocity = 0.0f; _verticalOffset = 0.0f; - _gravity = 5.0f; // slower than Earth's - _maxFallSpeed = 55.0f; // Terminal velocity of a sky diver in m/s. - _jumpSpeed = 5.0f; + _gravity = DEFAULT_GRAVITY; // slower than Earth's + _maxFallSpeed = TERMINAL_VELOCITY; // Terminal velocity of a sky diver in m/s. + _jumpSpeed = JUMP_SPEED; _isOnGround = false; _isJumping = false; _isHovering = true; @@ -350,6 +353,7 @@ bool CharacterController::recoverFromPenetration(btCollisionWorld* collisionWorl return penetration; } + void CharacterController::scanDown(btCollisionWorld* world) { // we test with downward raycast and if we don't find floor close enough then turn on "hover" btKinematicClosestNotMeRayResultCallback callback(_ghostObject); diff --git a/libraries/physics/src/ShapeManager.cpp b/libraries/physics/src/ShapeManager.cpp index b4d322a4a3..2c30887e3e 100644 --- a/libraries/physics/src/ShapeManager.cpp +++ b/libraries/physics/src/ShapeManager.cpp @@ -35,7 +35,7 @@ btCollisionShape* ShapeManager::getShape(const ShapeInfo& info) { // Very small or large objects are not supported. 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.0e4f; // 100 m cube + const float MAX_SHAPE_DIAGONAL_SQUARED = 3.0e6f; // 1000 m cube if (diagonal < MIN_SHAPE_DIAGONAL_SQUARED || diagonal > MAX_SHAPE_DIAGONAL_SQUARED) { // qDebug() << "ShapeManager::getShape -- not making shape due to size" << diagonal; return NULL; diff --git a/libraries/script-engine/src/SceneScriptingInterface.cpp b/libraries/script-engine/src/SceneScriptingInterface.cpp index 9cac521225..855701f536 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.cpp +++ b/libraries/script-engine/src/SceneScriptingInterface.cpp @@ -20,20 +20,47 @@ void SceneScriptingInterface::setStageLocation(float longitude, float latitude, _skyStage->setOriginLocation(longitude, latitude, altitude); } +float SceneScriptingInterface::getStageLocationLongitude() const { + return _skyStage->getOriginLongitude(); +} +float SceneScriptingInterface::getStageLocationLatitude() const { + return _skyStage->getOriginLatitude(); +} +float SceneScriptingInterface::getStageLocationAltitude() const { + return _skyStage->getOriginSurfaceAltitude(); +} + void SceneScriptingInterface::setStageDayTime(float hour) { _skyStage->setDayTime(hour); } + +float SceneScriptingInterface::getStageDayTime() const { + return _skyStage->getDayTime(); +} + void SceneScriptingInterface::setStageYearTime(int day) { _skyStage->setYearTime(day); } +int SceneScriptingInterface::getStageYearTime() const { + return _skyStage->getYearTime(); +} + void SceneScriptingInterface::setSunColor(const glm::vec3& color) { _skyStage->setSunColor(color); } + +const glm::vec3& SceneScriptingInterface::getSunColor() const { + return _skyStage->getSunColor(); +} + void SceneScriptingInterface::setSunIntensity(float intensity) { _skyStage->setSunIntensity(intensity); } +float SceneScriptingInterface::getSunIntensity() const { + return _skyStage->getSunIntensity(); +} model::SunSkyStagePointer SceneScriptingInterface::getSkyStage() const { return _skyStage; diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h index 8ae9424c95..0c36b303e9 100644 --- a/libraries/script-engine/src/SceneScriptingInterface.h +++ b/libraries/script-engine/src/SceneScriptingInterface.h @@ -24,12 +24,21 @@ class SceneScriptingInterface : public QObject, public Dependency { public: Q_INVOKABLE void setStageOrientation(const glm::quat& orientation); + Q_INVOKABLE void setStageLocation(float longitude, float latitude, float altitude); + Q_INVOKABLE float getStageLocationLongitude() const; + Q_INVOKABLE float getStageLocationLatitude() const; + Q_INVOKABLE float getStageLocationAltitude() const; + Q_INVOKABLE void setStageDayTime(float hour); + Q_INVOKABLE float getStageDayTime() const; Q_INVOKABLE void setStageYearTime(int day); + Q_INVOKABLE int getStageYearTime() const; Q_INVOKABLE void setSunColor(const glm::vec3& color); + Q_INVOKABLE const glm::vec3& getSunColor() const; Q_INVOKABLE void setSunIntensity(float intensity); + Q_INVOKABLE float getSunIntensity() const; model::SunSkyStagePointer getSkyStage() const; diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index c31f5be46a..390b889579 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -305,8 +305,6 @@ void ScriptEngine::init() { _isInitialized = true; - auto sceneScriptingInterface = DependencyManager::set(); - auto entityScriptingInterface = DependencyManager::get(); entityScriptingInterface->init(); @@ -350,7 +348,6 @@ void ScriptEngine::init() { registerGlobalObject("Vec3", &_vec3Library); registerGlobalObject("Uuid", &_uuidLibrary); registerGlobalObject("AnimationCache", DependencyManager::get().data()); - registerGlobalObject("Scene", DependencyManager::get().data()); // constants globalObject().setProperty("TREE_SCALE", newVariant(QVariant(TREE_SCALE))); diff --git a/libraries/script-engine/src/XMLHttpRequestClass.cpp b/libraries/script-engine/src/XMLHttpRequestClass.cpp index ae0486dd0c..9a6f81b19f 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.cpp +++ b/libraries/script-engine/src/XMLHttpRequestClass.cpp @@ -13,15 +13,14 @@ // #include -#include #include +#include #include #include -#include -#include "XMLHttpRequestClass.h" #include "ScriptEngine.h" +#include "XMLHttpRequestClass.h" const QString METAVERSE_API_URL = NetworkingConstants::METAVERSE_SERVER_URL.toString() + "/api/"; @@ -42,7 +41,6 @@ XMLHttpRequestClass::XMLHttpRequestClass(QScriptEngine* engine) : _onReadyStateChange(QScriptValue::NullValue), _readyState(XMLHttpRequestClass::UNSENT), _errorCode(QNetworkReply::NoError), - _file(NULL), _timeout(0), _timer(this), _numRedirects(0) { @@ -63,22 +61,6 @@ QScriptValue XMLHttpRequestClass::getStatus() const { if (_reply) { return QScriptValue(_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt()); } - if(_url.isLocalFile()) { - switch (_errorCode) { - case QNetworkReply::NoError: - return QScriptValue(200); - case QNetworkReply::ContentNotFoundError: - return QScriptValue(404); - case QNetworkReply::ContentConflictError: - return QScriptValue(409); - case QNetworkReply::TimeoutError: - return QScriptValue(408); - case QNetworkReply::ContentOperationNotPermittedError: - return QScriptValue(501); - default: - break; - } - } return QScriptValue(0); } @@ -86,22 +68,6 @@ QString XMLHttpRequestClass::getStatusText() const { if (_reply) { return _reply->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString(); } - if (_url.isLocalFile()) { - switch (_errorCode) { - case QNetworkReply::NoError: - return "OK"; - case QNetworkReply::ContentNotFoundError: - return "Not Found"; - case QNetworkReply::ContentConflictError: - return "Conflict"; - case QNetworkReply::TimeoutError: - return "Timeout"; - case QNetworkReply::ContentOperationNotPermittedError: - return "Not Implemented"; - default: - break; - } - } return ""; } @@ -147,13 +113,6 @@ QScriptValue XMLHttpRequestClass::getAllResponseHeaders() const { } return QString(headers.data()); } - if (_url.isLocalFile()) { - QString headers = QString("Content-Type: application/octet-stream\n"); - headers.append("Content-Length: "); - headers.append(QString("%1").arg(_rawResponseData.length())); - headers.append("\n"); - return headers; - } return QScriptValue(""); } @@ -161,14 +120,6 @@ QScriptValue XMLHttpRequestClass::getResponseHeader(const QString& name) const { if (_reply && _reply->hasRawHeader(name.toLatin1())) { return QScriptValue(QString(_reply->rawHeader(name.toLatin1()))); } - if (_url.isLocalFile()) { - if (name.toLower() == "content-type") { - return QString("application/octet-stream"); - } - if (name.toLower() == "content-length") { - return QString("%1").arg(_rawResponseData.length()); - } - } return QScriptValue::NullValue; } @@ -188,47 +139,24 @@ void XMLHttpRequestClass::open(const QString& method, const QString& url, bool a _url.setUrl(url); _async = async; - if (_url.isLocalFile()) { - if (_method.toUpper() == "GET" && !_async && username.isEmpty() && password.isEmpty()) { - _file = new QFile(_url.toLocalFile()); - if (!_file->exists()) { - qDebug() << "Can't find file " << _url.fileName(); - abortRequest(); - _errorCode = QNetworkReply::ContentNotFoundError; - setReadyState(DONE); - emit requestComplete(); - } else if (!_file->open(QIODevice::ReadOnly)) { - qDebug() << "Can't open file " << _url.fileName(); - abortRequest(); - _errorCode = QNetworkReply::ContentConflictError; - setReadyState(DONE); - emit requestComplete(); - } else { - setReadyState(OPENED); - } - } else { - notImplemented(); - } - } else { - if (url.toLower().left(METAVERSE_API_URL.length()) == METAVERSE_API_URL) { - AccountManager& accountManager = AccountManager::getInstance(); + if (url.toLower().left(METAVERSE_API_URL.length()) == METAVERSE_API_URL) { + AccountManager& accountManager = AccountManager::getInstance(); - if (accountManager.hasValidAccessToken()) { - QUrlQuery urlQuery(_url.query()); - urlQuery.addQueryItem("access_token", accountManager.getAccountInfo().getAccessToken().token); - _url.setQuery(urlQuery); - } + if (accountManager.hasValidAccessToken()) { + QUrlQuery urlQuery(_url.query()); + urlQuery.addQueryItem("access_token", accountManager.getAccountInfo().getAccessToken().token); + _url.setQuery(urlQuery); + } - } - if (!username.isEmpty()) { - _url.setUserName(username); - } - if (!password.isEmpty()) { - _url.setPassword(password); - } - _request.setUrl(_url); - setReadyState(OPENED); } + if (!username.isEmpty()) { + _url.setUserName(username); + } + if (!password.isEmpty()) { + _url.setPassword(password); + } + _request.setUrl(_url); + setReadyState(OPENED); } } @@ -239,23 +167,18 @@ void XMLHttpRequestClass::send() { void XMLHttpRequestClass::send(const QScriptValue& data) { if (_readyState == OPENED && !_reply) { if (!data.isNull()) { - if (_url.isLocalFile()) { - notImplemented(); - return; + _sendData = new QBuffer(this); + if (data.isObject()) { + QByteArray ba = qscriptvalue_cast(data); + _sendData->setData(ba); } else { - _sendData = new QBuffer(this); - if (data.isObject()) { - QByteArray ba = qscriptvalue_cast(data); - _sendData->setData(ba); - } else { - _sendData->setData(data.toString().toUtf8()); - } + _sendData->setData(data.toString().toUtf8()); } } doSend(); - if (!_async && !_url.isLocalFile()) { + if (!_async) { QEventLoop loop; connect(this, SIGNAL(requestComplete()), &loop, SLOT(quit())); loop.exec(); @@ -265,23 +188,13 @@ void XMLHttpRequestClass::send(const QScriptValue& data) { void XMLHttpRequestClass::doSend() { - if (!_url.isLocalFile()) { - _reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData); - connectToReply(_reply); - } + _reply = NetworkAccessManager::getInstance().sendCustomRequest(_request, _method.toLatin1(), _sendData); + connectToReply(_reply); if (_timeout > 0) { _timer.start(_timeout); connect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); } - - if (_url.isLocalFile()) { - setReadyState(HEADERS_RECEIVED); - setReadyState(LOADING); - _rawResponseData = _file->readAll(); - _file->close(); - requestFinished(); - } } void XMLHttpRequestClass::requestTimeout() { @@ -300,16 +213,10 @@ void XMLHttpRequestClass::requestError(QNetworkReply::NetworkError code) { void XMLHttpRequestClass::requestFinished() { disconnect(&_timer, SIGNAL(timeout()), this, SLOT(requestTimeout())); - if (!_url.isLocalFile()) { - _errorCode = _reply->error(); - } else { - _errorCode = QNetworkReply::NoError; - } + _errorCode = _reply->error(); if (_errorCode == QNetworkReply::NoError) { - if (!_url.isLocalFile()) { - _rawResponseData.append(_reply->readAll()); - } + _rawResponseData.append(_reply->readAll()); if (_responseType == "json") { _responseData = _engine->evaluate("(" + QString(_rawResponseData.data()) + ")"); @@ -338,19 +245,6 @@ void XMLHttpRequestClass::abortRequest() { _reply->deleteLater(); _reply = NULL; } - - if (_file != NULL) { - _file->close(); - _file = NULL; - } -} - -void XMLHttpRequestClass::notImplemented() { - abortRequest(); - //_errorCode = QNetworkReply::OperationNotImplementedError; TODO: Use this status code when update to Qt 5.3 - _errorCode = QNetworkReply::ContentOperationNotPermittedError; - setReadyState(DONE); - emit requestComplete(); } void XMLHttpRequestClass::connectToReply(QNetworkReply* reply) { diff --git a/libraries/script-engine/src/XMLHttpRequestClass.h b/libraries/script-engine/src/XMLHttpRequestClass.h index 55bf646476..c79859e895 100644 --- a/libraries/script-engine/src/XMLHttpRequestClass.h +++ b/libraries/script-engine/src/XMLHttpRequestClass.h @@ -97,7 +97,6 @@ private: void connectToReply(QNetworkReply* reply); void disconnectFromReply(QNetworkReply* reply); void abortRequest(); - void notImplemented(); QScriptEngine* _engine; bool _async; @@ -113,7 +112,6 @@ private: QScriptValue _onReadyStateChange; ReadyState _readyState; QNetworkReply::NetworkError _errorCode; - QFile* _file; int _timeout; QTimer _timer; int _numRedirects; diff --git a/tools/vhacd/src/VHACDUtil.cpp b/tools/vhacd/src/VHACDUtil.cpp index 4f2cb0e2b8..804a3562c6 100644 --- a/tools/vhacd/src/VHACDUtil.cpp +++ b/tools/vhacd/src/VHACDUtil.cpp @@ -38,6 +38,15 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re } + std::cout << "-------------------\n"; + foreach (const FBXMesh& mesh, geometry.meshes) { + foreach (const FBXMeshPart &meshPart, mesh.parts) { + std::cout << meshPart.triangleIndices.size() << " "; + } + } + std::cout << "\n"; + + //results->meshCount = geometry.meshes.count(); // qDebug() << "read in" << geometry.meshes.count() << "meshes"; @@ -52,14 +61,29 @@ bool vhacd::VHACDUtil::loadFBX(const QString filename, vhacd::LoadFBXResults *re vertices.append(glm::vec3(mesh.modelTransform * glm::vec4(vertex, 1.0f))); } - //get the triangle indices for each mesh + // get the triangle indices for each mesh QVector triangles; - foreach(FBXMeshPart part, mesh.parts){ - QVector indices = part.triangleIndices; + foreach(FBXMeshPart meshPart, mesh.parts){ + QVector indices = meshPart.triangleIndices; triangles += indices; + + 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.append(p0Index); + triangles.append(p1Index); + triangles.append(p2Index); + triangles.append(p0Index); + triangles.append(p2Index); + triangles.append(p3Index); + } } - //only read meshes with triangles + // only read meshes with triangles if (triangles.count() <= 0){ continue; } @@ -131,15 +155,16 @@ void vhacd::VHACDUtil::fattenMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFB auto d1 = p2 - p0; auto cp = glm::cross(d0, d1); - cp = 5.0f * glm::normalize(cp); + cp = -2.0f * glm::normalize(cp); auto p3 = p0 + cp; - auto p4 = p1 + cp; - auto p5 = p2 + cp; auto n = results->perMeshVertices.size(); - results->perMeshVertices[i] << p3 << p4 << p5; - results->perMeshTriangleIndices[i] << n << n+1 << n+2; + results->perMeshVertices[i] << p3; + + 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]; } results->meshCount++; @@ -148,21 +173,26 @@ void vhacd::VHACDUtil::fattenMeshes(vhacd::LoadFBXResults *meshes, vhacd::LoadFB -bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD::Parameters params, +bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *inMeshes, VHACD::IVHACD::Parameters params, vhacd::ComputeResults *results, - int startMeshIndex, int endMeshIndex, float minimumMeshSize) const { + int startMeshIndex, int endMeshIndex, float minimumMeshSize, + bool fattenFaces) const { - // vhacd::LoadFBXResults *meshes = new vhacd::LoadFBXResults; + vhacd::LoadFBXResults *meshes = new vhacd::LoadFBXResults; // combineMeshes(inMeshes, meshes); // vhacd::LoadFBXResults *meshes = new vhacd::LoadFBXResults; - // fattenMeshes(inMeshes, meshes); + + if (fattenFaces) { + fattenMeshes(inMeshes, meshes); + } else { + meshes = inMeshes; + } VHACD::IVHACD * interfaceVHACD = VHACD::CreateVHACD(); int meshCount = meshes->meshCount; int count = 0; - std::cout << "Performing V-HACD computation on " << meshCount << " meshes ..... " << std::endl; if (startMeshIndex < 0) { startMeshIndex = 0; @@ -171,6 +201,14 @@ bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD endMeshIndex = meshCount; } + for (int i = 0; i < meshCount; i++) { + std::cout << meshes->perMeshTriangleIndices.at(i).size() << " "; + } + std::cout << "\n"; + + + std::cout << "Performing V-HACD computation on " << endMeshIndex - startMeshIndex << " meshes ..... " << std::endl; + for (int i = startMeshIndex; i < endMeshIndex; i++){ qDebug() << "--------------------"; std::vector vertices = meshes->perMeshVertices.at(i).toStdVector(); @@ -211,7 +249,6 @@ bool vhacd::VHACDUtil::computeVHACD(vhacd::LoadFBXResults *meshes, VHACD::IVHACD } hull.m_points = m_points_copy; - int *m_triangles_copy = new int[hull.m_nTriangles * 3]; // std::copy(std::begin(hull.m_triangles), std::end(hull.m_triangles), std::begin(m_triangles_copy)); for (unsigned int i=0; i