mirror of
https://github.com/overte-org/overte.git
synced 2025-08-08 00:30:52 +02:00
merge upstream/master into andrew/isentropic
Conflicts: libraries/physics/src/PhysicsEngine.cpp
This commit is contained in:
commit
e49c76656f
18 changed files with 789 additions and 540 deletions
197
examples/billiards.js
Normal file
197
examples/billiards.js
Normal file
|
@ -0,0 +1,197 @@
|
||||||
|
// Pool Table
|
||||||
|
var tableParts = [];
|
||||||
|
var balls = [];
|
||||||
|
|
||||||
|
var LENGTH = 2.84;
|
||||||
|
var WIDTH = 1.42;
|
||||||
|
var HEIGHT = 0.80;
|
||||||
|
var SCALE = 2.0;
|
||||||
|
var BALL_SIZE = 0.05715;
|
||||||
|
var BUMPER_WIDTH = 0.15;
|
||||||
|
var BUMPER_HEIGHT = BALL_SIZE * 2.0;
|
||||||
|
var HOLE_SIZE = BALL_SIZE;
|
||||||
|
var DROP_HEIGHT = BALL_SIZE * 3.0;
|
||||||
|
var GRAVITY = -9.8;
|
||||||
|
var BALL_GAP = 0.001;
|
||||||
|
|
||||||
|
var startStroke = 0;
|
||||||
|
|
||||||
|
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||||
|
var screenSize = Controller.getViewportDimensions();
|
||||||
|
var reticle = Overlays.addOverlay("image", {
|
||||||
|
x: screenSize.x / 2 - 16,
|
||||||
|
y: screenSize.y / 2 - 16,
|
||||||
|
width: 32,
|
||||||
|
height: 32,
|
||||||
|
imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png",
|
||||||
|
color: { red: 255, green: 255, blue: 255},
|
||||||
|
alpha: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
function makeTable(pos) {
|
||||||
|
// Top
|
||||||
|
tableParts.push(Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: pos,
|
||||||
|
dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE },
|
||||||
|
color: { red: 0, green: 255, blue: 0 } }));
|
||||||
|
// Long Bumpers
|
||||||
|
tableParts.push(Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: { x: pos.x - LENGTH / 2.0,
|
||||||
|
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||||
|
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||||
|
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||||
|
color: { red: 237, green: 201, blue: 175 } }));
|
||||||
|
tableParts.push(Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: { x: pos.x + LENGTH / 2.0,
|
||||||
|
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||||
|
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||||
|
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||||
|
color: { red: 237, green: 201, blue: 175 } }));
|
||||||
|
|
||||||
|
tableParts.push(Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: { x: pos.x - LENGTH / 2.0,
|
||||||
|
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||||
|
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||||
|
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||||
|
color: { red: 237, green: 201, blue: 175 } }));
|
||||||
|
tableParts.push(Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: { x: pos.x + LENGTH / 2.0,
|
||||||
|
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||||
|
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||||
|
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||||
|
color: { red: 237, green: 201, blue: 175 } }));
|
||||||
|
// End bumpers
|
||||||
|
tableParts.push(Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||||
|
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||||
|
z: pos.z },
|
||||||
|
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||||
|
color: { red: 237, green: 201, blue: 175 } }));
|
||||||
|
|
||||||
|
tableParts.push(Entities.addEntity(
|
||||||
|
{ type: "Box",
|
||||||
|
position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||||
|
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||||
|
z: pos.z },
|
||||||
|
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||||
|
color: { red: 237, green: 201, blue: 175 } }));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeBalls(pos) {
|
||||||
|
var colors = [{ red: 255, green: 255, blue: 0}, // Yellow
|
||||||
|
{ red: 0, green: 0, blue: 255}, // Blue
|
||||||
|
{ red: 255, green: 0, blue: 0}, // Red
|
||||||
|
{ red: 128, green: 0, blue: 128}, // Purple
|
||||||
|
{ red: 255, green: 165, blue: 0}, // Orange
|
||||||
|
{ red: 0, green: 255, blue: 0}, // Green
|
||||||
|
{ red: 128, green: 0, blue: 0}, // Maroon
|
||||||
|
{ red: 0, green: 0, blue: 0}, // Black
|
||||||
|
{ red: 255, green: 255, blue: 224}, // Light Yellow
|
||||||
|
{ red: 173, green: 216, blue: 230}, // Light Blue
|
||||||
|
{ red: 205, green: 92, blue: 92}, // Indian Red
|
||||||
|
{ red: 218, green: 112, blue: 214}, // Orchid
|
||||||
|
{ red: 218, green: 165, blue: 32}, // GoldenRod
|
||||||
|
{ red: 255, green: 99, blue: 71}, // Tomato
|
||||||
|
{ red: 128, green: 128, blue: 128}]; // Gray
|
||||||
|
|
||||||
|
// Object balls
|
||||||
|
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z };
|
||||||
|
for (var row = 1; row <= 5; row++) {
|
||||||
|
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
|
||||||
|
for (var spot = 0; spot < row; spot++) {
|
||||||
|
balls.push(Entities.addEntity(
|
||||||
|
{ type: "Sphere",
|
||||||
|
position: ballPosition,
|
||||||
|
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||||
|
color: colors[balls.length],
|
||||||
|
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
damping: 0.40,
|
||||||
|
collisionsWillMove: true }));
|
||||||
|
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
|
||||||
|
}
|
||||||
|
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
|
||||||
|
}
|
||||||
|
// Cue Ball
|
||||||
|
ballPosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z };
|
||||||
|
balls.push(Entities.addEntity(
|
||||||
|
{ type: "Sphere",
|
||||||
|
position: ballPosition,
|
||||||
|
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||||
|
color: { red: 255, green: 255, blue: 255 },
|
||||||
|
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||||
|
ignoreCollisions: false,
|
||||||
|
damping: 0.40,
|
||||||
|
collisionsWillMove: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function shootCue(velocity) {
|
||||||
|
var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE;
|
||||||
|
var camera = Camera.getPosition();
|
||||||
|
var forwardVector = Quat.getFront(Camera.getOrientation());
|
||||||
|
var cuePosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
|
||||||
|
var velocity = Vec3.multiply(forwardVector, velocity);
|
||||||
|
var BULLET_LIFETIME = 3.0;
|
||||||
|
var BULLET_GRAVITY = 0.0;
|
||||||
|
bulletID = Entities.addEntity(
|
||||||
|
{ type: "Sphere",
|
||||||
|
position: cuePosition,
|
||||||
|
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||||
|
color: { red: 255, green: 255, blue: 255 },
|
||||||
|
velocity: velocity,
|
||||||
|
lifetime: BULLET_LIFETIME,
|
||||||
|
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 },
|
||||||
|
damping: 0.10,
|
||||||
|
density: 1000,
|
||||||
|
ignoreCollisions: false,
|
||||||
|
collisionsWillMove: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyReleaseEvent(event) {
|
||||||
|
if ((startStroke > 0) && event.text == "SPACE") {
|
||||||
|
var endTime = new Date().getTime();
|
||||||
|
var delta = endTime - startStroke;
|
||||||
|
shootCue(delta / 100.0);
|
||||||
|
startStroke = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function keyPressEvent(event) {
|
||||||
|
// Fire a cue ball
|
||||||
|
if ((startStroke == 0) && (event.text == "SPACE")) {
|
||||||
|
startStroke = new Date().getTime();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
for (var i = 0; i < tableParts.length; i++) {
|
||||||
|
if (!tableParts[i].isKnownID) {
|
||||||
|
tableParts[i] = Entities.identifyEntity(tableParts[i]);
|
||||||
|
}
|
||||||
|
Entities.deleteEntity(tableParts[i]);
|
||||||
|
}
|
||||||
|
for (var i = 0; i < balls.length; i++) {
|
||||||
|
if (!balls[i].isKnownID) {
|
||||||
|
balls[i] = Entities.identifyEntity(balls[i]);
|
||||||
|
}
|
||||||
|
Entities.deleteEntity(balls[i]);
|
||||||
|
}
|
||||||
|
Overlays.deleteOverlay(reticle);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
|
||||||
|
|
||||||
|
makeTable(tableCenter);
|
||||||
|
makeBalls(tableCenter);
|
||||||
|
|
||||||
|
Script.scriptEnding.connect(cleanup);
|
||||||
|
Controller.keyPressEvent.connect(keyPressEvent);
|
||||||
|
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
|
@ -44,10 +44,16 @@ var entityListTool = EntityListTool();
|
||||||
|
|
||||||
var hasShownPropertiesTool = false;
|
var hasShownPropertiesTool = false;
|
||||||
|
|
||||||
|
var entityListVisible = false;
|
||||||
|
|
||||||
selectionManager.addEventListener(function() {
|
selectionManager.addEventListener(function() {
|
||||||
selectionDisplay.updateHandles();
|
selectionDisplay.updateHandles();
|
||||||
if (selectionManager.hasSelection() && !hasShownPropertiesTool) {
|
if (selectionManager.hasSelection() && !hasShownPropertiesTool) {
|
||||||
|
// Open properties and model list, but force selection of model list tab
|
||||||
|
propertiesTool.setVisible(false);
|
||||||
|
entityListTool.setVisible(false);
|
||||||
propertiesTool.setVisible(true);
|
propertiesTool.setVisible(true);
|
||||||
|
entityListTool.setVisible(true);
|
||||||
hasShownPropertiesTool = true;
|
hasShownPropertiesTool = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -690,8 +696,8 @@ function setupModelMenus() {
|
||||||
print("delete exists... don't add ours");
|
print("delete exists... don't add ours");
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Entity List...", shortcutKey: "CTRL+META+L", afterItem: "Models" });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Model List..." });
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Entity List..." });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
|
||||||
afterItem: "Paste Models", isCheckable: true, isChecked: true });
|
afterItem: "Paste Models", isCheckable: true, isChecked: true });
|
||||||
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
|
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
|
||||||
|
@ -703,6 +709,7 @@ function setupModelMenus() {
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
|
||||||
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
|
||||||
|
|
||||||
|
|
||||||
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
|
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
|
||||||
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
|
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
|
||||||
|
|
||||||
|
@ -718,7 +725,7 @@ function cleanupModelMenus() {
|
||||||
Menu.removeMenuItem("Edit", "Delete");
|
Menu.removeMenuItem("Edit", "Delete");
|
||||||
}
|
}
|
||||||
|
|
||||||
Menu.removeMenuItem("Edit", "Model List...");
|
Menu.removeMenuItem("Edit", "Entity List...");
|
||||||
Menu.removeMenuItem("Edit", "Paste Models");
|
Menu.removeMenuItem("Edit", "Paste Models");
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
|
||||||
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
|
||||||
|
@ -755,14 +762,7 @@ Script.update.connect(function (deltaTime) {
|
||||||
selectionDisplay.checkMove();
|
selectionDisplay.checkMove();
|
||||||
});
|
});
|
||||||
|
|
||||||
function handeMenuEvent(menuItem) {
|
function deleteSelectedEntities() {
|
||||||
if (menuItem == "Allow Selecting of Small Models") {
|
|
||||||
allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
|
|
||||||
} else if (menuItem == "Allow Selecting of Large Models") {
|
|
||||||
allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models");
|
|
||||||
} else if (menuItem == "Allow Selecting of Lights") {
|
|
||||||
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
|
|
||||||
} else if (menuItem == "Delete") {
|
|
||||||
if (SelectionManager.hasSelection()) {
|
if (SelectionManager.hasSelection()) {
|
||||||
print(" Delete Entities");
|
print(" Delete Entities");
|
||||||
SelectionManager.saveProperties();
|
SelectionManager.saveProperties();
|
||||||
|
@ -782,38 +782,17 @@ function handeMenuEvent(menuItem) {
|
||||||
} else {
|
} else {
|
||||||
print(" Delete Entity.... not holding...");
|
print(" Delete Entity.... not holding...");
|
||||||
}
|
}
|
||||||
} else if (menuItem == "Model List...") {
|
|
||||||
var models = new Array();
|
|
||||||
models = Entities.findEntities(MyAvatar.position, Number.MAX_VALUE);
|
|
||||||
for (var i = 0; i < models.length; i++) {
|
|
||||||
models[i].properties = Entities.getEntityProperties(models[i]);
|
|
||||||
models[i].toString = function() {
|
|
||||||
var modelname;
|
|
||||||
if (this.properties.type == "Model") {
|
|
||||||
modelname = decodeURIComponent(
|
|
||||||
this.properties.modelURL.indexOf("/") != -1 ?
|
|
||||||
this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) :
|
|
||||||
this.properties.modelURL);
|
|
||||||
} else {
|
|
||||||
modelname = this.properties.id;
|
|
||||||
}
|
|
||||||
return "[" + this.properties.type + "] " + modelname;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var form = [{label: "Model: ", options: models}];
|
|
||||||
form.push({label: "Action: ", options: ["Properties", "Delete", "Teleport"]});
|
|
||||||
form.push({ button: "Cancel" });
|
|
||||||
if (Window.form("Model List", form)) {
|
|
||||||
var selectedModel = form[0].value;
|
|
||||||
if (form[1].value == "Properties") {
|
|
||||||
editModelID = selectedModel;
|
|
||||||
entityPropertyDialogBox.openDialog(editModelID);
|
|
||||||
} else if (form[1].value == "Delete") {
|
|
||||||
Entities.deleteEntity(selectedModel);
|
|
||||||
} else if (form[1].value == "Teleport") {
|
|
||||||
MyAvatar.position = selectedModel.properties.position;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handeMenuEvent(menuItem) {
|
||||||
|
if (menuItem == "Allow Selecting of Small Models") {
|
||||||
|
allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
|
||||||
|
} else if (menuItem == "Allow Selecting of Large Models") {
|
||||||
|
allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models");
|
||||||
|
} else if (menuItem == "Allow Selecting of Lights") {
|
||||||
|
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
|
||||||
|
} else if (menuItem == "Delete") {
|
||||||
|
deleteSelectedEntities();
|
||||||
} else if (menuItem == "Paste Models") {
|
} else if (menuItem == "Paste Models") {
|
||||||
modelImporter.paste();
|
modelImporter.paste();
|
||||||
} else if (menuItem == "Export Models") {
|
} else if (menuItem == "Export Models") {
|
||||||
|
@ -826,6 +805,10 @@ function handeMenuEvent(menuItem) {
|
||||||
}
|
}
|
||||||
} else if (menuItem == "Import Models") {
|
} else if (menuItem == "Import Models") {
|
||||||
modelImporter.doImport();
|
modelImporter.doImport();
|
||||||
|
} else if (menuItem == "Entity List...") {
|
||||||
|
if (isActive) {
|
||||||
|
entityListTool.toggleVisible();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tooltip.show(false);
|
tooltip.show(false);
|
||||||
}
|
}
|
||||||
|
@ -842,7 +825,7 @@ Controller.keyPressEvent.connect(function(event) {
|
||||||
Controller.keyReleaseEvent.connect(function (event) {
|
Controller.keyReleaseEvent.connect(function (event) {
|
||||||
// since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items
|
// since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items
|
||||||
if (event.text == "BACKSPACE" || event.text == "DELETE") {
|
if (event.text == "BACKSPACE" || event.text == "DELETE") {
|
||||||
handeMenuEvent("Delete");
|
deleteSelectedEntities();
|
||||||
} else if (event.text == "TAB") {
|
} else if (event.text == "TAB") {
|
||||||
selectionDisplay.toggleSpaceMode();
|
selectionDisplay.toggleSpaceMode();
|
||||||
} else if (event.text == "f") {
|
} else if (event.text == "f") {
|
||||||
|
|
|
@ -1,14 +1,32 @@
|
||||||
|
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css">
|
<link rel="stylesheet" type="text/css" href="style.css">
|
||||||
|
<script src="list.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
var entities = {};
|
var entities = {};
|
||||||
var selectedEntities = [];
|
var selectedEntities = [];
|
||||||
|
var currentSortColumn = 'type';
|
||||||
|
var currentSortOrder = 'asc';
|
||||||
|
var entityList = null;
|
||||||
|
var refreshEntityListTimer = null;
|
||||||
|
var ASC_STRING = ' ▾';
|
||||||
|
var DESC_STRING = ' ▴';
|
||||||
|
|
||||||
function loaded() {
|
function loaded() {
|
||||||
|
entityList = new List('entity-list', { valueNames: ['type', 'url']});
|
||||||
|
entityList.clear();
|
||||||
elEntityTable = document.getElementById("entity-table");
|
elEntityTable = document.getElementById("entity-table");
|
||||||
|
elEntityTableBody = document.getElementById("entity-table-body");
|
||||||
elRefresh = document.getElementById("refresh");
|
elRefresh = document.getElementById("refresh");
|
||||||
|
elDelete = document.getElementById("delete");
|
||||||
|
elTeleport = document.getElementById("teleport");
|
||||||
|
|
||||||
|
document.getElementById("entity-type").onclick = function() {
|
||||||
|
setSortColumn('type');
|
||||||
|
};
|
||||||
|
document.getElementById("entity-url").onclick = function() {
|
||||||
|
setSortColumn('url');
|
||||||
|
};
|
||||||
|
|
||||||
function onRowClicked(e) {
|
function onRowClicked(e) {
|
||||||
var id = this.dataset.entityId;
|
var id = this.dataset.entityId;
|
||||||
|
@ -20,7 +38,8 @@
|
||||||
|
|
||||||
selectedEntities = selection;
|
selectedEntities = selection;
|
||||||
|
|
||||||
entities[id].el.className = 'selected';
|
this.className = 'selected';
|
||||||
|
|
||||||
EventBridge.emitWebEvent(JSON.stringify({
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
type: "selectionUpdate",
|
type: "selectionUpdate",
|
||||||
focus: false,
|
focus: false,
|
||||||
|
@ -29,8 +48,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
function onRowDoubleClicked() {
|
function onRowDoubleClicked() {
|
||||||
var id = this.dataset.entityId;
|
|
||||||
|
|
||||||
EventBridge.emitWebEvent(JSON.stringify({
|
EventBridge.emitWebEvent(JSON.stringify({
|
||||||
type: "selectionUpdate",
|
type: "selectionUpdate",
|
||||||
focus: true,
|
focus: true,
|
||||||
|
@ -40,41 +57,91 @@
|
||||||
|
|
||||||
function addEntity(id, type, url) {
|
function addEntity(id, type, url) {
|
||||||
if (entities[id] === undefined) {
|
if (entities[id] === undefined) {
|
||||||
var el = document.createElement('tr');
|
var urlParts = url.split('/');
|
||||||
el.setAttribute('id', 'entity_' + id);
|
var filename = urlParts[urlParts.length - 1];
|
||||||
el.innerHTML += "<td>" + type + "</td>";
|
|
||||||
el.innerHTML += "<td>" + url + "</td>";
|
|
||||||
el.dataset.entityId = id;
|
|
||||||
el.onclick = onRowClicked;
|
|
||||||
el.ondblclick = onRowDoubleClicked;
|
|
||||||
elEntityTable.appendChild(el);
|
|
||||||
|
|
||||||
// Add element to local dict
|
entityList.add([{ id: id, type: type, url: filename }], function(items) {
|
||||||
|
var el = items[0].elm;
|
||||||
|
var id = items[0]._values.id;
|
||||||
entities[id] = {
|
entities[id] = {
|
||||||
id: id,
|
id: id,
|
||||||
name: id,
|
name: id,
|
||||||
el: el,
|
el: el,
|
||||||
};
|
};
|
||||||
}
|
el.setAttribute('id', 'entity_' + id);
|
||||||
}
|
el.setAttribute('title', url);
|
||||||
|
el.dataset.entityId = id;
|
||||||
|
el.onclick = onRowClicked;
|
||||||
|
el.ondblclick = onRowDoubleClicked;
|
||||||
|
el.innerHTML
|
||||||
|
});
|
||||||
|
|
||||||
function removeEntity(id) {
|
if (refreshEntityListTimer) {
|
||||||
if (entities[id] !== undefined) {
|
clearTimeout(refreshEntityListTimer);
|
||||||
elEntityTable.removeChild(entities[id].el);
|
}
|
||||||
delete entities[id];
|
refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearEntities() {
|
function clearEntities() {
|
||||||
for (id in entities) {
|
|
||||||
elEntityTable.removeChild(entities[id].el);
|
|
||||||
}
|
|
||||||
entities = {};
|
entities = {};
|
||||||
|
entityList.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
var elSortOrder = {
|
||||||
|
type: document.querySelector('#entity-type .sort-order'),
|
||||||
|
url: document.querySelector('#entity-url .sort-order'),
|
||||||
|
}
|
||||||
|
function setSortColumn(column) {
|
||||||
|
if (currentSortColumn == column) {
|
||||||
|
currentSortOrder = currentSortOrder == "asc" ? "desc" : "asc";
|
||||||
|
} else {
|
||||||
|
elSortOrder[currentSortColumn].style.display = 'none';
|
||||||
|
elSortOrder[column].style.display = 'inline';
|
||||||
|
currentSortColumn = column;
|
||||||
|
currentSortOrder = "asc";
|
||||||
|
}
|
||||||
|
elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASC_STRING : DESC_STRING;
|
||||||
|
entityList.sort(currentSortColumn, { order: currentSortOrder });
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshEntities() {
|
||||||
|
clearEntities();
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshEntityListObject() {
|
||||||
|
refreshEntityListTimer = null;
|
||||||
|
entityList.sort(currentSortColumn, { order: currentSortOrder });
|
||||||
|
entityList.search(document.getElementById("filter").value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateSelectedEntities(selectedEntities) {
|
||||||
|
var notFound = false;
|
||||||
|
for (var id in entities) {
|
||||||
|
entities[id].el.className = '';
|
||||||
|
}
|
||||||
|
for (var i = 0; i < selectedEntities.length; i++) {
|
||||||
|
var id = selectedEntities[i];
|
||||||
|
if (id in entities) {
|
||||||
|
var entity = entities[id];
|
||||||
|
entity.el.className = 'selected';
|
||||||
|
} else {
|
||||||
|
notFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return notFound;
|
||||||
}
|
}
|
||||||
|
|
||||||
elRefresh.onclick = function() {
|
elRefresh.onclick = function() {
|
||||||
clearEntities();
|
refreshEntities();
|
||||||
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
|
}
|
||||||
|
elTeleport.onclick = function() {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
|
||||||
|
}
|
||||||
|
elDelete.onclick = function() {
|
||||||
|
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
|
||||||
|
refreshEntities();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (window.EventBridge !== undefined) {
|
if (window.EventBridge !== undefined) {
|
||||||
|
@ -82,16 +149,9 @@
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
|
|
||||||
if (data.type == "selectionUpdate") {
|
if (data.type == "selectionUpdate") {
|
||||||
selectedEntities = data.selectedIDs;
|
var notFound = updateSelectedEntities(data.selectedIDs);
|
||||||
for (var id in entities) {
|
if (notFound) {
|
||||||
entities[id].el.className = '';
|
refreshEntities();
|
||||||
}
|
|
||||||
for (var i = 0; i < data.selectedIDs.length; i++) {
|
|
||||||
var id = data.selectedIDs[i];
|
|
||||||
if (id in entities) {
|
|
||||||
var entity = entities[id];
|
|
||||||
entity.el.className = 'selected';
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (data.type == "update") {
|
} else if (data.type == "update") {
|
||||||
var newEntities = data.entities;
|
var newEntities = data.entities;
|
||||||
|
@ -99,27 +159,39 @@
|
||||||
var id = newEntities[i].id;
|
var id = newEntities[i].id;
|
||||||
addEntity(id, newEntities[i].type, newEntities[i].url);
|
addEntity(id, newEntities[i].type, newEntities[i].url);
|
||||||
}
|
}
|
||||||
|
updateSelectedEntities(data.selectedIDs);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
setTimeout(refreshEntities, 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
</head>
|
</head>
|
||||||
<body onload='loaded();'>
|
<body onload='loaded();'>
|
||||||
<div>
|
<div>
|
||||||
<button id="refresh">Refresh</button>
|
<input type="button" id="refresh" value="Refresh"></button>
|
||||||
|
<input type="button" id="teleport" value="Teleport"></button>
|
||||||
|
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="entity-list">
|
||||||
|
<input type="text" class="search" id="filter" placeholder="Filter" />
|
||||||
<table id="entity-table">
|
<table id="entity-table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th id="entity-type">Type</th>
|
<th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline"> ▾</span></th>
|
||||||
<th id="entity-url">URL</th>
|
<th id="entity-url" data-sort="url">URL <span class="sort-order" style="display: none"> ▾</span></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="entity-table-body">
|
<tbody class="list" id="entity-table-body">
|
||||||
|
<tr>
|
||||||
|
<td class="id" style="display: none">Type</td>
|
||||||
|
<td class="type">Type</td>
|
||||||
|
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
|
||||||
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
1
examples/html/list.min.js
vendored
Normal file
1
examples/html/list.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -5,9 +5,10 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
background-color: #efefef;
|
background-color: rgb(76, 76, 76);
|
||||||
|
color: rgb(204, 204, 204);
|
||||||
font-family: Arial;
|
font-family: Arial;
|
||||||
font-size: 11.5px;
|
font-size: 11px;
|
||||||
|
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
|
@ -17,12 +18,6 @@ body {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.properties {
|
|
||||||
background-color: rgb(76, 76, 76);
|
|
||||||
color: rgb(204, 204, 204);
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.selectable {
|
.selectable {
|
||||||
-webkit-touch-callout: text;
|
-webkit-touch-callout: text;
|
||||||
-webkit-user-select: text;
|
-webkit-user-select: text;
|
||||||
|
@ -117,12 +112,13 @@ input.coord {
|
||||||
table#entity-table {
|
table#entity-table {
|
||||||
border-collapse: collapse;
|
border-collapse: collapse;
|
||||||
font-family: Sans-Serif;
|
font-family: Sans-Serif;
|
||||||
/* font-size: 12px; */
|
font-size: 10px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
#entity-table tr {
|
#entity-table tr {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
border-bottom: 1px solid rgb(63, 63, 63)
|
||||||
}
|
}
|
||||||
|
|
||||||
#entity-table tr.selected {
|
#entity-table tr.selected {
|
||||||
|
@ -139,17 +135,22 @@ table#entity-table {
|
||||||
}
|
}
|
||||||
|
|
||||||
#entity-table td {
|
#entity-table td {
|
||||||
|
font-size: 11px;
|
||||||
border: 0px black solid;
|
border: 0px black solid;
|
||||||
word-wrap: nowrap;
|
word-wrap: nowrap;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
#entity-table td.url {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
th#entity-type {
|
th#entity-type {
|
||||||
width: 60px;
|
width: 60px;
|
||||||
}
|
}
|
||||||
|
|
||||||
th#entity-url {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
div.input-area {
|
div.input-area {
|
||||||
|
@ -226,3 +227,20 @@ table#properties-list {
|
||||||
col#col-label {
|
col#col-label {
|
||||||
width: 130px;
|
width: 130px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.outer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
div.inner {
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
td {
|
||||||
|
|
||||||
|
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
|
@ -13,6 +13,10 @@ EntityListTool = function(opts) {
|
||||||
webView.setVisible(visible);
|
webView.setVisible(visible);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
that.toggleVisible = function() {
|
||||||
|
that.setVisible(!visible);
|
||||||
|
}
|
||||||
|
|
||||||
selectionManager.addEventListener(function() {
|
selectionManager.addEventListener(function() {
|
||||||
var selectedIDs = [];
|
var selectedIDs = [];
|
||||||
|
|
||||||
|
@ -24,10 +28,38 @@ EntityListTool = function(opts) {
|
||||||
type: 'selectionUpdate',
|
type: 'selectionUpdate',
|
||||||
selectedIDs: selectedIDs,
|
selectedIDs: selectedIDs,
|
||||||
};
|
};
|
||||||
|
print("Sending: " + JSON.stringify(data));
|
||||||
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function sendUpdate() {
|
||||||
|
var entities = [];
|
||||||
|
var ids = Entities.findEntities(MyAvatar.position, 100);
|
||||||
|
for (var i = 0; i < ids.length; i++) {
|
||||||
|
var id = ids[i];
|
||||||
|
var properties = Entities.getEntityProperties(id);
|
||||||
|
entities.push({
|
||||||
|
id: id.id,
|
||||||
|
type: properties.type,
|
||||||
|
url: properties.type == "Model" ? properties.modelURL : "",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedIDs = [];
|
||||||
|
for (var i = 0; i < selectionManager.selections.length; i++) {
|
||||||
|
selectedIDs.push(selectionManager.selections[i].id);
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = {
|
||||||
|
type: "update",
|
||||||
|
entities: entities,
|
||||||
|
selectedIDs: selectedIDs,
|
||||||
|
};
|
||||||
|
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
||||||
|
}
|
||||||
|
|
||||||
webView.eventBridge.webEventReceived.connect(function(data) {
|
webView.eventBridge.webEventReceived.connect(function(data) {
|
||||||
|
print("Got: " + data);
|
||||||
data = JSON.parse(data);
|
data = JSON.parse(data);
|
||||||
if (data.type == "selectionUpdate") {
|
if (data.type == "selectionUpdate") {
|
||||||
var ids = data.entityIds;
|
var ids = data.entityIds;
|
||||||
|
@ -46,22 +78,13 @@ EntityListTool = function(opts) {
|
||||||
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
|
||||||
}
|
}
|
||||||
} else if (data.type == "refresh") {
|
} else if (data.type == "refresh") {
|
||||||
var entities = [];
|
sendUpdate();
|
||||||
var ids = Entities.findEntities(MyAvatar.position, 100);
|
} else if (data.type == "teleport") {
|
||||||
for (var i = 0; i < ids.length; i++) {
|
if (selectionManager.hasSelection()) {
|
||||||
var id = ids[i];
|
MyAvatar.position = selectionManager.worldPosition;
|
||||||
var properties = Entities.getEntityProperties(id);
|
|
||||||
entities.push({
|
|
||||||
id: id.id,
|
|
||||||
type: properties.type,
|
|
||||||
url: properties.type == "Model" ? properties.modelURL : "",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
var data = {
|
} else if (data.type == "delete") {
|
||||||
type: "update",
|
deleteSelectedEntities();
|
||||||
entities: entities,
|
|
||||||
};
|
|
||||||
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -43,5 +43,5 @@ void main(void) {
|
||||||
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
|
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
|
||||||
|
|
||||||
// pass along the scaled/offset texture coordinates
|
// pass along the scaled/offset texture coordinates
|
||||||
gl_TexCoord[0] = vec4((heightCoord - heightScale.st) * colorScale, 0.0, 1.0);
|
gl_TexCoord[0] = vec4((heightCoord - vec2(0.5, 0.5)) * colorScale + vec2(0.5, 0.5), 0.0, 1.0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ void main(void) {
|
||||||
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
|
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
|
||||||
|
|
||||||
// compute the alpha values for each texture
|
// compute the alpha values for each texture
|
||||||
float value = texture2D(textureMap, (gl_MultiTexCoord0.st - heightScale) * textureScale).r;
|
float value = texture2D(textureMap, (gl_MultiTexCoord0.st - vec2(0.5, 0.5)) * textureScale + vec2(0.5, 0.5)).r;
|
||||||
vec4 valueVector = vec4(value, value, value, value);
|
vec4 valueVector = vec4(value, value, value, value);
|
||||||
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
|
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1425,8 +1425,18 @@ public:
|
||||||
char material;
|
char material;
|
||||||
|
|
||||||
void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; }
|
void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; }
|
||||||
|
|
||||||
|
void mix(const EdgeCrossing& first, const EdgeCrossing& second, float t);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, float t) {
|
||||||
|
point = glm::mix(first.point, second.point, t);
|
||||||
|
normal = glm::normalize(glm::mix(first.normal, second.normal, t));
|
||||||
|
color = qRgb(glm::mix(qRed(first.color), qRed(second.color), t), glm::mix(qGreen(first.color), qGreen(second.color), t),
|
||||||
|
glm::mix(qBlue(first.color), qBlue(second.color), t));
|
||||||
|
material = (t < 0.5f) ? first.material : second.material;
|
||||||
|
}
|
||||||
|
|
||||||
const int MAX_NORMALS_PER_VERTEX = 4;
|
const int MAX_NORMALS_PER_VERTEX = 4;
|
||||||
|
|
||||||
class NormalIndex {
|
class NormalIndex {
|
||||||
|
@ -1480,7 +1490,6 @@ public:
|
||||||
void swap(IndexVector& other) { QVector<NormalIndex>::swap(other); qSwap(position, other.position); }
|
void swap(IndexVector& other) { QVector<NormalIndex>::swap(other); qSwap(position, other.position); }
|
||||||
|
|
||||||
const NormalIndex& get(int y) const;
|
const NormalIndex& get(int y) const;
|
||||||
const NormalIndex& getClosest(int y) const;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const NormalIndex& IndexVector::get(int y) const {
|
const NormalIndex& IndexVector::get(int y) const {
|
||||||
|
@ -1489,56 +1498,6 @@ const NormalIndex& IndexVector::get(int y) const {
|
||||||
return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex;
|
return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
const NormalIndex& IndexVector::getClosest(int y) const {
|
|
||||||
static NormalIndex invalidIndex = { { -1, -1, -1, -1 } };
|
|
||||||
int relative = y - position;
|
|
||||||
if (relative < 0 || relative >= size()) {
|
|
||||||
return invalidIndex;
|
|
||||||
}
|
|
||||||
const NormalIndex& first = at(relative);
|
|
||||||
if (first.isValid()) {
|
|
||||||
return first;
|
|
||||||
}
|
|
||||||
for (int distance = 1; relative - distance >= 0 || relative + distance < size(); distance++) {
|
|
||||||
int previous = relative - distance;
|
|
||||||
if (previous >= 0) {
|
|
||||||
const NormalIndex& previousIndex = at(previous);
|
|
||||||
if (previousIndex.isValid()) {
|
|
||||||
return previousIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
int next = relative + distance;
|
|
||||||
if (next < size()) {
|
|
||||||
const NormalIndex& nextIndex = at(next);
|
|
||||||
if (nextIndex.isValid()) {
|
|
||||||
return nextIndex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return invalidIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void appendIndices(QVector<int>& indices, QMultiHash<VoxelCoord, int>& quadIndices,
|
|
||||||
const QVector<VoxelPoint>& vertices, float step, int i0, int i1, int i2, int i3) {
|
|
||||||
int newIndices[] = { i0, i1, i2, i3 };
|
|
||||||
glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX), maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX);
|
|
||||||
int indexIndex = indices.size();
|
|
||||||
for (unsigned int i = 0; i < sizeof(newIndices) / sizeof(newIndices[0]); i++) {
|
|
||||||
int index = newIndices[i];
|
|
||||||
indices.append(index);
|
|
||||||
const glm::vec3& vertex = vertices.at(index).vertex;
|
|
||||||
minima = glm::min(vertex, minima);
|
|
||||||
maxima = glm::max(vertex, maxima);
|
|
||||||
}
|
|
||||||
for (int z = (int)minima.z, endZ = (int)glm::ceil(maxima.z); z < endZ; z++) {
|
|
||||||
for (int y = (int)minima.x, endY = (int)glm::ceil(maxima.y); y < endY; y++) {
|
|
||||||
for (int x = (int)minima.x, endX = (int)glm::ceil(maxima.x); x < endX; x++) {
|
|
||||||
quadIndices.insert(qRgb(x, y, z), indexIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation,
|
void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation,
|
||||||
const glm::quat& rotation, const glm::vec3& scale, bool cursor) {
|
const glm::quat& rotation, const glm::vec3& scale, bool cursor) {
|
||||||
if (!node->getHeight()) {
|
if (!node->getHeight()) {
|
||||||
|
@ -1617,7 +1576,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
|
|
||||||
glGenTextures(1, &_colorTextureID);
|
glGenTextures(1, &_colorTextureID);
|
||||||
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
glBindTexture(GL_TEXTURE_2D, _colorTextureID);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
if (node->getColor()) {
|
if (node->getColor()) {
|
||||||
|
@ -1625,6 +1584,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, node->getColor()->getWidth(),
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, node->getColor()->getWidth(),
|
||||||
contents.size() / (node->getColor()->getWidth() * DataBlock::COLOR_BYTES),
|
contents.size() / (node->getColor()->getWidth() * DataBlock::COLOR_BYTES),
|
||||||
0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData());
|
0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData());
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
const quint8 WHITE_COLOR[] = { 255, 255, 255 };
|
const quint8 WHITE_COLOR[] = { 255, 255, 255 };
|
||||||
|
@ -1705,7 +1665,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
}
|
}
|
||||||
|
|
||||||
const int EDGES_PER_CUBE = 12;
|
const int EDGES_PER_CUBE = 12;
|
||||||
EdgeCrossing crossings[EDGES_PER_CUBE];
|
EdgeCrossing crossings[EDGES_PER_CUBE * 2];
|
||||||
|
|
||||||
// as we scan down the cube generating vertices between grid points, we remember the indices of the last
|
// as we scan down the cube generating vertices between grid points, we remember the indices of the last
|
||||||
// (element, line, section--x, y, z) so that we can connect generated vertices as quads
|
// (element, line, section--x, y, z) so that we can connect generated vertices as quads
|
||||||
|
@ -1736,13 +1696,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
lineSrc[stackWidth].getExtents(minimumY, maximumY);
|
lineSrc[stackWidth].getExtents(minimumY, maximumY);
|
||||||
}
|
}
|
||||||
if (maximumY >= minimumY) {
|
if (maximumY >= minimumY) {
|
||||||
int position = minimumY;
|
|
||||||
int count = maximumY - minimumY + 1;
|
|
||||||
NormalIndex lastIndexY = { { -1, -1, -1, -1 } };
|
|
||||||
indicesX.position = position;
|
|
||||||
indicesX.resize(count);
|
|
||||||
indicesZ[x].position = position;
|
|
||||||
indicesZ[x].resize(count);
|
|
||||||
float heightfieldHeight = *heightLineSrc * voxelScale;
|
float heightfieldHeight = *heightLineSrc * voxelScale;
|
||||||
float nextHeightfieldHeightX = heightLineSrc[1] * voxelScale;
|
float nextHeightfieldHeightX = heightLineSrc[1] * voxelScale;
|
||||||
float nextHeightfieldHeightZ = heightLineSrc[width] * voxelScale;
|
float nextHeightfieldHeightZ = heightLineSrc[width] * voxelScale;
|
||||||
|
@ -1753,6 +1706,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
const int LOWER_RIGHT_CORNER = 8;
|
const int LOWER_RIGHT_CORNER = 8;
|
||||||
const int NO_CORNERS = 0;
|
const int NO_CORNERS = 0;
|
||||||
const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER;
|
const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER;
|
||||||
|
const int CORNER_COUNT = 4;
|
||||||
|
const int NEXT_CORNERS[] = { 1, 3, 0, 2 };
|
||||||
int corners = NO_CORNERS;
|
int corners = NO_CORNERS;
|
||||||
if (heightfieldHeight != 0.0f) {
|
if (heightfieldHeight != 0.0f) {
|
||||||
corners |= UPPER_LEFT_CORNER;
|
corners |= UPPER_LEFT_CORNER;
|
||||||
|
@ -1767,37 +1722,38 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
corners |= LOWER_RIGHT_CORNER;
|
corners |= LOWER_RIGHT_CORNER;
|
||||||
}
|
}
|
||||||
bool stitchable = x != 0 && z != 0 && !(corners == NO_CORNERS || corners == ALL_CORNERS);
|
bool stitchable = x != 0 && z != 0 && !(corners == NO_CORNERS || corners == ALL_CORNERS);
|
||||||
VoxelPoint cornerPoints[4];
|
EdgeCrossing cornerCrossings[CORNER_COUNT];
|
||||||
int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0);
|
int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0);
|
||||||
|
int cornerMinimumY = INT_MAX, cornerMaximumY = -1;
|
||||||
if (stitchable) {
|
if (stitchable) {
|
||||||
for (unsigned int i = 0; i < sizeof(cornerPoints) / sizeof(cornerPoints[0]); i++) {
|
for (int i = 0; i < CORNER_COUNT; i++) {
|
||||||
if (!(corners & (1 << i))) {
|
if (!(corners & (1 << i))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
|
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
|
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
|
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
|
||||||
VoxelPoint& point = cornerPoints[i];
|
float heightValue = *height * voxelScale;
|
||||||
int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ;
|
int y = (int)heightValue;
|
||||||
point.vertex = glm::vec3(clampedOffsetX, *height * voxelScale, clampedOffsetZ) * step;
|
cornerMinimumY = qMin(cornerMinimumY, y);
|
||||||
|
cornerMaximumY = qMax(cornerMaximumY, y);
|
||||||
|
EdgeCrossing& crossing = cornerCrossings[i];
|
||||||
|
crossing.point = glm::vec3(offsetX, heightValue, offsetZ);
|
||||||
int left = height[-1];
|
int left = height[-1];
|
||||||
int right = height[1];
|
int right = height[1];
|
||||||
int down = height[-width];
|
int down = height[-width];
|
||||||
int up = height[width];
|
int up = height[width];
|
||||||
glm::vec3 normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right,
|
crossing.normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right,
|
||||||
2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up));
|
2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up));
|
||||||
point.normal[0] = normal.x * numeric_limits<qint8>::max();
|
int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ;
|
||||||
point.normal[1] = normal.y * numeric_limits<qint8>::max();
|
|
||||||
point.normal[2] = normal.z * numeric_limits<qint8>::max();
|
|
||||||
if (colorSrc) {
|
if (colorSrc) {
|
||||||
const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth +
|
const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth +
|
||||||
(int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES;
|
(int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES;
|
||||||
point.color[0] = color[0];
|
crossing.color = qRgb(color[0], color[1], color[2]);
|
||||||
point.color[1] = color[1];
|
|
||||||
point.color[2] = color[2];
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
point.color[0] = point.color[1] = point.color[2] = numeric_limits<quint8>::max();
|
crossing.color = qRgb(numeric_limits<quint8>::max(), numeric_limits<quint8>::max(),
|
||||||
|
numeric_limits<quint8>::max());
|
||||||
}
|
}
|
||||||
int material = 0;
|
int material = 0;
|
||||||
if (materialSrc) {
|
if (materialSrc) {
|
||||||
|
@ -1812,12 +1768,18 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
material = mapping;
|
material = mapping;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
point.materials[0] = material;
|
crossing.material = material;
|
||||||
point.materials[1] = point.materials[2] = point.materials[3] = 0;
|
|
||||||
point.materialWeights[0] = numeric_limits<quint8>::max();
|
|
||||||
point.materialWeights[1] = point.materialWeights[2] = point.materialWeights[3] = 0;
|
|
||||||
}
|
}
|
||||||
|
minimumY = qMin(minimumY, cornerMinimumY);
|
||||||
|
maximumY = qMax(maximumY, cornerMaximumY);
|
||||||
}
|
}
|
||||||
|
int position = minimumY;
|
||||||
|
int count = maximumY - minimumY + 1;
|
||||||
|
NormalIndex lastIndexY = { { -1, -1, -1, -1 } };
|
||||||
|
indicesX.position = position;
|
||||||
|
indicesX.resize(count);
|
||||||
|
indicesZ[x].position = position;
|
||||||
|
indicesZ[x].resize(count);
|
||||||
for (int y = position, end = position + count; y < end; y++) {
|
for (int y = position, end = position + count; y < end; y++) {
|
||||||
const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight);
|
const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight);
|
||||||
if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) {
|
if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) {
|
||||||
|
@ -1875,9 +1837,93 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
if (alphaTotal == 0 || alphaTotal == possibleTotal) {
|
if (alphaTotal == 0 || alphaTotal == possibleTotal) {
|
||||||
continue; // no corners set/all corners set
|
continue; // no corners set/all corners set
|
||||||
}
|
}
|
||||||
|
// we first look for crossings with the heightfield corner vertices; these take priority
|
||||||
|
int crossingCount = 0;
|
||||||
|
if (y >= cornerMinimumY && y <= cornerMaximumY) {
|
||||||
|
// first look for set corners, which override any interpolated values
|
||||||
|
int crossedCorners = NO_CORNERS;
|
||||||
|
for (int i = 0; i < CORNER_COUNT; i++) {
|
||||||
|
if (!(corners & (1 << i))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
|
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
|
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
|
||||||
|
float heightValue = *height * voxelScale;
|
||||||
|
if (heightValue >= y && heightValue < y + 1) {
|
||||||
|
crossedCorners |= (1 << i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (crossedCorners) {
|
||||||
|
case UPPER_LEFT_CORNER:
|
||||||
|
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER:
|
||||||
|
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER:
|
||||||
|
case UPPER_LEFT_CORNER | LOWER_RIGHT_CORNER:
|
||||||
|
crossings[crossingCount++] = cornerCrossings[0];
|
||||||
|
crossings[crossingCount - 1].point.y -= y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case UPPER_RIGHT_CORNER:
|
||||||
|
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER:
|
||||||
|
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER:
|
||||||
|
case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER:
|
||||||
|
crossings[crossingCount++] = cornerCrossings[1];
|
||||||
|
crossings[crossingCount - 1].point.y -= y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOWER_LEFT_CORNER:
|
||||||
|
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER:
|
||||||
|
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER:
|
||||||
|
crossings[crossingCount++] = cornerCrossings[2];
|
||||||
|
crossings[crossingCount - 1].point.y -= y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LOWER_RIGHT_CORNER:
|
||||||
|
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER:
|
||||||
|
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER:
|
||||||
|
crossings[crossingCount++] = cornerCrossings[3];
|
||||||
|
crossings[crossingCount - 1].point.y -= y;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NO_CORNERS:
|
||||||
|
for (int i = 0; i < CORNER_COUNT; i++) {
|
||||||
|
if (!(corners & (1 << i))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
|
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
|
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
|
||||||
|
float heightValue = *height * voxelScale;
|
||||||
|
int nextIndex = NEXT_CORNERS[i];
|
||||||
|
if (!(corners & (1 << nextIndex))) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int nextOffsetX = (nextIndex & X_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
|
int nextOffsetZ = (nextIndex & Y_MAXIMUM_FLAG) ? 1 : 0;
|
||||||
|
const quint16* nextHeight = heightLineSrc + nextOffsetZ * width + nextOffsetX;
|
||||||
|
float nextHeightValue = *nextHeight * voxelScale;
|
||||||
|
float divisor = (nextHeightValue - heightValue);
|
||||||
|
if (divisor == 0.0f) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float t1 = (y - heightValue) / divisor;
|
||||||
|
float t2 = (y + 1 - heightValue) / divisor;
|
||||||
|
if (t1 >= 0.0f && t1 <= 1.0f) {
|
||||||
|
crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t1);
|
||||||
|
crossings[crossingCount - 1].point.y -= y;
|
||||||
|
}
|
||||||
|
if (t2 >= 0.0f && t2 <= 1.0f) {
|
||||||
|
crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t2);
|
||||||
|
crossings[crossingCount - 1].point.y -= y;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// the terrifying conditional code that follows checks each cube edge for a crossing, gathering
|
// the terrifying conditional code that follows checks each cube edge for a crossing, gathering
|
||||||
// its properties (color, material, normal) if one is present; as before, boundary edges are excluded
|
// its properties (color, material, normal) if one is present; as before, boundary edges are excluded
|
||||||
int crossingCount = 0;
|
if (crossingCount == 0) {
|
||||||
const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight);
|
const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight);
|
||||||
if (middleX) {
|
if (middleX) {
|
||||||
const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX);
|
const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX);
|
||||||
|
@ -1898,7 +1944,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY);
|
crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY);
|
||||||
}
|
}
|
||||||
if (middleZ) {
|
if (middleZ) {
|
||||||
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ);
|
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y,
|
||||||
|
nextHeightfieldHeightZ);
|
||||||
const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry(
|
const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry(
|
||||||
y, nextHeightfieldHeightXZ);
|
y, nextHeightfieldHeightXZ);
|
||||||
const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry(
|
const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry(
|
||||||
|
@ -1910,7 +1957,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
}
|
}
|
||||||
if (alpha3 != alpha7) {
|
if (alpha3 != alpha7) {
|
||||||
EdgeCrossing& crossing = crossings[crossingCount++];
|
EdgeCrossing& crossing = crossings[crossingCount++];
|
||||||
const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, nextHeightfieldHeightX);
|
const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1,
|
||||||
|
nextHeightfieldHeightX);
|
||||||
crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal));
|
crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal));
|
||||||
crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY);
|
crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY);
|
||||||
}
|
}
|
||||||
|
@ -1942,7 +1990,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
}
|
}
|
||||||
if (middleZ) {
|
if (middleZ) {
|
||||||
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ);
|
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ);
|
||||||
const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1, nextHeightfieldHeightZ);
|
const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1,
|
||||||
|
nextHeightfieldHeightZ);
|
||||||
if (alpha0 != alpha4) {
|
if (alpha0 != alpha4) {
|
||||||
EdgeCrossing& crossing = crossings[crossingCount++];
|
EdgeCrossing& crossing = crossings[crossingCount++];
|
||||||
crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal));
|
crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal));
|
||||||
|
@ -1959,7 +2008,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ);
|
crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// determine whether we should ignore this vertex because it will be stitched
|
}
|
||||||
|
// make sure we have valid crossings to include
|
||||||
int validCrossings = 0;
|
int validCrossings = 0;
|
||||||
for (int i = 0; i < crossingCount; i++) {
|
for (int i = 0; i < crossingCount; i++) {
|
||||||
if (qAlpha(crossings[i].color) != 0) {
|
if (qAlpha(crossings[i].color) != 0) {
|
||||||
|
@ -2107,173 +2157,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
point.setNormal(normals[i]);
|
point.setNormal(normals[i]);
|
||||||
vertices.append(point);
|
vertices.append(point);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (stitchable) {
|
|
||||||
int nextIndex = vertices.size();
|
|
||||||
const NormalIndex& previousIndexX = lastIndicesX.getClosest(y);
|
|
||||||
const NormalIndex& previousIndexZ = lastIndicesZ[x].getClosest(y);
|
|
||||||
switch (corners) {
|
|
||||||
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[0]);
|
|
||||||
vertices.append(cornerPoints[3]);
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[0].vertex - cornerPoints[1].vertex,
|
|
||||||
cornerPoints[3].vertex - cornerPoints[1].vertex);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 1, nextIndex, nextIndex);
|
|
||||||
if (previousIndexX.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
nextIndex, previousIndexX.getClosestIndex(normal, vertices));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPPER_LEFT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[0]);
|
|
||||||
vertices.append(cornerPoints[3]);
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex,
|
|
||||||
cornerPoints[0].vertex - cornerPoints[2].vertex);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex, nextIndex + 1, nextIndex + 1);
|
|
||||||
if (previousIndexZ.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
previousIndexZ.getClosestIndex(normal, vertices), nextIndex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[1]);
|
|
||||||
vertices.append(cornerPoints[2]);
|
|
||||||
vertices.append(cornerPoints[3]);
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex,
|
|
||||||
cornerPoints[1].vertex - cornerPoints[2].vertex);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 2, nextIndex, nextIndex);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 1, nextIndex + 2, nextIndex + 2);
|
|
||||||
if (previousIndexX.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1);
|
|
||||||
}
|
|
||||||
if (previousIndexZ.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
nextIndex, previousIndexZ.getClosestIndex(normal, vertices));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[0]);
|
|
||||||
vertices.append(cornerPoints[1]);
|
|
||||||
vertices.append(cornerPoints[2]);
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[2].vertex - cornerPoints[0].vertex,
|
|
||||||
cornerPoints[1].vertex - cornerPoints[0].vertex);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 1, nextIndex, nextIndex);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex, nextIndex + 2, nextIndex + 2);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[0]);
|
|
||||||
vertices.append(cornerPoints[1]);
|
|
||||||
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[1].vertex - first,
|
|
||||||
cornerPoints[0].vertex - first);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 1, nextIndex, nextIndex);
|
|
||||||
if (previousIndexX.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
nextIndex, previousIndexX.getClosestIndex(normal, vertices));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[1]);
|
|
||||||
vertices.append(cornerPoints[3]);
|
|
||||||
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[3].vertex - first,
|
|
||||||
cornerPoints[1].vertex - first);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 1, nextIndex, nextIndex);
|
|
||||||
if (previousIndexZ.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
firstIndex, nextIndex, previousIndexZ.getClosestIndex(normal, vertices));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[3]);
|
|
||||||
vertices.append(cornerPoints[2]);
|
|
||||||
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[2].vertex - first,
|
|
||||||
cornerPoints[3].vertex - first);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 1, nextIndex, nextIndex);
|
|
||||||
if (previousIndexX.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[2]);
|
|
||||||
vertices.append(cornerPoints[0]);
|
|
||||||
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[0].vertex - first,
|
|
||||||
cornerPoints[2].vertex - first);
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex,
|
|
||||||
nextIndex + 1, nextIndex, nextIndex);
|
|
||||||
if (previousIndexZ.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
previousIndexZ.getClosestIndex(normal, vertices), nextIndex + 1);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPPER_LEFT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[0]);
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[0].vertex -
|
|
||||||
vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f));
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
if (previousIndexX.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
nextIndex, previousIndexX.getClosestIndex(normal, vertices));
|
|
||||||
}
|
|
||||||
if (previousIndexZ.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
previousIndexZ.getClosestIndex(normal, vertices), nextIndex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case UPPER_RIGHT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[1]);
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[1].vertex -
|
|
||||||
vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f));
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
if (previousIndexZ.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
nextIndex, previousIndexZ.getClosestIndex(normal, vertices));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case LOWER_LEFT_CORNER: {
|
|
||||||
vertices.append(cornerPoints[2]);
|
|
||||||
glm::vec3 normal = glm::cross(cornerPoints[2].vertex -
|
|
||||||
vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f));
|
|
||||||
int firstIndex = index.getClosestIndex(normal, vertices);
|
|
||||||
if (previousIndexX.isValid()) {
|
|
||||||
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
|
|
||||||
previousIndexX.getClosestIndex(normal, vertices), nextIndex);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// the first x, y, and z are repeated for the boundary edge; past that, we consider generating
|
// the first x, y, and z are repeated for the boundary edge; past that, we consider generating
|
||||||
|
@ -2407,7 +2290,14 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
baseBatch.heightTextureID = _heightTextureID;
|
baseBatch.heightTextureID = _heightTextureID;
|
||||||
baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f);
|
baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f);
|
||||||
baseBatch.colorTextureID = _colorTextureID;
|
baseBatch.colorTextureID = _colorTextureID;
|
||||||
baseBatch.colorScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight);
|
float widthMultiplier = 1.0f / (0.5f - 1.5f / width);
|
||||||
|
float heightMultiplier = 1.0f / (0.5f - 1.5f / height);
|
||||||
|
if (node->getColor()) {
|
||||||
|
int colorWidth = node->getColor()->getWidth();
|
||||||
|
int colorHeight = node->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES);
|
||||||
|
baseBatch.colorScale = glm::vec2((0.5f - 0.5f / colorWidth) * widthMultiplier,
|
||||||
|
(0.5f - 0.5f / colorHeight) * heightMultiplier);
|
||||||
|
}
|
||||||
Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch);
|
Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch);
|
||||||
|
|
||||||
if (!(cursor || _networkTextures.isEmpty())) {
|
if (!(cursor || _networkTextures.isEmpty())) {
|
||||||
|
@ -2422,7 +2312,12 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
|
||||||
splatBatch.heightTextureID = _heightTextureID;
|
splatBatch.heightTextureID = _heightTextureID;
|
||||||
splatBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, 0.0f, 0.0f);
|
splatBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, 0.0f, 0.0f);
|
||||||
splatBatch.materialTextureID = _materialTextureID;
|
splatBatch.materialTextureID = _materialTextureID;
|
||||||
splatBatch.textureScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight);
|
if (node->getMaterial()) {
|
||||||
|
int materialWidth = node->getMaterial()->getWidth();
|
||||||
|
int materialHeight = node->getMaterial()->getContents().size() / materialWidth;
|
||||||
|
splatBatch.textureScale = glm::vec2((0.5f - 0.5f / materialWidth) * widthMultiplier,
|
||||||
|
(0.5f - 0.5f / materialHeight) * heightMultiplier);
|
||||||
|
}
|
||||||
splatBatch.splatTextureOffset = glm::vec2(
|
splatBatch.splatTextureOffset = glm::vec2(
|
||||||
glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x,
|
glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x,
|
||||||
glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z);
|
glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z);
|
||||||
|
|
|
@ -41,19 +41,27 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid
|
||||||
|
|
||||||
_dockWidget = new QDockWidget(title, toolWindow);
|
_dockWidget = new QDockWidget(title, toolWindow);
|
||||||
_dockWidget->setFeatures(QDockWidget::DockWidgetMovable);
|
_dockWidget->setFeatures(QDockWidget::DockWidgetMovable);
|
||||||
QWebView* webView = new QWebView(_dockWidget);
|
|
||||||
webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge);
|
_webView = new QWebView(_dockWidget);
|
||||||
webView->setUrl(url);
|
_webView->setUrl(url);
|
||||||
_dockWidget->setWidget(webView);
|
addEventBridgeToWindowObject();
|
||||||
|
|
||||||
|
_dockWidget->setWidget(_webView);
|
||||||
|
|
||||||
toolWindow->addDockWidget(Qt::RightDockWidgetArea, _dockWidget);
|
toolWindow->addDockWidget(Qt::RightDockWidgetArea, _dockWidget);
|
||||||
|
|
||||||
|
connect(_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared,
|
||||||
|
this, &WebWindowClass::addEventBridgeToWindowObject);
|
||||||
connect(this, &WebWindowClass::destroyed, _dockWidget, &QWidget::deleteLater);
|
connect(this, &WebWindowClass::destroyed, _dockWidget, &QWidget::deleteLater);
|
||||||
}
|
}
|
||||||
|
|
||||||
WebWindowClass::~WebWindowClass() {
|
WebWindowClass::~WebWindowClass() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebWindowClass::addEventBridgeToWindowObject() {
|
||||||
|
_webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge);
|
||||||
|
}
|
||||||
|
|
||||||
void WebWindowClass::setVisible(bool visible) {
|
void WebWindowClass::setVisible(bool visible) {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
QMetaObject::invokeMethod(
|
QMetaObject::invokeMethod(
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include <QScriptContext>
|
#include <QScriptContext>
|
||||||
#include <QScriptEngine>
|
#include <QScriptEngine>
|
||||||
|
#include <QWebView>
|
||||||
|
|
||||||
class ScriptEventBridge : public QObject {
|
class ScriptEventBridge : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -42,9 +43,11 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void setVisible(bool visible);
|
void setVisible(bool visible);
|
||||||
ScriptEventBridge* getEventBridge() const { return _eventBridge; }
|
ScriptEventBridge* getEventBridge() const { return _eventBridge; }
|
||||||
|
void addEventBridgeToWindowObject();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QDockWidget* _dockWidget;
|
QDockWidget* _dockWidget;
|
||||||
|
QWebView* _webView;
|
||||||
ScriptEventBridge* _eventBridge;
|
ScriptEventBridge* _eventBridge;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -220,9 +220,14 @@ unsigned int Overlays::cloneOverlay(unsigned int id) {
|
||||||
} else if (_overlaysWorld.contains(id)) {
|
} else if (_overlaysWorld.contains(id)) {
|
||||||
thisOverlay = _overlaysWorld[id];
|
thisOverlay = _overlaysWorld[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (thisOverlay) {
|
||||||
return addOverlay(thisOverlay->createClone());
|
return addOverlay(thisOverlay->createClone());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return 0; // Not found
|
||||||
|
}
|
||||||
|
|
||||||
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {
|
||||||
QWriteLocker lock(&_lock);
|
QWriteLocker lock(&_lock);
|
||||||
Overlay* thisOverlay = NULL;
|
Overlay* thisOverlay = NULL;
|
||||||
|
|
|
@ -158,7 +158,8 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
|
||||||
void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||||
const JSONCallbackParameters& callbackParams,
|
const JSONCallbackParameters& callbackParams,
|
||||||
const QByteArray& dataByteArray,
|
const QByteArray& dataByteArray,
|
||||||
QHttpMultiPart* dataMultiPart) {
|
QHttpMultiPart* dataMultiPart,
|
||||||
|
const QVariantMap& propertyMap) {
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "invokedRequest",
|
QMetaObject::invokeMethod(this, "invokedRequest",
|
||||||
Q_ARG(const QString&, path),
|
Q_ARG(const QString&, path),
|
||||||
|
@ -166,13 +167,15 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan
|
||||||
Q_ARG(QNetworkAccessManager::Operation, operation),
|
Q_ARG(QNetworkAccessManager::Operation, operation),
|
||||||
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
||||||
Q_ARG(const QByteArray&, dataByteArray),
|
Q_ARG(const QByteArray&, dataByteArray),
|
||||||
Q_ARG(QHttpMultiPart*, dataMultiPart));
|
Q_ARG(QHttpMultiPart*, dataMultiPart),
|
||||||
|
Q_ARG(QVariantMap, propertyMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation,
|
||||||
const JSONCallbackParameters& callbackParams,
|
const JSONCallbackParameters& callbackParams,
|
||||||
const QByteArray& dataByteArray,
|
const QByteArray& dataByteArray,
|
||||||
QHttpMultiPart* dataMultiPart) {
|
QHttpMultiPart* dataMultiPart,
|
||||||
|
const QVariantMap& propertyMap) {
|
||||||
|
|
||||||
QMetaObject::invokeMethod(this, "invokedRequest",
|
QMetaObject::invokeMethod(this, "invokedRequest",
|
||||||
Q_ARG(const QString&, path),
|
Q_ARG(const QString&, path),
|
||||||
|
@ -180,14 +183,16 @@ void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessM
|
||||||
Q_ARG(QNetworkAccessManager::Operation, operation),
|
Q_ARG(QNetworkAccessManager::Operation, operation),
|
||||||
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
Q_ARG(const JSONCallbackParameters&, callbackParams),
|
||||||
Q_ARG(const QByteArray&, dataByteArray),
|
Q_ARG(const QByteArray&, dataByteArray),
|
||||||
Q_ARG(QHttpMultiPart*, dataMultiPart));
|
Q_ARG(QHttpMultiPart*, dataMultiPart),
|
||||||
|
Q_ARG(QVariantMap, propertyMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AccountManager::invokedRequest(const QString& path,
|
void AccountManager::invokedRequest(const QString& path,
|
||||||
bool requiresAuthentication,
|
bool requiresAuthentication,
|
||||||
QNetworkAccessManager::Operation operation,
|
QNetworkAccessManager::Operation operation,
|
||||||
const JSONCallbackParameters& callbackParams,
|
const JSONCallbackParameters& callbackParams,
|
||||||
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
|
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart,
|
||||||
|
const QVariantMap& propertyMap) {
|
||||||
|
|
||||||
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
|
||||||
|
|
||||||
|
@ -235,7 +240,9 @@ void AccountManager::invokedRequest(const QString& path,
|
||||||
} else {
|
} else {
|
||||||
networkReply = networkAccessManager.put(networkRequest, dataMultiPart);
|
networkReply = networkAccessManager.put(networkRequest, dataMultiPart);
|
||||||
}
|
}
|
||||||
dataMultiPart->setParent(networkReply);
|
|
||||||
|
// make sure dataMultiPart is destroyed when the reply is
|
||||||
|
connect(networkReply, &QNetworkReply::destroyed, dataMultiPart, &QHttpMultiPart::deleteLater);
|
||||||
} else {
|
} else {
|
||||||
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
|
||||||
if (operation == QNetworkAccessManager::PostOperation) {
|
if (operation == QNetworkAccessManager::PostOperation) {
|
||||||
|
@ -255,6 +262,14 @@ void AccountManager::invokedRequest(const QString& path,
|
||||||
}
|
}
|
||||||
|
|
||||||
if (networkReply) {
|
if (networkReply) {
|
||||||
|
if (!propertyMap.isEmpty()) {
|
||||||
|
// we have properties to set on the reply so the user can check them after
|
||||||
|
foreach(const QString& propertyKey, propertyMap.keys()) {
|
||||||
|
networkReply->setProperty(qPrintable(propertyKey), propertyMap.value(propertyKey));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!callbackParams.isEmpty()) {
|
if (!callbackParams.isEmpty()) {
|
||||||
// if we have information for a callback, insert the callbackParams into our local map
|
// if we have information for a callback, insert the callbackParams into our local map
|
||||||
_pendingCallbackMap.insert(networkReply, callbackParams);
|
_pendingCallbackMap.insert(networkReply, callbackParams);
|
||||||
|
|
|
@ -48,13 +48,15 @@ public:
|
||||||
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
||||||
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
||||||
const QByteArray& dataByteArray = QByteArray(),
|
const QByteArray& dataByteArray = QByteArray(),
|
||||||
QHttpMultiPart* dataMultiPart = NULL);
|
QHttpMultiPart* dataMultiPart = NULL,
|
||||||
|
const QVariantMap& propertyMap = QVariantMap());
|
||||||
|
|
||||||
void unauthenticatedRequest(const QString& path,
|
void unauthenticatedRequest(const QString& path,
|
||||||
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
|
||||||
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
|
||||||
const QByteArray& dataByteArray = QByteArray(),
|
const QByteArray& dataByteArray = QByteArray(),
|
||||||
QHttpMultiPart* dataMultiPart = NULL);
|
QHttpMultiPart* dataMultiPart = NULL,
|
||||||
|
const QVariantMap& propertyMap = QVariantMap()) ;
|
||||||
|
|
||||||
const QUrl& getAuthURL() const { return _authURL; }
|
const QUrl& getAuthURL() const { return _authURL; }
|
||||||
void setAuthURL(const QUrl& authURL);
|
void setAuthURL(const QUrl& authURL);
|
||||||
|
@ -109,7 +111,8 @@ private:
|
||||||
QNetworkAccessManager::Operation operation,
|
QNetworkAccessManager::Operation operation,
|
||||||
const JSONCallbackParameters& callbackParams,
|
const JSONCallbackParameters& callbackParams,
|
||||||
const QByteArray& dataByteArray,
|
const QByteArray& dataByteArray,
|
||||||
QHttpMultiPart* dataMultiPart);
|
QHttpMultiPart* dataMultiPart,
|
||||||
|
const QVariantMap& propertyMap);
|
||||||
|
|
||||||
QUrl _authURL;
|
QUrl _authURL;
|
||||||
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;
|
||||||
|
|
|
@ -119,14 +119,16 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
|
||||||
if (!handleUsername(lookupUrl.authority())) {
|
if (!handleUsername(lookupUrl.authority())) {
|
||||||
// we're assuming this is either a network address or global place name
|
// we're assuming this is either a network address or global place name
|
||||||
// check if it is a network address first
|
// check if it is a network address first
|
||||||
if (!handleNetworkAddress(lookupUrl.host()
|
if (handleNetworkAddress(lookupUrl.host()
|
||||||
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
|
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
|
||||||
// wasn't an address - lookup the place name
|
|
||||||
attemptPlaceNameLookup(lookupUrl.host());
|
|
||||||
}
|
|
||||||
|
|
||||||
// we may have a path that defines a relative viewpoint - if so we should jump to that now
|
// we may have a path that defines a relative viewpoint - if so we should jump to that now
|
||||||
handleRelativeViewpoint(lookupUrl.path());
|
handleRelativeViewpoint(lookupUrl.path());
|
||||||
|
} else {
|
||||||
|
// wasn't an address - lookup the place name
|
||||||
|
// we may have a path that defines a relative viewpoint - pass that through the lookup so we can go to it after
|
||||||
|
attemptPlaceNameLookup(lookupUrl.host(), lookupUrl.path());
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -164,12 +166,14 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
|
||||||
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
|
||||||
QJsonObject dataObject = responseObject["data"].toObject();
|
QJsonObject dataObject = responseObject["data"].toObject();
|
||||||
|
|
||||||
goToAddressFromObject(dataObject.toVariantMap());
|
goToAddressFromObject(dataObject.toVariantMap(), requestReply);
|
||||||
|
|
||||||
emit lookupResultsFinished();
|
emit lookupResultsFinished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
|
const char OVERRIDE_PATH_KEY[] = "override_path";
|
||||||
|
|
||||||
|
void AddressManager::goToAddressFromObject(const QVariantMap& dataObject, const QNetworkReply& reply) {
|
||||||
|
|
||||||
const QString DATA_OBJECT_PLACE_KEY = "place";
|
const QString DATA_OBJECT_PLACE_KEY = "place";
|
||||||
const QString DATA_OBJECT_USER_LOCATION_KEY = "location";
|
const QString DATA_OBJECT_USER_LOCATION_KEY = "location";
|
||||||
|
@ -203,6 +207,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
|
||||||
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
|
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
|
||||||
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
QString domainHostname = domainObject[DOMAIN_NETWORK_ADDRESS_KEY].toString();
|
||||||
|
|
||||||
|
qDebug() << "Possible domain change required to connect to" << domainHostname
|
||||||
|
<< "on" << DEFAULT_DOMAIN_SERVER_PORT;
|
||||||
emit possibleDomainChangeRequired(domainHostname, DEFAULT_DOMAIN_SERVER_PORT);
|
emit possibleDomainChangeRequired(domainHostname, DEFAULT_DOMAIN_SERVER_PORT);
|
||||||
} else {
|
} else {
|
||||||
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
QString iceServerAddress = domainObject[DOMAIN_ICE_SERVER_ADDRESS_KEY].toString();
|
||||||
|
@ -211,6 +217,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
|
||||||
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
|
QString domainIDString = domainObject[DOMAIN_ID_KEY].toString();
|
||||||
QUuid domainID(domainIDString);
|
QUuid domainID(domainIDString);
|
||||||
|
|
||||||
|
qDebug() << "Possible domain change required to connect to domain with ID" << domainID
|
||||||
|
<< "via ice-server at" << iceServerAddress;
|
||||||
|
|
||||||
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
|
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,6 +232,14 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
|
||||||
QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
|
QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
|
||||||
setRootPlaceName(newRootPlaceName);
|
setRootPlaceName(newRootPlaceName);
|
||||||
|
|
||||||
|
// check if we had a path to override the path returned
|
||||||
|
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
|
||||||
|
|
||||||
|
if (!overridePath.isEmpty()) {
|
||||||
|
if (!handleRelativeViewpoint(overridePath)){
|
||||||
|
qDebug() << "User entered path could not be handled as a viewpoint - " << overridePath;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// take the path that came back
|
// take the path that came back
|
||||||
const QString PLACE_PATH_KEY = "path";
|
const QString PLACE_PATH_KEY = "path";
|
||||||
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
|
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
|
||||||
|
@ -235,6 +252,9 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
|
||||||
qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
|
qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Received an address manager API response with no domain key. Cannot parse.";
|
qDebug() << "Received an address manager API response with no domain key. Cannot parse.";
|
||||||
qDebug() << locationMap;
|
qDebug() << locationMap;
|
||||||
|
@ -260,12 +280,21 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) {
|
||||||
|
|
||||||
const QString GET_PLACE = "/api/v1/places/%1";
|
const QString GET_PLACE = "/api/v1/places/%1";
|
||||||
|
|
||||||
void AddressManager::attemptPlaceNameLookup(const QString& lookupString) {
|
void AddressManager::attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath) {
|
||||||
// assume this is a place name and see if we can get any info on it
|
// assume this is a place name and see if we can get any info on it
|
||||||
QString placeName = QUrl::toPercentEncoding(lookupString);
|
QString placeName = QUrl::toPercentEncoding(lookupString);
|
||||||
|
|
||||||
|
QVariantMap requestParams;
|
||||||
|
if (!overridePath.isEmpty()) {
|
||||||
|
requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
|
||||||
|
}
|
||||||
|
|
||||||
AccountManager::getInstance().unauthenticatedRequest(GET_PLACE.arg(placeName),
|
AccountManager::getInstance().unauthenticatedRequest(GET_PLACE.arg(placeName),
|
||||||
QNetworkAccessManager::GetOperation,
|
QNetworkAccessManager::GetOperation,
|
||||||
apiCallbackParameters());
|
apiCallbackParameters(),
|
||||||
|
QByteArray(),
|
||||||
|
NULL,
|
||||||
|
requestParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
|
||||||
|
|
|
@ -47,7 +47,7 @@ public:
|
||||||
const QString& getRootPlaceName() const { return _rootPlaceName; }
|
const QString& getRootPlaceName() const { return _rootPlaceName; }
|
||||||
void setRootPlaceName(const QString& rootPlaceName);
|
void setRootPlaceName(const QString& rootPlaceName);
|
||||||
|
|
||||||
void attemptPlaceNameLookup(const QString& lookupString);
|
void attemptPlaceNameLookup(const QString& lookupString, const QString& overridePath = QString());
|
||||||
|
|
||||||
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
|
void setPositionGetter(PositionGetter positionGetter) { _positionGetter = positionGetter; }
|
||||||
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
|
void setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
|
||||||
|
@ -57,7 +57,7 @@ public:
|
||||||
public slots:
|
public slots:
|
||||||
void handleLookupString(const QString& lookupString);
|
void handleLookupString(const QString& lookupString);
|
||||||
void goToUser(const QString& username);
|
void goToUser(const QString& username);
|
||||||
void goToAddressFromObject(const QVariantMap& addressMap);
|
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
|
||||||
|
|
||||||
void storeCurrentAddress();
|
void storeCurrentAddress();
|
||||||
|
|
||||||
|
|
|
@ -54,14 +54,17 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
|
||||||
assert(entity);
|
assert(entity);
|
||||||
void* physicsInfo = entity->getPhysicsInfo();
|
void* physicsInfo = entity->getPhysicsInfo();
|
||||||
if (!physicsInfo) {
|
if (!physicsInfo) {
|
||||||
|
ShapeInfo shapeInfo;
|
||||||
|
entity->computeShapeInfo(shapeInfo);
|
||||||
|
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
|
||||||
|
if (shape) {
|
||||||
EntityMotionState* motionState = new EntityMotionState(entity);
|
EntityMotionState* motionState = new EntityMotionState(entity);
|
||||||
if (addObject(motionState)) {
|
|
||||||
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
entity->setPhysicsInfo(static_cast<void*>(motionState));
|
||||||
_entityMotionStates.insert(motionState);
|
_entityMotionStates.insert(motionState);
|
||||||
|
addObject(shapeInfo, shape, motionState);
|
||||||
} else {
|
} else {
|
||||||
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
|
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
|
||||||
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
|
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
|
||||||
delete motionState;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -306,12 +309,10 @@ void PhysicsEngine::computeCollisionEvents() {
|
||||||
// CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing
|
// CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing
|
||||||
// CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing
|
// CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing
|
||||||
|
|
||||||
bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
|
void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState) {
|
||||||
|
assert(shape);
|
||||||
assert(motionState);
|
assert(motionState);
|
||||||
ShapeInfo shapeInfo;
|
|
||||||
motionState->computeShapeInfo(shapeInfo);
|
|
||||||
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
|
|
||||||
if (shape) {
|
|
||||||
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
btVector3 inertia(0.0f, 0.0f, 0.0f);
|
||||||
float mass = 0.0f;
|
float mass = 0.0f;
|
||||||
btRigidBody* body = NULL;
|
btRigidBody* body = NULL;
|
||||||
|
@ -350,15 +351,11 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// wtf?
|
|
||||||
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
|
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
|
||||||
body->setRestitution(motionState->_restitution);
|
body->setRestitution(motionState->_restitution);
|
||||||
body->setFriction(motionState->_friction);
|
body->setFriction(motionState->_friction);
|
||||||
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
|
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
|
||||||
_dynamicsWorld->addRigidBody(body);
|
_dynamicsWorld->addRigidBody(body);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
|
bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
|
||||||
|
|
|
@ -78,7 +78,7 @@ public:
|
||||||
|
|
||||||
/// \param motionState pointer to Object's MotionState
|
/// \param motionState pointer to Object's MotionState
|
||||||
/// \return true if Object added
|
/// \return true if Object added
|
||||||
bool addObject(ObjectMotionState* motionState);
|
void addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState);
|
||||||
|
|
||||||
/// \param motionState pointer to Object's MotionState
|
/// \param motionState pointer to Object's MotionState
|
||||||
/// \return true if Object removed
|
/// \return true if Object removed
|
||||||
|
|
Loading…
Reference in a new issue