Merge branch 'master' of github.com:highfidelity/hifi into detect-ballistic

This commit is contained in:
Seth Alves 2015-04-23 13:09:22 -07:00
commit bc65616bb3
91 changed files with 3866 additions and 885 deletions

View file

@ -92,8 +92,17 @@ else ()
if (NOT QT_CMAKE_PREFIX_PATH)
set(QT_CMAKE_PREFIX_PATH $ENV{QT_CMAKE_PREFIX_PATH})
endif ()
if (NOT QT_CMAKE_PREFIX_PATH)
get_filename_component(QT_CMAKE_PREFIX_PATH "${Qt5_DIR}/.." REALPATH)
endif ()
endif ()
if (WIN32)
if (NOT EXISTS ${QT_CMAKE_PREFIX_PATH})
message(FATAL_ERROR "Could not determine QT_CMAKE_PREFIX_PATH.")
endif ()
endif()
# figure out where the qt dir is
get_filename_component(QT_DIR "${QT_CMAKE_PREFIX_PATH}/../../" ABSOLUTE)

View file

@ -28,6 +28,7 @@ Script.include([
"libraries/gridTool.js",
"libraries/entityList.js",
"libraries/lightOverlayManager.js",
"libraries/zoneOverlayManager.js",
]);
var selectionDisplay = SelectionDisplay;
@ -35,6 +36,7 @@ var selectionManager = SelectionManager;
var entityPropertyDialogBox = EntityPropertyDialogBox;
var lightOverlayManager = new LightOverlayManager();
var zoneOverlayManager = new ZoneOverlayManager();
var cameraManager = new CameraManager();
@ -47,6 +49,7 @@ var entityListTool = EntityListTool();
selectionManager.addEventListener(function() {
selectionDisplay.updateHandles();
lightOverlayManager.updatePositions();
zoneOverlayManager.updatePositions();
});
var windowDimensions = Controller.getViewportDimensions();
@ -77,11 +80,13 @@ var DEFAULT_LIGHT_DIMENSIONS = Vec3.multiply(20, DEFAULT_DIMENSIONS);
var MENU_AUTO_FOCUS_ON_SELECT = "Auto Focus on Select";
var MENU_EASE_ON_FOCUS = "Ease Orientation on Focus";
var MENU_SHOW_LIGHTS_IN_EDIT_MODE = "Show Lights in Edit Mode";
var MENU_SHOW_ZONES_IN_EDIT_MODE = "Show Zones in Edit Mode";
var SETTING_INSPECT_TOOL_ENABLED = "inspectToolEnabled";
var SETTING_AUTO_FOCUS_ON_SELECT = "autoFocusOnSelect";
var SETTING_EASE_ON_FOCUS = "cameraEaseOnFocus";
var SETTING_SHOW_LIGHTS_IN_EDIT_MODE = "showLightsInEditMode";
var SETTING_SHOW_ZONES_IN_EDIT_MODE = "showZonesInEditMode";
var INSUFFICIENT_PERMISSIONS_ERROR_MSG = "You do not have the necessary permissions to edit on this domain."
var INSUFFICIENT_PERMISSIONS_IMPORT_ERROR_MSG = "You do not have the necessary permissions to place items on this domain."
@ -135,6 +140,7 @@ var toolBar = (function () {
newSphereButton,
newLightButton,
newTextButton,
newZoneButton,
browseMarketplaceButton;
function initialize() {
@ -201,6 +207,14 @@ var toolBar = (function () {
alpha: 0.9,
visible: false
});
newZoneButton = toolBar.addTool({
imageURL: toolIconUrl + "zonecube3.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH + 208, width: 256, height: 256 },
width: toolWidth,
height: toolHeight,
alpha: 0.9,
visible: false
});
that.setActive(false);
}
@ -232,6 +246,7 @@ var toolBar = (function () {
}
toolBar.selectTool(activeButton, isActive);
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
};
// Sets visibility of tool buttons, excluding the power button
@ -241,6 +256,7 @@ var toolBar = (function () {
toolBar.showTool(newSphereButton, doShow);
toolBar.showTool(newLightButton, doShow);
toolBar.showTool(newTextButton, doShow);
toolBar.showTool(newZoneButton, doShow);
};
var RESIZE_INTERVAL = 50;
@ -412,6 +428,21 @@ var toolBar = (function () {
return true;
}
if (newZoneButton === toolBar.clicked(clickedOverlay)) {
var position = getPositionToCreateEntity();
if (position.x > 0 && position.y > 0 && position.z > 0) {
placingEntityID = Entities.addEntity({
type: "Zone",
position: grid.snapToSurface(grid.snapToGrid(position, false, DEFAULT_DIMENSIONS), DEFAULT_DIMENSIONS),
dimensions: { x: 10, y: 10, z: 10 },
});
} else {
print("Can't create box: Text would be out of bounds.");
}
return true;
}
return false;
};
@ -486,12 +517,22 @@ function rayPlaneIntersection(pickRay, point, normal) {
}
function findClickedEntity(event) {
var pickZones = event.isControl;
if (pickZones) {
Entities.setZonesArePickable(true);
}
var pickRay = Camera.computePickRay(event.x, event.y);
var entityResult = Entities.findRayIntersection(pickRay, true); // want precision picking
var lightResult = lightOverlayManager.findRayIntersection(pickRay);
lightResult.accurate = true;
if (pickZones) {
Entities.setZonesArePickable(false);
}
var result;
if (!entityResult.intersects && !lightResult.intersects) {
@ -776,6 +817,8 @@ function setupModelMenus() {
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_LIGHTS_IN_EDIT_MODE, afterItem: MENU_EASE_ON_FOCUS,
isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE) == "true" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_SHOW_ZONES_IN_EDIT_MODE, afterItem: MENU_SHOW_LIGHTS_IN_EDIT_MODE,
isCheckable: true, isChecked: Settings.getValue(SETTING_SHOW_ZONES_IN_EDIT_MODE) == "true" });
Entities.setLightsArePickable(false);
}
@ -804,12 +847,14 @@ function cleanupModelMenus() {
Menu.removeMenuItem("View", MENU_AUTO_FOCUS_ON_SELECT);
Menu.removeMenuItem("View", MENU_EASE_ON_FOCUS);
Menu.removeMenuItem("View", MENU_SHOW_LIGHTS_IN_EDIT_MODE);
Menu.removeMenuItem("View", MENU_SHOW_ZONES_IN_EDIT_MODE);
}
Script.scriptEnding.connect(function() {
Settings.setValue(SETTING_AUTO_FOCUS_ON_SELECT, Menu.isOptionChecked(MENU_AUTO_FOCUS_ON_SELECT));
Settings.setValue(SETTING_EASE_ON_FOCUS, Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
Settings.setValue(SETTING_SHOW_LIGHTS_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
Settings.setValue(SETTING_SHOW_ZONES_IN_EDIT_MODE, Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
progressDialog.cleanup();
toolBar.cleanup();
@ -942,6 +987,8 @@ function handeMenuEvent(menuItem) {
selectAllEtitiesInCurrentSelectionBox(true);
} else if (menuItem == MENU_SHOW_LIGHTS_IN_EDIT_MODE) {
lightOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_LIGHTS_IN_EDIT_MODE));
} else if (menuItem == MENU_SHOW_ZONES_IN_EDIT_MODE) {
zoneOverlayManager.setVisible(isActive && Menu.isOptionChecked(MENU_SHOW_ZONES_IN_EDIT_MODE));
}
tooltip.show(false);
}

View file

@ -0,0 +1,72 @@
//
// zoneEntityExample.js
// examples
//
// Created by Brad Hefta-Gaub on 4/16/15.
// Copyright 2015 High Fidelity, Inc.
//
// This is an example script that demonstrates creating and editing a entity
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
var count = 0;
var stopAfter = 1000;
var zoneEntityA = Entities.addEntity({
type: "Zone",
position: { x: 5, y: 5, z: 5 },
dimensions: { x: 10, y: 10, z: 10 },
keyLightColor: { red: 255, green: 0, blue: 0 },
stageSunModelEnabled: false,
keyLightDirection: { x: 0, y: -1.0, z: 0 },
shapeType: "sphere"
});
print("zoneEntityA:" + zoneEntityA);
var zoneEntityB = Entities.addEntity({
type: "Zone",
position: { x: 5, y: 5, z: 5 },
dimensions: { x: 2, y: 2, z: 2 },
keyLightColor: { red: 0, green: 255, blue: 0 },
keyLightIntensity: 0.9,
stageLatitude: 37.777,
stageLongitude: 122.407,
stageAltitude: 0.03,
stageDay: 60,
stageHour: 12,
stageSunModelEnabled: true
});
print("zoneEntityB:" + zoneEntityB);
var zoneEntityC = Entities.addEntity({
type: "Zone",
position: { x: 5, y: 10, z: 5 },
dimensions: { x: 10, y: 10, z: 10 },
keyLightColor: { red: 0, green: 0, blue: 255 },
keyLightIntensity: 0.75,
keyLightDirection: { x: 0, y: 0, z: -1 },
stageSunModelEnabled: false,
shapeType: "compound",
compoundShapeURL: "http://headache.hungry.com/~seth/hifi/cube.fbx"
});
print("zoneEntityC:" + zoneEntityC);
// register the call back so it fires before each data send
Script.update.connect(function(deltaTime) {
// stop it...
if (count >= stopAfter) {
print("calling Script.stop()");
Script.stop();
}
count++;
});

View file

@ -162,7 +162,7 @@
var elModelSections = document.querySelectorAll(".model-section");
var elModelURL = document.getElementById("property-model-url");
var elCollisionModelURL = document.getElementById("property-collision-model-url");
var elCompoundShapeURL = document.getElementById("property-compound-shape-url");
var elModelAnimationURL = document.getElementById("property-model-animation-url");
var elModelAnimationPlaying = document.getElementById("property-model-animation-playing");
var elModelAnimationFPS = document.getElementById("property-model-animation-fps");
@ -182,6 +182,23 @@
var elTextBackgroundColorGreen = document.getElementById("property-text-background-color-green");
var elTextBackgroundColorBlue = document.getElementById("property-text-background-color-blue");
var elZoneSections = document.querySelectorAll(".zone-section");
var elZoneStageSunModelEnabled = document.getElementById("property-zone-stage-sun-model-enabled");
var elZoneKeyLightColorRed = document.getElementById("property-zone-key-light-color-red");
var elZoneKeyLightColorGreen = document.getElementById("property-zone-key-light-color-green");
var elZoneKeyLightColorBlue = document.getElementById("property-zone-key-light-color-blue");
var elZoneKeyLightIntensity = document.getElementById("property-zone-key-intensity");
var elZoneKeyLightAmbientIntensity = document.getElementById("property-zone-key-ambient-intensity");
var elZoneKeyLightDirectionX = document.getElementById("property-zone-key-light-direction-x");
var elZoneKeyLightDirectionY = document.getElementById("property-zone-key-light-direction-y");
var elZoneKeyLightDirectionZ = document.getElementById("property-zone-key-light-direction-z");
var elZoneStageLatitude = document.getElementById("property-zone-stage-latitude");
var elZoneStageLongitude = document.getElementById("property-zone-stage-longitude");
var elZoneStageAltitude = document.getElementById("property-zone-stage-altitude");
var elZoneStageDay = document.getElementById("property-zone-stage-day");
var elZoneStageHour = document.getElementById("property-zone-stage-hour");
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
data = JSON.parse(data);
@ -306,7 +323,7 @@
}
elModelURL.value = properties.modelURL;
elCollisionModelURL.value = properties.collisionModelURL;
elCompoundShapeURL.value = properties.compoundShapeURL;
elModelAnimationURL.value = properties.animationURL;
elModelAnimationPlaying.checked = properties.animationIsPlaying;
elModelAnimationFPS.value = properties.animationFPS;
@ -356,6 +373,32 @@
elLightCutoff.value = properties.cutoff;
}
if (properties.type != "Zone") {
for (var i = 0; i < elZoneSections.length; i++) {
elZoneSections[i].style.display = 'none';
}
} else {
for (var i = 0; i < elZoneSections.length; i++) {
elZoneSections[i].style.display = 'block';
}
elZoneStageSunModelEnabled.checked = properties.stageSunModelEnabled;
elZoneKeyLightColorRed.value = properties.keyLightColor.red;
elZoneKeyLightColorGreen.value = properties.keyLightColor.green;
elZoneKeyLightColorBlue.value = properties.keyLightColor.blue;
elZoneKeyLightIntensity.value = properties.keyLightIntensity.toFixed(2);
elZoneKeyLightAmbientIntensity.value = properties.keyLightAmbientIntensity.toFixed(2);
elZoneKeyLightDirectionX.value = properties.keyLightDirection.x.toFixed(2);
elZoneKeyLightDirectionY.value = properties.keyLightDirection.y.toFixed(2);
elZoneKeyLightDirectionZ.value = properties.keyLightDirection.z.toFixed(2);
elZoneStageLatitude.value = properties.stageLatitude.toFixed(2);
elZoneStageLongitude.value = properties.stageLongitude.toFixed(2);
elZoneStageAltitude.value = properties.stageAltitude.toFixed(2);
elZoneStageDay.value = properties.stageDay;
elZoneStageHour.value = properties.stageHour;
}
if (selected) {
activeElement.focus();
activeElement.select();
@ -444,7 +487,7 @@
elLightCutoff.addEventListener('change', createEmitNumberPropertyUpdateFunction('cutoff'));
elModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('modelURL'));
elCollisionModelURL.addEventListener('change', createEmitTextPropertyUpdateFunction('collisionModelURL'));
elCompoundShapeURL.addEventListener('change', createEmitTextPropertyUpdateFunction('compoundShapeURL'));
elModelAnimationURL.addEventListener('change', createEmitTextPropertyUpdateFunction('animationURL'));
elModelAnimationPlaying.addEventListener('change', createEmitCheckedPropertyUpdateFunction('animationIsPlaying'));
elModelAnimationFPS.addEventListener('change', createEmitNumberPropertyUpdateFunction('animationFPS'));
@ -468,6 +511,26 @@
elTextBackgroundColorGreen.addEventListener('change', textBackgroundColorChangeFunction);
elTextBackgroundColorBlue.addEventListener('change', textBackgroundColorChangeFunction);
elZoneStageSunModelEnabled.addEventListener('change', createEmitCheckedPropertyUpdateFunction('stageSunModelEnabled'));
var zoneKeyLightColorChangeFunction = createEmitColorPropertyUpdateFunction(
'keyLightColor', elZoneKeyLightColorRed, elZoneKeyLightColorGreen, elZoneKeyLightColorBlue);
elZoneKeyLightColorRed.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorGreen.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightColorBlue.addEventListener('change', zoneKeyLightColorChangeFunction);
elZoneKeyLightIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightIntensity'));
elZoneKeyLightAmbientIntensity.addEventListener('change', createEmitNumberPropertyUpdateFunction('keyLightAmbientIntensity'));
var zoneKeyLightDirectionChangeFunction = createEmitVec3PropertyUpdateFunction(
'keyLightDirection', elZoneKeyLightDirectionX, elZoneKeyLightDirectionY, elZoneKeyLightDirectionZ);
elZoneKeyLightDirectionX.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionY.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneKeyLightDirectionZ.addEventListener('change', zoneKeyLightDirectionChangeFunction);
elZoneStageLatitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLatitude'));
elZoneStageLongitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageLongitude'));
elZoneStageAltitude.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageAltitude'));
elZoneStageDay.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageDay'));
elZoneStageHour.addEventListener('change', createEmitNumberPropertyUpdateFunction('stageHour'));
elMoveSelectionToGrid.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({
type: "action",
@ -712,9 +775,9 @@
</div>
</div>
<div class="model-section property">
<div class="label">Collision Model URL</div>
<div class="label">Compound Shape URL</div>
<div class="value">
<input type="text" id="property-collision-model-url" class="url"></input>
<input type="text" id="property-compound-shape-url" class="url"></input>
</div>
</div>
<div class="model-section property">
@ -833,6 +896,73 @@
<input class="coord" type='number' id="property-light-cutoff"></input>
</div>
</div>
<div class="zone-section property">
<span class="label">Stage Sun Model Enabled</span>
<span class="value">
<input type='checkbox' id="property-zone-stage-sun-model-enabled">
</span>
</div>
<div class="zone-section property">
<div class="label">Key Light Color</div>
<div class="value">
<div class="input-area">R <input class="coord" type='number' id="property-zone-key-light-color-red" min="0" max="255" step="1"></input></div>
<div class="input-area">G <input class="coord" type='number' id="property-zone-key-light-color-green" min="0" max="255" step="1"></input></div>
<div class="input-area">B <input class="coord" type='number' id="property-zone-key-light-color-blue" min="0" max="255" step="1"></input></div>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Intensity</div>
<div class="value">
<input class="coord" type='number' id="property-zone-key-intensity" min="0" max="10" step="0.1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Ambient Intensity</div>
<div class="value">
<input class="coord" type='number' id="property-zone-key-ambient-intensity" min="0" max="10" step="0.1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Key Light Direction</div>
<div class="value">
<div class="input-area">Pitch <input class="coord" type='number' id="property-zone-key-light-direction-x"></input></div>
<div class="input-area">Yaw <input class="coord" type='number' id="property-zone-key-light-direction-y"></input></div>
<div class="input-area">Roll <input class="coord" type='number' id="property-zone-key-light-direction-z"></input></div>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Latitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-latitude" min="-90" max="90" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Longitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-longitude" min="-180" max="180" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Altitude</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-altitude" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Day</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-day" min="0" max="365" step="1"></input>
</div>
</div>
<div class="zone-section property">
<div class="label">Stage Hour</div>
<div class="value">
<input class="coord" type='number' id="property-zone-stage-hour" min="0" max="24" step="0.5"></input>
</div>
</div>
</div>
</body>

View file

@ -53,7 +53,7 @@ function Tooltip() {
text += "ID: " + properties.id + "\n"
if (properties.type == "Model") {
text += "Model URL: " + properties.modelURL + "\n"
text += "Collision Model URL: " + properties.collisionModelURL + "\n"
text += "Compound Shape URL: " + properties.compoundShapeURL + "\n"
text += "Animation URL: " + properties.animationURL + "\n"
text += "Animation is playing: " + properties.animationIsPlaying + "\n"
if (properties.sittingPoints && properties.sittingPoints.length > 0) {

View file

@ -52,7 +52,7 @@ EntityPropertyDialogBox = (function () {
if (properties.type == "Model") {
array.push({ label: "Model URL:", value: properties.modelURL });
index++;
array.push({ label: "Collision Model URL:", value: properties.collisionModelURL });
array.push({ label: "Compound Shape URL:", value: properties.compoundShapeURL });
index++;
array.push({ label: "Animation URL:", value: properties.animationURL });
index++;
@ -284,7 +284,7 @@ EntityPropertyDialogBox = (function () {
properties.locked = array[index++].value;
if (properties.type == "Model") {
properties.modelURL = array[index++].value;
properties.collisionModelURL = array[index++].value;
properties.compoundShapeURL = array[index++].value;
properties.animationURL = array[index++].value;
var newAnimationIsPlaying = array[index++].value;

View file

@ -122,7 +122,7 @@ LightOverlayManager = function() {
Entities.clearingEntities.connect(clearEntities);
// Add existing entities
var ids = Entities.findEntities(MyAvatar.position, 100);
var ids = Entities.findEntities(MyAvatar.position, 64000);
for (var i = 0; i < ids.length; i++) {
addEntity(ids[i]);
}

View file

@ -0,0 +1,145 @@
ZoneOverlayManager = function(isEntityFunc, entityAddedFunc, entityRemovedFunc, entityMovedFunc) {
var self = this;
var visible = false;
// List of all created overlays
var allOverlays = [];
// List of overlays not currently being used
var unusedOverlays = [];
// Map from EntityItemID.id to overlay id
var entityOverlays = {};
// Map from EntityItemID.id to EntityItemID object
var entityIDs = {};
this.updatePositions = function(ids) {
for (var id in entityIDs) {
var entityID = entityIDs[id];
var properties = Entities.getEntityProperties(entityID);
Overlays.editOverlay(entityOverlays[entityID.id].solid, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
});
Overlays.editOverlay(entityOverlays[entityID.id].outline, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
});
}
};
this.setVisible = function(isVisible) {
if (visible != isVisible) {
visible = isVisible;
for (var id in entityOverlays) {
Overlays.editOverlay(entityOverlays[id].solid, { visible: visible });
Overlays.editOverlay(entityOverlays[id].outline, { visible: visible });
}
}
};
// Allocate or get an unused overlay
function getOverlay() {
if (unusedOverlays.length == 0) {
var overlay = Overlays.addOverlay("cube", {
});
allOverlays.push(overlay);
} else {
var overlay = unusedOverlays.pop();
};
return overlay;
}
function releaseOverlay(overlay) {
unusedOverlays.push(overlay);
Overlays.editOverlay(overlay, { visible: false });
}
function addEntity(entityID) {
var properties = Entities.getEntityProperties(entityID);
if (properties.type == "Zone" && !(entityID.id in entityOverlays)) {
var overlaySolid = getOverlay();
var overlayOutline = getOverlay();
entityOverlays[entityID.id] = {
solid: overlaySolid,
outline: overlayOutline,
}
entityIDs[entityID.id] = entityID;
var color = {
red: Math.round(Math.random() * 255),
green: Math.round(Math.random() * 255),
blue: Math.round(Math.random() * 255)
};
Overlays.editOverlay(overlaySolid, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
visible: visible,
solid: true,
alpha: 0.1,
color: color,
ignoreRayIntersection: true,
});
Overlays.editOverlay(overlayOutline, {
position: properties.position,
rotation: properties.rotation,
dimensions: properties.dimensions,
visible: visible,
solid: false,
dashed: false,
lineWidth: 2.0,
alpha: 1.0,
color: color,
ignoreRayIntersection: true,
});
}
}
function deleteEntity(entityID) {
if (entityID.id in entityOverlays) {
releaseOverlay(entityOverlays[entityID.id].outline);
releaseOverlay(entityOverlays[entityID.id].solid);
delete entityOverlays[entityID.id];
}
}
function changeEntityID(oldEntityID, newEntityID) {
entityOverlays[newEntityID.id] = entityOverlays[oldEntityID.id];
entityIDs[newEntityID.id] = newEntityID;
delete entityOverlays[oldEntityID.id];
delete entityIDs[oldEntityID.id];
}
function clearEntities() {
for (var id in entityOverlays) {
releaseOverlay(entityOverlays[id].outline);
releaseOverlay(entityOverlays[id].solid);
}
entityOverlays = {};
entityIDs = {};
}
Entities.addingEntity.connect(addEntity);
Entities.changingEntityID.connect(changeEntityID);
Entities.deletingEntity.connect(deleteEntity);
Entities.clearingEntities.connect(clearEntities);
// Add existing entities
var ids = Entities.findEntities(MyAvatar.position, 64000);
for (var i = 0; i < ids.length; i++) {
addEntity(ids[i]);
}
Script.scriptEnding.connect(function() {
for (var i = 0; i < allOverlays.length; i++) {
Overlays.deleteOverlay(allOverlays[i]);
}
});
};

View file

@ -54,7 +54,7 @@ else ()
list(REMOVE_ITEM INTERFACE_SRCS ${SPEECHRECOGNIZER_CPP})
endif ()
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Script Svg WebKitWidgets)
find_package(Qt5 COMPONENTS Gui Multimedia Network OpenGL Qml Quick Script Svg WebKitWidgets)
# grab the ui files in resources/ui
file (GLOB_RECURSE QT_UI_FILES ui/*.ui)

View file

@ -0,0 +1,76 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
CustomDialog {
title: "Go to..."
objectName: "AddressBarDialog"
height: 128
width: 512
destroyOnCloseButton: false
onVisibleChanged: {
if (!visible) {
reset();
}
}
onEnabledChanged: {
if (enabled) {
addressLine.forceActiveFocus();
}
}
function reset() {
addressLine.text = ""
goButton.source = "../images/address-bar-submit.svg"
}
AddressBarDialog {
id: addressBarDialog
// The client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
CustomBorder {
height: 64
anchors.left: parent.left
anchors.leftMargin: 0
anchors.right: goButton.left
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
CustomTextInput {
id: addressLine
anchors.fill: parent
helperText: "domain, location, @user, /x,y,z"
anchors.margins: 8
onAccepted: {
addressBarDialog.loadAddress(addressLine.text)
}
}
}
Image {
id: goButton
width: 32
height: 32
anchors.right: parent.right
anchors.rightMargin: 8
source: "../images/address-bar-submit.svg"
anchors.verticalCenter: parent.verticalCenter
MouseArea {
anchors.fill: parent
onClicked: {
parent.source = "../images/address-bar-submit-active.svg"
addressBarDialog.loadAddress(addressLine.text)
}
}
}
}
}

View file

@ -0,0 +1,45 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import QtWebKit 3.0
CustomDialog {
title: "Test Dlg"
id: testDialog
objectName: "Browser"
width: 1280
height: 720
Item {
id: clientArea
// The client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
ScrollView {
anchors.fill: parent
WebView {
id: webview
url: "http://slashdot.org"
anchors.fill: parent
}
}
}
}
/*
// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property
MouseArea {
anchors.fill: parent
}
*/

View file

@ -0,0 +1,12 @@
import QtQuick 2.3
Rectangle {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
property int margin: 5
color: myPalette.window
border.color: myPalette.dark
border.width: 5
radius: border.width * 2
}

View file

@ -0,0 +1,23 @@
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
Button {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
text: "Text"
width: 128
height: 64
style: ButtonStyle {
background: CustomBorder {
anchors.fill: parent
}
label: CustomText {
renderType: Text.NativeRendering
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: control.text
color: control.enabled ? myPalette.text : myPalette.dark
}
}
}

View file

@ -0,0 +1,162 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
import "hifiConstants.js" as HifiConstants
Item {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
id: dialog
width: 256
height: 256
scale: 0.0
enabled: false
property int animationDuration: 400
property bool destroyOnInvisible: false
property bool destroyOnCloseButton: true
property bool resizable: false
property int minX: 256
property int minY: 256
clip: true
onEnabledChanged: {
scale = enabled ? 1.0 : 0.0
}
onScaleChanged: {
visible = (scale != 0.0);
}
onVisibleChanged: {
if (!visible && destroyOnInvisible) {
console.log("Destroying closed component");
destroy();
}
}
function close() {
if (destroyOnCloseButton) {
destroyOnInvisible = true
}
enabled = false;
}
function deltaSize(dx, dy) {
width = Math.max(width + dx, minX)
height = Math.max(height + dy, minY)
}
Behavior on scale {
NumberAnimation {
//This specifies how long the animation takes
duration: dialog.animationDuration
//This selects an easing curve to interpolate with, the default is Easing.Linear
easing.type: Easing.InOutBounce
}
}
property int topMargin: dialog.height - clientBorder.height + 8
property int margins: 8
property string title
property int titleSize: titleBorder.height + 12
property string frameColor: HifiConstants.color
property string backgroundColor: myPalette.window
property string headerBackgroundColor: myPalette.dark
CustomBorder {
id: windowBorder
anchors.fill: parent
border.color: dialog.frameColor
color: dialog.backgroundColor
CustomBorder {
id: titleBorder
height: 48
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
border.color: dialog.frameColor
color: dialog.headerBackgroundColor
CustomText {
id: titleText
color: "white"
text: dialog.title
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
anchors.fill: parent
}
MouseArea {
id: titleDrag
anchors.right: closeButton.left
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.top: parent.top
anchors.rightMargin: 4
drag {
target: dialog
minimumX: 0
minimumY: 0
maximumX: dialog.parent ? dialog.parent.width - dialog.width : 0
maximumY: dialog.parent ? dialog.parent.height - dialog.height : 0
}
}
Image {
id: closeButton
x: 360
height: 16
anchors.verticalCenter: parent.verticalCenter
width: 16
anchors.right: parent.right
anchors.rightMargin: 12
source: "../styles/close.svg"
MouseArea {
anchors.fill: parent
onClicked: {
dialog.close();
}
}
}
} // header border
CustomBorder {
id: clientBorder
border.color: dialog.frameColor
color: "#00000000"
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: titleBorder.bottom
anchors.topMargin: -titleBorder.border.width
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.leftMargin: 0
clip: true
} // client border
} // window border
MouseArea {
id: sizeDrag
property int startX
property int startY
anchors.right: parent.right
anchors.bottom: parent.bottom
width: 16
height: 16
hoverEnabled: true
onPressed: {
startX = mouseX
startY = mouseY
}
onPositionChanged: {
if (pressed && dialog.resizable) {
dialog.deltaSize((mouseX - startX), (mouseY - startY))
startX = mouseX
startY = mouseY
}
}
}
}

View file

@ -0,0 +1,7 @@
import QtQuick 2.3
Text {
font.family: "Helvetica"
font.pointSize: 18
}

View file

@ -0,0 +1,10 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
TextArea {
font.family: "Helvetica"
font.pointSize: 18
backgroundVisible: false
readOnly: true
}

View file

@ -0,0 +1,7 @@
import QtQuick 2.3
TextEdit {
font.family: "Helvetica"
font.pointSize: 18
}

View file

@ -0,0 +1,34 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
TextInput {
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
property string helperText: ""
font.family: "Helvetica"
font.pointSize: 18
width: 256
height: 64
color: myPalette.text
clip: true
verticalAlignment: TextInput.AlignVCenter
onTextChanged: {
if (text == "") {
helperText.visible = true;
} else {
helperText.visible = false;
}
}
Text {
id: helperText
anchors.fill: parent
font.pointSize: parent.font.pointSize
font.family: "Helvetica"
verticalAlignment: TextInput.AlignVCenter
text: parent.helperText
color: myPalette.dark
clip: true
}
}

View file

@ -0,0 +1,8 @@
import QtQuick 1.0
Image {
id: icon
width: 64
height: 64
source: "file.svg"
}

View file

@ -0,0 +1,24 @@
import QtQuick 2.3
import QtQuick.Controls 1.3
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
Button {
text: "Text"
style: ButtonStyle {
background: Item { anchors.fill: parent }
label: Text {
id: icon
width: height
verticalAlignment: Text.AlignVCenter
renderType: Text.NativeRendering
font.family: iconFont.name
font.pointSize: 18
property alias unicode: icon.text
FontLoader { id: iconFont; source: "/fonts/fontawesome-webfont.ttf"; }
text: control.text
color: control.enabled ? "white" : "dimgray"
}
}
}

View file

@ -0,0 +1,191 @@
import Hifi 1.0
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Controls.Styles 1.3
import "hifiConstants.js" as HifiConstants
CustomDialog {
title: "Login"
SystemPalette { id: myPalette; colorGroup: SystemPalette.Active }
objectName: "LoginDialog"
height: 512
width: 384
onVisibleChanged: {
if (!visible) {
reset()
}
}
onEnabledChanged: {
if (enabled) {
username.forceActiveFocus();
}
}
function reset() {
username.text = ""
password.text = ""
loginDialog.statusText = ""
}
LoginDialog {
id: loginDialog
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
Column {
anchors.topMargin: 8
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.top: parent.top
spacing: 8
Image {
height: 64
anchors.horizontalCenter: parent.horizontalCenter
width: 64
source: "../images/hifi-logo.svg"
}
CustomBorder {
width: 304
height: 64
anchors.horizontalCenter: parent.horizontalCenter
CustomTextInput {
id: username
anchors.fill: parent
helperText: "Username or Email"
anchors.margins: 8
KeyNavigation.tab: password
KeyNavigation.backtab: password
onAccepted: {
password.forceActiveFocus()
}
}
}
CustomBorder {
width: 304
height: 64
anchors.horizontalCenter: parent.horizontalCenter
CustomTextInput {
id: password
anchors.fill: parent
echoMode: TextInput.Password
helperText: "Password"
anchors.margins: 8
KeyNavigation.tab: username
KeyNavigation.backtab: username
onAccepted: {
if (username.text == "") {
username.forceActiveFocus()
} else {
loginDialog.login(username.text, password.text)
}
}
onFocusChanged: {
if (password.focus) {
password.selectAll()
}
}
}
}
CustomText {
anchors.horizontalCenter: parent.horizontalCenter
textFormat: Text.StyledText
width: parent.width
height: 96
wrapMode: Text.WordWrap
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
text: loginDialog.statusText
}
}
Column {
anchors.bottomMargin: 5
anchors.right: parent.right
anchors.rightMargin: 0
anchors.left: parent.left
anchors.bottom: parent.bottom
Rectangle {
width: 192
height: 64
anchors.horizontalCenter: parent.horizontalCenter
color: HifiConstants.color
border.width: 0
radius: 10
MouseArea {
anchors.bottom: parent.bottom
anchors.bottomMargin: 0
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
onClicked: {
loginDialog.login(username.text, password.text)
}
}
Row {
anchors.centerIn: parent
anchors.verticalCenter: parent.verticalCenter
spacing: 8
Image {
id: loginIcon
height: 32
width: 32
source: "../images/login.svg"
}
CustomText {
text: "Login"
color: "white"
width: 64
height: parent.height
}
}
}
CustomText {
width: parent.width
height: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text:"Create Account"
font.pointSize: 12
font.bold: true
color: HifiConstants.color
MouseArea {
anchors.fill: parent
onClicked: {
loginDialog.openUrl(loginDialog.rootUrl + "/signup")
}
}
}
CustomText {
width: parent.width
height: 24
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pointSize: 12
text: "Recover Password"
color: HifiConstants.color
MouseArea {
anchors.fill: parent
onClicked: {
loginDialog.openUrl(loginDialog.rootUrl + "/users/password/new")
}
}
}
}
}
}

View file

@ -0,0 +1,236 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
Rectangle {
color: "teal"
height: 512
width: 192
SystemPalette { id: sp; colorGroup: SystemPalette.Active }
SystemPalette { id: spi; colorGroup: SystemPalette.Inactive }
SystemPalette { id: spd; colorGroup: SystemPalette.Disabled }
Column {
anchors.margins: 8
anchors.fill: parent
spacing: 8
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "base" }
Rectangle { height: parent.height; width: 16; color: sp.base }
Rectangle { height: parent.height; width: 16; color: spi.base }
Rectangle { height: parent.height; width: 16; color: spd.base }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "alternateBase" }
Rectangle { height: parent.height; width: 16; color: sp.alternateBase }
Rectangle { height: parent.height; width: 16; color: spi.alternateBase }
Rectangle { height: parent.height; width: 16; color: spd.alternateBase }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "dark" }
Rectangle { height: parent.height; width: 16; color: sp.dark }
Rectangle { height: parent.height; width: 16; color: spi.dark }
Rectangle { height: parent.height; width: 16; color: spd.dark }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "mid" }
Rectangle { height: parent.height; width: 16; color: sp.mid }
Rectangle { height: parent.height; width: 16; color: spi.mid }
Rectangle { height: parent.height; width: 16; color: spd.mid }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "mid light" }
Rectangle { height: parent.height; width: 16; color: sp.midlight }
Rectangle { height: parent.height; width: 16; color: spi.midlight }
Rectangle { height: parent.height; width: 16; color: spd.midlight }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "light" }
Rectangle { height: parent.height; width: 16; color: sp.light}
Rectangle { height: parent.height; width: 16; color: spi.light}
Rectangle { height: parent.height; width: 16; color: spd.light}
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "shadow" }
Rectangle { height: parent.height; width: 16; color: sp.shadow}
Rectangle { height: parent.height; width: 16; color: spi.shadow}
Rectangle { height: parent.height; width: 16; color: spd.shadow}
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "text" }
Rectangle { height: parent.height; width: 16; color: sp.text }
Rectangle { height: parent.height; width: 16; color: spi.text }
Rectangle { height: parent.height; width: 16; color: spd.text }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "window" }
Rectangle { height: parent.height; width: 16; color: sp.window }
Rectangle { height: parent.height; width: 16; color: spi.window }
Rectangle { height: parent.height; width: 16; color: spd.window }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "window text" }
Rectangle { height: parent.height; width: 16; color: sp.windowText }
Rectangle { height: parent.height; width: 16; color: spi.windowText }
Rectangle { height: parent.height; width: 16; color: spd.windowText }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "button" }
Rectangle { height: parent.height; width: 16; color: sp.button }
Rectangle { height: parent.height; width: 16; color: spi.button }
Rectangle { height: parent.height; width: 16; color: spd.button }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "buttonText" }
Rectangle { height: parent.height; width: 16; color: sp.buttonText }
Rectangle { height: parent.height; width: 16; color: spi.buttonText }
Rectangle { height: parent.height; width: 16; color: spd.buttonText }
}
Item {
height: 16
width:parent.width
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "highlight" }
Rectangle { height: parent.height; width: 16; color: sp.highlight }
Rectangle { height: parent.height; width: 16; color: spi.highlight }
Rectangle { height: parent.height; width: 16; color: spd.highlight }
}
Row {
width: parent.width
height: 16
Text { height: parent.height; width: 128; text: "highlighted text" }
Rectangle { height: parent.height; width: 16; color: sp.highlightedText}
Rectangle { height: parent.height; width: 16; color: spi.highlightedText}
Rectangle { height: parent.height; width: 16; color: spd.highlightedText}
}
}
/*
CustomDialog {
title: "Test Dlg"
anchors.fill: parent
Rectangle {
property int d: 100
id: square
objectName: "testRect"
width: d
height: d
anchors.centerIn: parent
color: "red"
NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
}
CustomTextEdit {
anchors.left: parent.left
anchors.leftMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
clip: true
text: "test edit"
anchors.top: parent.top
anchors.topMargin: parent.titleSize + 12
}
CustomButton {
x: 128
y: 192
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
onClicked: {
console.log("Click");
if (square.visible) {
square.visible = false
} else {
square.visible = true
}
}
}
CustomButton {
id: customButton2
y: 192
text: "Close"
anchors.left: parent.left
anchors.leftMargin: 12
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
onClicked: {
onClicked: testDialog.x == 0 ? testDialog.x = 200 : testDialog.x = 0
}
}
Keys.onPressed: {
console.log("Key " + event.key);
switch (event.key) {
case Qt.Key_Q:
if (Qt.ControlModifier == event.modifiers) {
event.accepted = true;
break;
}
}
}
}
*/
}
/*
// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property
MouseArea {
anchors.fill: parent
}
*/

View file

@ -0,0 +1,9 @@
import Hifi 1.0
import QtQuick 2.3
Root {
id: root
width: 1280
height: 720
}

View file

@ -0,0 +1,106 @@
import QtQuick 2.3
import QtQuick.Controls 1.2
import QtQuick.Window 2.2
import QtQuick.Dialogs 1.2
import QtQuick.Controls.Styles 1.3
CustomDialog {
title: "Test Dialog"
id: testDialog
objectName: "TestDialog"
width: 512
height: 512
animationDuration: 200
onEnabledChanged: {
if (enabled) {
edit.forceActiveFocus();
}
}
Item {
id: clientArea
// The client area
anchors.fill: parent
anchors.margins: parent.margins
anchors.topMargin: parent.topMargin
Rectangle {
property int d: 100
id: square
objectName: "testRect"
width: d
height: d
anchors.centerIn: parent
color: "red"
NumberAnimation on rotation { from: 0; to: 360; duration: 2000; loops: Animation.Infinite; }
}
CustomTextEdit {
id: edit
anchors.left: parent.left
anchors.leftMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
clip: true
text: "test edit"
anchors.top: parent.top
anchors.topMargin: 12
}
CustomButton {
x: 128
y: 192
text: "Test"
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
anchors.right: parent.right
anchors.rightMargin: 12
onClicked: {
console.log("Click");
if (square.visible) {
square.visible = false
} else {
square.visible = true
}
}
}
CustomButton {
id: customButton2
y: 192
text: "Move"
anchors.left: parent.left
anchors.leftMargin: 12
anchors.bottom: parent.bottom
anchors.bottomMargin: 12
onClicked: {
onClicked: testDialog.x == 0 ? testDialog.x = 200 : testDialog.x = 0
}
}
Keys.onPressed: {
console.log("Key " + event.key);
switch (event.key) {
case Qt.Key_Q:
if (Qt.ControlModifier == event.modifiers) {
event.accepted = true;
break;
}
}
}
}
}
/*
// This is the behavior, and it applies a NumberAnimation to any attempt to set the x property
MouseArea {
anchors.fill: parent
}
*/

View file

@ -0,0 +1,35 @@
import Hifi 1.0
import QtQuick 2.3
Root {
id: root
width: 1280
height: 720
CustomButton {
id: messageBox
anchors.right: createDialog.left
anchors.rightMargin: 24
anchors.bottom: parent.bottom
anchors.bottomMargin: 24
text: "Message"
onClicked: {
console.log("Foo")
root.information("a")
console.log("Bar")
}
}
CustomButton {
id: createDialog
anchors.right: parent.right
anchors.rightMargin: 24
anchors.bottom: parent.bottom
anchors.bottomMargin: 24
text: "Create"
onClicked: {
root.loadChild("TestDialog.qml");
}
}
}

View file

@ -0,0 +1,29 @@
var component;
var instance;
var parent;
function createObject(parentObject, url) {
parent = parentObject;
component = Qt.createComponent(url);
if (component.status == Component.Ready)
finishCreation();
else
component.statusChanged.connect(finishCreation);
}
function finishCreation() {
if (component.status == Component.Ready) {
instance = component.createObject(parent, {"x": 100, "y": 100});
if (instance == null) {
// Error Handling
console.log("Error creating object");
} else {
instance.enabled = true
}
} else if (component.status == Component.Error) {
// Error Handling
console.log("Error loading component:", component.errorString());
} else {
console.log("Unknown component status: " + component.status);
}
}

View file

@ -0,0 +1,4 @@
var color = "#0e7077"
var Colors = {
hifiBlue: "#0e7077"
}

View file

@ -135,6 +135,7 @@
#include "ui/Snapshot.h"
#include "ui/StandAloneJSConsole.h"
#include "ui/Stats.h"
#include "ui/AddressBarDialog.h"
// ON WIndows PC, NVidia Optimus laptop, we want to enable NVIDIA GPU
#if defined(Q_OS_WIN)
@ -207,8 +208,12 @@ public:
void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString& message) {
QString logMessage = LogHandler::getInstance().printMessage((LogMsgType) type, context, message);
if (!logMessage.isEmpty()) {
#ifdef Q_OS_WIN
OutputDebugStringA(logMessage.toLocal8Bit().constData());
OutputDebugStringA("\n");
#endif
Application::getInstance()->getLogger()->addMessage(qPrintable(logMessage + "\n"));
}
}
@ -257,6 +262,7 @@ bool setupEssentials(int& argc, char** argv) {
#endif
auto discoverabilityManager = DependencyManager::set<DiscoverabilityManager>();
auto sceneScriptingInterface = DependencyManager::set<SceneScriptingInterface>();
auto offscreenUi = DependencyManager::set<OffscreenUi>();
return true;
}
@ -313,8 +319,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
#ifdef Q_OS_WIN
installNativeEventFilter(&MyNativeEventFilter::getInstance());
#endif
_logger = new FileLogger(this); // After setting organization name in order to get correct directory
qInstallMessageHandler(messageHandler);
QFontDatabase::addApplicationFont(PathUtils::resourcesPath() + "styles/Inconsolata.otf");
@ -559,8 +567,12 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
#endif
this->installEventFilter(this);
// The offscreen UI needs to intercept the mouse and keyboard
// events coming from the onscreen window
_glWidget->installEventFilter(DependencyManager::get<OffscreenUi>().data());
}
void Application::aboutToQuit() {
emit beforeAboutToQuit();
@ -634,6 +646,7 @@ Application::~Application() {
// stop the glWidget frame timer so it doesn't call paintGL
_glWidget->stopFrameTimer();
DependencyManager::destroy<OffscreenUi>();
DependencyManager::destroy<AvatarManager>();
DependencyManager::destroy<AnimationCache>();
DependencyManager::destroy<TextureCache>();
@ -721,10 +734,45 @@ void Application::initializeGL() {
// update before the first render
update(1.0f / _fps);
// The UI can't be created until the primary OpenGL
// context is created, because it needs to share
// texture resources
initializeUi();
InfoView::showFirstTime(INFO_HELP_PATH);
}
void Application::initializeUi() {
AddressBarDialog::registerType();
LoginDialog::registerType();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_glWidget->context()->contextHandle());
offscreenUi->resize(_glWidget->size());
offscreenUi->setProxyWindow(_window->windowHandle());
offscreenUi->setBaseUrl(QUrl::fromLocalFile(PathUtils::resourcesPath() + "/qml/"));
offscreenUi->load("Root.qml");
offscreenUi->setMouseTranslator([this](const QPointF& p){
if (OculusManager::isConnected()) {
glm::vec2 pos = _applicationOverlay.screenToOverlay(toGlm(p));
return QPointF(pos.x, pos.y);
}
return QPointF(p);
});
offscreenUi->resume();
connect(_window, &MainWindow::windowGeometryChanged, [this](const QRect & r){
static qreal oldDevicePixelRatio = 0;
qreal devicePixelRatio = _glWidget->devicePixelRatio();
if (devicePixelRatio != oldDevicePixelRatio) {
oldDevicePixelRatio = devicePixelRatio;
qDebug() << "Device pixel ratio changed, triggering GL resize";
resizeGL(_glWidget->width(),
_glWidget->height());
}
});
}
void Application::paintGL() {
PROFILE_RANGE(__FUNCTION__);
PerformanceTimer perfTimer("paintGL");
@ -819,7 +867,7 @@ void Application::paintGL() {
{
PerformanceTimer perfTimer("renderOverlay");
_applicationOverlay.renderOverlay(true);
_applicationOverlay.renderOverlay();
_applicationOverlay.displayOverlayTexture();
}
}
@ -864,6 +912,9 @@ void Application::resizeGL(int width, int height) {
updateProjectionMatrix();
glLoadIdentity();
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->resize(_glWidget->size());
// update Stats width
// let's set horizontal offset to give stats some margin to mirror
int horizontalOffset = MIRROR_VIEW_WIDTH + MIRROR_VIEW_LEFT_PADDING * 2;
@ -912,6 +963,44 @@ bool Application::importSVOFromURL(const QString& urlString) {
}
bool Application::event(QEvent* event) {
switch (event->type()) {
case QEvent::MouseMove:
mouseMoveEvent((QMouseEvent*)event);
return true;
case QEvent::MouseButtonPress:
mousePressEvent((QMouseEvent*)event);
return true;
case QEvent::MouseButtonRelease:
mouseReleaseEvent((QMouseEvent*)event);
return true;
case QEvent::KeyPress:
keyPressEvent((QKeyEvent*)event);
return true;
case QEvent::KeyRelease:
keyReleaseEvent((QKeyEvent*)event);
return true;
case QEvent::FocusOut:
focusOutEvent((QFocusEvent*)event);
return true;
case QEvent::TouchBegin:
touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
return true;
case QEvent::TouchEnd:
touchEndEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::TouchUpdate:
touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::Wheel:
wheelEvent(static_cast<QWheelEvent*>(event));
return true;
case QEvent::Drop:
dropEvent(static_cast<QDropEvent*>(event));
return true;
default:
break;
}
// handle custom URL
if (event->type() == QEvent::FileOpen) {
@ -932,7 +1021,7 @@ bool Application::event(QEvent* event) {
if (HFActionEvent::types().contains(event->type())) {
_controllerScriptingInterface.handleMetaEvent(static_cast<HFMetaEvent*>(event));
}
return QApplication::event(event);
}
@ -964,14 +1053,17 @@ void Application::keyPressEvent(QKeyEvent* event) {
bool isShifted = event->modifiers().testFlag(Qt::ShiftModifier);
bool isMeta = event->modifiers().testFlag(Qt::ControlModifier);
bool isOption = event->modifiers().testFlag(Qt::AltModifier);
bool isKeypad = event->modifiers().testFlag(Qt::KeypadModifier);
switch (event->key()) {
break;
case Qt::Key_L:
if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
} else if (isMeta) {
if (isShifted && isMeta) {
Menu::getInstance()->triggerOption(MenuOption::Log);
}
} else if (isMeta) {
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
} else if (isShifted) {
Menu::getInstance()->triggerOption(MenuOption::LodTools);
}
break;
case Qt::Key_E:
@ -1031,11 +1123,6 @@ void Application::keyPressEvent(QKeyEvent* event) {
}
break;
case Qt::Key_Return:
case Qt::Key_Enter:
Menu::getInstance()->triggerOption(MenuOption::AddressBar);
break;
case Qt::Key_Backslash:
Menu::getInstance()->triggerOption(MenuOption::Chat);
break;
@ -1495,6 +1582,17 @@ void Application::dropEvent(QDropEvent *event) {
}
}
void Application::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData* mimeData = event->mimeData();
foreach(QUrl url, mimeData->urls()) {
auto urlString = url.toString();
if (canAcceptURL(urlString)) {
event->acceptProposedAction();
break;
}
}
}
bool Application::acceptSnapshot(const QString& urlString) {
QUrl url(urlString);
QString snapshotPath = url.toLocalFile();
@ -1744,10 +1842,9 @@ void Application::setActiveFaceTracker() {
DependencyManager::get<Faceshift>()->setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift));
#endif
#ifdef HAVE_DDE
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::DDEFaceRegression);
bool isUsingDDE = Menu::getInstance()->isOptionChecked(MenuOption::UseCamera);
Menu::getInstance()->getActionForOption(MenuOption::UseAudioForMouth)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::DDEFiltering)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::ResetDDETracking)->setVisible(isUsingDDE);
Menu::getInstance()->getActionForOption(MenuOption::VelocityFilter)->setVisible(isUsingDDE);
DependencyManager::get<DdeFaceTracker>()->setEnabled(isUsingDDE);
#endif
}

View file

@ -30,6 +30,7 @@
#include <NetworkPacket.h>
#include <NodeList.h>
#include <OctreeQuery.h>
#include <OffscreenUi.h>
#include <PacketHeaders.h>
#include <PhysicsEngine.h>
#include <ScriptEngine.h>
@ -151,6 +152,7 @@ public:
void setPreviousScriptLocation(const QString& previousScriptLocation);
void clearScriptsBeforeRunning();
void initializeGL();
void initializeUi();
void paintGL();
void resizeGL(int width, int height);
@ -169,7 +171,8 @@ public:
void touchUpdateEvent(QTouchEvent* event);
void wheelEvent(QWheelEvent* event);
void dropEvent(QDropEvent *event);
void dropEvent(QDropEvent* event);
void dragEnterEvent(QDragEnterEvent* event);
bool event(QEvent* event);
bool eventFilter(QObject* object, QEvent* event);

View file

@ -82,30 +82,6 @@ void GLCanvas::resizeGL(int width, int height) {
Application::getInstance()->resizeGL(width, height);
}
void GLCanvas::keyPressEvent(QKeyEvent* event) {
Application::getInstance()->keyPressEvent(event);
}
void GLCanvas::keyReleaseEvent(QKeyEvent* event) {
Application::getInstance()->keyReleaseEvent(event);
}
void GLCanvas::focusOutEvent(QFocusEvent* event) {
Application::getInstance()->focusOutEvent(event);
}
void GLCanvas::mouseMoveEvent(QMouseEvent* event) {
Application::getInstance()->mouseMoveEvent(event);
}
void GLCanvas::mousePressEvent(QMouseEvent* event) {
Application::getInstance()->mousePressEvent(event);
}
void GLCanvas::mouseReleaseEvent(QMouseEvent* event) {
Application::getInstance()->mouseReleaseEvent(event);
}
void GLCanvas::activeChanged(Qt::ApplicationState state) {
switch (state) {
case Qt::ApplicationActive:
@ -139,6 +115,7 @@ void GLCanvas::throttleRender() {
OculusManager::beginFrameTiming();
}
makeCurrent();
Application::getInstance()->paintGL();
swapBuffers();
@ -151,40 +128,37 @@ void GLCanvas::throttleRender() {
int updateTime = 0;
bool GLCanvas::event(QEvent* event) {
switch (event->type()) {
case QEvent::MouseMove:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::FocusIn:
case QEvent::FocusOut:
case QEvent::Resize:
case QEvent::TouchBegin:
Application::getInstance()->touchBeginEvent(static_cast<QTouchEvent*>(event));
event->accept();
return true;
case QEvent::TouchEnd:
Application::getInstance()->touchEndEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::TouchUpdate:
Application::getInstance()->touchUpdateEvent(static_cast<QTouchEvent*>(event));
return true;
case QEvent::Wheel:
case QEvent::DragEnter:
case QEvent::Drop:
if (QCoreApplication::sendEvent(QCoreApplication::instance(), event)) {
return true;
}
break;
case QEvent::Paint:
// Ignore paint events that occur after we've decided to quit
if (Application::getInstance()->isAboutToQuit()) {
return true;
}
break;
default:
break;
}
return QGLWidget::event(event);
}
void GLCanvas::wheelEvent(QWheelEvent* event) {
Application::getInstance()->wheelEvent(event);
}
void GLCanvas::dragEnterEvent(QDragEnterEvent* event) {
const QMimeData* mimeData = event->mimeData();
foreach (QUrl url, mimeData->urls()) {
auto urlString = url.toString();
if (Application::getInstance()->canAcceptURL(urlString)) {
event->acceptProposedAction();
break;
}
}
}
void GLCanvas::dropEvent(QDropEvent* event) {
Application::getInstance()->dropEvent(event);
}
// Pressing Alt (and Meta) key alone activates the menubar because its style inherits the
// SHMenuBarAltKeyNavigation from QWindowsStyle. This makes it impossible for a scripts to

View file

@ -40,23 +40,8 @@ protected:
virtual void initializeGL();
virtual void paintGL();
virtual void resizeGL(int width, int height);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void focusOutEvent(QFocusEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual bool event(QEvent* event);
virtual void wheelEvent(QWheelEvent* event);
virtual void dragEnterEvent(QDragEnterEvent *event);
virtual void dropEvent(QDropEvent* event);
private slots:
void activeChanged(Qt::ApplicationState state);
void throttleRender();

View file

@ -96,7 +96,7 @@ Menu::Menu() {
addActionToQMenuAndActionHash(fileMenu,
MenuOption::AddressBar,
Qt::Key_Enter,
Qt::CTRL | Qt::Key_L,
dialogsManager.data(),
SLOT(toggleAddressBar()));
auto addressManager = DependencyManager::get<AddressManager>();
@ -150,7 +150,8 @@ Menu::Menu() {
connect(speechRecognizer.data(), SIGNAL(enabledUpdated(bool)), speechRecognizerAction, SLOT(setChecked(bool)));
#endif
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat, Qt::Key_Backslash,
addActionToQMenuAndActionHash(toolsMenu, MenuOption::Chat,
0, // QML Qt::Key_Backslash,
dialogsManager.data(), SLOT(showIRCLink()));
addActionToQMenuAndActionHash(toolsMenu, MenuOption::AddRemoveFriends, 0,
qApp, SLOT(showFriendsWindow()));
@ -193,7 +194,7 @@ Menu::Menu() {
addActionToQMenuAndActionHash(toolsMenu,
MenuOption::ResetSensors,
Qt::Key_Apostrophe,
0, // QML Qt::Key_Apostrophe,
qApp,
SLOT(resetSensors()));
@ -206,17 +207,17 @@ Menu::Menu() {
QMenu* avatarSizeMenu = avatarMenu->addMenu("Size");
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::IncreaseAvatarSize,
Qt::Key_Plus,
0, // QML Qt::Key_Plus,
avatar,
SLOT(increaseSize()));
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::DecreaseAvatarSize,
Qt::Key_Minus,
0, // QML Qt::Key_Minus,
avatar,
SLOT(decreaseSize()));
addActionToQMenuAndActionHash(avatarSizeMenu,
MenuOption::ResetAvatarSize,
Qt::Key_Equal,
0, // QML Qt::Key_Equal,
avatar,
SLOT(resetSize()));
@ -245,11 +246,15 @@ Menu::Menu() {
qApp,
SLOT(setFullscreen(bool)));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson, Qt::Key_P, true,
qApp,SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror, Qt::SHIFT | Qt::Key_H, true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror, Qt::Key_H, false,
qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FirstPerson,
0, // QML Qt::Key_P,
true, qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Mirror,
0, //QML Qt::SHIFT | Qt::Key_H,
true);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::FullscreenMirror,
0, // QML Qt::Key_H,
false, qApp, SLOT(cameraMenuChanged()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::HMDTools,
#ifdef Q_OS_MAC
@ -282,8 +287,12 @@ Menu::Menu() {
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::TurnWithHead, 0, false);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats, Qt::Key_Slash);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log, Qt::CTRL | Qt::Key_L, qApp, SLOT(toggleLogDialog()));
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats,
0); // QML Qt::Key_Slash);
addCheckableActionToQMenuAndActionHash(viewMenu, MenuOption::Stats);
addActionToQMenuAndActionHash(viewMenu, MenuOption::Log,
Qt::CTRL | Qt::SHIFT | Qt::Key_L,
qApp, SLOT(toggleLogDialog()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::BandwidthDetails, 0,
dialogsManager.data(), SLOT(bandwidthDetails()));
addActionToQMenuAndActionHash(viewMenu, MenuOption::OctreeStats, 0,
@ -293,7 +302,9 @@ Menu::Menu() {
QMenu* developerMenu = addMenu("Developer");
QMenu* renderOptionsMenu = developerMenu->addMenu("Render");
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere, Qt::SHIFT | Qt::Key_A, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Atmosphere,
0, // QML Qt::SHIFT | Qt::Key_A,
true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::AmbientOcclusion);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::DontFadeOnOctreeServerChanges);
@ -345,13 +356,16 @@ Menu::Menu() {
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionThird, 0, false));
resolutionGroup->addAction(addCheckableActionToQMenuAndActionHash(resolutionMenu, MenuOption::RenderResolutionQuarter, 0, false));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars, Qt::Key_Asterisk, true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Stars,
0, // QML Qt::Key_Asterisk,
true);
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::EnableGlowEffect, 0, true,
DependencyManager::get<GlowEffect>().data(), SLOT(toggleGlowEffect(bool)));
addCheckableActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::Wireframe, Qt::ALT | Qt::Key_W, false);
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools, Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
addActionToQMenuAndActionHash(renderOptionsMenu, MenuOption::LodTools,
0, // QML Qt::SHIFT | Qt::Key_L,
dialogsManager.data(), SLOT(lodTools()));
QMenu* avatarDebugMenu = developerMenu->addMenu("Avatar");
@ -371,7 +385,7 @@ Menu::Menu() {
faceTrackerGroup->addAction(faceshiftFaceTracker);
#endif
#ifdef HAVE_DDE
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::DDEFaceRegression,
QAction* ddeFaceTracker = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseCamera,
0, false,
qApp, SLOT(setActiveFaceTracker()));
faceTrackerGroup->addAction(ddeFaceTracker);
@ -381,13 +395,8 @@ Menu::Menu() {
faceTrackingMenu->addSeparator();
QAction* useAudioForMouth = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::UseAudioForMouth, 0, true);
useAudioForMouth->setVisible(false);
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::DDEFiltering, 0, true);
QAction* ddeFiltering = addCheckableActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::VelocityFilter, 0, true);
ddeFiltering->setVisible(false);
QAction* ddeFaceTrackerReset = addActionToQMenuAndActionHash(faceTrackingMenu, MenuOption::ResetDDETracking,
Qt::CTRL | Qt::Key_Apostrophe,
DependencyManager::get<DdeFaceTracker>().data(), SLOT(resetTracking()));
ddeFaceTrackerReset->setVisible(false);
faceTrackingMenu->addAction(ddeFaceTrackerReset);
#endif
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderSkeletonCollisionShapes);

View file

@ -130,8 +130,6 @@ namespace MenuOption {
const QString ControlWithSpeech = "Control With Speech";
const QString CopyAddress = "Copy Address to Clipboard";
const QString CopyPath = "Copy Path to Clipboard";
const QString DDEFaceRegression = "DDE Face Regression";
const QString DDEFiltering = "DDE Filtering";
const QString DecreaseAvatarSize = "Decrease Avatar Size";
const QString DeleteBookmark = "Delete Bookmark...";
const QString DisableActivityLogger = "Disable Activity Logger";
@ -224,7 +222,6 @@ namespace MenuOption {
const QString RenderAmbientLight8 = "CAMPUS_SUNSET";
const QString RenderAmbientLight9 = "FUNSTON_BEACH_SUNSET";
const QString ResetAvatarSize = "Reset Avatar Size";
const QString ResetDDETracking = "Reset DDE Tracking";
const QString ResetSensors = "Reset Sensors";
const QString RunningScripts = "Running Scripts";
const QString RunTimingTests = "Run Timing Tests";
@ -247,6 +244,8 @@ namespace MenuOption {
const QString TransmitterDrive = "Transmitter Drive";
const QString TurnWithHead = "Turn using Head";
const QString UseAudioForMouth = "Use Audio for Mouth";
const QString UseCamera = "Use Camera";
const QString VelocityFilter = "Velocity Filter";
const QString VisibleToEveryone = "Everyone";
const QString VisibleToFriends = "Friends";
const QString VisibleToNoOne = "No one";

View file

@ -200,7 +200,7 @@ void DdeFaceTracker::setEnabled(bool enabled) {
qDebug() << "[Info] DDE Face Tracker Starting";
_ddeProcess = new QProcess(qApp);
connect(_ddeProcess, SIGNAL(finished(int, QProcess::ExitStatus)), SLOT(processFinished(int, QProcess::ExitStatus)));
_ddeProcess->start(QCoreApplication::applicationDirPath() + DDE_PROGRAM_PATH, DDE_ARGUMENTS);
_ddeProcess->start(QCoreApplication::applicationDirPath() + DDE_PROGRAM_PATH, DDE_ARGUMENTS, QIODevice::ReadOnly);
}
if (!enabled && _ddeProcess) {
@ -222,10 +222,14 @@ void DdeFaceTracker::processFinished(int exitCode, QProcess::ExitStatus exitStat
}
}
void DdeFaceTracker::resetTracking() {
void DdeFaceTracker::reset() {
_reset = true;
qDebug() << "[Info] Reset DDE Tracking";
const char* DDE_RESET_COMMAND = "reset";
_udpSocket.writeDatagram(DDE_RESET_COMMAND, DDE_SERVER_ADDR, _controlPort);
_reset = true;
}
bool DdeFaceTracker::isActive() const {
@ -281,7 +285,7 @@ float DdeFaceTracker::getBlendshapeCoefficient(int index) const {
void DdeFaceTracker::decodePacket(const QByteArray& buffer) {
if(buffer.size() > MIN_PACKET_SIZE) {
bool isFiltering = Menu::getInstance()->isOptionChecked(MenuOption::DDEFiltering);
bool isFiltering = Menu::getInstance()->isOptionChecked(MenuOption::VelocityFilter);
Packet packet;
int bytesToCopy = glm::min((int)sizeof(packet), buffer.size());

View file

@ -28,7 +28,7 @@ class DdeFaceTracker : public FaceTracker, public Dependency {
SINGLETON_DEPENDENCY
public:
virtual void reset() { _reset = true; }
virtual void reset();
virtual bool isActive() const;
virtual bool isTracking() const { return isActive(); }
@ -50,7 +50,6 @@ public:
public slots:
void setEnabled(bool enabled);
void resetTracking();
private slots:
void processFinished(int exitCode, QProcess::ExitStatus exitStatus);

View file

@ -525,7 +525,7 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
// We only need to render the overlays to a texture once, then we just render the texture on the hemisphere
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true);
applicationOverlay.renderOverlay();
//Bind our framebuffer object. If we are rendering the glow effect, we let the glow effect shader take care of it
if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {

View file

@ -101,7 +101,7 @@ void TV3DManager::display(Camera& whichCamera) {
// We only need to render the overlays to a texture once, then we just render the texture as a quad
// PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()
applicationOverlay.renderOverlay(true);
applicationOverlay.renderOverlay();
DependencyManager::get<GlowEffect>()->prepare();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

View file

@ -1,148 +1,47 @@
//
// AddressBarDialog.cpp
// interface/src/ui
//
// Created by Stojce Slavkovski on 9/22/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "AddressBarDialog.h"
#include <QMessageBox>
#include <PathUtils.h>
#include "AddressBarDialog.h"
#include "DependencyManager.h"
#include "AddressManager.h"
#include "Application.h"
#include "MainWindow.h"
const QString ADDRESSBAR_GO_BUTTON_ICON = "images/address-bar-submit.svg";
const QString ADDRESSBAR_GO_BUTTON_ACTIVE_ICON = "images/address-bar-submit-active.svg";
QML_DIALOG_DEF(AddressBarDialog)
AddressBarDialog::AddressBarDialog(QWidget* parent) :
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP)
{
setAttribute(Qt::WA_DeleteOnClose, false);
setupUI();
AddressBarDialog::AddressBarDialog(QQuickItem* parent) : OffscreenQmlDialog(parent) {
auto addressManager = DependencyManager::get<AddressManager>();
connect(addressManager.data(), &AddressManager::lookupResultIsOffline, this, &AddressBarDialog::displayAddressOfflineMessage);
connect(addressManager.data(), &AddressManager::lookupResultIsNotFound, this, &AddressBarDialog::displayAddressNotFoundMessage);
connect(addressManager.data(), &AddressManager::lookupResultsFinished, this, &AddressBarDialog::hide);
}
void AddressBarDialog::setupUI() {
const QString DIALOG_STYLESHEET = "font-family: Helvetica, Arial, sans-serif;";
const QString ADDRESSBAR_PLACEHOLDER = "Go to: domain, location, @user, /x,y,z";
const QString ADDRESSBAR_STYLESHEET = "padding: 5px 10px; font-size: 20px;";
const int ADDRESSBAR_MIN_WIDTH = 200;
const int ADDRESSBAR_MAX_WIDTH = 615;
const int ADDRESSBAR_HEIGHT = 42;
const int ADDRESSBAR_STRETCH = 60;
const int BUTTON_SPACER_SIZE = 5;
const int DEFAULT_SPACER_SIZE = 20;
const int ADDRESS_LAYOUT_RIGHT_MARGIN = 10;
const int GO_BUTTON_SIZE = 42;
const int CLOSE_BUTTON_SIZE = 16;
const QString CLOSE_BUTTON_ICON = "styles/close.svg";
const int DIALOG_HEIGHT = 62;
const int DIALOG_INITIAL_WIDTH = 560;
QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
setSizePolicy(sizePolicy);
setMinimumSize(QSize(DIALOG_INITIAL_WIDTH, DIALOG_HEIGHT));
setMaximumHeight(DIALOG_HEIGHT);
setStyleSheet(DIALOG_STYLESHEET);
_verticalLayout = new QVBoxLayout(this);
_verticalLayout->setContentsMargins(0, 0, 0, 0);
_addressLayout = new QHBoxLayout();
_addressLayout->setContentsMargins(0, 0, ADDRESS_LAYOUT_RIGHT_MARGIN, 0);
_leftSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE,
DEFAULT_SPACER_SIZE,
QSizePolicy::MinimumExpanding,
QSizePolicy::Minimum);
_addressLayout->addItem(_leftSpacer);
_addressLineEdit = new QLineEdit(this);
_addressLineEdit->setAttribute(Qt::WA_MacShowFocusRect, 0);
_addressLineEdit->setPlaceholderText(ADDRESSBAR_PLACEHOLDER);
QSizePolicy sizePolicyLineEdit(QSizePolicy::Preferred, QSizePolicy::Fixed);
sizePolicyLineEdit.setHorizontalStretch(ADDRESSBAR_STRETCH);
_addressLineEdit->setSizePolicy(sizePolicyLineEdit);
_addressLineEdit->setMinimumSize(QSize(ADDRESSBAR_MIN_WIDTH, ADDRESSBAR_HEIGHT));
_addressLineEdit->setMaximumSize(QSize(ADDRESSBAR_MAX_WIDTH, ADDRESSBAR_HEIGHT));
_addressLineEdit->setStyleSheet(ADDRESSBAR_STYLESHEET);
_addressLayout->addWidget(_addressLineEdit);
_buttonSpacer = new QSpacerItem(BUTTON_SPACER_SIZE, BUTTON_SPACER_SIZE, QSizePolicy::Fixed, QSizePolicy::Minimum);
_addressLayout->addItem(_buttonSpacer);
_goButton = new QPushButton(this);
_goButton->setSizePolicy(sizePolicy);
_goButton->setMinimumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
_goButton->setMaximumSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
_goButton->setIcon(QIcon(PathUtils::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON));
_goButton->setIconSize(QSize(GO_BUTTON_SIZE, GO_BUTTON_SIZE));
_goButton->setDefault(true);
_goButton->setFlat(true);
_addressLayout->addWidget(_goButton);
_rightSpacer = new QSpacerItem(DEFAULT_SPACER_SIZE,
DEFAULT_SPACER_SIZE,
QSizePolicy::MinimumExpanding,
QSizePolicy::Minimum);
_addressLayout->addItem(_rightSpacer);
_closeButton = new QPushButton(this);
_closeButton->setSizePolicy(sizePolicy);
_closeButton->setMinimumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
_closeButton->setMaximumSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
QIcon icon(PathUtils::resourcesPath() + CLOSE_BUTTON_ICON);
_closeButton->setIcon(icon);
_closeButton->setIconSize(QSize(CLOSE_BUTTON_SIZE, CLOSE_BUTTON_SIZE));
_closeButton->setFlat(true);
_addressLayout->addWidget(_closeButton, 0, Qt::AlignRight);
_verticalLayout->addLayout(_addressLayout);
connect(_goButton, &QPushButton::clicked, this, &AddressBarDialog::accept);
connect(_closeButton, &QPushButton::clicked, this, &QDialog::close);
void AddressBarDialog::hide() {
((QQuickItem*)parent())->setEnabled(false);
}
void AddressBarDialog::showEvent(QShowEvent* event) {
_goButton->setIcon(QIcon(PathUtils::resourcesPath() + ADDRESSBAR_GO_BUTTON_ICON));
_addressLineEdit->setText(QString());
_addressLineEdit->setFocus();
FramelessDialog::showEvent(event);
}
void AddressBarDialog::accept() {
if (!_addressLineEdit->text().isEmpty()) {
_goButton->setIcon(QIcon(PathUtils::resourcesPath() + ADDRESSBAR_GO_BUTTON_ACTIVE_ICON));
auto addressManager = DependencyManager::get<AddressManager>();
connect(addressManager.data(), &AddressManager::lookupResultsFinished, this, &QDialog::hide);
addressManager->handleLookupString(_addressLineEdit->text());
void AddressBarDialog::loadAddress(const QString& address) {
qDebug() << "Called LoadAddress with address " << address;
if (!address.isEmpty()) {
DependencyManager::get<AddressManager>()->handleLookupString(address);
}
}
void AddressBarDialog::displayAddressOfflineMessage() {
QMessageBox::information(Application::getInstance()->getWindow(), "Address offline",
"That user or place is currently offline.");
OffscreenUi::information("Address offline",
"That user or place is currently offline.");
}
void AddressBarDialog::displayAddressNotFoundMessage() {
QMessageBox::information(Application::getInstance()->getWindow(), "Address not found",
"There is no address information for that user or place.");
}
OffscreenUi::information("Address not found",
"There is no address information for that user or place.");
}

View file

@ -1,47 +1,33 @@
//
// AddressBarDialog.h
// interface/src/ui
//
// Created by Stojce Slavkovski on 9/22/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 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
//
#pragma once
#ifndef hifi_AddressBarDialog_h
#define hifi_AddressBarDialog_h
#include "FramelessDialog.h"
#include <OffscreenQmlDialog.h>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QSpacerItem>
#include <QVBoxLayout>
class AddressBarDialog : public FramelessDialog {
class AddressBarDialog : public OffscreenQmlDialog
{
Q_OBJECT
QML_DIALOG_DECL
public:
AddressBarDialog(QWidget* parent);
private:
void setupUI();
void showEvent(QShowEvent* event);
QVBoxLayout *_verticalLayout;
QHBoxLayout *_addressLayout;
QSpacerItem *_leftSpacer;
QSpacerItem *_rightSpacer;
QSpacerItem *_buttonSpacer;
QPushButton *_goButton;
QPushButton *_closeButton;
QLineEdit *_addressLineEdit;
private slots:
void accept();
AddressBarDialog(QQuickItem* parent = nullptr);
protected:
void displayAddressOfflineMessage();
void displayAddressNotFoundMessage();
void hide();
Q_INVOKABLE void loadAddress(const QString& address);
};
#endif // hifi_AddressBarDialog_h
#endif

View file

@ -17,6 +17,7 @@
#include <GLMHelpers.h>
#include <PathUtils.h>
#include <PerfStat.h>
#include <OffscreenUi.h>
#include "AudioClient.h"
#include "audio/AudioIOStatsRenderer.h"
@ -161,13 +162,27 @@ ApplicationOverlay::ApplicationOverlay() :
_domainStatusBorder = geometryCache->allocateID();
_magnifierBorder = geometryCache->allocateID();
// Once we move UI rendering and screen rendering to different
// threads, we need to use a sync object to deteremine when
// the current UI texture is no longer being read from, and only
// then release it back to the UI for re-use
auto offscreenUi = DependencyManager::get<OffscreenUi>();
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [&](GLuint textureId) {
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->lockTexture(textureId);
assert(!glGetError());
std::swap(_newUiTexture, textureId);
if (textureId) {
offscreenUi->releaseTexture(textureId);
}
});
}
ApplicationOverlay::~ApplicationOverlay() {
}
// Renders the overlays either to a texture or to the screen
void ApplicationOverlay::renderOverlay(bool renderToTexture) {
void ApplicationOverlay::renderOverlay() {
PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), "ApplicationOverlay::displayOverlay()");
Overlays& overlays = qApp->getOverlays();
auto glCanvas = Application::getInstance()->getGLWidget();
@ -183,11 +198,9 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
if (renderToTexture) {
_overlays.buildFramebufferObject();
_overlays.bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
_overlays.buildFramebufferObject();
_overlays.bind();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix(); {
const float NEAR_CLIP = -10000;
@ -218,9 +231,25 @@ void ApplicationOverlay::renderOverlay(bool renderToTexture) {
glEnable(GL_LIGHTING);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
if (renderToTexture) {
_overlays.release();
_overlays.release();
}
// A quick and dirty solution for compositing the old overlay
// texture with the new one
template <typename F>
void with_each_texture(GLuint firstPassTexture, GLuint secondPassTexture, F f) {
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
if (firstPassTexture) {
glBindTexture(GL_TEXTURE_2D, firstPassTexture);
f();
}
if (secondPassTexture) {
glBindTexture(GL_TEXTURE_2D, secondPassTexture);
f();
}
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
}
// Draws the FBO texture for the screen
@ -229,30 +258,24 @@ void ApplicationOverlay::displayOverlayTexture() {
return;
}
auto glCanvas = Application::getInstance()->getGLWidget();
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
_overlays.bindTexture();
glMatrixMode(GL_PROJECTION);
glPushMatrix(); {
glLoadIdentity();
glOrtho(0, glCanvas->getDeviceWidth(), glCanvas->getDeviceHeight(), 0, -1.0, 1.0);
glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
glm::vec2 topLeft(0.0f, 0.0f);
glm::vec2 bottomRight(glCanvas->getDeviceWidth(), glCanvas->getDeviceHeight());
glm::vec2 texCoordTopLeft(0.0f, 1.0f);
glm::vec2 texCoordBottomRight(1.0f, 0.0f);
if (_alpha < 1.0) {
glEnable(GL_BLEND);
}
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, _alpha));
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
static const glm::vec2 topLeft(-1, 1);
static const glm::vec2 bottomRight(1, -1);
static const glm::vec2 texCoordTopLeft(0.0f, 1.0f);
static const glm::vec2 texCoordBottomRight(1.0f, 0.0f);
DependencyManager::get<GeometryCache>()->renderQuad(topLeft, bottomRight, texCoordTopLeft, texCoordBottomRight,
glm::vec4(1.0f, 1.0f, 1.0f, _alpha));
});
} glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
// Draws the FBO texture for Oculus rift.
@ -260,10 +283,7 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
if (_alpha == 0.0f) {
return;
}
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
_overlays.bindTexture();
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
glEnable(GL_DEPTH_TEST);
@ -271,8 +291,8 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
glDisable(GL_LIGHTING);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.01f);
//Update and draw the magnifiers
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const glm::quat& orientation = myAvatar->getOrientation();
@ -303,8 +323,9 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
//Render magnifier, but dont show border for mouse magnifier
glm::vec2 projection = screenToOverlay(glm::vec2(_reticlePosition[MOUSE].x(),
_reticlePosition[MOUSE].y()));
renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
renderMagnifier(projection, _magSizeMult[i], i != MOUSE);
});
}
}
@ -319,12 +340,15 @@ void ApplicationOverlay::displayOverlayTextureOculus(Camera& whichCamera) {
_overlays.buildVBO(_textureFov, _textureAspectRatio, 80, 80);
}
_overlays.render();
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
_overlays.render();
});
if (!Application::getInstance()->isMouseHidden()) {
renderPointersOculus(myAvatar->getDefaultEyePosition());
}
glDepthMask(GL_TRUE);
_overlays.releaseTexture();
glDisable(GL_TEXTURE_2D);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
@ -341,14 +365,10 @@ void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float as
MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();
const glm::vec3& viewMatrixTranslation = qApp->getViewMatrixTranslation();
glActiveTexture(GL_TEXTURE0);
glEnable(GL_BLEND);
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
_overlays.bindTexture();
glEnable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glMatrixMode(GL_MODELVIEW);
@ -382,13 +402,15 @@ void ApplicationOverlay::displayOverlayTexture3DTV(Camera& whichCamera, float as
GLfloat y = -halfQuadHeight;
glDisable(GL_DEPTH_TEST);
DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
with_each_texture(_overlays.getTexture(), _newUiTexture, [&] {
DependencyManager::get<GeometryCache>()->renderQuad(glm::vec3(x, y + quadHeight, -distance),
glm::vec3(x + quadWidth, y + quadHeight, -distance),
glm::vec3(x + quadWidth, y, -distance),
glm::vec3(x, y, -distance),
glm::vec2(0.0f, 1.0f), glm::vec2(1.0f, 1.0f),
glm::vec2(1.0f, 0.0f), glm::vec2(0.0f, 0.0f),
overlayColor);
});
auto glCanvas = Application::getInstance()->getGLWidget();
if (_crosshairTexture == 0) {
@ -993,14 +1015,6 @@ void ApplicationOverlay::TexturedHemisphere::release() {
_framebufferObject->release();
}
void ApplicationOverlay::TexturedHemisphere::bindTexture() {
glBindTexture(GL_TEXTURE_2D, _framebufferObject->texture());
}
void ApplicationOverlay::TexturedHemisphere::releaseTexture() {
glBindTexture(GL_TEXTURE_2D, 0);
}
void ApplicationOverlay::TexturedHemisphere::buildVBO(const float fov,
const float aspectRatio,
const int slices,
@ -1099,14 +1113,14 @@ void ApplicationOverlay::TexturedHemisphere::buildFramebufferObject() {
}
_framebufferObject = new QOpenGLFramebufferObject(size, QOpenGLFramebufferObject::Depth);
bindTexture();
glBindTexture(GL_TEXTURE_2D, getTexture());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
GLfloat borderColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
releaseTexture();
glBindTexture(GL_TEXTURE_2D, 0);
}
//Renders a hemisphere with texture coordinates.
@ -1137,6 +1151,10 @@ void ApplicationOverlay::TexturedHemisphere::render() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
GLuint ApplicationOverlay::TexturedHemisphere::getTexture() {
return _framebufferObject->texture();
}
glm::vec2 ApplicationOverlay::directionToSpherical(glm::vec3 direction) const {
glm::vec2 result;
// Compute yaw

View file

@ -23,12 +23,13 @@ const float MAGNIFY_MULT = 2.0f;
const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f;
// Handles the drawing of the overlays to the screen
class ApplicationOverlay {
class ApplicationOverlay : public QObject {
Q_OBJECT
public:
ApplicationOverlay();
~ApplicationOverlay();
void renderOverlay(bool renderToTexture = false);
void renderOverlay();
void displayOverlayTexture();
void displayOverlayTextureOculus(Camera& whichCamera);
void displayOverlayTexture3DTV(Camera& whichCamera, float aspectRatio, float fov);
@ -75,8 +76,7 @@ private:
void bind();
void release();
void bindTexture();
void releaseTexture();
GLuint getTexture();
void buildFramebufferObject();
void buildVBO(const float fov, const float aspectRatio, const int slices, const int stacks);
@ -122,6 +122,9 @@ private:
float _trailingAudioLoudness;
GLuint _crosshairTexture;
// TODO, move divide up the rendering, displaying and input handling
// facilities of this class
GLuint _newUiTexture{ 0 };
int _reticleQuad;
int _magnifierQuad;

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "DialogsManager.h"
#include <QMessageBox>
#include <AccountManager.h>
@ -29,14 +31,9 @@
#include "PreferencesDialog.h"
#include "ScriptEditorWindow.h"
#include "DialogsManager.h"
void DialogsManager::toggleAddressBar() {
maybeCreateDialog(_addressBarDialog);
if (!_addressBarDialog->isVisible()) {
_addressBarDialog->show();
}
AddressBarDialog::toggle();
}
void DialogsManager::toggleDiskCacheEditor() {
@ -45,13 +42,11 @@ void DialogsManager::toggleDiskCacheEditor() {
}
void DialogsManager::toggleLoginDialog() {
maybeCreateDialog(_loginDialog);
_loginDialog->toggleQAction();
LoginDialog::toggleAction();
}
void DialogsManager::showLoginDialog() {
maybeCreateDialog(_loginDialog);
_loginDialog->showLoginForCurrentDomain();
LoginDialog::show();
}
void DialogsManager::octreeStatsDetails() {
@ -180,3 +175,4 @@ void DialogsManager::showIRCLink() {
_ircInfoBox->raise();
}

View file

@ -1,166 +0,0 @@
//
// FramelessDialog.cpp
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QScreen>
#include <QWindow>
#include <PathUtils.h>
#include "Application.h"
#include "FramelessDialog.h"
#include "Menu.h"
const int RESIZE_HANDLE_WIDTH = 7;
FramelessDialog::FramelessDialog(QWidget *parent, Qt::WindowFlags flags, Position position) :
QDialog(parent, flags | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint),
_allowResize(true),
_isResizing(false),
_resizeInitialWidth(0),
_selfHidden(false),
_position(position),
_hideOnBlur(true) {
setAttribute(Qt::WA_DeleteOnClose);
// handle rezize and move events
parentWidget()->installEventFilter(this);
// handle minimize, restore and focus events
Application::getInstance()->installEventFilter(this);
}
bool FramelessDialog::eventFilter(QObject* sender, QEvent* event) {
switch (event->type()) {
case QEvent::Move:
if (sender == parentWidget()) {
resizeAndPosition(false);
}
break;
case QEvent::Resize:
if (sender == parentWidget()) {
resizeAndPosition(false);
}
break;
case QEvent::WindowStateChange:
if (_hideOnBlur && parentWidget()->isMinimized()) {
if (isVisible()) {
_selfHidden = true;
setHidden(true);
}
} else if (_selfHidden) {
_selfHidden = false;
setHidden(false);
}
break;
case QEvent::ApplicationDeactivate:
// hide on minimize and focus lost
if (_hideOnBlur && isVisible()) {
_selfHidden = true;
setHidden(true);
}
break;
case QEvent::ApplicationActivate:
if (_selfHidden) {
_selfHidden = false;
setHidden(false);
}
break;
default:
break;
}
return false;
}
void FramelessDialog::setStyleSheetFile(const QString& fileName) {
QFile globalStyleSheet(PathUtils::resourcesPath() + "styles/global.qss");
QFile styleSheet(PathUtils::resourcesPath() + fileName);
if (styleSheet.open(QIODevice::ReadOnly) && globalStyleSheet.open(QIODevice::ReadOnly) ) {
QDir::setCurrent(PathUtils::resourcesPath());
setStyleSheet(globalStyleSheet.readAll() + styleSheet.readAll());
}
}
void FramelessDialog::showEvent(QShowEvent* event) {
resizeAndPosition();
QDialog::showEvent(event);
}
void FramelessDialog::resizeAndPosition(bool resizeParent) {
QRect parentGeometry = Application::getInstance()->getDesirableApplicationGeometry();
QSize parentSize = parentGeometry.size();
// keep full app height or width depending on position
if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
setFixedHeight(parentSize.height());
} else {
setFixedWidth(parentSize.width());
}
// resize parrent if width is smaller than this dialog
if (resizeParent && parentSize.width() < size().width()) {
parentWidget()->resize(size().width(), parentSize.height());
}
if (_position == POSITION_LEFT || _position == POSITION_TOP) {
// move to upper left corner
move(parentGeometry.topLeft());
} else if (_position == POSITION_RIGHT) {
// move to upper right corner
QPoint pos = parentGeometry.topRight();
pos.setX(pos.x() - size().width());
move(pos);
}
repaint();
}
void FramelessDialog::mousePressEvent(QMouseEvent* mouseEvent) {
if (_allowResize && mouseEvent->button() == Qt::LeftButton) {
if (_position == POSITION_LEFT || _position == POSITION_RIGHT) {
bool hitLeft = (_position == POSITION_LEFT) && (abs(mouseEvent->pos().x() - size().width()) < RESIZE_HANDLE_WIDTH);
bool hitRight = (_position == POSITION_RIGHT) && (mouseEvent->pos().x() < RESIZE_HANDLE_WIDTH);
if (hitLeft || hitRight) {
_isResizing = true;
_resizeInitialWidth = size().width();
setCursor(Qt::SizeHorCursor);
}
} else {
bool hitTop = (_position == POSITION_TOP) && (abs(mouseEvent->pos().y() - size().height()) < RESIZE_HANDLE_WIDTH);
if (hitTop) {
_isResizing = true;
_resizeInitialWidth = size().height();
setCursor(Qt::SizeHorCursor);
}
}
}
}
void FramelessDialog::mouseReleaseEvent(QMouseEvent* mouseEvent) {
unsetCursor();
_isResizing = false;
}
void FramelessDialog::mouseMoveEvent(QMouseEvent* mouseEvent) {
if (_isResizing) {
if (_position == POSITION_LEFT) {
resize(mouseEvent->pos().x(), size().height());
} else if (_position == POSITION_RIGHT) {
setUpdatesEnabled(false);
resize(_resizeInitialWidth - mouseEvent->pos().x(), size().height());
resizeAndPosition();
_resizeInitialWidth = size().width();
setUpdatesEnabled(true);
} else if (_position == POSITION_TOP) {
resize(size().width(), mouseEvent->pos().y());
}
}
}

View file

@ -1,50 +0,0 @@
//
// FramelessDialog.h
// interface/src/ui
//
// Created by Stojce Slavkovski on 2/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_FramelessDialog_h
#define hifi_FramelessDialog_h
#include <QDialog>
class FramelessDialog : public QDialog {
Q_OBJECT
public:
enum Position { POSITION_LEFT, POSITION_RIGHT, POSITION_TOP };
FramelessDialog(QWidget* parent, Qt::WindowFlags flags = 0, Position position = POSITION_LEFT);
void setStyleSheetFile(const QString& fileName);
void setAllowResize(bool allowResize) { _allowResize = allowResize; }
bool getAllowResize() { return _allowResize; }
void setHideOnBlur(bool hideOnBlur) { _hideOnBlur = hideOnBlur; }
bool getHideOnBlur() { return _hideOnBlur; }
void resizeAndPosition(bool resizeParent = true);
protected:
virtual void mouseMoveEvent(QMouseEvent* mouseEvent);
virtual void mousePressEvent(QMouseEvent* mouseEvent);
virtual void mouseReleaseEvent(QMouseEvent* mouseEvent);
virtual void showEvent(QShowEvent* event);
bool eventFilter(QObject* sender, QEvent* event);
private:
bool _allowResize;
bool _isResizing;
int _resizeInitialWidth;
bool _selfHidden; ///< true when the dialog itself because of a window event (deactivation or minimization)
Position _position;
bool _hideOnBlur;
};
#endif // hifi_FramelessDialog_h

View file

@ -1,120 +1,35 @@
//
//
// LoginDialog.cpp
// interface/src/ui
//
// Created by Ryan Huffman on 4/23/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QWidget>
#include <QPushButton>
#include <QPixmap>
#include <NetworkingConstants.h>
#include <PathUtils.h>
#include "Application.h"
#include "Menu.h"
#include "AccountManager.h"
#include "ui_loginDialog.h"
#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";
#include "DependencyManager.h"
#include "AccountManager.h"
#include "Menu.h"
#include <NetworkingConstants.h>
LoginDialog::LoginDialog(QWidget* parent) :
FramelessDialog(parent, 0, FramelessDialog::POSITION_TOP),
_ui(new Ui::LoginDialog) {
_ui->setupUi(this);
reset();
setAttribute(Qt::WA_DeleteOnClose, false);
QML_DIALOG_DEF(LoginDialog)
LoginDialog::LoginDialog(QQuickItem *parent) : OffscreenQmlDialog(parent), _rootUrl(NetworkingConstants::METAVERSE_SERVER_URL.toString()) {
connect(&AccountManager::getInstance(), &AccountManager::loginComplete,
this, &LoginDialog::handleLoginCompleted);
this, &LoginDialog::handleLoginCompleted);
connect(&AccountManager::getInstance(), &AccountManager::loginFailed,
this, &LoginDialog::handleLoginFailed);
connect(_ui->loginButton, &QPushButton::clicked,
this, &LoginDialog::handleLoginClicked);
connect(_ui->closeButton, &QPushButton::clicked,
this, &LoginDialog::close);
UIUtil::scaleWidgetFontSizes(this);
_ui->accountLabel->setText(_ui->accountLabel->text().arg(CREATE_ACCOUNT_URL, FORGOT_PASSWORD_URL));
// Initialize toggle connection
toggleQAction();
};
LoginDialog::~LoginDialog() {
delete _ui;
};
void LoginDialog::reset() {
_ui->errorLabel->hide();
_ui->emailLineEdit->setFocus();
_ui->logoLabel->setPixmap(QPixmap(PathUtils::resourcesPath() + "images/hifi-logo.svg"));
_ui->loginButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/login.svg"));
_ui->closeButton->setIcon(QIcon(PathUtils::resourcesPath() + "images/close.svg"));
_ui->infoLabel->setVisible(false);
_ui->errorLabel->setVisible(false);
_ui->emailLineEdit->setText("");
_ui->passwordLineEdit->setText("");
_ui->loginArea->setDisabled(false);
this, &LoginDialog::handleLoginFailed);
}
void LoginDialog::handleLoginCompleted(const QUrl& authURL) {
reset();
close();
};
void LoginDialog::handleLoginFailed() {
_ui->infoLabel->setVisible(false);
_ui->errorLabel->setVisible(true);
_ui->errorLabel->show();
_ui->loginArea->setDisabled(false);
// Move focus to password and select the entire line
_ui->passwordLineEdit->setFocus();
_ui->passwordLineEdit->setSelection(0, _ui->emailLineEdit->maxLength());
};
void LoginDialog::handleLoginClicked() {
// If the email or password inputs are empty, move focus to them, otherwise attempt to login.
if (_ui->emailLineEdit->text().isEmpty()) {
_ui->emailLineEdit->setFocus();
} else if (_ui->passwordLineEdit->text().isEmpty()) {
_ui->passwordLineEdit->setFocus();
} else {
_ui->infoLabel->setVisible(true);
_ui->errorLabel->setVisible(false);
_ui->loginArea->setDisabled(true);
AccountManager::getInstance().requestAccessToken(_ui->emailLineEdit->text(), _ui->passwordLineEdit->text());
}
};
void LoginDialog::moveEvent(QMoveEvent* event) {
// Modal dialogs seemed to get repositioned automatically. Combat this by moving the window if needed.
resizeAndPosition();
};
void LoginDialog::toggleQAction() {
void LoginDialog::toggleAction() {
AccountManager& accountManager = AccountManager::getInstance();
QAction* loginAction = Menu::getInstance()->getActionForOption(MenuOption::Login);
Q_CHECK_PTR(loginAction);
disconnect(loginAction, 0, 0, 0);
if (accountManager.isLoggedIn()) {
// change the menu item to logout
loginAction->setText("Logout " + accountManager.getAccountInfo().getUsername());
@ -122,11 +37,39 @@ void LoginDialog::toggleQAction() {
} else {
// change the menu item to login
loginAction->setText("Login");
connect(loginAction, &QAction::triggered, this, &LoginDialog::showLoginForCurrentDomain);
connect(loginAction, &QAction::triggered, &LoginDialog::show);
}
}
void LoginDialog::showLoginForCurrentDomain() {
show();
resizeAndPosition(false);
void LoginDialog::handleLoginCompleted(const QUrl&) {
hide();
}
void LoginDialog::handleLoginFailed() {
setStatusText("<font color = \"#267077\">Invalid username or password.< / font>");
}
void LoginDialog::setStatusText(const QString& statusText) {
if (statusText != _statusText) {
_statusText = statusText;
emit statusTextChanged();
}
}
QString LoginDialog::statusText() const {
return _statusText;
}
QString LoginDialog::rootUrl() const {
return _rootUrl;
}
void LoginDialog::login(const QString& username, const QString& password) {
qDebug() << "Attempting to login " << username;
setStatusText("Authenticating...");
AccountManager::getInstance().requestAccessToken(username, password);
}
void LoginDialog::openUrl(const QString& url) {
qDebug() << url;
}

View file

@ -1,46 +1,49 @@
//
// LoginDialog.h
// interface/src/ui
//
// Created by Ryan Huffman on 4/23/14.
// Copyright 2014 High Fidelity, Inc.
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 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
//
#pragma once
#ifndef hifi_LoginDialog_h
#define hifi_LoginDialog_h
#include <QObject>
#include "FramelessDialog.h"
#include <OffscreenQmlDialog.h>
namespace Ui {
class LoginDialog;
}
class LoginDialog : public FramelessDialog {
class LoginDialog : public OffscreenQmlDialog
{
Q_OBJECT
QML_DIALOG_DECL
Q_PROPERTY(QString statusText READ statusText WRITE setStatusText NOTIFY statusTextChanged)
Q_PROPERTY(QString rootUrl READ rootUrl)
public:
LoginDialog(QWidget* parent);
~LoginDialog();
static void toggleAction();
public slots:
void toggleQAction();
void showLoginForCurrentDomain();
protected slots:
void reset();
void handleLoginClicked();
LoginDialog(QQuickItem* parent = nullptr);
void setStatusText(const QString& statusText);
QString statusText() const;
QString rootUrl() const;
signals:
void statusTextChanged();
protected:
void handleLoginCompleted(const QUrl& authURL);
void handleLoginFailed();
protected:
void moveEvent(QMoveEvent* event);
Q_INVOKABLE void login(const QString& username, const QString& password);
Q_INVOKABLE void openUrl(const QString& url);
private:
Ui::LoginDialog* _ui = nullptr;
QString _statusText;
const QString _rootUrl;
};
#endif // hifi_LoginDialog_h

View file

@ -77,7 +77,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
if (_drawOnHUD) {
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, glm::vec4(1.0f, 1.0f, 1.0f, alpha));
}
glPopMatrix();
@ -89,7 +89,7 @@ void Cube3DOverlay::render(RenderArgs* args) {
if (_drawOnHUD) {
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
} else {
DependencyManager::get<DeferredLightingEffect>()->renderSolidCube(1.0f, cubeColor);
DependencyManager::get<GeometryCache>()->renderSolidCube(1.0f, cubeColor);
}
glPopMatrix();
} else {

View file

@ -23,6 +23,7 @@
#include <Model.h>
#include <NetworkAccessManager.h>
#include <PerfStat.h>
#include <SceneScriptingInterface.h>
#include <ScriptEngine.h>
#include "EntityTreeRenderer.h"
@ -30,12 +31,12 @@
#include "RenderableBoxEntityItem.h"
#include "RenderableLightEntityItem.h"
#include "RenderableModelEntityItem.h"
#include "RenderableParticleEffectEntityItem.h"
#include "RenderableSphereEntityItem.h"
#include "RenderableTextEntityItem.h"
#include "RenderableParticleEffectEntityItem.h"
#include "RenderableZoneEntityItem.h"
#include "EntitiesRendererLogging.h"
EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterface* viewState,
AbstractScriptingServicesInterface* scriptingServices) :
OctreeRenderer(),
@ -56,6 +57,7 @@ EntityTreeRenderer::EntityTreeRenderer(bool wantScripts, AbstractViewStateInterf
REGISTER_ENTITY_TYPE_WITH_FACTORY(Light, RenderableLightEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Text, RenderableTextEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(ParticleEffect, RenderableParticleEffectEntityItem::factory)
REGISTER_ENTITY_TYPE_WITH_FACTORY(Zone, RenderableZoneEntityItem::factory)
_currentHoverOverEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
_currentClickingOnEntityID = EntityItemID::createInvalidEntityID(); // makes it the unknown ID
@ -393,10 +395,51 @@ void EntityTreeRenderer::render(RenderArgs::RenderMode renderMode, RenderArgs::R
_tree->lockForRead();
// Whenever you're in an intersection between zones, we will always choose the smallest zone.
_bestZone = NULL;
_bestZoneVolume = std::numeric_limits<float>::max();
_tree->recurseTreeWithOperation(renderOperation, &args);
// Model::RenderMode modelRenderMode = renderMode == RenderArgs::SHADOW_RENDER_MODE
// ? RenderArgs::SHADOW_RENDER_MODE : RenderArgs::DEFAULT_RENDER_MODE;
QSharedPointer<SceneScriptingInterface> scene = DependencyManager::get<SceneScriptingInterface>();
if (_bestZone) {
if (!_hasPreviousZone) {
_previousKeyLightColor = scene->getKeyLightColor();
_previousKeyLightIntensity = scene->getKeyLightIntensity();
_previousKeyLightAmbientIntensity = scene->getKeyLightAmbientIntensity();
_previousKeyLightDirection = scene->getKeyLightDirection();
_previousStageSunModelEnabled = scene->isStageSunModelEnabled();
_previousStageLongitude = scene->getStageLocationLongitude();
_previousStageLatitude = scene->getStageLocationLatitude();
_previousStageAltitude = scene->getStageLocationAltitude();
_previousStageHour = scene->getStageDayTime();
_previousStageDay = scene->getStageYearTime();
_hasPreviousZone = true;
}
scene->setKeyLightColor(_bestZone->getKeyLightColorVec3());
scene->setKeyLightIntensity(_bestZone->getKeyLightIntensity());
scene->setKeyLightAmbientIntensity(_bestZone->getKeyLightAmbientIntensity());
scene->setKeyLightDirection(_bestZone->getKeyLightDirection());
scene->setStageSunModelEnable(_bestZone->getStageSunModelEnabled());
scene->setStageLocation(_bestZone->getStageLongitude(), _bestZone->getStageLatitude(),
_bestZone->getStageAltitude());
scene->setStageDayTime(_bestZone->getStageHour());
scene->setStageYearTime(_bestZone->getStageDay());
} else {
if (_hasPreviousZone) {
scene->setKeyLightColor(_previousKeyLightColor);
scene->setKeyLightIntensity(_previousKeyLightIntensity);
scene->setKeyLightAmbientIntensity(_previousKeyLightAmbientIntensity);
scene->setKeyLightDirection(_previousKeyLightDirection);
scene->setStageSunModelEnable(_previousStageSunModelEnabled);
scene->setStageLocation(_previousStageLongitude, _previousStageLatitude,
_previousStageAltitude);
scene->setStageDayTime(_previousStageHour);
scene->setStageYearTime(_previousStageDay);
_hasPreviousZone = false;
}
}
// we must call endScene while we still have the tree locked so that no one deletes a model
// on us while rendering the scene
@ -454,7 +497,7 @@ const FBXGeometry* EntityTreeRenderer::getCollisionGeometryForEntity(const Entit
if (entityItem->getType() == EntityTypes::Model) {
const RenderableModelEntityItem* constModelEntityItem = dynamic_cast<const RenderableModelEntityItem*>(entityItem);
if (constModelEntityItem->hasCollisionModel()) {
if (constModelEntityItem->hasCompoundShapeURL()) {
RenderableModelEntityItem* modelEntityItem = const_cast<RenderableModelEntityItem*>(constModelEntityItem);
Model* model = modelEntityItem->getModel(this);
if (model) {
@ -596,34 +639,59 @@ void EntityTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args)
EntityItem* entityItem = entityItems[i];
if (entityItem->isVisible()) {
// render entityItem
AABox entityBox = entityItem->getAABox();
// TODO: some entity types (like lights) might want to be rendered even
// when they are outside of the view frustum...
float distance = args->_viewFrustum->distanceToCamera(entityBox.calcCenter());
bool outOfView = args->_viewFrustum->boxInFrustum(entityBox) == ViewFrustum::OUTSIDE;
if (!outOfView) {
bool bigEnoughToRender = _viewState->shouldRenderMesh(entityBox.getLargestDimension(), distance);
if (bigEnoughToRender) {
renderProxies(entityItem, args);
Glower* glower = NULL;
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel());
// NOTE: Zone Entities are a special case we handle here... Zones don't render
// like other entity types. So we will skip the normal rendering tests
if (entityItem->getType() == EntityTypes::Zone) {
if (entityItem->contains(args->_viewFrustum->getPosition())) {
float entityVolumeEstimate = entityItem->getVolumeEstimate();
if (entityVolumeEstimate < _bestZoneVolume) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = dynamic_cast<const ZoneEntityItem*>(entityItem);
} else if (entityVolumeEstimate == _bestZoneVolume) {
if (!_bestZone) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = dynamic_cast<const ZoneEntityItem*>(entityItem);
} else {
// in the case of the volume being equal, we will use the
// EntityItemID to deterministically pick one entity over the other
if (entityItem->getEntityItemID() < _bestZone->getEntityItemID()) {
_bestZoneVolume = entityVolumeEstimate;
_bestZone = dynamic_cast<const ZoneEntityItem*>(entityItem);
}
}
}
entityItem->render(args);
args->_itemsRendered++;
if (glower) {
delete glower;
}
} else {
args->_itemsTooSmall++;
}
} else {
args->_itemsOutOfView++;
// render entityItem
AABox entityBox = entityItem->getAABox();
// TODO: some entity types (like lights) might want to be rendered even
// when they are outside of the view frustum...
float distance = args->_viewFrustum->distanceToCamera(entityBox.calcCenter());
bool outOfView = args->_viewFrustum->boxInFrustum(entityBox) == ViewFrustum::OUTSIDE;
if (!outOfView) {
bool bigEnoughToRender = _viewState->shouldRenderMesh(entityBox.getLargestDimension(), distance);
if (bigEnoughToRender) {
renderProxies(entityItem, args);
Glower* glower = NULL;
if (entityItem->getGlowLevel() > 0.0f) {
glower = new Glower(entityItem->getGlowLevel());
}
entityItem->render(args);
args->_itemsRendered++;
if (glower) {
delete glower;
}
} else {
args->_itemsTooSmall++;
}
} else {
args->_itemsOutOfView++;
}
}
}
}

View file

@ -12,18 +12,20 @@
#ifndef hifi_EntityTreeRenderer_h
#define hifi_EntityTreeRenderer_h
#include <QSet>
#include <QStack>
#include <EntityTree.h>
#include <EntityScriptingInterface.h> // for RayToEntityIntersectionResult
#include <MouseEvent.h>
#include <OctreeRenderer.h>
#include <ScriptCache.h>
class AbstractScriptingServicesInterface;
class AbstractViewStateInterface;
class Model;
class ScriptEngine;
class AbstractViewStateInterface;
class AbstractScriptingServicesInterface;
class ScriptEngine;
class ZoneEntityItem;
class EntityScriptDetails {
public:
@ -164,6 +166,21 @@ private:
bool _shuttingDown = false;
QMultiMap<QUrl, EntityItemID> _waitingOnPreload;
bool _hasPreviousZone = false;
const ZoneEntityItem* _bestZone;
float _bestZoneVolume;
glm::vec3 _previousKeyLightColor;
float _previousKeyLightIntensity;
float _previousKeyLightAmbientIntensity;
glm::vec3 _previousKeyLightDirection;
bool _previousStageSunModelEnabled;
float _previousStageLongitude;
float _previousStageLatitude;
float _previousStageAltitude;
float _previousStageHour;
int _previousStageDay;
};

View file

@ -224,10 +224,10 @@ Model* RenderableModelEntityItem::getModel(EntityTreeRenderer* renderer) {
// if we have a previously allocated model, but its URL doesn't match
// then we need to let our renderer update our model for us.
if (_model && QUrl(getModelURL()) != _model->getURL()) {
result = _model = _myRenderer->updateModel(_model, getModelURL(), getCollisionModelURL());
result = _model = _myRenderer->updateModel(_model, getModelURL(), getCompoundShapeURL());
_needsInitialSimulation = true;
} else if (!_model) { // if we don't yet have a model, then we want our renderer to allocate one
result = _model = _myRenderer->allocateModel(getModelURL(), getCollisionModelURL());
result = _model = _myRenderer->allocateModel(getModelURL(), getCompoundShapeURL());
_needsInitialSimulation = true;
} else { // we already have the model we want...
result = _model;
@ -267,8 +267,8 @@ bool RenderableModelEntityItem::findDetailedRayIntersection(const glm::vec3& ori
return _model->findRayIntersectionAgainstSubMeshes(origin, direction, distance, face, extraInfo, precisionPicking);
}
void RenderableModelEntityItem::setCollisionModelURL(const QString& url) {
ModelEntityItem::setCollisionModelURL(url);
void RenderableModelEntityItem::setCompoundShapeURL(const QString& url) {
ModelEntityItem::setCompoundShapeURL(url);
if (_model) {
_model->setCollisionModelURL(QUrl(url));
}
@ -410,8 +410,17 @@ void RenderableModelEntityItem::computeShapeInfo(ShapeInfo& info) {
}
glm::vec3 collisionModelDimensions = box.getDimensions();
info.setParams(type, collisionModelDimensions, _collisionModelURL);
info.setParams(type, collisionModelDimensions, _compoundShapeURL);
info.setConvexHulls(_points);
}
}
bool RenderableModelEntityItem::contains(const glm::vec3& point) const {
if (EntityItem::contains(point) && _model && _model->getCollisionGeometry()) {
const QSharedPointer<NetworkGeometry> collisionNetworkGeometry = _model->getCollisionGeometry();
const FBXGeometry& collisionGeometry = collisionNetworkGeometry->getFBXGeometry();
return collisionGeometry.convexHullContains(worldToEntity(point));
}
return false;
}

View file

@ -52,10 +52,12 @@ public:
bool needsToCallUpdate() const;
virtual void setCollisionModelURL(const QString& url);
virtual void setCompoundShapeURL(const QString& url);
bool isReadyToComputeShape();
void computeShapeInfo(ShapeInfo& info);
virtual bool contains(const glm::vec3& point) const;
private:
void remapTextures();

View file

@ -0,0 +1,57 @@
//
// RenderableZoneEntityItem.cpp
//
//
// Created by Clement on 4/22/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "RenderableZoneEntityItem.h"
#include <DependencyManager.h>
#include <GeometryCache.h>
EntityItem* RenderableZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
return new RenderableZoneEntityItem(entityID, properties);
}
bool RenderableZoneEntityItem::setProperties(const EntityItemProperties& properties) {
QString oldShapeURL = getCompoundShapeURL();
bool somethingChanged = ZoneEntityItem::setProperties(properties);
if (somethingChanged && oldShapeURL != getCompoundShapeURL()) {
_compoundShapeModel = DependencyManager::get<GeometryCache>()->getGeometry(getCompoundShapeURL(), QUrl(), true);
}
return somethingChanged;
}
int RenderableZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
QString oldShapeURL = getCompoundShapeURL();
int bytesRead = ZoneEntityItem::readEntitySubclassDataFromBuffer(data, bytesLeftToRead,
args, propertyFlags, overwriteLocalData);
if (oldShapeURL != getCompoundShapeURL()) {
_compoundShapeModel = DependencyManager::get<GeometryCache>()->getGeometry(getCompoundShapeURL(), QUrl(), true);
}
return bytesRead;
}
bool RenderableZoneEntityItem::contains(const glm::vec3& point) const {
if (getShapeType() != SHAPE_TYPE_COMPOUND) {
return EntityItem::contains(point);
}
if (!_compoundShapeModel && hasCompoundShapeURL()) {
const_cast<RenderableZoneEntityItem*>(this)->_compoundShapeModel = DependencyManager::get<GeometryCache>()->getGeometry(getCompoundShapeURL(), QUrl(), true);
}
if (EntityItem::contains(point) && _compoundShapeModel && _compoundShapeModel->isLoaded()) {
const FBXGeometry& geometry = _compoundShapeModel->getFBXGeometry();
glm::vec3 meshDimensions = geometry.getUnscaledMeshExtents().maximum - geometry.getUnscaledMeshExtents().minimum;
return geometry.convexHullContains(worldToEntity(point) * meshDimensions);
}
return false;
}

View file

@ -0,0 +1,39 @@
//
// RenderableZoneEntityItem.h
//
//
// Created by Clement on 4/22/15.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_RenderableZoneEntityItem_h
#define hifi_RenderableZoneEntityItem_h
#include <ZoneEntityItem.h>
class NetworkGeometry;
class RenderableZoneEntityItem : public ZoneEntityItem {
public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
RenderableZoneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
ZoneEntityItem(entityItemID, properties)
{ }
virtual bool setProperties(const EntityItemProperties& properties);
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
virtual bool contains(const glm::vec3& point) const;
private:
QSharedPointer<NetworkGeometry> _compoundShapeModel;
};
#endif // hifi_RenderableZoneEntityItem_h

View file

@ -11,6 +11,8 @@
#include <QtCore/QObject>
#include <glm/gtx/transform.hpp>
#include <ByteCountCoding.h>
#include <GLMHelpers.h>
#include <Octree.h>
@ -599,7 +601,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef
auto nodeList = DependencyManager::get<NodeList>();
const QUuid& myNodeID = nodeList->getSessionUUID();
if (_simulatorID == myNodeID) {
if (_simulatorID == myNodeID && !_simulatorID.isNull()) {
// the packet that produced this bitstream originally came from physics simulations performed by
// this node, so our version has to be newer than what the packet contained.
_position = savePosition;
@ -821,7 +823,27 @@ bool EntityItem::isMoving() const {
return hasVelocity() || hasAngularVelocity();
}
bool EntityItem::lifetimeHasExpired() const {
glm::mat4 EntityItem::getEntityToWorldMatrix() const {
glm::mat4 translation = glm::translate(getPosition());
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 scale = glm::scale(getDimensions());
glm::mat4 registration = glm::translate(ENTITY_ITEM_DEFAULT_REGISTRATION_POINT - getRegistrationPoint());
return translation * rotation * scale * registration;
}
glm::mat4 EntityItem::getWorldToEntityMatrix() const {
return glm::inverse(getEntityToWorldMatrix());
}
glm::vec3 EntityItem::entityToWorld(const glm::vec3 point) const {
return glm::vec3(getEntityToWorldMatrix() * glm::vec4(point, 1.0f));
}
glm::vec3 EntityItem::worldToEntity(const glm::vec3 point) const {
return glm::vec3(getWorldToEntityMatrix() * glm::vec4(point, 1.0f));
}
bool EntityItem::lifetimeHasExpired() const {
return isMortal() && (getAge() > getLifetime());
}
@ -1011,12 +1033,6 @@ AABox EntityItem::getAABox() const {
return AABox(rotatedExtentsRelativeToRegistrationPoint);
}
AABox EntityItem::getAABoxInDomainUnits() const {
AABox box = getAABox();
box.scale(1.0f / (float)TREE_SCALE);
return box;
}
// NOTE: This should only be used in cases of old bitstreams which only contain radius data
// 0,0,0 --> maxDimension,maxDimension,maxDimension
// ... has a corner to corner distance of glm::length(maxDimension,maxDimension,maxDimension)
@ -1044,6 +1060,16 @@ float EntityItem::getRadius() const {
return 0.5f * glm::length(_dimensions);
}
bool EntityItem::contains(const glm::vec3& point) const {
if (getShapeType() == SHAPE_TYPE_COMPOUND) {
return getAABox().contains(point);
} else {
ShapeInfo info;
info.setParams(getShapeType(), glm::vec3(0.5f));
return info.contains(worldToEntity(point));
}
}
void EntityItem::computeShapeInfo(ShapeInfo& info) {
info.setParams(getShapeType(), 0.5f * getDimensions());
}

View file

@ -145,25 +145,16 @@ public:
// attributes applicable to all entity types
EntityTypes::EntityType getType() const { return _type; }
glm::vec3 getPositionInDomainUnits() const { return _position / (float)TREE_SCALE; } /// get position in domain scale units (0.0 - 1.0)
const glm::vec3& getPosition() const { return _position; } /// get position in meters
/// set position in domain scale units (0.0 - 1.0)
void setPositionInDomainUnits(const glm::vec3& value)
{ setPosition(glm::clamp(value, 0.0f, 1.0f) * (float)TREE_SCALE); }
void setPosition(const glm::vec3& value) {
_position = value;
}
glm::vec3 getCenterInDomainUnits() const { return getCenter() / (float) TREE_SCALE; }
glm::vec3 getCenter() const;
glm::vec3 getDimensionsInDomainUnits() const { return _dimensions / (float)TREE_SCALE; } /// get dimensions in domain scale units (0.0 - 1.0)
const glm::vec3& getDimensions() const { return _dimensions; } /// get dimensions in meters
/// set dimensions in domain scale units (0.0 - 1.0)
virtual void setDimensionsInDomainUnits(const glm::vec3& value) { _dimensions = glm::abs(value) * (float)TREE_SCALE; }
/// set dimensions in meter units (0.0 - TREE_SCALE)
virtual void setDimensions(const glm::vec3& value) { _dimensions = glm::abs(value); }
@ -182,15 +173,11 @@ public:
float getDensity() const { return _density; }
glm::vec3 getVelocityInDomainUnits() const { return _velocity / (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second
const glm::vec3 getVelocity() const { return _velocity; } /// get velocity in meters
void setVelocityInDomainUnits(const glm::vec3& value) { _velocity = value * (float)TREE_SCALE; } /// velocity in domain scale units (0.0-1.0) per second
void setVelocity(const glm::vec3& value) { _velocity = value; } /// velocity in meters
bool hasVelocity() const { return _velocity != ENTITY_ITEM_ZERO_VEC3; }
glm::vec3 getGravityInDomainUnits() const { return _gravity / (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared
const glm::vec3& getGravity() const { return _gravity; } /// get gravity in meters
void setGravityInDomainUnits(const glm::vec3& value) { _gravity = value * (float)TREE_SCALE; } /// gravity in domain scale units (0.0-1.0) per second squared
void setGravity(const glm::vec3& value) { _gravity = value; } /// gravity in meters
bool hasGravity() const { return _gravity != ENTITY_ITEM_ZERO_VEC3; }
@ -220,7 +207,6 @@ public:
AACube getMaximumAACube() const;
AACube getMinimumAACube() const;
AABox getAABox() const; /// axis aligned bounding box in world-frame (meters)
AABox getAABoxInDomainUnits() const; /// axis aligned bounding box in domain scale units (0.0 - 1.0)
const QString& getScript() const { return _script; }
void setScript(const QString& value) { _script = value; }
@ -265,11 +251,11 @@ public:
// TODO: get rid of users of getRadius()...
float getRadius() const;
virtual bool contains(const glm::vec3& point) const { return getAABox().contains(point); }
virtual bool containsInDomainUnits(const glm::vec3& point) const { return getAABoxInDomainUnits().contains(point); }
virtual bool contains(const glm::vec3& point) const;
virtual bool isReadyToComputeShape() { return true; }
virtual void computeShapeInfo(ShapeInfo& info);
virtual float getVolumeEstimate() const { return _dimensions.x * _dimensions.y * _dimensions.z; }
/// return preferred shape type (actual physical shape may differ)
virtual ShapeType getShapeType() const { return SHAPE_TYPE_NONE; }
@ -309,6 +295,11 @@ public:
static void setSendPhysicsUpdates(bool value) { _sendPhysicsUpdates = value; }
static bool getSendPhysicsUpdates() { return _sendPhysicsUpdates; }
glm::mat4 getEntityToWorldMatrix() const;
glm::mat4 getWorldToEntityMatrix() const;
glm::vec3 worldToEntity(const glm::vec3 point) const;
glm::vec3 entityToWorld(const glm::vec3 point) const;
protected:
static bool _sendPhysicsUpdates;

View file

@ -18,14 +18,14 @@
#include <GLMHelpers.h>
#include <RegisteredMetaTypes.h>
#include "EntitiesLogging.h"
#include "EntityItem.h"
#include "EntityItemProperties.h"
#include "EntityItemPropertiesDefaults.h"
#include "ModelEntityItem.h"
#include "TextEntityItem.h"
#include "EntitiesLogging.h"
#include "ParticleEffectEntityItem.h"
#include "TextEntityItem.h"
#include "ZoneEntityItem.h"
EntityPropertyList PROP_LAST_ITEM = (EntityPropertyList)(PROP_AFTER_LAST_ITEM - 1);
@ -44,7 +44,7 @@ EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(script, ENTITY_ITEM_DEFAULT_SCRIPT),
CONSTRUCT_PROPERTY(color, ),
CONSTRUCT_PROPERTY(modelURL, ""),
CONSTRUCT_PROPERTY(collisionModelURL, ""),
CONSTRUCT_PROPERTY(compoundShapeURL, ""),
CONSTRUCT_PROPERTY(animationURL, ""),
CONSTRUCT_PROPERTY(animationFPS, ModelEntityItem::DEFAULT_ANIMATION_FPS),
CONSTRUCT_PROPERTY(animationFrameIndex, ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX),
@ -76,6 +76,16 @@ EntityItemProperties::EntityItemProperties() :
CONSTRUCT_PROPERTY(localGravity, ParticleEffectEntityItem::DEFAULT_LOCAL_GRAVITY),
CONSTRUCT_PROPERTY(particleRadius, ParticleEffectEntityItem::DEFAULT_PARTICLE_RADIUS),
CONSTRUCT_PROPERTY(marketplaceID, ENTITY_ITEM_DEFAULT_MARKETPLACE_ID),
CONSTRUCT_PROPERTY(keyLightColor, ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR),
CONSTRUCT_PROPERTY(keyLightIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY),
CONSTRUCT_PROPERTY(keyLightAmbientIntensity, ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY),
CONSTRUCT_PROPERTY(keyLightDirection, ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION),
CONSTRUCT_PROPERTY(stageSunModelEnabled, ZoneEntityItem::DEFAULT_STAGE_SUN_MODEL_ENABLED),
CONSTRUCT_PROPERTY(stageLatitude, ZoneEntityItem::DEFAULT_STAGE_LATITUDE),
CONSTRUCT_PROPERTY(stageLongitude, ZoneEntityItem::DEFAULT_STAGE_LONGITUDE),
CONSTRUCT_PROPERTY(stageAltitude, ZoneEntityItem::DEFAULT_STAGE_ALTITUDE),
CONSTRUCT_PROPERTY(stageDay, ZoneEntityItem::DEFAULT_STAGE_DAY),
CONSTRUCT_PROPERTY(stageHour, ZoneEntityItem::DEFAULT_STAGE_HOUR),
_id(UNKNOWN_ENTITY_ID),
_idSet(false),
@ -165,7 +175,7 @@ void EntityItemProperties::debugDump() const {
qCDebug(entities) << " _position=" << _position.x << "," << _position.y << "," << _position.z;
qCDebug(entities) << " _dimensions=" << getDimensions();
qCDebug(entities) << " _modelURL=" << _modelURL;
qCDebug(entities) << " _collisionModelURL=" << _collisionModelURL;
qCDebug(entities) << " _compoundShapeURL=" << _compoundShapeURL;
qCDebug(entities) << " changed properties...";
EntityPropertyFlags props = getChangedProperties();
props.debugDumpBits();
@ -235,7 +245,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_SCRIPT, script);
CHECK_PROPERTY_CHANGE(PROP_COLOR, color);
CHECK_PROPERTY_CHANGE(PROP_MODEL_URL, modelURL);
CHECK_PROPERTY_CHANGE(PROP_COLLISION_MODEL_URL, collisionModelURL);
CHECK_PROPERTY_CHANGE(PROP_COMPOUND_SHAPE_URL, compoundShapeURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_URL, animationURL);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_PLAYING, animationIsPlaying);
CHECK_PROPERTY_CHANGE(PROP_ANIMATION_FRAME_INDEX, animationFrameIndex);
@ -268,6 +278,16 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const {
CHECK_PROPERTY_CHANGE(PROP_LOCAL_GRAVITY, localGravity);
CHECK_PROPERTY_CHANGE(PROP_PARTICLE_RADIUS, particleRadius);
CHECK_PROPERTY_CHANGE(PROP_MARKETPLACE_ID, marketplaceID);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_COLOR, keyLightColor);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_INTENSITY, keyLightIntensity);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_AMBIENT_INTENSITY, keyLightAmbientIntensity);
CHECK_PROPERTY_CHANGE(PROP_KEYLIGHT_DIRECTION, keyLightDirection);
CHECK_PROPERTY_CHANGE(PROP_STAGE_SUN_MODEL_ENABLED, stageSunModelEnabled);
CHECK_PROPERTY_CHANGE(PROP_STAGE_LATITUDE, stageLatitude);
CHECK_PROPERTY_CHANGE(PROP_STAGE_LONGITUDE, stageLongitude);
CHECK_PROPERTY_CHANGE(PROP_STAGE_ALTITUDE, stageAltitude);
CHECK_PROPERTY_CHANGE(PROP_STAGE_DAY, stageDay);
CHECK_PROPERTY_CHANGE(PROP_STAGE_HOUR, stageHour);
return changedProperties;
}
@ -302,7 +322,7 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(visible);
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(color);
COPY_PROPERTY_TO_QSCRIPTVALUE(modelURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(collisionModelURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(compoundShapeURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationURL);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationIsPlaying);
COPY_PROPERTY_TO_QSCRIPTVALUE(animationFPS);
@ -334,6 +354,17 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine) cons
COPY_PROPERTY_TO_QSCRIPTVALUE(particleRadius);
COPY_PROPERTY_TO_QSCRIPTVALUE(marketplaceID);
COPY_PROPERTY_TO_QSCRIPTVALUE_COLOR(keyLightColor);
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightIntensity);
COPY_PROPERTY_TO_QSCRIPTVALUE(keyLightAmbientIntensity);
COPY_PROPERTY_TO_QSCRIPTVALUE_VEC3(keyLightDirection);
COPY_PROPERTY_TO_QSCRIPTVALUE(stageSunModelEnabled);
COPY_PROPERTY_TO_QSCRIPTVALUE(stageLatitude);
COPY_PROPERTY_TO_QSCRIPTVALUE(stageLongitude);
COPY_PROPERTY_TO_QSCRIPTVALUE(stageAltitude);
COPY_PROPERTY_TO_QSCRIPTVALUE(stageDay);
COPY_PROPERTY_TO_QSCRIPTVALUE(stageHour);
// Sitting properties support
QScriptValue sittingPoints = engine->newObject();
for (int i = 0; i < _sittingPoints.size(); ++i) {
@ -386,7 +417,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(visible, setVisible);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(color, setColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(modelURL, setModelURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(collisionModelURL, setCollisionModelURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(compoundShapeURL, setCompoundShapeURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(animationURL, setAnimationURL);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(animationIsPlaying, setAnimationIsPlaying);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(animationFPS, setAnimationFPS);
@ -418,6 +449,17 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object) {
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(particleRadius, setParticleRadius);
COPY_PROPERTY_FROM_QSCRIPTVALUE_STRING(marketplaceID, setMarketplaceID);
COPY_PROPERTY_FROM_QSCRIPTVALUE_COLOR(keyLightColor, setKeyLightColor);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(keyLightIntensity, setKeyLightIntensity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(keyLightAmbientIntensity, setKeyLightAmbientIntensity);
COPY_PROPERTY_FROM_QSCRIPTVALUE_VEC3(keyLightDirection, setKeyLightDirection);
COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(stageSunModelEnabled, setStageSunModelEnabled);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageLatitude, setStageLatitude);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageLongitude, setStageLongitude);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageAltitude, setStageAltitude);
COPY_PROPERTY_FROM_QSCRIPTVALUE_INT(stageDay, setStageDay);
COPY_PROPERTY_FROM_QSCRIPTVALUE_FLOAT(stageHour, setStageHour);
_lastEdited = usecTimestampNow();
}
@ -576,7 +618,7 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
if (properties.getType() == EntityTypes::Model) {
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, properties.getModelURL());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, properties.getCollisionModelURL());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, properties.getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, properties.getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, properties.getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, properties.getAnimationFrameIndex());
@ -603,6 +645,22 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
APPEND_ENTITY_PROPERTY(PROP_LOCAL_GRAVITY, appendValue, properties.getLocalGravity());
APPEND_ENTITY_PROPERTY(PROP_PARTICLE_RADIUS, appendValue, properties.getParticleRadius());
}
if (properties.getType() == EntityTypes::Zone) {
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, appendColor, properties.getKeyLightColor());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, appendValue, properties.getKeyLightIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, appendValue, properties.getKeyLightAmbientIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, appendValue, properties.getKeyLightDirection());
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, appendValue, properties.getStageSunModelEnabled());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, appendValue, properties.getStageLatitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, appendValue, properties.getStageLongitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, properties.getStageAltitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, properties.getStageDay());
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, properties.getStageHour());
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)properties.getShapeType());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, properties.getCompoundShapeURL());
}
APPEND_ENTITY_PROPERTY(PROP_MARKETPLACE_ID, appendValue, properties.getMarketplaceID());
}
@ -809,7 +867,7 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
if (properties.getType() == EntityTypes::Model) {
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MODEL_URL, setModelURL);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COLLISION_MODEL_URL, setCollisionModelURL);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_ANIMATION_URL, setAnimationURL);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FPS, float, setAnimationFPS);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_ANIMATION_FRAME_INDEX, float, setAnimationFrameIndex);
@ -836,6 +894,21 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_LOCAL_GRAVITY, float, setLocalGravity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_PARTICLE_RADIUS, float, setParticleRadius);
}
if (properties.getType() == EntityTypes::Zone) {
READ_ENTITY_PROPERTY_COLOR_TO_PROPERTIES(PROP_KEYLIGHT_COLOR, setKeyLightColor);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_INTENSITY, float, setKeyLightIntensity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, setKeyLightAmbientIntensity);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEYLIGHT_DIRECTION, glm::vec3, setKeyLightDirection);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_SUN_MODEL_ENABLED, bool, setStageSunModelEnabled);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_LATITUDE, float, setStageLatitude);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_LONGITUDE, float, setStageLongitude);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_ALTITUDE, float, setStageAltitude);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_DAY, quint16, setStageDay);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_STAGE_HOUR, float, setStageHour);
READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SHAPE_TYPE, ShapeType, setShapeType);
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL);
}
READ_ENTITY_PROPERTY_STRING_TO_PROPERTIES(PROP_MARKETPLACE_ID, setMarketplaceID);
@ -890,7 +963,7 @@ void EntityItemProperties::markAllChanged() {
_visibleChanged = true;
_colorChanged = true;
_modelURLChanged = true;
_collisionModelURLChanged = true;
_compoundShapeURLChanged = true;
_animationURLChanged = true;
_animationIsPlayingChanged = true;
_animationFrameIndexChanged = true;
@ -923,6 +996,17 @@ void EntityItemProperties::markAllChanged() {
_particleRadiusChanged = true;
_marketplaceIDChanged = true;
_keyLightColorChanged = true;
_keyLightIntensityChanged = true;
_keyLightAmbientIntensityChanged = true;
_keyLightDirectionChanged = true;
_stageSunModelEnabledChanged = true;
_stageLatitudeChanged = true;
_stageLongitudeChanged = true;
_stageAltitudeChanged = true;
_stageDayChanged = true;
_stageHourChanged = true;
}
/// The maximum bounding cube for the entity, independent of it's rotation.

View file

@ -93,17 +93,16 @@ enum EntityPropertyList {
PROP_LOCAL_GRAVITY,
PROP_PARTICLE_RADIUS,
PROP_COLLISION_MODEL_URL,
PROP_COMPOUND_SHAPE_URL,
PROP_MARKETPLACE_ID,
PROP_ACCELERATION,
PROP_SIMULATOR_ID,
////////////////////////////////////////////////////////////////////////////////////////////////////
// ATTENTION: add new properties ABOVE this line
PROP_AFTER_LAST_ITEM,
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
// WARNING! Do not add props here unless you intentionally mean to reuse PROP_ indexes
//
@ -113,7 +112,22 @@ enum EntityPropertyList {
PROP_TEXT = PROP_MODEL_URL,
PROP_LINE_HEIGHT = PROP_ANIMATION_URL,
PROP_BACKGROUND_COLOR = PROP_ANIMATION_FPS,
PROP_COLLISION_MODEL_URL_OLD_VERSION = PROP_ANIMATION_FPS + 1
PROP_COLLISION_MODEL_URL_OLD_VERSION = PROP_ANIMATION_FPS + 1,
// Aliases/Piggyback properties for Zones. These properties intentionally reuse the enum values for
// other properties which will never overlap with each other. We do this so that we don't have to expand
// the size of the properties bitflags mask
PROP_KEYLIGHT_COLOR = PROP_COLOR,
PROP_KEYLIGHT_INTENSITY = PROP_INTENSITY,
PROP_KEYLIGHT_AMBIENT_INTENSITY = PROP_CUTOFF,
PROP_KEYLIGHT_DIRECTION = PROP_EXPONENT,
PROP_STAGE_SUN_MODEL_ENABLED = PROP_IS_SPOTLIGHT,
PROP_STAGE_LATITUDE = PROP_DIFFUSE_COLOR_UNUSED,
PROP_STAGE_LONGITUDE = PROP_AMBIENT_COLOR_UNUSED,
PROP_STAGE_ALTITUDE = PROP_SPECULAR_COLOR_UNUSED,
PROP_STAGE_DAY = PROP_LINEAR_ATTENUATION_UNUSED,
PROP_STAGE_HOUR = PROP_QUADRATIC_ATTENUATION_UNUSED,
// WARNING!!! DO NOT ADD PROPS_xxx here unless you really really meant to.... Add them UP above
};
@ -137,6 +151,7 @@ class EntityItemProperties {
friend class LightEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class TextEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class ParticleEffectEntityItem; // TODO: consider removing this friend relationship and use public methods
friend class ZoneEntityItem; // TODO: consider removing this friend relationship and use public methods
public:
EntityItemProperties();
virtual ~EntityItemProperties();
@ -182,7 +197,7 @@ public:
DEFINE_PROPERTY_REF(PROP_SCRIPT, Script, script, QString);
DEFINE_PROPERTY_REF(PROP_COLOR, Color, color, xColor);
DEFINE_PROPERTY_REF(PROP_MODEL_URL, ModelURL, modelURL, QString);
DEFINE_PROPERTY_REF(PROP_COLLISION_MODEL_URL, CollisionModelURL, collisionModelURL, QString);
DEFINE_PROPERTY_REF(PROP_COMPOUND_SHAPE_URL, CompoundShapeURL, compoundShapeURL, QString);
DEFINE_PROPERTY_REF(PROP_ANIMATION_URL, AnimationURL, animationURL, QString);
DEFINE_PROPERTY(PROP_ANIMATION_FPS, AnimationFPS, animationFPS, float);
DEFINE_PROPERTY(PROP_ANIMATION_FRAME_INDEX, AnimationFrameIndex, animationFrameIndex, float);
@ -214,6 +229,17 @@ public:
DEFINE_PROPERTY(PROP_LOCAL_GRAVITY, LocalGravity, localGravity, float);
DEFINE_PROPERTY(PROP_PARTICLE_RADIUS, ParticleRadius, particleRadius, float);
DEFINE_PROPERTY_REF(PROP_MARKETPLACE_ID, MarketplaceID, marketplaceID, QString);
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_COLOR, KeyLightColor, keyLightColor, xColor);
DEFINE_PROPERTY(PROP_KEYLIGHT_INTENSITY, KeyLightIntensity, keyLightIntensity, float);
DEFINE_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, KeyLightAmbientIntensity, keyLightAmbientIntensity, float);
DEFINE_PROPERTY_REF(PROP_KEYLIGHT_DIRECTION, KeyLightDirection, keyLightDirection, glm::vec3);
DEFINE_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, StageSunModelEnabled, stageSunModelEnabled, bool);
DEFINE_PROPERTY(PROP_STAGE_LATITUDE, StageLatitude, stageLatitude, float);
DEFINE_PROPERTY(PROP_STAGE_LONGITUDE, StageLongitude, stageLongitude, float);
DEFINE_PROPERTY(PROP_STAGE_ALTITUDE, StageAltitude, stageAltitude, float);
DEFINE_PROPERTY(PROP_STAGE_DAY, StageDay, stageDay, quint16);
DEFINE_PROPERTY(PROP_STAGE_HOUR, StageHour, stageHour, float);
public:
float getMaxDimension() const { return glm::max(_dimensions.x, _dimensions.y, _dimensions.z); }
@ -314,7 +340,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) {
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Script, script, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, Color, color, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, ModelURL, modelURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CollisionModelURL, collisionModelURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, CompoundShapeURL, compoundShapeURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationURL, animationURL, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFPS, animationFPS, "");
DEBUG_PROPERTY_IF_CHANGED(debug, properties, AnimationFrameIndex, animationFrameIndex, "");

View file

@ -228,6 +228,15 @@
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_INT(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \
int newValue = P.toVariant().toInt(); \
if (_defaultSettings || newValue != _##P) { \
S(newValue); \
} \
}
#define COPY_PROPERTY_FROM_QSCRIPTVALUE_BOOL(P, S) \
QScriptValue P = object.property(#P); \
if (P.isValid()) { \

View file

@ -15,6 +15,7 @@
#include "EntityTree.h"
#include "LightEntityItem.h"
#include "ModelEntityItem.h"
#include "ZoneEntityItem.h"
EntityScriptingInterface::EntityScriptingInterface() :
@ -306,6 +307,14 @@ bool EntityScriptingInterface::getLightsArePickable() const {
return LightEntityItem::getLightsArePickable();
}
void EntityScriptingInterface::setZonesArePickable(bool value) {
ZoneEntityItem::setZonesArePickable(value);
}
bool EntityScriptingInterface::getZonesArePickable() const {
return ZoneEntityItem::getZonesArePickable();
}
void EntityScriptingInterface::setSendPhysicsUpdates(bool value) {
EntityItem::setSendPhysicsUpdates(value);
}

View file

@ -111,6 +111,9 @@ public slots:
Q_INVOKABLE void setLightsArePickable(bool value);
Q_INVOKABLE bool getLightsArePickable() const;
Q_INVOKABLE void setZonesArePickable(bool value);
Q_INVOKABLE bool getZonesArePickable() const;
Q_INVOKABLE void setSendPhysicsUpdates(bool value);
Q_INVOKABLE bool getSendPhysicsUpdates() const;

View file

@ -21,9 +21,10 @@
#include "BoxEntityItem.h"
#include "LightEntityItem.h"
#include "ModelEntityItem.h"
#include "ParticleEffectEntityItem.h"
#include "SphereEntityItem.h"
#include "TextEntityItem.h"
#include "ParticleEffectEntityItem.h"
#include "ZoneEntityItem.h"
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
@ -39,7 +40,7 @@ REGISTER_ENTITY_TYPE(Sphere)
REGISTER_ENTITY_TYPE(Light)
REGISTER_ENTITY_TYPE(Text)
REGISTER_ENTITY_TYPE(ParticleEffect)
REGISTER_ENTITY_TYPE(Zone)
const QString& EntityTypes::getEntityTypeName(EntityType entityType) {
QMap<EntityType, QString>::iterator matchedTypeName = _typeToNameMap.find(entityType);

View file

@ -36,7 +36,8 @@ public:
Light,
Text,
ParticleEffect,
LAST = ParticleEffect
Zone,
LAST = Zone
} EntityType;
static const QString& getEntityTypeName(EntityType entityType);

View file

@ -21,7 +21,7 @@
#include "ModelEntityItem.h"
const QString ModelEntityItem::DEFAULT_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_COLLISION_MODEL_URL = QString("");
const QString ModelEntityItem::DEFAULT_COMPOUND_SHAPE_URL = QString("");
const QString ModelEntityItem::DEFAULT_ANIMATION_URL = QString("");
const float ModelEntityItem::DEFAULT_ANIMATION_FRAME_INDEX = 0.0f;
const bool ModelEntityItem::DEFAULT_ANIMATION_IS_PLAYING = false;
@ -47,7 +47,7 @@ EntityItemProperties ModelEntityItem::getProperties() const {
COPY_ENTITY_PROPERTY_TO_PROPERTIES(color, getXColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(modelURL, getModelURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(collisionModelURL, getCollisionModelURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationURL, getAnimationURL);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationIsPlaying, getAnimationIsPlaying);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(animationFrameIndex, getAnimationFrameIndex);
@ -65,7 +65,7 @@ bool ModelEntityItem::setProperties(const EntityItemProperties& properties) {
SET_ENTITY_PROPERTY_FROM_PROPERTIES(color, setColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(modelURL, setModelURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(collisionModelURL, setCollisionModelURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationURL, setAnimationURL);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationIsPlaying, setAnimationIsPlaying);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(animationFrameIndex, setAnimationFrameIndex);
@ -98,11 +98,11 @@ int ModelEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data,
READ_ENTITY_PROPERTY_COLOR(PROP_COLOR, _color);
READ_ENTITY_PROPERTY_STRING(PROP_MODEL_URL, setModelURL);
if (args.bitstreamVersion < VERSION_ENTITIES_HAS_COLLISION_MODEL) {
setCollisionModelURL("");
setCompoundShapeURL("");
} else if (args.bitstreamVersion == VERSION_ENTITIES_HAS_COLLISION_MODEL) {
READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL_OLD_VERSION, setCollisionModelURL);
READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL);
} else {
READ_ENTITY_PROPERTY_STRING(PROP_COLLISION_MODEL_URL, setCollisionModelURL);
READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL);
}
READ_ENTITY_PROPERTY_STRING(PROP_ANIMATION_URL, setAnimationURL);
@ -140,7 +140,7 @@ EntityPropertyFlags ModelEntityItem::getEntityProperties(EncodeBitstreamParams&
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_MODEL_URL;
requestedProperties += PROP_COLLISION_MODEL_URL;
requestedProperties += PROP_COMPOUND_SHAPE_URL;
requestedProperties += PROP_ANIMATION_URL;
requestedProperties += PROP_ANIMATION_FPS;
requestedProperties += PROP_ANIMATION_FRAME_INDEX;
@ -164,7 +164,7 @@ void ModelEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBit
APPEND_ENTITY_PROPERTY(PROP_COLOR, appendColor, getColor());
APPEND_ENTITY_PROPERTY(PROP_MODEL_URL, appendValue, getModelURL());
APPEND_ENTITY_PROPERTY(PROP_COLLISION_MODEL_URL, appendValue, getCollisionModelURL());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, getCompoundShapeURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_URL, appendValue, getAnimationURL());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FPS, appendValue, getAnimationFPS());
APPEND_ENTITY_PROPERTY(PROP_ANIMATION_FRAME_INDEX, appendValue, getAnimationFrameIndex());
@ -272,7 +272,7 @@ void ModelEntityItem::debugDump() const {
qCDebug(entities) << " position:" << getPosition();
qCDebug(entities) << " dimensions:" << getDimensions();
qCDebug(entities) << " model URL:" << getModelURL();
qCDebug(entities) << " collision model URL:" << getCollisionModelURL();
qCDebug(entities) << " compound shape URL:" << getCompoundShapeURL();
}
void ModelEntityItem::updateShapeType(ShapeType type) {
@ -280,8 +280,8 @@ void ModelEntityItem::updateShapeType(ShapeType type) {
// we have allowed inconsistent ShapeType's to be stored in SVO files in the past (this was a bug)
// but we are now enforcing the entity properties to be consistent. To make the possible we're
// introducing a temporary workaround: we will ignore ShapeType updates that conflict with the
// _collisionModelURL.
if (hasCollisionModel()) {
// _compoundShapeURL.
if (hasCompoundShapeURL()) {
type = SHAPE_TYPE_COMPOUND;
}
// END_TEMPORARY_WORKAROUND
@ -295,17 +295,17 @@ void ModelEntityItem::updateShapeType(ShapeType type) {
// virtual
ShapeType ModelEntityItem::getShapeType() const {
if (_shapeType == SHAPE_TYPE_COMPOUND) {
return hasCollisionModel() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
return hasCompoundShapeURL() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
} else {
return _shapeType;
}
}
void ModelEntityItem::setCollisionModelURL(const QString& url) {
if (_collisionModelURL != url) {
_collisionModelURL = url;
void ModelEntityItem::setCompoundShapeURL(const QString& url) {
if (_compoundShapeURL != url) {
_compoundShapeURL = url;
_dirtyFlags |= EntityItem::DIRTY_SHAPE | EntityItem::DIRTY_MASS;
_shapeType = _collisionModelURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND;
_shapeType = _compoundShapeURL.isEmpty() ? SHAPE_TYPE_NONE : SHAPE_TYPE_COMPOUND;
}
}

View file

@ -57,13 +57,13 @@ public:
const rgbColor& getColor() const { return _color; }
xColor getXColor() const { xColor color = { _color[RED_INDEX], _color[GREEN_INDEX], _color[BLUE_INDEX] }; return color; }
bool hasModel() const { return !_modelURL.isEmpty(); }
virtual bool hasCollisionModel() const { return !_collisionModelURL.isEmpty(); }
virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); }
static const QString DEFAULT_MODEL_URL;
const QString& getModelURL() const { return _modelURL; }
static const QString DEFAULT_COLLISION_MODEL_URL;
const QString& getCollisionModelURL() const { return _collisionModelURL; }
static const QString DEFAULT_COMPOUND_SHAPE_URL;
const QString& getCompoundShapeURL() const { return _compoundShapeURL; }
bool hasAnimation() const { return !_animationURL.isEmpty(); }
static const QString DEFAULT_ANIMATION_URL;
@ -78,7 +78,7 @@ public:
// model related properties
void setModelURL(const QString& url) { _modelURL = url; }
virtual void setCollisionModelURL(const QString& url);
virtual void setCompoundShapeURL(const QString& url);
void setAnimationURL(const QString& url);
static const float DEFAULT_ANIMATION_FRAME_INDEX;
void setAnimationFrameIndex(float value);
@ -126,7 +126,7 @@ protected:
rgbColor _color;
QString _modelURL;
QString _collisionModelURL;
QString _compoundShapeURL;
quint64 _lastAnimated;
QString _animationURL;

View file

@ -96,11 +96,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const {
// determine the ray in the frame of the entity transformed from a unit sphere
glm::mat4 translation = glm::translate(getPosition());
glm::mat4 rotation = glm::mat4_cast(getRotation());
glm::mat4 scale = glm::scale(getDimensions());
glm::mat4 registration = glm::translate(glm::vec3(0.5f, 0.5f, 0.5f) - getRegistrationPoint());
glm::mat4 entityToWorldMatrix = translation * rotation * scale * registration;
glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix();
glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix);
glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f));
glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)));
@ -112,7 +108,7 @@ bool SphereEntityItem::findDetailedRayIntersection(const glm::vec3& origin, cons
glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance);
// then translate back to work coordinates
glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f));
distance = glm::distance(origin,hitAt);
distance = glm::distance(origin, hitAt);
return true;
}
return false;

View file

@ -50,9 +50,6 @@ public:
_color[BLUE_INDEX] = value.blue;
}
// TODO: implement proper contains for 3D ellipsoid
//virtual bool contains(const glm::vec3& point) const;
virtual ShapeType getShapeType() const { return SHAPE_TYPE_SPHERE; }
virtual bool supportsDetailedRayIntersection() const { return true; }

View file

@ -48,11 +48,6 @@ void TextEntityItem::setDimensions(const glm::vec3& value) {
_dimensions = glm::vec3(value.x, value.y, TEXT_ENTITY_ITEM_FIXED_DEPTH);
}
void TextEntityItem::setDimensionsInDomainUnits(const glm::vec3& value) {
// NOTE: Text Entities always have a "depth" of 1cm.
_dimensions = glm::vec3(value.x * (float)TREE_SCALE, value.y * (float)TREE_SCALE, TEXT_ENTITY_ITEM_FIXED_DEPTH);
}
EntityItemProperties TextEntityItem::getProperties() const {
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class

View file

@ -24,7 +24,6 @@ public:
/// set dimensions in domain scale units (0.0 - 1.0) this will also reset radius appropriately
virtual void setDimensions(const glm::vec3& value);
virtual void setDimensionsInDomainUnits(const glm::vec3& value);
virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
// methods for getting/setting all properties of an entity

View file

@ -0,0 +1,224 @@
//
// ZoneEntityItem.cpp
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <QDebug>
#include <ByteCountCoding.h>
#include "ZoneEntityItem.h"
#include "EntityTree.h"
#include "EntitiesLogging.h"
#include "EntityTreeElement.h"
bool ZoneEntityItem::_zonesArePickable = false;
const xColor ZoneEntityItem::DEFAULT_KEYLIGHT_COLOR = { 255, 255, 255 };
const float ZoneEntityItem::DEFAULT_KEYLIGHT_INTENSITY = 1.0f;
const float ZoneEntityItem::DEFAULT_KEYLIGHT_AMBIENT_INTENSITY = 0.5f;
const glm::vec3 ZoneEntityItem::DEFAULT_KEYLIGHT_DIRECTION = { 0.0f, -1.0f, 0.0f };
const bool ZoneEntityItem::DEFAULT_STAGE_SUN_MODEL_ENABLED = false;
const float ZoneEntityItem::DEFAULT_STAGE_LATITUDE = 37.777f;
const float ZoneEntityItem::DEFAULT_STAGE_LONGITUDE = 122.407f;
const float ZoneEntityItem::DEFAULT_STAGE_ALTITUDE = 0.03f;
const quint16 ZoneEntityItem::DEFAULT_STAGE_DAY = 60;
const float ZoneEntityItem::DEFAULT_STAGE_HOUR = 12.0f;
const ShapeType ZoneEntityItem::DEFAULT_SHAPE_TYPE = SHAPE_TYPE_BOX;
const QString ZoneEntityItem::DEFAULT_COMPOUND_SHAPE_URL = "";
EntityItem* ZoneEntityItem::factory(const EntityItemID& entityID, const EntityItemProperties& properties) {
EntityItem* result = new ZoneEntityItem(entityID, properties);
return result;
}
ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties) :
EntityItem(entityItemID)
{
_type = EntityTypes::Zone;
_created = properties.getCreated();
_keyLightColor[RED_INDEX] = DEFAULT_KEYLIGHT_COLOR.red;
_keyLightColor[GREEN_INDEX] = DEFAULT_KEYLIGHT_COLOR.green;
_keyLightColor[BLUE_INDEX] = DEFAULT_KEYLIGHT_COLOR.blue;
_keyLightIntensity = DEFAULT_KEYLIGHT_INTENSITY;
_keyLightAmbientIntensity = DEFAULT_KEYLIGHT_AMBIENT_INTENSITY;
_keyLightDirection = DEFAULT_KEYLIGHT_DIRECTION;
_stageSunModelEnabled = DEFAULT_STAGE_SUN_MODEL_ENABLED;
_stageLatitude = DEFAULT_STAGE_LATITUDE;
_stageLongitude = DEFAULT_STAGE_LONGITUDE;
_stageAltitude = DEFAULT_STAGE_ALTITUDE;
_stageDay = DEFAULT_STAGE_DAY;
_stageHour = DEFAULT_STAGE_HOUR;
_shapeType = DEFAULT_SHAPE_TYPE;
_compoundShapeURL = DEFAULT_COMPOUND_SHAPE_URL;
setProperties(properties);
}
EntityItemProperties ZoneEntityItem::getProperties() const {
EntityItemProperties properties = EntityItem::getProperties(); // get the properties from our base class
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightColor, getKeyLightColor);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightIntensity, getKeyLightIntensity);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightAmbientIntensity, getKeyLightAmbientIntensity);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightDirection, getKeyLightDirection);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageSunModelEnabled, getStageSunModelEnabled);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageLatitude, getStageLatitude);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageLongitude, getStageLongitude);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageAltitude, getStageAltitude);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageDay, getStageDay);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(stageHour, getStageHour);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType);
COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL);
return properties;
}
bool ZoneEntityItem::setProperties(const EntityItemProperties& properties) {
bool somethingChanged = false;
somethingChanged = EntityItem::setProperties(properties); // set the properties in our base class
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightColor, setKeyLightColor);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightIntensity, setKeyLightIntensity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightAmbientIntensity, setKeyLightAmbientIntensity);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightDirection, setKeyLightDirection);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageSunModelEnabled, setStageSunModelEnabled);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageLatitude, setStageLatitude);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageLongitude, setStageLongitude);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageAltitude, setStageAltitude);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageDay, setStageDay);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(stageHour, setStageHour);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, updateShapeType);
SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL);
if (somethingChanged) {
bool wantDebug = false;
if (wantDebug) {
uint64_t now = usecTimestampNow();
int elapsed = now - getLastEdited();
qCDebug(entities) << "ZoneEntityItem::setProperties() AFTER update... edited AGO=" << elapsed <<
"now=" << now << " getLastEdited()=" << getLastEdited();
}
setLastEdited(properties._lastEdited);
}
return somethingChanged;
}
int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData) {
int bytesRead = 0;
const unsigned char* dataAt = data;
READ_ENTITY_PROPERTY_COLOR(PROP_KEYLIGHT_COLOR, _keyLightColor);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, float, _keyLightIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, float, _keyLightAmbientIntensity);
READ_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, glm::vec3, _keyLightDirection);
READ_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, bool, _stageSunModelEnabled);
READ_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, float, _stageLatitude);
READ_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, float, _stageLongitude);
READ_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, float, _stageAltitude);
READ_ENTITY_PROPERTY(PROP_STAGE_DAY, quint16, _stageDay);
READ_ENTITY_PROPERTY(PROP_STAGE_HOUR, float, _stageHour);
READ_ENTITY_PROPERTY_SETTER(PROP_SHAPE_TYPE, ShapeType, updateShapeType);
READ_ENTITY_PROPERTY_STRING(PROP_COMPOUND_SHAPE_URL, setCompoundShapeURL);
return bytesRead;
}
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& params) const {
EntityPropertyFlags requestedProperties = EntityItem::getEntityProperties(params);
requestedProperties += PROP_KEYLIGHT_COLOR;
requestedProperties += PROP_KEYLIGHT_INTENSITY;
requestedProperties += PROP_KEYLIGHT_AMBIENT_INTENSITY;
requestedProperties += PROP_KEYLIGHT_DIRECTION;
requestedProperties += PROP_STAGE_SUN_MODEL_ENABLED;
requestedProperties += PROP_STAGE_LATITUDE;
requestedProperties += PROP_STAGE_LONGITUDE;
requestedProperties += PROP_STAGE_ALTITUDE;
requestedProperties += PROP_STAGE_DAY;
requestedProperties += PROP_STAGE_HOUR;
requestedProperties += PROP_SHAPE_TYPE;
requestedProperties += PROP_COMPOUND_SHAPE_URL;
return requestedProperties;
}
void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const {
bool successPropertyFits = true;
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_COLOR, appendColor, _keyLightColor);
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_INTENSITY, appendValue, getKeyLightIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_AMBIENT_INTENSITY, appendValue, getKeyLightAmbientIntensity());
APPEND_ENTITY_PROPERTY(PROP_KEYLIGHT_DIRECTION, appendValue, getKeyLightDirection());
APPEND_ENTITY_PROPERTY(PROP_STAGE_SUN_MODEL_ENABLED, appendValue, getStageSunModelEnabled());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LATITUDE, appendValue, getStageLatitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_LONGITUDE, appendValue, getStageLongitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_ALTITUDE, appendValue, getStageAltitude());
APPEND_ENTITY_PROPERTY(PROP_STAGE_DAY, appendValue, getStageDay());
APPEND_ENTITY_PROPERTY(PROP_STAGE_HOUR, appendValue, getStageHour());
APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, appendValue, (uint32_t)getShapeType());
APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, appendValue, getCompoundShapeURL());
}
void ZoneEntityItem::debugDump() const {
quint64 now = usecTimestampNow();
qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------";
qCDebug(entities) << " keyLightColor:" << _keyLightColor[0] << "," << _keyLightColor[1] << "," << _keyLightColor[2];
qCDebug(entities) << " position:" << debugTreeVector(_position);
qCDebug(entities) << " dimensions:" << debugTreeVector(_dimensions);
qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now);
qCDebug(entities) << " _keyLightIntensity:" << _keyLightIntensity;
qCDebug(entities) << " _keyLightAmbientIntensity:" << _keyLightAmbientIntensity;
qCDebug(entities) << " _keyLightDirection:" << _keyLightDirection;
qCDebug(entities) << " _stageSunModelEnabled:" << _stageSunModelEnabled;
qCDebug(entities) << " _stageLatitude:" << _stageLatitude;
qCDebug(entities) << " _stageLongitude:" << _stageLongitude;
qCDebug(entities) << " _stageAltitude:" << _stageAltitude;
qCDebug(entities) << " _stageDay:" << _stageDay;
qCDebug(entities) << " _stageHour:" << _stageHour;
}
ShapeType ZoneEntityItem::getShapeType() const {
if (_shapeType == SHAPE_TYPE_COMPOUND) {
return hasCompoundShapeURL() ? SHAPE_TYPE_COMPOUND : SHAPE_TYPE_NONE;
} else {
return _shapeType;
}
}
void ZoneEntityItem::setCompoundShapeURL(const QString& url) {
_compoundShapeURL = url;
if (!_compoundShapeURL.isEmpty()) {
updateShapeType(SHAPE_TYPE_COMPOUND);
} else if (_shapeType == SHAPE_TYPE_COMPOUND) {
_shapeType = DEFAULT_SHAPE_TYPE;
}
}
bool ZoneEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const {
return _zonesArePickable;
}

View file

@ -0,0 +1,142 @@
//
// ZoneEntityItem.h
// libraries/entities/src
//
// Created by Brad Hefta-Gaub on 12/4/13.
// Copyright 2013 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_ZoneEntityItem_h
#define hifi_ZoneEntityItem_h
#include "EntityItem.h"
class ZoneEntityItem : public EntityItem {
public:
static EntityItem* factory(const EntityItemID& entityID, const EntityItemProperties& properties);
ZoneEntityItem(const EntityItemID& entityItemID, const EntityItemProperties& properties);
ALLOW_INSTANTIATION // This class can be instantiated
// methods for getting/setting all properties of an entity
virtual EntityItemProperties getProperties() const;
virtual bool setProperties(const EntityItemProperties& properties);
// TODO: eventually only include properties changed since the params.lastViewFrustumSent time
virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const;
virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params,
EntityTreeElementExtraEncodeData* modelTreeElementExtraEncodeData,
EntityPropertyFlags& requestedProperties,
EntityPropertyFlags& propertyFlags,
EntityPropertyFlags& propertiesDidntFit,
int& propertyCount,
OctreeElement::AppendState& appendState) const;
virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead,
ReadBitstreamToTreeParams& args,
EntityPropertyFlags& propertyFlags, bool overwriteLocalData);
// NOTE: Apparently if you begin to return a shape type, then the physics system will prevent an avatar
// from penetrating the walls of the entity. This fact will likely be important to Clement as he works
// on better defining the shape/volume of a zone.
//virtual ShapeType getShapeType() const { return SHAPE_TYPE_BOX; }
xColor getKeyLightColor() const { xColor color = { _keyLightColor[RED_INDEX], _keyLightColor[GREEN_INDEX], _keyLightColor[BLUE_INDEX] }; return color; }
void setKeyLightColor(const xColor& value) {
_keyLightColor[RED_INDEX] = value.red;
_keyLightColor[GREEN_INDEX] = value.green;
_keyLightColor[BLUE_INDEX] = value.blue;
}
glm::vec3 getKeyLightColorVec3() const {
const quint8 MAX_COLOR = 255;
glm::vec3 color = { _keyLightColor[RED_INDEX] / MAX_COLOR,
_keyLightColor[GREEN_INDEX] / MAX_COLOR,
_keyLightColor[BLUE_INDEX] / MAX_COLOR };
return color;
}
float getKeyLightIntensity() const { return _keyLightIntensity; }
void setKeyLightIntensity(float value) { _keyLightIntensity = value; }
float getKeyLightAmbientIntensity() const { return _keyLightAmbientIntensity; }
void setKeyLightAmbientIntensity(float value) { _keyLightAmbientIntensity = value; }
const glm::vec3& getKeyLightDirection() const { return _keyLightDirection; }
void setKeyLightDirection(const glm::vec3& value) { _keyLightDirection = value; }
bool getStageSunModelEnabled() const { return _stageSunModelEnabled; }
void setStageSunModelEnabled(bool value) { _stageSunModelEnabled = value; }
float getStageLatitude() const { return _stageLatitude; }
void setStageLatitude(float value) { _stageLatitude = value; }
float getStageLongitude() const { return _stageLongitude; }
void setStageLongitude(float value) { _stageLongitude = value; }
float getStageAltitude() const { return _stageAltitude; }
void setStageAltitude(float value) { _stageAltitude = value; }
uint16_t getStageDay() const { return _stageDay; }
void setStageDay(uint16_t value) { _stageDay = value; }
float getStageHour() const { return _stageHour; }
void setStageHour(float value) { _stageHour = value; }
static bool getZonesArePickable() { return _zonesArePickable; }
static void setZonesArePickable(bool value) { _zonesArePickable = value; }
virtual bool isReadyToComputeShape() { return false; }
void updateShapeType(ShapeType type) { _shapeType = type; }
virtual ShapeType getShapeType() const;
virtual bool hasCompoundShapeURL() const { return !_compoundShapeURL.isEmpty(); }
const QString getCompoundShapeURL() const { return _compoundShapeURL; }
virtual void setCompoundShapeURL(const QString& url);
virtual bool supportsDetailedRayIntersection() const { return true; }
virtual bool findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
bool& keepSearching, OctreeElement*& element, float& distance, BoxFace& face,
void** intersectedObject, bool precisionPicking) const;
virtual void debugDump() const;
static const xColor DEFAULT_KEYLIGHT_COLOR;
static const float DEFAULT_KEYLIGHT_INTENSITY;
static const float DEFAULT_KEYLIGHT_AMBIENT_INTENSITY;
static const glm::vec3 DEFAULT_KEYLIGHT_DIRECTION;
static const bool DEFAULT_STAGE_SUN_MODEL_ENABLED;
static const float DEFAULT_STAGE_LATITUDE;
static const float DEFAULT_STAGE_LONGITUDE;
static const float DEFAULT_STAGE_ALTITUDE;
static const quint16 DEFAULT_STAGE_DAY;
static const float DEFAULT_STAGE_HOUR;
static const ShapeType DEFAULT_SHAPE_TYPE;
static const QString DEFAULT_COMPOUND_SHAPE_URL;
protected:
// properties of the "sun" in the zone
rgbColor _keyLightColor;
float _keyLightIntensity;
float _keyLightAmbientIntensity;
glm::vec3 _keyLightDirection;
bool _stageSunModelEnabled;
float _stageLatitude;
float _stageLongitude;
float _stageAltitude;
uint16_t _stageDay;
float _stageHour;
ShapeType _shapeType = SHAPE_TYPE_NONE;
QString _compoundShapeURL;
static bool _zonesArePickable;
};
#endif // hifi_ZoneEntityItem_h

View file

@ -125,6 +125,51 @@ Extents FBXGeometry::getUnscaledMeshExtents() const {
return scaledExtents;
}
// TODO: Move to model::Mesh when Sam's ready
bool FBXGeometry::convexHullContains(const glm::vec3& point) const {
if (!getUnscaledMeshExtents().containsPoint(point)) {
return false;
}
auto checkEachPrimitive = [=](FBXMesh& mesh, QVector<int> indices, int primitiveSize) -> bool {
// Check whether the point is "behind" all the primitives.
for (unsigned int j = 0; j < indices.size(); j += primitiveSize) {
if (!isPointBehindTrianglesPlane(point,
mesh.vertices[indices[j]],
mesh.vertices[indices[j + 1]],
mesh.vertices[indices[j + 2]])) {
// it's not behind at least one so we bail
return false;
}
}
return true;
};
// Check that the point is contained in at least one convex mesh.
for (auto mesh : meshes) {
bool insideMesh = true;
// To be considered inside a convex mesh,
// the point needs to be "behind" all the primitives respective planes.
for (auto part : mesh.parts) {
// run through all the triangles and quads
if (!checkEachPrimitive(mesh, part.triangleIndices, 3) ||
!checkEachPrimitive(mesh, part.quadIndices, 4)) {
// If not, the point is outside, bail for this mesh
insideMesh = false;
continue;
}
}
if (insideMesh) {
// It's inside this mesh, return true.
return true;
}
}
// It wasn't in any mesh, return false.
return false;
}
QString FBXGeometry::getModelNameOfMesh(int meshIndex) const {
if (meshIndicesToModelNames.contains(meshIndex)) {
return meshIndicesToModelNames.value(meshIndex);
@ -132,8 +177,6 @@ QString FBXGeometry::getModelNameOfMesh(int meshIndex) const {
return QString();
}
static int fbxGeometryMetaTypeId = qRegisterMetaType<FBXGeometry>();
static int fbxAnimationFrameMetaTypeId = qRegisterMetaType<FBXAnimationFrame>();
static int fbxAnimationFrameVectorMetaTypeId = qRegisterMetaType<QVector<FBXAnimationFrame> >();

View file

@ -252,6 +252,7 @@ public:
/// Returns the unscaled extents of the model's mesh
Extents getUnscaledMeshExtents() const;
bool convexHullContains(const glm::vec3& point) const;
QHash<int, QString> meshIndicesToModelNames;

View file

@ -74,7 +74,7 @@ PacketVersion versionForPacketType(PacketType type) {
return 1;
case PacketTypeEntityAddOrEdit:
case PacketTypeEntityData:
return VERSION_ENTITIES_HAVE_UUIDS;
return VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE;
case PacketTypeEntityErase:
return 2;
case PacketTypeAudioStreamStats:

View file

@ -119,6 +119,7 @@ PacketType packetTypeForPacket(const char* packet);
int arithmeticCodingValueFromBuffer(const char* checkValue);
int numBytesArithmeticCodingFromBuffer(const char* checkValue);
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
const PacketVersion VERSION_ENTITIES_HAVE_ANIMATION = 1;
const PacketVersion VERSION_ROOT_ELEMENT_HAS_DATA = 2;
const PacketVersion VERSION_ENTITIES_SUPPORT_SPLIT_MTU = 3;
@ -136,6 +137,7 @@ const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID_DAMAGED = 13;
const PacketVersion VERSION_ENTITIES_HAS_MARKETPLACE_ID = 14;
const PacketVersion VERSION_ENTITIES_HAVE_ACCELERATION = 15;
const PacketVersion VERSION_ENTITIES_HAVE_UUIDS = 16;
const PacketVersion VERSION_OCTREE_HAS_FILE_BREAKS = 1;
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_EXIST = 17;
const PacketVersion VERSION_ENTITIES_ZONE_ENTITIES_HAVE_DYNAMIC_SHAPE = 18;
#endif // hifi_PacketHeaders_h

View file

@ -6,7 +6,7 @@ AUTOSCRIBE_SHADER_LIB(gpu model)
qt5_add_resources(QT_RESOURCES_FILE "${CMAKE_CURRENT_SOURCE_DIR}/res/fonts/fonts.qrc")
# use setup_hifi_library macro to setup our project and link appropriate Qt modules
setup_hifi_library(Widgets OpenGL Network Script)
setup_hifi_library(Widgets OpenGL Network Qml Quick Script)
add_dependency_external_projects(glm)
find_package(GLM REQUIRED)

View file

@ -0,0 +1,98 @@
//
// OffscreenGlCanvas.cpp
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "FboCache.h"
#include <QOpenGLFramebufferObject>
#include <QDebug>
#include "ThreadHelpers.h"
FboCache::FboCache() {
// Why do we even HAVE that lever?
}
void FboCache::lockTexture(int texture) {
withLock(_lock, [&] {
Q_ASSERT(_fboMap.count(texture));
if (!_fboLocks.count(texture)) {
Q_ASSERT(_readyFboQueue.front()->texture() == texture);
_readyFboQueue.pop_front();
_fboLocks[texture] = 1;
} else {
_fboLocks[texture]++;
}
});
}
void FboCache::releaseTexture(int texture) {
withLock(_lock, [&] {
Q_ASSERT(_fboMap.count(texture));
Q_ASSERT(_fboLocks.count(texture));
int newLockCount = --_fboLocks[texture];
if (!newLockCount) {
auto fbo = _fboMap[texture].data();
if (fbo->size() != _size) {
// Move the old FBO to the destruction queue.
// We can't destroy the FBO here because we might
// not be on the right thread or have the context active
_destroyFboQueue.push_back(_fboMap[texture]);
_fboMap.remove(texture);
} else {
_readyFboQueue.push_back(fbo);
}
_fboLocks.remove(texture);
}
});
}
QOpenGLFramebufferObject* FboCache::getReadyFbo() {
QOpenGLFramebufferObject* result = nullptr;
withLock(_lock, [&] {
// Delete any FBOs queued for deletion
_destroyFboQueue.clear();
if (_readyFboQueue.empty()) {
qDebug() << "Building new offscreen FBO number " << _fboMap.size() + 1;
result = new QOpenGLFramebufferObject(_size, QOpenGLFramebufferObject::CombinedDepthStencil);
_fboMap[result->texture()] = QSharedPointer<QOpenGLFramebufferObject>(result);
_readyFboQueue.push_back(result);
} else {
result = _readyFboQueue.front();
}
});
return result;
}
void FboCache::setSize(const QSize& newSize) {
if (_size == newSize) {
return;
}
_size = newSize;
withLock(_lock, [&] {
// Clear out any fbos with the old id
_readyFboQueue.clear();
QSet<int> outdatedFbos;
// FBOs that are locked will be removed as they are unlocked
foreach(int texture, _fboMap.keys()) {
if (!_fboLocks.count(texture)) {
outdatedFbos.insert(texture);
}
}
// Implicitly deletes the FBO via the shared pointer destruction mechanism
foreach(int texture, outdatedFbos) {
_fboMap.remove(texture);
}
});
}

View file

@ -0,0 +1,51 @@
//
// OffscreenGlCanvas.h
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// Copyright 2015 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
//
#pragma once
#ifndef hifi_FboCache_h
#define hifi_FboCache_h
#include <QOpenGLContext>
#include <QOffscreenSurface>
#include <QQueue>
#include <QMap>
#include <QSharedPointer>
#include <QMutex>
class QOpenGLFramebufferObject;
class FboCache : public QObject {
public:
FboCache();
// setSize() and getReadyFbo() must consitently be called from only a single
// thread. Additionally, it is the caller's responsibility to ensure that
// the appropriate OpenGL context is active when doing so.
// Important.... textures are sharable resources, but FBOs ARE NOT.
void setSize(const QSize& newSize);
QOpenGLFramebufferObject* getReadyFbo();
// These operations are thread safe and require no OpenGL context. They manipulate the
// internal locks and pointers but execute no OpenGL opreations.
void lockTexture(int texture);
void releaseTexture(int texture);
protected:
QMap<int, QSharedPointer<QOpenGLFramebufferObject>> _fboMap;
QMap<int, int> _fboLocks;
QQueue<QOpenGLFramebufferObject*> _readyFboQueue;
QQueue<QSharedPointer<QOpenGLFramebufferObject>> _destroyFboQueue;
QMutex _lock;
QSize _size;
};
#endif // hifi_FboCache_h

View file

@ -0,0 +1,45 @@
//
// OffscreenGlCanvas.cpp
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OffscreenGlCanvas.h"
OffscreenGlCanvas::OffscreenGlCanvas() {
}
void OffscreenGlCanvas::create(QOpenGLContext* sharedContext) {
if (nullptr != sharedContext) {
sharedContext->doneCurrent();
_context.setFormat(sharedContext->format());
_context.setShareContext(sharedContext);
} else {
QSurfaceFormat format;
format.setDepthBufferSize(16);
format.setStencilBufferSize(8);
format.setMajorVersion(4);
format.setMinorVersion(1);
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
_context.setFormat(format);
}
_context.create();
_offscreenSurface.setFormat(_context.format());
_offscreenSurface.create();
}
bool OffscreenGlCanvas::makeCurrent() {
return _context.makeCurrent(&_offscreenSurface);
}
void OffscreenGlCanvas::doneCurrent() {
_context.doneCurrent();
}

View file

@ -0,0 +1,31 @@
//
// OffscreenGlCanvas.h
// interface/src/renderer
//
// Created by Bradley Austin Davis on 2014/04/09.
// Copyright 2015 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
//
#pragma once
#ifndef hifi_OffscreenGlCanvas_h
#define hifi_OffscreenGlCanvas_h
#include <QOpenGLContext>
#include <QOffscreenSurface>
class OffscreenGlCanvas : public QObject {
public:
OffscreenGlCanvas();
void create(QOpenGLContext* sharedContext = nullptr);
bool makeCurrent();
void doneCurrent();
protected:
QOpenGLContext _context;
QOffscreenSurface _offscreenSurface;
};
#endif // hifi_OffscreenGlCanvas_h

View file

@ -0,0 +1,18 @@
//
// OffscreenQmlDialog.cpp
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OffscreenQmlDialog.h"
OffscreenQmlDialog::OffscreenQmlDialog(QQuickItem* parent)
: QQuickItem(parent) { }
void OffscreenQmlDialog::hide() {
static_cast<QQuickItem*>(parent())->setEnabled(false);
}

View file

@ -0,0 +1,56 @@
//
// OffscreenQmlDialog.h
//
// Created by Bradley Austin Davis on 2015/04/14
// Copyright 2015 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
//
#pragma once
#ifndef hifi_OffscreenQmlDialog_h
#define hifi_OffscreenQmlDialog_h
#include <QQuickItem>
#include "OffscreenUi.h"
#define QML_DIALOG_DECL \
private: \
static const QString NAME; \
static const QUrl QML; \
public: \
static void registerType(); \
static void show(); \
static void toggle(); \
private:
#define QML_DIALOG_DEF(x) \
const QUrl x::QML = QUrl(#x ".qml"); \
const QString x::NAME = #x; \
\
void x::registerType() { \
qmlRegisterType<x>("Hifi", 1, 0, NAME.toLocal8Bit().constData()); \
} \
\
void x::show() { \
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
offscreenUi->show(QML, NAME); \
} \
\
void x::toggle() { \
auto offscreenUi = DependencyManager::get<OffscreenUi>(); \
offscreenUi->toggle(QML, NAME); \
}
class OffscreenQmlDialog : public QQuickItem
{
Q_OBJECT
public:
OffscreenQmlDialog(QQuickItem* parent = nullptr);
protected:
void hide();
};
#endif

View file

@ -0,0 +1,412 @@
//
// OffscreenUi.cpp
// interface/src/render-utils
//
// Created by Bradley Austin Davis on 2015-04-04
// Copyright 2015 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include "OffscreenUi.h"
#include <QOpenGLFramebufferObject>
#include <QOpenGLDebugLogger>
#include <QGLWidget>
#include <QtQml>
// Time between receiving a request to render the offscreen UI actually triggering
// the render. Could possibly be increased depending on the framerate we expect to
// achieve.
static const int SMALL_INTERVAL = 5;
class OffscreenUiRoot : public QQuickItem {
Q_OBJECT
public:
OffscreenUiRoot(QQuickItem* parent = 0);
Q_INVOKABLE void information(const QString& title, const QString& text);
Q_INVOKABLE void loadChild(const QUrl& url) {
DependencyManager::get<OffscreenUi>()->load(url);
}
};
OffscreenUiRoot::OffscreenUiRoot(QQuickItem* parent) : QQuickItem(parent) {
}
void OffscreenUiRoot::information(const QString& title, const QString& text) {
OffscreenUi::information(title, text);
}
OffscreenUi::OffscreenUi() {
::qmlRegisterType<OffscreenUiRoot>("Hifi", 1, 0, "Root");
}
OffscreenUi::~OffscreenUi() {
// Make sure the context is current while doing cleanup. Note that we use the
// offscreen surface here because passing 'this' at this point is not safe: the
// underlying platform window may already be destroyed. To avoid all the trouble, use
// another surface that is valid for sure.
makeCurrent();
// Delete the render control first since it will free the scenegraph resources.
// Destroy the QQuickWindow only afterwards.
delete _renderControl;
delete _qmlComponent;
delete _quickWindow;
delete _qmlEngine;
doneCurrent();
}
void OffscreenUi::create(QOpenGLContext* shareContext) {
OffscreenGlCanvas::create(shareContext);
makeCurrent();
// Create a QQuickWindow that is associated with out render control. Note that this
// window never gets created or shown, meaning that it will never get an underlying
// native (platform) window.
QQuickWindow::setDefaultAlphaBuffer(true);
_quickWindow = new QQuickWindow(_renderControl);
_quickWindow->setColor(QColor(255, 255, 255, 0));
_quickWindow->setFlags(_quickWindow->flags() | static_cast<Qt::WindowFlags>(Qt::WA_TranslucentBackground));
// Create a QML engine.
_qmlEngine = new QQmlEngine;
if (!_qmlEngine->incubationController()) {
_qmlEngine->setIncubationController(_quickWindow->incubationController());
}
// When Quick says there is a need to render, we will not render immediately. Instead,
// a timer with a small interval is used to get better performance.
_updateTimer.setSingleShot(true);
_updateTimer.setInterval(SMALL_INTERVAL);
connect(&_updateTimer, &QTimer::timeout, this, &OffscreenUi::updateQuick);
// Now hook up the signals. For simplicy we don't differentiate between
// renderRequested (only render is needed, no sync) and sceneChanged (polish and sync
// is needed too).
connect(_renderControl, &QQuickRenderControl::renderRequested, this, &OffscreenUi::requestRender);
connect(_renderControl, &QQuickRenderControl::sceneChanged, this, &OffscreenUi::requestUpdate);
_quickWindow->focusObject();
_qmlComponent = new QQmlComponent(_qmlEngine);
// Initialize the render control and our OpenGL resources.
makeCurrent();
_renderControl->initialize(&_context);
}
void OffscreenUi::addImportPath(const QString& path) {
_qmlEngine->addImportPath(path);
}
void OffscreenUi::resize(const QSize& newSize) {
makeCurrent();
// Clear out any fbos with the old size
qreal pixelRatio = _renderControl->_renderWindow ? _renderControl->_renderWindow->devicePixelRatio() : 1.0;
qDebug() << "Offscreen UI resizing to " << newSize.width() << "x" << newSize.height() << " with pixel ratio " << pixelRatio;
_fboCache.setSize(newSize * pixelRatio);
// Update our members
if (_rootItem) {
_rootItem->setSize(newSize);
}
if (_quickWindow) {
_quickWindow->setGeometry(QRect(QPoint(), newSize));
}
doneCurrent();
}
QQmlContext* OffscreenUi::qmlContext() {
if (nullptr == _rootItem) {
return _qmlComponent->creationContext();
}
return QQmlEngine::contextForObject(_rootItem);
}
void OffscreenUi::setBaseUrl(const QUrl& baseUrl) {
_qmlEngine->setBaseUrl(baseUrl);
}
void OffscreenUi::load(const QUrl& qmlSource, std::function<void(QQmlContext*)> f) {
qDebug() << "Loading QML from URL " << qmlSource;
_qmlComponent->loadUrl(qmlSource);
if (_qmlComponent->isLoading()) {
connect(_qmlComponent, &QQmlComponent::statusChanged, this, []{});
} else {
finishQmlLoad();
}
}
void OffscreenUi::requestUpdate() {
_polish = true;
if (!_updateTimer.isActive()) {
_updateTimer.start();
}
}
void OffscreenUi::requestRender() {
if (!_updateTimer.isActive()) {
_updateTimer.start();
}
}
void OffscreenUi::finishQmlLoad() {
disconnect(_qmlComponent, &QQmlComponent::statusChanged, this, &OffscreenUi::finishQmlLoad);
if (_qmlComponent->isError()) {
QList<QQmlError> errorList = _qmlComponent->errors();
foreach(const QQmlError &error, errorList) {
qWarning() << error.url() << error.line() << error;
}
return;
}
QObject* newObject = _qmlComponent->create();
if (_qmlComponent->isError()) {
QList<QQmlError> errorList = _qmlComponent->errors();
foreach(const QQmlError &error, errorList)
qWarning() << error.url() << error.line() << error;
if (!_rootItem) {
qFatal("Unable to finish loading QML root");
}
return;
}
QQuickItem* newItem = qobject_cast<QQuickItem*>(newObject);
if (!newItem) {
qWarning("run: Not a QQuickItem");
delete newObject;
if (!_rootItem) {
qFatal("Unable to find root QQuickItem");
}
return;
}
// Make sure we make items focusable (critical for
// supporting keyboard shortcuts)
newItem->setFlag(QQuickItem::ItemIsFocusScope, true);
if (!_rootItem) {
// The root item is ready. Associate it with the window.
_rootItem = newItem;
_rootItem->setParentItem(_quickWindow->contentItem());
_rootItem->setSize(_quickWindow->renderTargetSize());
} else {
// Allow child windows to be destroyed from JS
QQmlEngine::setObjectOwnership(newItem, QQmlEngine::JavaScriptOwnership);
newItem->setParent(_rootItem);
newItem->setParentItem(_rootItem);
newItem->setEnabled(true);
}
}
void OffscreenUi::updateQuick() {
if (_paused) {
return;
}
if (!makeCurrent()) {
return;
}
// Polish, synchronize and render the next frame (into our fbo). In this example
// everything happens on the same thread and therefore all three steps are performed
// in succession from here. In a threaded setup the render() call would happen on a
// separate thread.
if (_polish) {
_renderControl->polishItems();
_renderControl->sync();
_polish = false;
}
QOpenGLFramebufferObject* fbo = _fboCache.getReadyFbo();
_quickWindow->setRenderTarget(fbo);
fbo->bind();
glClearColor(0, 0, 0, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
_renderControl->render();
Q_ASSERT(!glGetError());
_quickWindow->resetOpenGLState();
QOpenGLFramebufferObject::bindDefault();
// Force completion of all the operations before we emit the texture as being ready for use
glFinish();
emit textureUpdated(fbo->texture());
}
QPointF OffscreenUi::mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject) {
vec2 sourceSize;
if (dynamic_cast<QWidget*>(sourceObject)) {
sourceSize = toGlm(((QWidget*)sourceObject)->size());
} else if (dynamic_cast<QWindow*>(sourceObject)) {
sourceSize = toGlm(((QWindow*)sourceObject)->size());
}
vec2 offscreenPosition = toGlm(sourcePosition);
offscreenPosition /= sourceSize;
offscreenPosition *= vec2(toGlm(_quickWindow->size()));
return QPointF(offscreenPosition.x, offscreenPosition.y);
}
///////////////////////////////////////////////////////
//
// Event handling customization
//
bool OffscreenUi::eventFilter(QObject* originalDestination, QEvent* event) {
// Only intercept events while we're in an active state
if (_paused) {
return false;
}
// Don't intercept our own events, or we enter an infinite recursion
if (originalDestination == _quickWindow) {
return false;
}
switch (event->type()) {
case QEvent::Resize: {
QResizeEvent* resizeEvent = static_cast<QResizeEvent*>(event);
QGLWidget* widget = dynamic_cast<QGLWidget*>(originalDestination);
if (widget) {
this->resize(resizeEvent->size());
}
return false;
}
case QEvent::KeyPress:
case QEvent::KeyRelease: {
event->ignore();
if (QCoreApplication::sendEvent(_quickWindow, event)) {
return event->isAccepted();
}
break;
}
case QEvent::Wheel: {
QWheelEvent* wheelEvent = static_cast<QWheelEvent*>(event);
QWheelEvent mappedEvent(
mapWindowToUi(wheelEvent->pos(), originalDestination),
wheelEvent->delta(), wheelEvent->buttons(),
wheelEvent->modifiers(), wheelEvent->orientation());
mappedEvent.ignore();
if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) {
return mappedEvent.isAccepted();
}
break;
}
// Fall through
case QEvent::MouseButtonDblClick:
case QEvent::MouseButtonPress:
case QEvent::MouseButtonRelease:
case QEvent::MouseMove: {
QMouseEvent* mouseEvent = static_cast<QMouseEvent*>(event);
QPointF originalPos = mouseEvent->localPos();
QPointF transformedPos = _mouseTranslator(originalPos);
QMouseEvent mappedEvent(mouseEvent->type(),
mapWindowToUi(transformedPos, originalDestination),
mouseEvent->screenPos(), mouseEvent->button(),
mouseEvent->buttons(), mouseEvent->modifiers());
mappedEvent.ignore();
if (QCoreApplication::sendEvent(_quickWindow, &mappedEvent)) {
return mappedEvent.isAccepted();
}
break;
}
default:
break;
}
return false;
}
void OffscreenUi::lockTexture(int texture) {
_fboCache.lockTexture(texture);
}
void OffscreenUi::releaseTexture(int texture) {
_fboCache.releaseTexture(texture);
}
void OffscreenUi::pause() {
_paused = true;
}
void OffscreenUi::resume() {
_paused = false;
requestRender();
}
bool OffscreenUi::isPaused() const {
return _paused;
}
void OffscreenUi::setProxyWindow(QWindow* window) {
_renderControl->_renderWindow = window;
}
void OffscreenUi::show(const QUrl& url, const QString& name) {
QQuickItem* item = _rootItem->findChild<QQuickItem*>(name);
// First load?
if (!item) {
load(url);
return;
}
item->setEnabled(true);
}
void OffscreenUi::toggle(const QUrl& url, const QString& name) {
QQuickItem* item = _rootItem->findChild<QQuickItem*>(name);
// First load?
if (!item) {
load(url);
return;
}
item->setEnabled(!item->isEnabled());
}
void OffscreenUi::messageBox(const QString& title, const QString& text,
QMessageBox::Icon icon,
QMessageBox::StandardButtons buttons,
ButtonCallback f) {
}
void OffscreenUi::information(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
ButtonCallback callback) {
callback(QMessageBox::information(nullptr, title, text, buttons));
}
void OffscreenUi::question(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
ButtonCallback callback) {
callback(QMessageBox::question(nullptr, title, text, buttons));
}
void OffscreenUi::warning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
ButtonCallback callback) {
callback(QMessageBox::warning(nullptr, title, text, buttons));
}
void OffscreenUi::critical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons,
ButtonCallback callback) {
callback(QMessageBox::critical(nullptr, title, text, buttons));
}
OffscreenUi::ButtonCallback OffscreenUi::NO_OP_CALLBACK = [](QMessageBox::StandardButton) {};
#include "OffscreenUi.moc"

View file

@ -0,0 +1,136 @@
//
// OffscreenUi.h
// interface/src/entities
//
// Created by Bradley Austin Davis on 2015-04-04
// Copyright 2015 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
//
#pragma once
#ifndef hifi_OffscreenUi_h
#define hifi_OffscreenUi_h
#include <QQmlEngine>
#include <QQmlComponent>
#include <QQuickItem>
#include <QQuickWindow>
#include <QQuickRenderControl>
#include <QQuickImageProvider>
#include <QTimer>
#include <QMessageBox>
#include <atomic>
#include <functional>
#include <GLMHelpers.h>
#include <ThreadHelpers.h>
#include <DependencyManager.h>
#include "OffscreenGlCanvas.h"
#include "FboCache.h"
#include <QQuickItem>
class OffscreenUi : public OffscreenGlCanvas, public Dependency {
Q_OBJECT
class QMyQuickRenderControl : public QQuickRenderControl {
protected:
QWindow* renderWindow(QPoint* offset) Q_DECL_OVERRIDE{
if (nullptr == _renderWindow) {
return QQuickRenderControl::renderWindow(offset);
}
if (nullptr != offset) {
offset->rx() = offset->ry() = 0;
}
return _renderWindow;
}
private:
QWindow* _renderWindow{ nullptr };
friend class OffscreenUi;
};
public:
using MouseTranslator = std::function<QPointF(const QPointF&)>;
OffscreenUi();
virtual ~OffscreenUi();
void create(QOpenGLContext* context);
void resize(const QSize& size);
void load(const QUrl& qmlSource, std::function<void(QQmlContext*)> f = [](QQmlContext*) {});
void load(const QString& qmlSourceFile, std::function<void(QQmlContext*)> f = [](QQmlContext*) {}) {
load(QUrl(qmlSourceFile), f);
}
void show(const QUrl& url, const QString& name);
void toggle(const QUrl& url, const QString& name);
void setBaseUrl(const QUrl& baseUrl);
void addImportPath(const QString& path);
QQmlContext* qmlContext();
void pause();
void resume();
bool isPaused() const;
void setProxyWindow(QWindow* window);
QPointF mapWindowToUi(const QPointF& sourcePosition, QObject* sourceObject);
virtual bool eventFilter(QObject* originalDestination, QEvent* event);
void setMouseTranslator(MouseTranslator mouseTranslator) {
_mouseTranslator = mouseTranslator;
}
// Messagebox replacement functions
using ButtonCallback = std::function<void(QMessageBox::StandardButton)>;
static ButtonCallback NO_OP_CALLBACK;
static void messageBox(const QString& title, const QString& text,
QMessageBox::Icon icon,
QMessageBox::StandardButtons buttons,
ButtonCallback f);
static void information(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
ButtonCallback callback = NO_OP_CALLBACK);
static void question(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::StandardButtons(QMessageBox::Yes | QMessageBox::No),
ButtonCallback callback = [](QMessageBox::StandardButton) {});
static void warning(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
ButtonCallback callback = [](QMessageBox::StandardButton) {});
static void critical(const QString& title, const QString& text,
QMessageBox::StandardButtons buttons = QMessageBox::Ok,
ButtonCallback callback = [](QMessageBox::StandardButton) {});
protected:
private slots:
void updateQuick();
void finishQmlLoad();
public slots:
void requestUpdate();
void requestRender();
void lockTexture(int texture);
void releaseTexture(int texture);
signals:
void textureUpdated(GLuint texture);
private:
QMyQuickRenderControl* _renderControl{ new QMyQuickRenderControl };
QQuickWindow* _quickWindow{ nullptr };
QQmlEngine* _qmlEngine{ nullptr };
QQmlComponent* _qmlComponent{ nullptr };
QQuickItem* _rootItem{ nullptr };
QTimer _updateTimer;
FboCache _fboCache;
bool _polish{ true };
bool _paused{ true };
MouseTranslator _mouseTranslator{ [](const QPointF& p) { return p; } };
};
#endif

View file

@ -15,4 +15,54 @@
/// Renders a quad from (-1, -1, 0) to (1, 1, 0) with texture coordinates from (sMin, tMin) to (sMax, tMax).
void renderFullscreenQuad(float sMin = 0.0f, float sMax = 1.0f, float tMin = 0.0f, float tMax = 1.0f);
template <typename F, GLenum matrix>
void withMatrixPush(F f) {
glMatrixMode(matrix);
glPushMatrix();
f();
glPopMatrix();
}
template <typename F>
void withProjectionPush(F f) {
withMatrixPush<GL_PROJECTION>(f);
}
template <typename F>
void withProjectionIdentity(F f) {
withProjectionPush([&] {
glLoadIdentity();
f();
});
}
template <typename F>
void withProjectionMatrix(GLfloat* matrix, F f) {
withProjectionPush([&] {
glLoadMatrixf(matrix);
f();
});
}
template <typename F>
void withModelviewPush(F f) {
withMatrixPush<GL_MODELVIEW>(f);
}
template <typename F>
void withModelviewIdentity(F f) {
withModelviewPush([&] {
glLoadIdentity();
f();
});
}
template <typename F>
void withModelviewMatrix(GLfloat* matrix, F f) {
withModelviewPush([&] {
glLoadMatrixf(matrix);
f();
});
}
#endif // hifi_RenderUtil_h

View file

@ -203,6 +203,13 @@ glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2) {
return glm::angleAxis(angle, axis);
}
bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2) {
glm::vec3 v1 = p0 - p1, v2 = p2 - p1; // Non-collinear vectors contained in the plane
glm::vec3 n = glm::cross(v1, v2); // Plane's normal vector, pointing out of the triangle
float d = -glm::dot(n, p0); // Compute plane's equation constant
return (glm::dot(n, point) + d) >= 0;
}
glm::vec3 extractTranslation(const glm::mat4& matrix) {
return glm::vec3(matrix[3][0], matrix[3][1], matrix[3][2]);
}

View file

@ -17,6 +17,17 @@
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
// Bring the most commonly used GLM types into the default namespace
using glm::ivec3;
using glm::ivec2;
using glm::uvec2;
using glm::mat3;
using glm::mat4;
using glm::vec2;
using glm::vec3;
using glm::vec4;
using glm::quat;
#include <QtCore/QByteArray>
#include <QtGui/QMatrix4x4>
#include <QtGui/QColor>
@ -71,6 +82,8 @@ float angleBetween(const glm::vec3& v1, const glm::vec3& v2);
glm::quat rotationBetween(const glm::vec3& v1, const glm::vec3& v2);
bool isPointBehindTrianglesPlane(glm::vec3 point, glm::vec3 p0, glm::vec3 p1, glm::vec3 p2);
glm::vec3 extractTranslation(const glm::mat4& matrix);
void setTranslation(glm::mat4& matrix, const glm::vec3& translation);

View file

@ -114,7 +114,7 @@ float ShapeInfo::computeVolume() const {
}
case SHAPE_TYPE_CAPSULE_Y: {
float radius = _halfExtents.x;
volume = PI * radius * radius * (2.0f * _halfExtents.y + 4.0f * radius / 3.0f);
volume = PI * radius * radius * (2.0f * (_halfExtents.y - _halfExtents.x) + 4.0f * radius / 3.0f);
break;
}
default:
@ -124,6 +124,54 @@ float ShapeInfo::computeVolume() const {
return volume;
}
bool ShapeInfo::contains(const glm::vec3& point) const {
switch(_type) {
case SHAPE_TYPE_SPHERE:
return glm::length(point) <= _halfExtents.x;
case SHAPE_TYPE_ELLIPSOID: {
glm::vec3 scaledPoint = glm::abs(point) / _halfExtents;
return glm::length(scaledPoint) <= 1.0f;
}
case SHAPE_TYPE_CYLINDER_X:
return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z;
case SHAPE_TYPE_CYLINDER_Y:
return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x;
case SHAPE_TYPE_CYLINDER_Z:
return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y;
case SHAPE_TYPE_CAPSULE_X: {
if (glm::abs(point.x) <= _halfExtents.x) {
return glm::length(glm::vec2(point.y, point.z)) <= _halfExtents.z;
} else {
glm::vec3 absPoint = glm::abs(point) - _halfExtents.x;
return glm::length(absPoint) <= _halfExtents.z;
}
}
case SHAPE_TYPE_CAPSULE_Y: {
if (glm::abs(point.y) <= _halfExtents.y) {
return glm::length(glm::vec2(point.x, point.z)) <= _halfExtents.x;
} else {
glm::vec3 absPoint = glm::abs(point) - _halfExtents.y;
return glm::length(absPoint) <= _halfExtents.x;
}
}
case SHAPE_TYPE_CAPSULE_Z: {
if (glm::abs(point.z) <= _halfExtents.z) {
return glm::length(glm::vec2(point.x, point.y)) <= _halfExtents.y;
} else {
glm::vec3 absPoint = glm::abs(point) - _halfExtents.z;
return glm::length(absPoint) <= _halfExtents.y;
}
}
case SHAPE_TYPE_BOX:
default: {
glm::vec3 absPoint = glm::abs(point);
return absPoint.x <= _halfExtents.x
&& absPoint.y <= _halfExtents.y
&& absPoint.z <= _halfExtents.z;
}
}
}
const DoubleHashKey& ShapeInfo::getHash() const {
// NOTE: we cache the key so we only ever need to compute it once for any valid ShapeInfo instance.
if (_doubleHashKey.isNull() && _type != SHAPE_TYPE_NONE) {

View file

@ -57,6 +57,10 @@ public:
void appendToPoints (const QVector<glm::vec3>& newPoints) { _points << newPoints; }
float computeVolume() const;
/// Returns whether point is inside the shape
/// For compound shapes it will only return whether it is inside the bounding box
bool contains(const glm::vec3& point) const;
const DoubleHashKey& getHash() const;

View file

@ -0,0 +1,29 @@
//
// ThreadHelpers.h
//
// Created by Bradley Austin Davis on 2015-04-04
// Copyright 2015 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
//
#pragma once
#ifndef hifi_ThreadHelpers_h
#define hifi_ThreadHelpers_h
#include <exception>
#include <QMutex>
#include <QMutexLocker>
template <typename L, typename F>
void withLock(L lock, F function) {
throw std::exception();
}
template <typename F>
void withLock(QMutex& lock, F function) {
QMutexLocker locker(&lock);
function();
}
#endif

View file

@ -74,7 +74,7 @@ void OctreeTests::propertyFlagsTests(bool verbose) {
props.setHasProperty(PROP_POSITION);
props.setHasProperty(PROP_RADIUS);
props.setHasProperty(PROP_MODEL_URL);
props.setHasProperty(PROP_COLLISION_MODEL_URL);
props.setHasProperty(PROP_COMPOUND_SHAPE_URL);
props.setHasProperty(PROP_ROTATION);
QByteArray encoded = props.encode();

View file

@ -10,6 +10,7 @@
#include "TextRenderer.h"
#include "MatrixStack.h"
#include "OffscreenUi.h"
#include <QWindow>
#include <QFile>
@ -21,13 +22,16 @@
#include <QOpenGLBuffer>
#include <QOpenGLShaderProgram>
#include <QResizeEvent>
#include <QLoggingCategory>
#include <QOpenGLTexture>
#include <QOpenGLVertexArrayObject>
#include <QApplication>
#include <QOpenGLDebugLogger>
#include <unordered_map>
#include <memory>
#include <glm/glm.hpp>
#include <PathUtils.h>
#include <QDir>
class RateCounter {
std::vector<float> times;
@ -65,115 +69,191 @@ public:
}
};
const QString& getQmlDir() {
static QString dir;
if (dir.isEmpty()) {
QDir path(__FILE__);
path.cdUp();
dir = path.cleanPath(path.absoluteFilePath("../../../interface/resources/qml/")) + "/";
qDebug() << "Qml Path: " << dir;
}
return dir;
}
// Create a simple OpenGL window that renders text in various ways
class QTestWindow: public QWindow {
class QTestWindow : public QWindow {
Q_OBJECT
QOpenGLContext * _context;
QOpenGLContext* _context{ nullptr };
QSize _size;
TextRenderer* _textRenderer[4];
RateCounter fps;
int testQmlTexture{ 0 };
//ProgramPtr _planeProgam;
//ShapeWrapperPtr _planeShape;
protected:
void resizeEvent(QResizeEvent * ev) override {
QWindow::resizeEvent(ev);
_size = ev->size();
resizeGl();
}
void resizeGl() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _size.width(), _size.height(), 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glViewport(0, 0, _size.width(), _size.height());
void renderText();
void renderQml();
private:
void resizeWindow(const QSize& size) {
_size = size;
DependencyManager::get<OffscreenUi>()->resize(_size);
}
public:
QTestWindow();
virtual ~QTestWindow() {
QTestWindow() {
DependencyManager::set<OffscreenUi>();
setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format;
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
format.setDepthBufferSize(16);
format.setStencilBufferSize(8);
format.setVersion(4, 5);
format.setProfile(QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
format.setOption(QSurfaceFormat::DebugContext);
setFormat(format);
_context = new QOpenGLContext;
_context->setFormat(format);
_context->create();
show();
makeCurrent();
{
QOpenGLDebugLogger* logger = new QOpenGLDebugLogger(this);
logger->initialize(); // initializes in the current context, i.e. ctx
logger->enableMessages();
connect(logger, &QOpenGLDebugLogger::messageLogged, this, [&](const QOpenGLDebugMessage & debugMessage) {
qDebug() << debugMessage;
});
// logger->startLogging(QOpenGLDebugLogger::SynchronousLogging);
}
qDebug() << (const char*)glGetString(GL_VERSION);
#ifdef WIN32
glewExperimental = true;
GLenum err = glewInit();
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
const GLubyte * errStr = glewGetErrorString(err);
qDebug("Error: %s\n", errStr);
}
qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
if (wglewGetExtension("WGL_EXT_swap_control")) {
int swapInterval = wglGetSwapIntervalEXT();
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
}
glGetError();
#endif
_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false);
_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false,
TextRenderer::SHADOW_EFFECT);
_textRenderer[2] = TextRenderer::getInstance(MONO_FONT_FAMILY, 48, -1,
false, TextRenderer::OUTLINE_EFFECT);
_textRenderer[3] = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, 24);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.2f, 0.2f, 0.2f, 1);
glDisable(GL_DEPTH_TEST);
auto offscreenUi = DependencyManager::get<OffscreenUi>();
offscreenUi->create(_context);
// FIXME, need to switch to a QWindow for mouse and keyboard input to work
offscreenUi->setProxyWindow(this);
// "#0e7077"
setFramePosition(QPoint(-1000, 0));
resize(QSize(800, 600));
offscreenUi->setBaseUrl(QUrl::fromLocalFile(getQmlDir()));
offscreenUi->load(QUrl("TestRoot.qml"));
offscreenUi->addImportPath(getQmlDir());
offscreenUi->addImportPath(".");
connect(offscreenUi.data(), &OffscreenUi::textureUpdated, this, [this, offscreenUi](int textureId) {
offscreenUi->lockTexture(textureId);
assert(!glGetError());
GLuint oldTexture = testQmlTexture;
testQmlTexture = textureId;
if (oldTexture) {
offscreenUi->releaseTexture(oldTexture);
}
});
installEventFilter(offscreenUi.data());
offscreenUi->resume();
}
virtual ~QTestWindow() {
}
void draw();
void makeCurrent() {
_context->makeCurrent(this);
}
void draw();
protected:
void resizeEvent(QResizeEvent* ev) override {
resizeWindow(ev->size());
}
void keyPressEvent(QKeyEvent* event) {
switch (event->key()) {
case Qt::Key_L:
if (event->modifiers() & Qt::CTRL) {
DependencyManager::get<OffscreenUi>()->toggle(QString("TestDialog.qml"), "TestDialog");
}
break;
}
QWindow::keyPressEvent(event);
}
void moveEvent(QMoveEvent* event) {
static qreal oldPixelRatio = 0.0;
if (devicePixelRatio() != oldPixelRatio) {
oldPixelRatio = devicePixelRatio();
resizeWindow(size());
}
QWindow::moveEvent(event);
}
};
#ifndef SERIF_FONT_FAMILY
#define SERIF_FONT_FAMILY "Times New Roman"
#endif
QTestWindow::QTestWindow() {
setSurfaceType(QSurface::OpenGLSurface);
QSurfaceFormat format;
// Qt Quick may need a depth and stencil buffer. Always make sure these are available.
format.setDepthBufferSize(16);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(
QSurfaceFormat::OpenGLContextProfile::CompatibilityProfile);
setFormat(format);
_context = new QOpenGLContext;
_context->setFormat(format);
_context->create();
show();
makeCurrent();
qDebug() << (const char*) glGetString(GL_VERSION);
#ifdef WIN32
glewExperimental = true;
GLenum err = glewInit();
if (GLEW_OK != err) {
/* Problem: glewInit failed, something is seriously wrong. */
const GLubyte * errStr = glewGetErrorString(err);
qDebug("Error: %s\n", errStr);
}
qDebug("Status: Using GLEW %s\n", glewGetString(GLEW_VERSION));
if (wglewGetExtension("WGL_EXT_swap_control")) {
int swapInterval = wglGetSwapIntervalEXT();
qDebug("V-Sync is %s\n", (swapInterval > 0 ? "ON" : "OFF"));
}
glGetError();
#endif
setFramePosition(QPoint(100, -900));
resize(QSize(800, 600));
_size = QSize(800, 600);
_textRenderer[0] = TextRenderer::getInstance(SANS_FONT_FAMILY, 12, false);
_textRenderer[1] = TextRenderer::getInstance(SERIF_FONT_FAMILY, 12, false,
TextRenderer::SHADOW_EFFECT);
_textRenderer[2] = TextRenderer::getInstance(MONO_FONT_FAMILY, 48, -1,
false, TextRenderer::OUTLINE_EFFECT);
_textRenderer[3] = TextRenderer::getInstance(INCONSOLATA_FONT_FAMILY, 24);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glClearColor(0.2f, 0.2f, 0.2f, 1);
glDisable(GL_DEPTH_TEST);
resizeGl();
}
static const wchar_t * EXAMPLE_TEXT = L"Hello";
//static const wchar_t * EXAMPLE_TEXT = L"\xC1y Hello 1.0\ny\xC1 line 2\n\xC1y";
static const wchar_t* EXAMPLE_TEXT = L"Hello";
//static const wchar_t* EXAMPLE_TEXT = L"\xC1y Hello 1.0\ny\xC1 line 2\n\xC1y";
static const glm::uvec2 QUAD_OFFSET(10, 10);
static const glm::vec3 COLORS[4] = { { 1.0, 1.0, 1.0 }, { 0.5, 1.0, 0.5 }, {
1.0, 0.5, 0.5 }, { 0.5, 0.5, 1.0 } };
void QTestWindow::draw() {
makeCurrent();
glClear(GL_COLOR_BUFFER_BIT);
void QTestWindow::renderText() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0, _size.width(), _size.height(), 0, 1, -1);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
const glm::uvec2 size = glm::uvec2(_size.width() / 2, _size.height() / 2);
const glm::uvec2 offsets[4] = { { QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x
+ QUAD_OFFSET.x, QUAD_OFFSET.y }, { size.x + QUAD_OFFSET.x, size.y
+ QUAD_OFFSET.y }, { QUAD_OFFSET.x, size.y + QUAD_OFFSET.y }, };
const glm::uvec2 offsets[4] = {
{ QUAD_OFFSET.x, QUAD_OFFSET.y },
{ size.x + QUAD_OFFSET.x, QUAD_OFFSET.y },
{ size.x + QUAD_OFFSET.x, size.y + QUAD_OFFSET.y },
{ QUAD_OFFSET.x, size.y + QUAD_OFFSET.y },
};
QString str = QString::fromWCharArray(EXAMPLE_TEXT);
for (int i = 0; i < 4; ++i) {
@ -200,8 +280,45 @@ void QTestWindow::draw() {
glm::vec4(COLORS[i], 1.0f));
}
}
}
void QTestWindow::renderQml() {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if (testQmlTexture > 0) {
glEnable(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, testQmlTexture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
glBegin(GL_QUADS);
{
glTexCoord2f(0, 0);
glVertex2f(-1, -1);
glTexCoord2f(0, 1);
glVertex2f(-1, 1);
glTexCoord2f(1, 1);
glVertex2f(1, 1);
glTexCoord2f(1, 0);
glVertex2f(1, -1);
}
glEnd();
}
void QTestWindow::draw() {
makeCurrent();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(0, 0, _size.width() * devicePixelRatio(), _size.height() * devicePixelRatio());
//renderText();
renderQml();
_context->swapBuffers(this);
glFinish();
fps.increment();
if (fps.elapsed() >= 2.0f) {
qDebug() << "FPS: " << fps.rate();
@ -211,7 +328,9 @@ void QTestWindow::draw() {
int main(int argc, char** argv) {
QApplication app(argc, argv);
//QLoggingCategory::setFilterRules("qt.quick.mouse.debug = true");
QTestWindow window;
QTimer timer;
timer.setInterval(1);
app.connect(&timer, &QTimer::timeout, &app, [&] {