Merge branch 'master' of https://github.com/highfidelity/hifi into temp0bis

This commit is contained in:
Sam Gateau 2015-01-21 10:07:58 -08:00
commit 33dcbded22
61 changed files with 4192 additions and 4494 deletions

View file

@ -311,7 +311,7 @@ MetavoxelPersister::MetavoxelPersister(MetavoxelServer* server) :
const char* SAVE_FILE = "/resources/metavoxels.dat";
const int FILE_MAGIC = 0xDADAFACE;
const int FILE_VERSION = 2;
const int FILE_VERSION = 4;
void MetavoxelPersister::load() {
QString path = QCoreApplication::applicationDirPath() + SAVE_FILE;

View file

@ -42,6 +42,11 @@ function(AUTOSCRIBE_SHADER SHADER_FILE)
set(GLPROFILE MAC_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
elseif (UNIX)
set(GLPROFILE LINUX_GL)
set(SCRIBE_ARGS -c++ -D GLPROFILE ${GLPROFILE} ${SCRIBE_INCLUDES} -o ${SHADER_TARGET} ${SHADER_FILE})
add_custom_command(OUTPUT ${SHADER_TARGET} COMMAND scribe ${SCRIBE_ARGS} DEPENDS scribe ${SHADER_INCLUDE_FILES} ${SHADER_FILE})
else (APPLE)
set(GLPROFILE PC_GL)

View file

@ -8,11 +8,9 @@
#
macro(INCLUDE_BULLET)
find_package(Bullet)
if (BULLET_FOUND)
include_directories("${BULLET_INCLUDE_DIRS}")
if (APPLE OR UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DUSE_BULLET_PHYSICS -isystem ${BULLET_INCLUDE_DIRS}")
endif ()
endif (BULLET_FOUND)
find_package(Bullet REQUIRED)
include_directories("${BULLET_INCLUDE_DIRS}")
if (APPLE OR UNIX)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -isystem ${BULLET_INCLUDE_DIRS}")
endif()
endmacro(INCLUDE_BULLET)

View file

@ -0,0 +1,93 @@
# - Try to find the Bullet physics engine
#
# This module defines the following variables
#
# BULLET_FOUND - Was bullet found
# BULLET_INCLUDE_DIRS - the Bullet include directories
# BULLET_LIBRARIES - Link to this, by default it includes
# all bullet components (Dynamics,
# Collision, LinearMath, & SoftBody)
#
# This module accepts the following variables
#
# BULLET_ROOT - Can be set to bullet install path or Windows build path
#
# Modified on 2015.01.15 by Andrew Meadows
# This is an adapted version of the FindBullet.cmake module distributed with Cmake 2.8.12.2
# The original license for that file is displayed below.
#
#=============================================================================
# Copyright 2009 Kitware, Inc.
# Copyright 2009 Philip Lowman <philip@yhbt.com>
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
include("${MACRO_DIR}/HifiLibrarySearchHints.cmake")
hifi_library_search_hints("bullet")
macro(_FIND_BULLET_LIBRARY _var)
find_library(${_var}
NAMES
${ARGN}
HINTS
${BULLET_SEARCH_DIRS}
$ENV{BULLET_ROOT_DIR}
${BULLET_ROOT}
${BULLET_ROOT}/out/release8/libs
${BULLET_ROOT}/out/debug8/libs
PATH_SUFFIXES lib lib/Release lib/Debug
)
mark_as_advanced(${_var})
endmacro()
macro(_BULLET_APPEND_LIBRARIES _list _release)
set(_debug ${_release}_DEBUG)
if(${_debug})
set(${_list} ${${_list}} optimized ${${_release}} debug ${${_debug}})
else()
set(${_list} ${${_list}} ${${_release}})
endif()
endmacro()
find_path(BULLET_INCLUDE_DIR NAMES btBulletCollisionCommon.h
HINTS
${BULLET_SEARCH_DIRS}/include
$ENV{BULLET_ROOT_DIR}
${BULLET_ROOT}/include
${BULLET_ROOT}/src
PATH_SUFFIXES bullet
)
# Find the libraries
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY BulletDynamics)
_FIND_BULLET_LIBRARY(BULLET_DYNAMICS_LIBRARY_DEBUG BulletDynamics_Debug BulletDynamics_d)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY BulletCollision)
_FIND_BULLET_LIBRARY(BULLET_COLLISION_LIBRARY_DEBUG BulletCollision_Debug BulletCollision_d)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY BulletMath LinearMath)
_FIND_BULLET_LIBRARY(BULLET_MATH_LIBRARY_DEBUG BulletMath_Debug BulletMath_d LinearMath_Debug LinearMath_d)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY BulletSoftBody)
_FIND_BULLET_LIBRARY(BULLET_SOFTBODY_LIBRARY_DEBUG BulletSoftBody_Debug BulletSoftBody_d)
find_package_handle_standard_args(Bullet "Could NOT find Bullet, try to set the path to Bullet root folder in the system variable BULLET_ROOT_DIR"
BULLET_DYNAMICS_LIBRARY BULLET_COLLISION_LIBRARY BULLET_MATH_LIBRARY
BULLET_INCLUDE_DIR
)
set(BULLET_INCLUDE_DIRS ${BULLET_INCLUDE_DIR})
if(BULLET_FOUND)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_DYNAMICS_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_COLLISION_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_MATH_LIBRARY)
_BULLET_APPEND_LIBRARIES(BULLET_LIBRARIES BULLET_SOFTBODY_LIBRARY)
endif()

197
examples/billiards.js Normal file
View 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);

View file

@ -44,10 +44,16 @@ var entityListTool = EntityListTool();
var hasShownPropertiesTool = false;
var entityListVisible = false;
selectionManager.addEventListener(function() {
selectionDisplay.updateHandles();
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);
entityListTool.setVisible(true);
hasShownPropertiesTool = true;
}
});
@ -690,8 +696,8 @@ function setupModelMenus() {
print("delete exists... don't add ours");
}
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Model List..." });
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: "Entity List..." });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
afterItem: "Paste Models", isCheckable: true, isChecked: true });
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: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
@ -718,7 +725,7 @@ function cleanupModelMenus() {
Menu.removeMenuItem("Edit", "Delete");
}
Menu.removeMenuItem("Edit", "Model List...");
Menu.removeMenuItem("Edit", "Entity List...");
Menu.removeMenuItem("Edit", "Paste Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
@ -755,6 +762,28 @@ Script.update.connect(function (deltaTime) {
selectionDisplay.checkMove();
});
function deleteSelectedEntities() {
if (SelectionManager.hasSelection()) {
print(" Delete Entities");
SelectionManager.saveProperties();
var savedProperties = [];
for (var i = 0; i < selectionManager.selections.length; i++) {
var entityID = SelectionManager.selections[i];
var initialProperties = SelectionManager.savedProperties[entityID.id];
SelectionManager.savedProperties[entityID.id];
savedProperties.push({
entityID: entityID,
properties: initialProperties
});
Entities.deleteEntity(entityID);
}
SelectionManager.clearSelections();
pushCommandForSelections([], savedProperties);
} else {
print(" Delete Entity.... not holding...");
}
}
function handeMenuEvent(menuItem) {
if (menuItem == "Allow Selecting of Small Models") {
allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
@ -763,57 +792,7 @@ function handeMenuEvent(menuItem) {
} else if (menuItem == "Allow Selecting of Lights") {
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
} else if (menuItem == "Delete") {
if (SelectionManager.hasSelection()) {
print(" Delete Entities");
SelectionManager.saveProperties();
var savedProperties = [];
for (var i = 0; i < selectionManager.selections.length; i++) {
var entityID = SelectionManager.selections[i];
var initialProperties = SelectionManager.savedProperties[entityID.id];
SelectionManager.savedProperties[entityID.id];
savedProperties.push({
entityID: entityID,
properties: initialProperties
});
Entities.deleteEntity(entityID);
}
SelectionManager.clearSelections();
pushCommandForSelections([], savedProperties);
} else {
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;
}
}
deleteSelectedEntities();
} else if (menuItem == "Paste Models") {
modelImporter.paste();
} else if (menuItem == "Export Models") {
@ -826,6 +805,10 @@ function handeMenuEvent(menuItem) {
}
} else if (menuItem == "Import Models") {
modelImporter.doImport();
} else if (menuItem == "Entity List...") {
if (isActive) {
entityListTool.toggleVisible();
}
}
tooltip.show(false);
}
@ -842,7 +825,7 @@ Controller.keyPressEvent.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
if (event.text == "BACKSPACE" || event.text == "DELETE") {
handeMenuEvent("Delete");
deleteSelectedEntities();
} else if (event.text == "TAB") {
selectionDisplay.toggleSpaceMode();
} else if (event.text == "f") {

View file

@ -1,14 +1,32 @@
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="list.min.js"></script>
<script>
var entities = {};
var selectedEntities = [];
var currentSortColumn = 'type';
var currentSortOrder = 'asc';
var entityList = null;
var refreshEntityListTimer = null;
var ASC_STRING = '&nbsp;&#x25BE;';
var DESC_STRING = '&nbsp;&#x25B4;';
function loaded() {
entityList = new List('entity-list', { valueNames: ['type', 'url']});
entityList.clear();
elEntityTable = document.getElementById("entity-table");
elEntityTableBody = document.getElementById("entity-table-body");
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) {
var id = this.dataset.entityId;
@ -20,7 +38,8 @@
selectedEntities = selection;
entities[id].el.className = 'selected';
this.className = 'selected';
EventBridge.emitWebEvent(JSON.stringify({
type: "selectionUpdate",
focus: false,
@ -29,8 +48,6 @@
}
function onRowDoubleClicked() {
var id = this.dataset.entityId;
EventBridge.emitWebEvent(JSON.stringify({
type: "selectionUpdate",
focus: true,
@ -40,41 +57,91 @@
function addEntity(id, type, url) {
if (entities[id] === undefined) {
var el = document.createElement('tr');
el.setAttribute('id', 'entity_' + id);
el.innerHTML += "<td>" + type + "</td>";
el.innerHTML += "<td>" + url + "</td>";
el.dataset.entityId = id;
el.onclick = onRowClicked;
el.ondblclick = onRowDoubleClicked;
elEntityTable.appendChild(el);
var urlParts = url.split('/');
var filename = urlParts[urlParts.length - 1];
// Add element to local dict
entities[id] = {
id: id,
name: id,
el: el,
};
}
}
entityList.add([{ id: id, type: type, url: filename }], function(items) {
var el = items[0].elm;
var id = items[0]._values.id;
entities[id] = {
id: id,
name: id,
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 (entities[id] !== undefined) {
elEntityTable.removeChild(entities[id].el);
delete entities[id];
if (refreshEntityListTimer) {
clearTimeout(refreshEntityListTimer);
}
refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
}
}
function clearEntities() {
for (id in entities) {
elEntityTable.removeChild(entities[id].el);
}
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() {
clearEntities();
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
refreshEntities();
}
elTeleport.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
}
elDelete.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
refreshEntities();
}
if (window.EventBridge !== undefined) {
@ -82,16 +149,9 @@
data = JSON.parse(data);
if (data.type == "selectionUpdate") {
selectedEntities = data.selectedIDs;
for (var id in entities) {
entities[id].el.className = '';
}
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';
}
var notFound = updateSelectedEntities(data.selectedIDs);
if (notFound) {
refreshEntities();
}
} else if (data.type == "update") {
var newEntities = data.entities;
@ -99,27 +159,39 @@
var id = newEntities[i].id;
addEntity(id, newEntities[i].type, newEntities[i].url);
}
updateSelectedEntities(data.selectedIDs);
}
});
setTimeout(refreshEntities, 1000);
}
}
</script>
</head>
<body onload='loaded();'>
<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>
<table id="entity-table">
<thead>
<tr>
<th id="entity-type">Type</th>
<th id="entity-url">URL</th>
</tr>
</thead>
<tbody id="entity-table-body">
</tbody>
</table>
<div id="entity-list">
<input type="text" class="search" id="filter" placeholder="Filter" />
<table id="entity-table">
<thead>
<tr>
<th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline">&nbsp;&#x25BE;</span></th>
<th id="entity-url" data-sort="url">URL <span class="sort-order" style="display: none">&nbsp;&#x25BE;</span></th>
</tr>
</thead>
<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>
</table>
</div>
</body>
</html>

1
examples/html/list.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -5,9 +5,10 @@ body {
margin: 0;
padding: 0;
background-color: #efefef;
background-color: rgb(76, 76, 76);
color: rgb(204, 204, 204);
font-family: Arial;
font-size: 11.5px;
font-size: 11px;
-webkit-touch-callout: none;
-webkit-user-select: none;
@ -17,12 +18,6 @@ body {
user-select: none;
}
body.properties {
background-color: rgb(76, 76, 76);
color: rgb(204, 204, 204);
font-size: 11px;
}
.selectable {
-webkit-touch-callout: text;
-webkit-user-select: text;
@ -117,12 +112,13 @@ input.coord {
table#entity-table {
border-collapse: collapse;
font-family: Sans-Serif;
/* font-size: 12px; */
font-size: 10px;
width: 100%;
}
#entity-table tr {
cursor: pointer;
border-bottom: 1px solid rgb(63, 63, 63)
}
#entity-table tr.selected {
@ -139,17 +135,22 @@ table#entity-table {
}
#entity-table td {
font-size: 11px;
border: 0px black solid;
word-wrap: nowrap;
white-space: nowrap;
text-overflow: ellipsis;
}
#entity-table td.url {
white-space: nowrap;
overflow: hidden;
}
th#entity-type {
width: 60px;
}
th#entity-url {
}
div.input-area {
@ -226,3 +227,20 @@ table#properties-list {
col#col-label {
width: 130px;
}
div.outer {
position: relative;
}
div.inner {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
position: absolute;
width: 100%;
}
td {
vertical-align: top;
}

View file

@ -13,6 +13,10 @@ EntityListTool = function(opts) {
webView.setVisible(visible);
};
that.toggleVisible = function() {
that.setVisible(!visible);
}
selectionManager.addEventListener(function() {
var selectedIDs = [];
@ -24,10 +28,38 @@ EntityListTool = function(opts) {
type: 'selectionUpdate',
selectedIDs: selectedIDs,
};
print("Sending: " + 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) {
print("Got: " + data);
data = JSON.parse(data);
if (data.type == "selectionUpdate") {
var ids = data.entityIds;
@ -46,22 +78,13 @@ EntityListTool = function(opts) {
Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
}
} else if (data.type == "refresh") {
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 : "",
});
sendUpdate();
} else if (data.type == "teleport") {
if (selectionManager.hasSelection()) {
MyAvatar.position = selectionManager.worldPosition;
}
var data = {
type: "update",
entities: entities,
};
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
} else if (data.type == "delete") {
deleteSelectedEntities();
}
});

View file

@ -43,5 +43,5 @@ void main(void) {
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
// 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);
}

View file

@ -22,7 +22,8 @@ varying vec4 alphaValues;
void main(void) {
// blend the splat textures
gl_FragColor = gl_Color * (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
gl_FragColor = vec4(gl_Color.rgb, step(1.0, gl_Color.a + 1.0 / 512.0)) *
(texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y +
texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z +
texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w);

View file

@ -58,7 +58,7 @@ void main(void) {
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
// 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);
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
}

View file

@ -11,6 +11,9 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the splat texture offset
uniform vec3 splatTextureOffset;
// the splat textures scales on the S axis
uniform vec4 splatTextureScalesS;
@ -43,7 +46,7 @@ void main(void) {
normal = gl_Normal;
// pass along the scaled/offset texture coordinates
vec4 textureSpacePosition = gl_Vertex.xyyz;
vec4 textureSpacePosition = (gl_Vertex.xyz + splatTextureOffset).xyyz;
gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0],
splatTextureScalesS[0], splatTextureScalesT[0]);
gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1],

View file

@ -190,9 +190,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
_frameCount(0),
_fps(60.0f),
_justStarted(true),
#ifdef USE_BULLET_PHYSICS
_physicsEngine(glm::vec3(0.0f)),
#endif // USE_BULLET_PHYSICS
_entities(true, this, this),
_entityCollisionSystem(),
_entityClipboardRenderer(false, this, this),
@ -1725,12 +1723,10 @@ void Application::init() {
// save settings when avatar changes
connect(_myAvatar, &MyAvatar::transformChanged, this, &Application::bumpSettings);
#ifdef USE_BULLET_PHYSICS
EntityTree* tree = _entities.getTree();
_physicsEngine.setEntityTree(tree);
tree->setSimulation(&_physicsEngine);
_physicsEngine.init(&_entityEditSender);
#endif // USE_BULLET_PHYSICS
// make sure our texture cache knows about window size changes
DependencyManager::get<TextureCache>()->associateWithWidget(glCanvas.data());
@ -2041,12 +2037,10 @@ void Application::update(float deltaTime) {
updateDialogs(deltaTime); // update various stats dialogs if present
updateCursor(deltaTime); // Handle cursor updates
#ifdef USE_BULLET_PHYSICS
{
PerformanceTimer perfTimer("physics");
_physicsEngine.stepSimulation();
}
#endif // USE_BULLET_PHYSICS
if (!_aboutToQuit) {
PerformanceTimer perfTimer("entities");
@ -3682,7 +3676,6 @@ void Application::openUrl(const QUrl& url) {
void Application::updateMyAvatarTransform() {
bumpSettings();
#ifdef USE_BULLET_PHYSICS
const float SIMULATION_OFFSET_QUANTIZATION = 16.0f; // meters
glm::vec3 avatarPosition = _myAvatar->getPosition();
glm::vec3 physicsWorldOffset = _physicsEngine.getOriginOffset();
@ -3696,7 +3689,6 @@ void Application::updateMyAvatarTransform() {
// TODO: Andrew to replace this with method that actually moves existing object positions in PhysicsEngine
_physicsEngine.setOriginOffset(newOriginOffset);
}
#endif // USE_BULLET_PHYSICS
}
void Application::domainSettingsReceived(const QJsonObject& domainSettingsObject) {

View file

@ -463,9 +463,7 @@ private:
bool _justStarted;
Stars _stars;
#ifdef USE_BULLET_PHYSICS
PhysicsEngine _physicsEngine;
#endif // USE_BULLET_PHYSICS
EntityTreeRenderer _entities;
EntityCollisionSystem _entityCollisionSystem;

View file

@ -419,9 +419,8 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarDebugMenu, MenuOption::RenderFocusIndicator, 0, false);
QMenu* metavoxelOptionsMenu = developerMenu->addMenu("Metavoxels");
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false,
Application::getInstance()->getMetavoxels(), SLOT(refreshVoxelData()));
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderSpanners, 0, true);
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::DisplayHermiteData, 0, false);
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderHeightfields, 0, true);
addCheckableActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::RenderDualContourSurfaces, 0, true);
addActionToQMenuAndActionHash(metavoxelOptionsMenu, MenuOption::NetworkSimulator, 0, this,
SLOT(showMetavoxelNetworkSimulator()));

View file

@ -424,9 +424,9 @@ namespace MenuOption {
const QString RenderDualContourSurfaces = "Render Dual Contour Surfaces";
const QString RenderFocusIndicator = "Show Eye Focus";
const QString RenderHeadCollisionShapes = "Show Head Collision Shapes";
const QString RenderHeightfields = "Render Heightfields";
const QString RenderLookAtVectors = "Show Look-at Vectors";
const QString RenderSkeletonCollisionShapes = "Show Skeleton Collision Shapes";
const QString RenderSpanners = "Render Spanners";
const QString RenderTargetFramerate = "Framerate";
const QString RenderTargetFramerateUnlimited = "Unlimited";
const QString RenderTargetFramerate60 = "60";

File diff suppressed because it is too large Load diff

View file

@ -26,8 +26,8 @@
class HeightfieldBaseLayerBatch;
class HeightfieldSplatBatch;
class HermiteBatch;
class MetavoxelBatch;
class Model;
class VoxelBatch;
class VoxelSplatBatch;
/// Renders a metavoxel tree.
@ -59,48 +59,35 @@ public:
void setNetworkSimulation(const NetworkSimulation& simulation);
NetworkSimulation getNetworkSimulation();
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; }
void simulate(float deltaTime);
void render();
void renderHeightfieldCursor(const glm::vec3& position, float radius);
void renderVoxelCursor(const glm::vec3& position, float radius);
bool findFirstRayVoxelIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance);
Q_INVOKABLE void paintHeightfieldColor(const glm::vec3& position, float radius, const QColor& color);
Q_INVOKABLE void paintHeightfieldMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);
Q_INVOKABLE void paintVoxelColor(const glm::vec3& position, float radius, const QColor& color);
Q_INVOKABLE void paintVoxelMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material);
Q_INVOKABLE void setHeightfieldColor(const SharedObjectPointer& spanner, const QColor& color, bool paint = false);
Q_INVOKABLE void setVoxelColor(const SharedObjectPointer& spanner, const QColor& color);
Q_INVOKABLE void setVoxelMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material);
Q_INVOKABLE void setHeightfieldMaterial(const SharedObjectPointer& spanner,
const SharedObjectPointer& material, bool paint = false);
void addHeightfieldBaseBatch(const HeightfieldBaseLayerBatch& batch) { _heightfieldBaseBatches.append(batch); }
void addHeightfieldSplatBatch(const HeightfieldSplatBatch& batch) { _heightfieldSplatBatches.append(batch); }
void addVoxelBaseBatch(const VoxelBatch& batch) { _voxelBaseBatches.append(batch); }
void addVoxelBaseBatch(const MetavoxelBatch& batch) { _voxelBaseBatches.append(batch); }
void addVoxelSplatBatch(const VoxelSplatBatch& batch) { _voxelSplatBatches.append(batch); }
void addHermiteBatch(const HermiteBatch& batch) { _hermiteBatches.append(batch); }
Q_INVOKABLE void deleteTextures(int heightTextureID, int colorTextureID, int materialTextureID) const;
Q_INVOKABLE void deleteBuffers(int vertexBufferID, int indexBufferID, int hermiteBufferID) const;
signals:
void rendering();
public slots:
void refreshVoxelData();
protected:
Q_INVOKABLE void applyMaterialEdit(const MetavoxelEditMessage& message, bool reliable = false);
@ -111,9 +98,6 @@ private:
void guideToAugmented(MetavoxelVisitor& visitor, bool render = false);
AttributePointer _heightfieldBufferAttribute;
AttributePointer _voxelBufferAttribute;
MetavoxelLOD _lod;
QReadWriteLock _lodLock;
Frustum _frustum;
@ -123,7 +107,7 @@ private:
QVector<HeightfieldBaseLayerBatch> _heightfieldBaseBatches;
QVector<HeightfieldSplatBatch> _heightfieldSplatBatches;
QVector<VoxelBatch> _voxelBaseBatches;
QVector<MetavoxelBatch> _voxelBaseBatches;
QVector<VoxelSplatBatch> _voxelSplatBatches;
QVector<HermiteBatch> _hermiteBatches;
@ -166,16 +150,21 @@ private:
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
};
/// Base class for heightfield batches.
class HeightfieldBatch {
/// Base class for all batches.
class MetavoxelBatch {
public:
QOpenGLBuffer* vertexBuffer;
QOpenGLBuffer* indexBuffer;
GLuint vertexBufferID;
GLuint indexBufferID;
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale;
int vertexCount;
int indexCount;
};
/// Base class for heightfield batches.
class HeightfieldBatch : public MetavoxelBatch {
public:
GLuint heightTextureID;
glm::vec4 heightScale;
};
@ -199,18 +188,10 @@ public:
int materialIndex;
};
/// Base class for voxel batches.
class VoxelBatch {
public:
QOpenGLBuffer* vertexBuffer;
QOpenGLBuffer* indexBuffer;
int vertexCount;
int indexCount;
};
/// A batch containing a voxel splat.
class VoxelSplatBatch : public VoxelBatch {
class VoxelSplatBatch : public MetavoxelBatch {
public:
glm::vec3 splatTextureOffset;
int splatTextureIDs[4];
glm::vec4 splatTextureScalesS;
glm::vec4 splatTextureScalesT;
@ -220,7 +201,10 @@ public:
/// A batch containing Hermite data for debugging.
class HermiteBatch {
public:
QOpenGLBuffer* vertexBuffer;
GLuint vertexBufferID;
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale;
int vertexCount;
};
@ -271,8 +255,6 @@ public:
void setRenderedAugmentedData(const MetavoxelData& data) { _renderedAugmentedData = data; }
virtual int parseData(const QByteArray& packet);
Q_INVOKABLE void refreshVoxelData();
protected:
@ -295,7 +277,8 @@ public:
virtual ~BufferData();
virtual void render(bool cursor = false) = 0;
virtual void render(const glm::vec3& translation, const glm::quat& rotation,
const glm::vec3& scale, bool cursor = false) = 0;
};
typedef QExplicitlySharedDataPointer<BufferData> BufferDataPointer;
@ -334,44 +317,34 @@ public:
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices, const QVector<glm::vec3>& hermite,
const QMultiHash<VoxelCoord, int>& quadIndices, int size, const QVector<SharedObjectPointer>& materials =
QVector<SharedObjectPointer>());
virtual ~VoxelBuffer();
bool isHermiteEnabled() const { return _hermiteEnabled; }
/// Finds the first intersection between the described ray and the voxel data.
/// \param entry the entry point of the ray in relative coordinates, from (0, 0, 0) to (1, 1, 1)
bool findFirstRayIntersection(const glm::vec3& entry, const glm::vec3& origin,
const glm::vec3& direction, float& distance) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
virtual void render(bool cursor = false);
virtual void render(const glm::vec3& translation, const glm::quat& rotation,
const glm::vec3& scale, bool cursor = false);
private:
QVector<VoxelPoint> _vertices;
QVector<int> _indices;
QVector<glm::vec3> _hermite;
bool _hermiteEnabled;
QMultiHash<VoxelCoord, int> _quadIndices;
int _size;
int _vertexCount;
int _indexCount;
int _hermiteCount;
QOpenGLBuffer _vertexBuffer;
QOpenGLBuffer _indexBuffer;
QOpenGLBuffer _hermiteBuffer;
GLuint _vertexBufferID;
GLuint _indexBufferID;
GLuint _hermiteBufferID;
QVector<SharedObjectPointer> _materials;
QVector<NetworkTexturePointer> _networkTextures;
};
/// A client-side attribute that stores renderable buffers.
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE BufferDataAttribute(const QString& name = QString());
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
/// Renders metavoxels as points.
class DefaultMetavoxelRendererImplementation : public MetavoxelRendererImplementation {
Q_OBJECT
@ -380,7 +353,6 @@ public:
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
virtual void simulate(MetavoxelData& data, float deltaTime, MetavoxelInfo& info, const MetavoxelLOD& lod);
virtual void render(MetavoxelData& data, MetavoxelInfo& info, const MetavoxelLOD& lod);
};
@ -450,6 +422,9 @@ public:
HeightfieldNodeRenderer();
virtual ~HeightfieldNodeRenderer();
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
void render(const HeightfieldNodePointer& node, const glm::vec3& translation,
const glm::quat& rotation, const glm::vec3& scale, bool cursor);
@ -460,6 +435,8 @@ private:
GLuint _materialTextureID;
QVector<NetworkTexturePointer> _networkTextures;
BufferDataPointer _voxels;
typedef QPair<int, int> IntPair;
typedef QPair<QOpenGLBuffer, QOpenGLBuffer> BufferPair;
static QHash<IntPair, BufferPair> _bufferPairs;

View file

@ -41,19 +41,27 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid
_dockWidget = new QDockWidget(title, toolWindow);
_dockWidget->setFeatures(QDockWidget::DockWidgetMovable);
QWebView* webView = new QWebView(_dockWidget);
webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge);
webView->setUrl(url);
_dockWidget->setWidget(webView);
_webView = new QWebView(_dockWidget);
_webView->setUrl(url);
addEventBridgeToWindowObject();
_dockWidget->setWidget(_webView);
toolWindow->addDockWidget(Qt::RightDockWidgetArea, _dockWidget);
connect(_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared,
this, &WebWindowClass::addEventBridgeToWindowObject);
connect(this, &WebWindowClass::destroyed, _dockWidget, &QWidget::deleteLater);
}
WebWindowClass::~WebWindowClass() {
}
void WebWindowClass::addEventBridgeToWindowObject() {
_webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge);
}
void WebWindowClass::setVisible(bool visible) {
if (visible) {
QMetaObject::invokeMethod(

View file

@ -14,6 +14,7 @@
#include <QScriptContext>
#include <QScriptEngine>
#include <QWebView>
class ScriptEventBridge : public QObject {
Q_OBJECT
@ -42,9 +43,11 @@ public:
public slots:
void setVisible(bool visible);
ScriptEventBridge* getEventBridge() const { return _eventBridge; }
void addEventBridgeToWindowObject();
private:
QDockWidget* _dockWidget;
QWebView* _webView;
ScriptEventBridge* _eventBridge;
};

View file

@ -97,7 +97,7 @@ MetavoxelEditor::MetavoxelEditor() :
_gridSpacing->setMinimum(-FLT_MAX);
_gridSpacing->setMaximum(FLT_MAX);
_gridSpacing->setPrefix("2^");
_gridSpacing->setValue(-3.0);
_gridSpacing->setValue(0.0);
connect(_gridSpacing, SIGNAL(valueChanged(double)), SLOT(alignGridPosition()));
formLayout->addRow("Grid Position:", _gridPosition = new QDoubleSpinBox());
@ -125,13 +125,13 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new InsertSpannerTool(this));
addTool(new RemoveSpannerTool(this));
addTool(new ClearSpannersTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new HeightfieldHeightBrushTool(this));
addTool(new HeightfieldMaterialBrushTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new VoxelMaterialBoxTool(this));
addTool(new VoxelMaterialSpannerTool(this));
addTool(new VoxelMaterialBrushTool(this));
addTool(new VoxelSculptBrushTool(this));
addTool(new HeightfieldSculptBrushTool(this));
addTool(new HeightfieldFillBrushTool(this));
addTool(new HeightfieldMaterialBoxTool(this));
addTool(new HeightfieldMaterialSpannerTool(this));
updateAttributes();
@ -331,6 +331,9 @@ void MetavoxelEditor::render() {
MetavoxelTool* tool = getActiveTool();
if (tool) {
tool->render();
if (!tool->getUsesGrid()) {
return;
}
}
glDepthMask(GL_FALSE);
@ -386,10 +389,11 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const {
ProgramObject MetavoxelEditor::_gridProgram;
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing, bool usesGrid) :
_editor(editor),
_usesValue(usesValue),
_userFacing(userFacing) {
_userFacing(userFacing),
_usesGrid(usesGrid) {
QVBoxLayout* layout = new QVBoxLayout();
setLayout(layout);
@ -670,7 +674,7 @@ void InsertSpannerTool::applyEdit(const AttributePointer& attribute, const Share
}
RemoveSpannerTool::RemoveSpannerTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Remove Spanner", false) {
MetavoxelTool(editor, "Remove Spanner", false, true, false) {
}
bool RemoveSpannerTool::appliesTo(const AttributePointer& attribute) const {
@ -697,7 +701,7 @@ bool RemoveSpannerTool::eventFilter(QObject* watched, QEvent* event) {
}
ClearSpannersTool::ClearSpannersTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Clear Spanners", false) {
MetavoxelTool(editor, "Clear Spanners", false, true, false) {
QPushButton* button = new QPushButton("Clear");
layout()->addWidget(button);
@ -718,7 +722,7 @@ void ClearSpannersTool::clear() {
}
HeightfieldTool::HeightfieldTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false) {
MetavoxelTool(editor, name, false, true, false) {
QWidget* widget = new QWidget();
widget->setLayout(_form = new QFormLayout());
@ -807,7 +811,7 @@ void ImportHeightfieldTool::updateSpanner() {
}
HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false),
MetavoxelTool(editor, name, false, true, false),
_positionValid(false) {
QWidget* widget = new QWidget();
@ -817,7 +821,7 @@ HeightfieldBrushTool::HeightfieldBrushTool(MetavoxelEditor* editor, const QStrin
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
_radius->setSingleStep(0.01);
_radius->setMaximum(FLT_MAX);
_radius->setValue(1.0);
_radius->setValue(5.0);
}
bool HeightfieldBrushTool::appliesTo(const AttributePointer& attribute) const {
@ -865,11 +869,19 @@ HeightfieldHeightBrushTool::HeightfieldHeightBrushTool(MetavoxelEditor* editor)
_height->setMinimum(-FLT_MAX);
_height->setMaximum(FLT_MAX);
_height->setValue(1.0);
_form->addRow("Mode:", _mode = new QComboBox());
_mode->addItem("Raise/Lower");
_mode->addItem("Set");
_mode->addItem("Erase");
}
QVariant HeightfieldHeightBrushTool::createEdit(bool alternate) {
const int SET_MODE_INDEX = 1;
const int ERASE_MODE_INDEX = 2;
return QVariant::fromValue(PaintHeightfieldHeightEdit(_position, _radius->value(),
alternate ? -_height->value() : _height->value()));
alternate ? -_height->value() : _height->value(), _mode->currentIndex() == SET_MODE_INDEX,
_mode->currentIndex() == ERASE_MODE_INDEX));
}
MaterialControl::MaterialControl(QWidget* widget, QFormLayout* form, bool clearable) :
@ -939,16 +951,58 @@ HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* edit
}
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
if (alternate) {
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(0, 0, 0, 0), true));
} else {
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), _materialControl->getMaterial(),
_materialControl->getColor()));
}
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
_materialControl->getMaterial(), _materialControl->getColor(), true));
}
}
VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Voxel Material (Box)", false) {
HeightfieldSculptBrushTool::HeightfieldSculptBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Sculpt Brush"),
_materialControl(new MaterialControl(this, _form, true)) {
}
QVariant HeightfieldSculptBrushTool::createEdit(bool alternate) {
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
if (alternate) {
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(0, 0, 0, 0)));
} else {
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
_materialControl->getMaterial(), _materialControl->getColor()));
}
}
HeightfieldFillBrushTool::HeightfieldFillBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Fill Brush") {
_form->addRow("Mode:", _mode = new QComboBox());
_mode->addItem("Fill");
_mode->addItem("Voxelize");
}
QVariant HeightfieldFillBrushTool::createEdit(bool alternate) {
const int FILL_MODE_INDEX = 0;
if (_mode->currentIndex() == FILL_MODE_INDEX) {
return QVariant::fromValue(FillHeightfieldHeightEdit(_position, _radius->value()));
}
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
return QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(), false, true));
}
HeightfieldMaterialBoxTool::HeightfieldMaterialBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Material (Box)", false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
@ -965,32 +1019,32 @@ VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
_materialControl = new MaterialControl(this, form, true);
}
bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
bool HeightfieldMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SpannerSetAttribute");
}
bool VoxelMaterialBoxTool::shouldSnapToGrid() {
bool HeightfieldMaterialBoxTool::shouldSnapToGrid() {
return _snapToGrid->isChecked();
}
QColor VoxelMaterialBoxTool::getColor() {
QColor HeightfieldMaterialBoxTool::getColor() {
return _materialControl->getColor();
}
void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
void HeightfieldMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
Cuboid* cuboid = new Cuboid();
cuboid->setTranslation((maximum + minimum) * 0.5f);
glm::vec3 vector = (maximum - minimum) * 0.5f;
cuboid->setScale(vector.x);
cuboid->setAspectY(vector.y / vector.x);
cuboid->setAspectZ(vector.z / vector.x);
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(cuboid),
MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(SharedObjectPointer(cuboid),
_materialControl->getMaterial(), _materialControl->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
PlaceSpannerTool(editor, "Set Voxel Material (Spanner)", QString(), false) {
HeightfieldMaterialSpannerTool::HeightfieldMaterialSpannerTool(MetavoxelEditor* editor) :
PlaceSpannerTool(editor, "Set Material (Spanner)", QString(), false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
@ -1004,110 +1058,25 @@ VoxelMaterialSpannerTool::VoxelMaterialSpannerTool(MetavoxelEditor* editor) :
QPushButton* place = new QPushButton("Set");
layout()->addWidget(place);
connect(place, &QPushButton::clicked, this, &VoxelMaterialSpannerTool::place);
connect(place, &QPushButton::clicked, this, &HeightfieldMaterialSpannerTool::place);
}
bool VoxelMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
bool HeightfieldMaterialSpannerTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("SpannerSetAttribute");
}
SharedObjectPointer VoxelMaterialSpannerTool::getSpanner() {
SharedObjectPointer HeightfieldMaterialSpannerTool::getSpanner() {
return _spannerEditor->getObject();
}
QColor VoxelMaterialSpannerTool::getColor() {
QColor HeightfieldMaterialSpannerTool::getColor() {
return _materialControl->getColor();
}
void VoxelMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
void HeightfieldMaterialSpannerTool::applyEdit(const AttributePointer& attribute, const SharedObjectPointer& spanner) {
static_cast<Spanner*>(spanner.data())->setWillBeVoxelized(true);
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSpannerEdit(spanner,
MetavoxelEditMessage message = { QVariant::fromValue(HeightfieldMaterialSpannerEdit(spanner,
_materialControl->getMaterial(), _materialControl->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelBrushTool::VoxelBrushTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false, true),
_positionValid(false) {
QWidget* widget = new QWidget();
widget->setLayout(_form = new QFormLayout());
layout()->addWidget(widget);
_form->addRow("Radius:", _radius = new QDoubleSpinBox());
_radius->setSingleStep(0.01);
_radius->setMaximum(FLT_MAX);
_radius->setValue(0.25);
}
bool VoxelBrushTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
void VoxelBrushTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
// find the intersection with the voxels
glm::vec3 origin = Application::getInstance()->getMouseRayOrigin();
glm::vec3 direction = Application::getInstance()->getMouseRayDirection();
float heightfieldDistance = FLT_MAX, voxelDistance = FLT_MAX;
if (!(Application::getInstance()->getMetavoxels()->findFirstRayHeightfieldIntersection(
origin, direction, heightfieldDistance) |
Application::getInstance()->getMetavoxels()->findFirstRayVoxelIntersection(origin, direction, voxelDistance))) {
_positionValid = false;
return;
}
_positionValid = true;
Application::getInstance()->getMetavoxels()->renderVoxelCursor(
_position = origin + qMin(heightfieldDistance, voxelDistance) * direction, _radius->value());
}
bool VoxelBrushTool::eventFilter(QObject* watched, QEvent* event) {
if (event->type() == QEvent::Wheel) {
float angle = static_cast<QWheelEvent*>(event)->angleDelta().y();
const float ANGLE_SCALE = 1.0f / 1000.0f;
_radius->setValue(_radius->value() * glm::pow(2.0f, angle * ANGLE_SCALE));
return true;
} else if (event->type() == QEvent::MouseButtonPress && _positionValid) {
MetavoxelEditMessage message = { createEdit(static_cast<QMouseEvent*>(event)->button() == Qt::RightButton) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
return true;
}
return false;
}
VoxelMaterialBrushTool::VoxelMaterialBrushTool(MetavoxelEditor* editor) :
VoxelBrushTool(editor, "Material Brush"),
_materialControl(new MaterialControl(this, _form)) {
}
QVariant VoxelMaterialBrushTool::createEdit(bool alternate) {
if (alternate) {
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
} else {
return QVariant::fromValue(PaintVoxelMaterialEdit(_position, _radius->value(),
_materialControl->getMaterial(), _materialControl->getColor()));
}
}
VoxelSculptBrushTool::VoxelSculptBrushTool(MetavoxelEditor* editor) :
VoxelBrushTool(editor, "Sculpt Brush"),
_materialControl(new MaterialControl(this, _form, true)) {
}
QVariant VoxelSculptBrushTool::createEdit(bool alternate) {
Sphere* sphere = new Sphere();
sphere->setTranslation(_position);
sphere->setScale(_radius->value());
if (alternate) {
return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere),
SharedObjectPointer(), QColor(0, 0, 0, 0)));
} else {
return QVariant::fromValue(VoxelMaterialSpannerEdit(SharedObjectPointer(sphere),
_materialControl->getMaterial(), _materialControl->getColor()));
}
}

View file

@ -92,12 +92,15 @@ class MetavoxelTool : public QWidget {
public:
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true,
bool userFacing = true, bool usesGrid = true);
bool getUsesValue() const { return _usesValue; }
bool isUserFacing() const { return _userFacing; }
bool getUsesGrid() const { return _usesGrid; }
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void simulate(float deltaTime);
@ -113,6 +116,7 @@ protected:
MetavoxelEditor* _editor;
bool _usesValue;
bool _userFacing;
bool _usesGrid;
};
/// Base class for tools that allow dragging out a 3D box.
@ -342,6 +346,7 @@ protected:
private:
QDoubleSpinBox* _height;
QComboBox* _mode;
};
/// Contains widgets for editing materials.
@ -387,13 +392,47 @@ private:
MaterialControl* _materialControl;
};
/// Allows setting voxel materials by dragging out a box.
class VoxelMaterialBoxTool : public BoxTool {
/// Allows sculpting parts of the heightfield.
class HeightfieldSculptBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
VoxelMaterialBoxTool(MetavoxelEditor* editor);
HeightfieldSculptBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
MaterialControl* _materialControl;
};
/// Allows "filling" (removing dual contour stack data) parts of the heightfield.
class HeightfieldFillBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
HeightfieldFillBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
QComboBox* _mode;
};
/// Allows setting heightfield materials by dragging out a box.
class HeightfieldMaterialBoxTool : public BoxTool {
Q_OBJECT
public:
HeightfieldMaterialBoxTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
@ -411,13 +450,13 @@ private:
MaterialControl* _materialControl;
};
/// Allows setting voxel materials by placing a spanner.
class VoxelMaterialSpannerTool : public PlaceSpannerTool {
/// Allows setting heightfield materials by placing a spanner.
class HeightfieldMaterialSpannerTool : public PlaceSpannerTool {
Q_OBJECT
public:
VoxelMaterialSpannerTool(MetavoxelEditor* editor);
HeightfieldMaterialSpannerTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
@ -433,63 +472,4 @@ private:
MaterialControl* _materialControl;
};
/// Base class for voxel brush tools.
class VoxelBrushTool : public MetavoxelTool {
Q_OBJECT
public:
VoxelBrushTool(MetavoxelEditor* editor, const QString& name);
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QVariant createEdit(bool alternate) = 0;
QFormLayout* _form;
QDoubleSpinBox* _radius;
glm::vec3 _position;
bool _positionValid;
};
/// Allows texturing parts of the voxel field.
class VoxelMaterialBrushTool : public VoxelBrushTool {
Q_OBJECT
public:
VoxelMaterialBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
MaterialControl* _materialControl;
};
/// Allows sculpting parts of the voxel field.
class VoxelSculptBrushTool : public VoxelBrushTool {
Q_OBJECT
public:
VoxelSculptBrushTool(MetavoxelEditor* editor);
protected:
virtual QVariant createEdit(bool alternate);
private:
MaterialControl* _materialControl;
};
#endif // hifi_MetavoxelEditor_h

View file

@ -220,7 +220,12 @@ unsigned int Overlays::cloneOverlay(unsigned int id) {
} else if (_overlaysWorld.contains(id)) {
thisOverlay = _overlaysWorld[id];
}
return addOverlay(thisOverlay->createClone());
if (thisOverlay) {
return addOverlay(thisOverlay->createClone());
}
return 0; // Not found
}
bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) {

View file

@ -238,8 +238,8 @@ QSizeF Text3DOverlay::textSize(const QString& text) const {
QFont font(SANS_FONT_FAMILY, FIXED_FONT_POINT_SIZE); // Same font properties as render()
QFontMetrics fontMetrics(font);
const float TEXT_SCALE_ADJUST = 1.02f; // Experimentally detemined for the specified font
const int TEXT_HEIGHT_ADJUST = -6;
const float TEXT_SCALE_ADJUST = 1.025f; // Experimentally detemined for the specified font
const int TEXT_HEIGHT_ADJUST = -10;
float scaleFactor = _lineHeight * TEXT_SCALE_ADJUST * LINE_SCALE_RATIO / (float)FIXED_FONT_POINT_SIZE;
QStringList lines = text.split(QRegExp("\r\n|\r|\n"));

View file

@ -20,13 +20,9 @@
#include "Spanner.h"
REGISTER_META_OBJECT(FloatAttribute)
REGISTER_META_OBJECT(MaterialObject)
REGISTER_META_OBJECT(SharedObjectAttribute)
REGISTER_META_OBJECT(SharedObjectSetAttribute)
REGISTER_META_OBJECT(SpannerSetAttribute)
REGISTER_META_OBJECT(VoxelColorAttribute)
REGISTER_META_OBJECT(VoxelHermiteAttribute)
REGISTER_META_OBJECT(VoxelMaterialAttribute)
static int attributePointerMetaTypeId = qRegisterMetaType<AttributePointer>();
static int ownedAttributeValueMetaTypeId = qRegisterMetaType<OwnedAttributeValue>();
@ -41,21 +37,12 @@ AttributeRegistry::AttributeRegistry() :
new DefaultMetavoxelGuide()))),
_rendererAttribute(registerAttribute(new SharedObjectAttribute("renderer", &MetavoxelRenderer::staticMetaObject,
new DefaultMetavoxelRenderer()))),
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))),
_voxelColorAttribute(registerAttribute(new VoxelColorAttribute("voxelColor"))),
_voxelMaterialAttribute(registerAttribute(new VoxelMaterialAttribute("voxelMaterial"))),
_voxelHermiteAttribute(registerAttribute(new VoxelHermiteAttribute("voxelHermite"))) {
_spannersAttribute(registerAttribute(new SpannerSetAttribute("spanners", &Spanner::staticMetaObject))) {
// our baseline LOD threshold is for voxels; spanners and heightfields are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 8.0f;
// our baseline LOD threshold is for voxels; spanners are a different story
const float SPANNER_LOD_THRESHOLD_MULTIPLIER = 16.0f;
_spannersAttribute->setLODThresholdMultiplier(SPANNER_LOD_THRESHOLD_MULTIPLIER);
_spannersAttribute->setUserFacing(true);
const float VOXEL_LOD_THRESHOLD_MULTIPLIER = 16.0f;
_voxelColorAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
_voxelColorAttribute->setUserFacing(true);
_voxelMaterialAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
_voxelHermiteAttribute->setLODThresholdMultiplier(VOXEL_LOD_THRESHOLD_MULTIPLIER);
}
static QScriptValue qDebugFunction(QScriptContext* context, QScriptEngine* engine) {
@ -275,846 +262,6 @@ FloatAttribute::FloatAttribute(const QString& name) :
SimpleInlineAttribute(name) {
}
const float CHAR_SCALE = 127.0f;
const float INVERSE_CHAR_SCALE = 1.0f / CHAR_SCALE;
QRgb packNormal(const glm::vec3& normal) {
return qRgb((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE));
}
QRgb packNormal(const glm::vec3& normal, int alpha) {
return qRgba((char)(normal.x * CHAR_SCALE), (char)(normal.y * CHAR_SCALE), (char)(normal.z * CHAR_SCALE), alpha);
}
glm::vec3 unpackNormal(QRgb value) {
return glm::vec3((char)qRed(value) * INVERSE_CHAR_SCALE, (char)qGreen(value) * INVERSE_CHAR_SCALE,
(char)qBlue(value) * INVERSE_CHAR_SCALE);
}
DataBlock::~DataBlock() {
}
MaterialObject::MaterialObject() :
_scaleS(1.0f),
_scaleT(1.0f) {
}
static QHash<uchar, int> countIndices(const QByteArray& contents) {
QHash<uchar, int> counts;
for (const uchar* src = (const uchar*)contents.constData(), *end = src + contents.size(); src != end; src++) {
if (*src != 0) {
counts[*src]++;
}
}
return counts;
}
const float EIGHT_BIT_MAXIMUM = 255.0f;
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents) {
if (!(material && static_cast<MaterialObject*>(material.data())->getDiffuse().isValid())) {
return 0;
}
// first look for a matching existing material, noting the first reusable slot
int firstEmptyIndex = -1;
for (int i = 0; i < materials.size(); i++) {
const SharedObjectPointer& existingMaterial = materials.at(i);
if (existingMaterial) {
if (existingMaterial->equals(material.data())) {
return i + 1;
}
} else if (firstEmptyIndex == -1) {
firstEmptyIndex = i;
}
}
// if nothing found, use the first empty slot or append
if (firstEmptyIndex != -1) {
materials[firstEmptyIndex] = material;
return firstEmptyIndex + 1;
}
if (materials.size() < EIGHT_BIT_MAXIMUM) {
materials.append(material);
return materials.size();
}
// last resort: find the least-used material and remove it
QHash<uchar, int> counts = countIndices(contents);
uchar materialIndex = 0;
int lowestCount = INT_MAX;
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
if (it.value() < lowestCount) {
materialIndex = it.key();
lowestCount = it.value();
}
}
contents.replace((char)materialIndex, (char)0);
return materialIndex;
}
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents) {
QHash<uchar, int> counts = countIndices(contents);
for (int i = 0; i < materials.size(); i++) {
if (counts.value(i + 1) == 0) {
materials[i] = SharedObjectPointer();
}
}
while (!(materials.isEmpty() || materials.last())) {
materials.removeLast();
}
}
const int VOXEL_COLOR_HEADER_SIZE = sizeof(qint32) * 6;
static QByteArray encodeVoxelColor(int offsetX, int offsetY, int offsetZ,
int sizeX, int sizeY, int sizeZ, const QVector<QRgb>& contents) {
QByteArray inflated(VOXEL_COLOR_HEADER_SIZE, 0);
qint32* header = (qint32*)inflated.data();
*header++ = offsetX;
*header++ = offsetY;
*header++ = offsetZ;
*header++ = sizeX;
*header++ = sizeY;
*header++ = sizeZ;
inflated.append((const char*)contents.constData(), contents.size() * sizeof(QRgb));
return qCompress(inflated);
}
static QVector<QRgb> decodeVoxelColor(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ,
int& sizeX, int& sizeY, int& sizeZ) {
QByteArray inflated = qUncompress(encoded);
const qint32* header = (const qint32*)inflated.constData();
offsetX = *header++;
offsetY = *header++;
offsetZ = *header++;
sizeX = *header++;
sizeY = *header++;
sizeZ = *header++;
int payloadSize = inflated.size() - VOXEL_COLOR_HEADER_SIZE;
QVector<QRgb> contents(payloadSize / sizeof(QRgb));
memcpy(contents.data(), inflated.constData() + VOXEL_COLOR_HEADER_SIZE, payloadSize);
return contents;
}
VoxelColorData::VoxelColorData(const QVector<QRgb>& contents, int size) :
_contents(contents),
_size(size) {
}
VoxelColorData::VoxelColorData(Bitstream& in, int bytes) {
read(in, bytes);
}
VoxelColorData::VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference) {
if (!reference) {
read(in, bytes);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->setEncodedDelta(in.readAligned(bytes));
reference->setDeltaData(DataBlockPointer(this));
_contents = reference->getContents();
_size = reference->getSize();
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
QVector<QRgb> delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
if (delta.isEmpty()) {
return;
}
if (offsetX == 0) {
_contents = delta;
_size = sizeX;
return;
}
int minX = offsetX - 1;
int minY = offsetY - 1;
int minZ = offsetZ - 1;
const QRgb* src = delta.constData();
int size2 = _size * _size;
QRgb* planeDest = _contents.data() + minZ * size2 + minY * _size + minX;
int length = sizeX * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeDest += size2) {
QRgb* dest = planeDest;
for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) {
memcpy(dest, src, length);
}
}
}
void VoxelColorData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
_encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
}
void VoxelColorData::writeDelta(Bitstream& out, const VoxelColorDataPointer& reference) {
if (!reference || reference->getSize() != _size) {
write(out);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
int minX = _size, minY = _size, minZ = _size;
int maxX = -1, maxY = -1, maxZ = -1;
const QRgb* src = _contents.constData();
const QRgb* ref = reference->getContents().constData();
for (int z = 0; z < _size; z++) {
bool differenceZ = false;
for (int y = 0; y < _size; y++) {
bool differenceY = false;
for (int x = 0; x < _size; x++) {
if (*src++ != *ref++) {
minX = qMin(minX, x);
maxX = qMax(maxX, x);
differenceY = differenceZ = true;
}
}
if (differenceY) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
}
}
if (differenceZ) {
minZ = qMin(minZ, z);
maxZ = qMax(maxZ, z);
}
}
QVector<QRgb> delta;
int sizeX = 0, sizeY = 0, sizeZ = 0;
if (maxX >= minX) {
sizeX = maxX - minX + 1;
sizeY = maxY - minY + 1;
sizeZ = maxZ - minZ + 1;
delta = QVector<QRgb>(sizeX * sizeY * sizeZ, 0);
QRgb* dest = delta.data();
int size2 = _size * _size;
const QRgb* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX;
int length = sizeX * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeSrc += size2) {
src = planeSrc;
for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) {
memcpy(dest, src, length);
}
}
}
reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
reference->setDeltaData(DataBlockPointer(this));
}
out << reference->getEncodedDelta().size();
out.writeAligned(reference->getEncodedDelta());
}
void VoxelColorData::read(Bitstream& in, int bytes) {
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
_contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
_size = sizeX;
}
VoxelColorAttribute::VoxelColorAttribute(const QString& name) :
InlineAttribute<VoxelColorDataPointer>(name) {
}
void VoxelColorAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer();
} else {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(in, size));
}
}
void VoxelColorAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelColorDataPointer data = decodeInline<VoxelColorDataPointer>(value);
if (data) {
data->write(out);
} else {
out << 0;
}
}
void VoxelColorAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer();
} else {
*(VoxelColorDataPointer*)&value = VoxelColorDataPointer(new VoxelColorData(
in, size, decodeInline<VoxelColorDataPointer>(reference)));
}
}
void VoxelColorAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelColorDataPointer data = decodeInline<VoxelColorDataPointer>(value);
if (data) {
data->writeDelta(out, decodeInline<VoxelColorDataPointer>(reference));
} else {
out << 0;
}
}
bool VoxelColorAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelColorDataPointer pointer = decodeInline<VoxelColorDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getSize());
}
}
if (maxSize == 0) {
*(VoxelColorDataPointer*)&parent = VoxelColorDataPointer();
return true;
}
int size = maxSize;
int area = size * size;
QVector<QRgb> contents(area * size);
int halfSize = size / 2;
int halfSizeComplement = size - halfSize;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelColorDataPointer child = decodeInline<VoxelColorDataPointer>(children[i]);
if (!child) {
continue;
}
const QVector<QRgb>& childContents = child->getContents();
int childSize = child->getSize();
int childArea = childSize * childSize;
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
int Z_SHIFT = 2;
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
QRgb* dest = contents.data() + (zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize);
const QRgb* src = childContents.data();
const int MAX_ALPHA = 255;
if (childSize == size) {
// simple case: one destination value for four child values
for (int z = 0; z < halfSizeComplement; z++) {
int offset4 = (z == halfSize) ? 0 : childArea;
for (int y = 0; y < halfSizeComplement; y++) {
int offset2 = (y == halfSize) ? 0 : childSize;
int offset6 = offset4 + offset2;
for (QRgb* end = dest + halfSizeComplement; dest != end; ) {
int offset1 = (dest == end - 1) ? 0 : 1;
QRgb v0 = src[0], v1 = src[offset1], v2 = src[offset2], v3 = src[offset2 + offset1], v4 = src[offset4],
v5 = src[offset4 + offset1], v6 = src[offset6], v7 = src[offset6 + offset1];
src += (1 + offset1);
int a0 = qAlpha(v0), a1 = qAlpha(v1), a2 = qAlpha(v2), a3 = qAlpha(v3),
a4 = qAlpha(v4), a5 = qAlpha(v5), a6 = qAlpha(v6), a7 = qAlpha(v7);
if (a0 == 0) {
*dest++ = qRgba(0, 0, 0, 0);
continue;
}
int alphaTotal = a0 + a1 + a2 + a3 + a4 + a5 + a6 + a7;
*dest++ = qRgba(
(qRed(v0) * a0 + qRed(v1) * a1 + qRed(v2) * a2 + qRed(v3) * a3 +
qRed(v4) * a4 + qRed(v5) * a5 + qRed(v6) * a6 + qRed(v7) * a7) / alphaTotal,
(qGreen(v0) * a0 + qGreen(v1) * a1 + qGreen(v2) * a2 + qGreen(v3) * a3 +
qGreen(v4) * a4 + qGreen(v5) * a5 + qGreen(v6) * a6 + qGreen(v7) * a7) / alphaTotal,
(qBlue(v0) * a0 + qBlue(v1) * a1 + qBlue(v2) * a2 + qBlue(v3) * a3 +
qBlue(v4) * a4 + qBlue(v5) * a5 + qBlue(v6) * a6 + qBlue(v7) * a7) / alphaTotal,
MAX_ALPHA);
}
dest += halfSize;
src += offset2;
}
dest += halfSize * size;
src += offset4;
}
} else {
// more complex: N destination values for four child values
// ...
}
}
*(VoxelColorDataPointer*)&parent = VoxelColorDataPointer(new VoxelColorData(contents, size));
return false;
}
const int VOXEL_MATERIAL_HEADER_SIZE = sizeof(qint32) * 6;
static QByteArray encodeVoxelMaterial(int offsetX, int offsetY, int offsetZ,
int sizeX, int sizeY, int sizeZ, const QByteArray& contents) {
QByteArray inflated(VOXEL_MATERIAL_HEADER_SIZE, 0);
qint32* header = (qint32*)inflated.data();
*header++ = offsetX;
*header++ = offsetY;
*header++ = offsetZ;
*header++ = sizeX;
*header++ = sizeY;
*header++ = sizeZ;
inflated.append(contents);
return qCompress(inflated);
}
static QByteArray decodeVoxelMaterial(const QByteArray& encoded, int& offsetX, int& offsetY, int& offsetZ,
int& sizeX, int& sizeY, int& sizeZ) {
QByteArray inflated = qUncompress(encoded);
const qint32* header = (const qint32*)inflated.constData();
offsetX = *header++;
offsetY = *header++;
offsetZ = *header++;
sizeX = *header++;
sizeY = *header++;
sizeZ = *header++;
return inflated.mid(VOXEL_MATERIAL_HEADER_SIZE);
}
VoxelMaterialData::VoxelMaterialData(const QByteArray& contents, int size, const QVector<SharedObjectPointer>& materials) :
_contents(contents),
_size(size),
_materials(materials) {
}
VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes) {
read(in, bytes);
}
VoxelMaterialData::VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference) {
if (!reference) {
read(in, bytes);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->setEncodedDelta(in.readAligned(bytes));
in.readDelta(_materials, reference->getMaterials());
reference->setDeltaData(DataBlockPointer(this));
_contents = reference->getContents();
_size = reference->getSize();
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
QByteArray delta = decodeVoxelMaterial(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
if (delta.isEmpty()) {
return;
}
if (offsetX == 0) {
_contents = delta;
_size = sizeX;
return;
}
int minX = offsetX - 1;
int minY = offsetY - 1;
int minZ = offsetZ - 1;
const char* src = delta.constData();
int size2 = _size * _size;
char* planeDest = _contents.data() + minZ * size2 + minY * _size + minX;
for (int z = 0; z < sizeZ; z++, planeDest += size2) {
char* dest = planeDest;
for (int y = 0; y < sizeY; y++, src += sizeX, dest += _size) {
memcpy(dest, src, sizeX);
}
}
}
void VoxelMaterialData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
_encoded = encodeVoxelMaterial(0, 0, 0, _size, _size, _size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
out << _materials;
}
void VoxelMaterialData::writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference) {
if (!reference || reference->getSize() != _size) {
write(out);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
int minX = _size, minY = _size, minZ = _size;
int maxX = -1, maxY = -1, maxZ = -1;
const char* src = _contents.constData();
const char* ref = reference->getContents().constData();
for (int z = 0; z < _size; z++) {
bool differenceZ = false;
for (int y = 0; y < _size; y++) {
bool differenceY = false;
for (int x = 0; x < _size; x++) {
if (*src++ != *ref++) {
minX = qMin(minX, x);
maxX = qMax(maxX, x);
differenceY = differenceZ = true;
}
}
if (differenceY) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
}
}
if (differenceZ) {
minZ = qMin(minZ, z);
maxZ = qMax(maxZ, z);
}
}
QByteArray delta;
int sizeX = 0, sizeY = 0, sizeZ = 0;
if (maxX >= minX) {
sizeX = maxX - minX + 1;
sizeY = maxY - minY + 1;
sizeZ = maxZ - minZ + 1;
delta = QByteArray(sizeX * sizeY * sizeZ, 0);
char* dest = delta.data();
int size2 = _size * _size;
const char* planeSrc = _contents.constData() + minZ * size2 + minY * _size + minX;
for (int z = 0; z < sizeZ; z++, planeSrc += size2) {
src = planeSrc;
for (int y = 0; y < sizeY; y++, src += _size, dest += sizeX) {
memcpy(dest, src, sizeX);
}
}
}
reference->setEncodedDelta(encodeVoxelMaterial(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
reference->setDeltaData(DataBlockPointer(this));
}
out << reference->getEncodedDelta().size();
out.writeAligned(reference->getEncodedDelta());
out.writeDelta(_materials, reference->getMaterials());
}
void VoxelMaterialData::read(Bitstream& in, int bytes) {
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
_contents = decodeVoxelMaterial(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
_size = sizeX;
in >> _materials;
}
VoxelMaterialAttribute::VoxelMaterialAttribute(const QString& name) :
InlineAttribute<VoxelMaterialDataPointer>(name) {
}
void VoxelMaterialAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer();
} else {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(in, size));
}
}
void VoxelMaterialAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelMaterialDataPointer data = decodeInline<VoxelMaterialDataPointer>(value);
if (data) {
data->write(out);
} else {
out << 0;
}
}
void VoxelMaterialAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer();
} else {
*(VoxelMaterialDataPointer*)&value = VoxelMaterialDataPointer(new VoxelMaterialData(
in, size, decodeInline<VoxelMaterialDataPointer>(reference)));
}
}
void VoxelMaterialAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelMaterialDataPointer data = decodeInline<VoxelMaterialDataPointer>(value);
if (data) {
data->writeDelta(out, decodeInline<VoxelMaterialDataPointer>(reference));
} else {
out << 0;
}
}
bool VoxelMaterialAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelMaterialDataPointer pointer = decodeInline<VoxelMaterialDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getSize());
}
}
*(VoxelMaterialDataPointer*)&parent = VoxelMaterialDataPointer();
return maxSize == 0;
}
VoxelHermiteData::VoxelHermiteData(const QVector<QRgb>& contents, int size) :
_contents(contents),
_size(size) {
}
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes) {
read(in, bytes);
}
VoxelHermiteData::VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference) {
if (!reference) {
read(in, bytes);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
reference->setEncodedDelta(in.readAligned(bytes));
reference->setDeltaData(DataBlockPointer(this));
_contents = reference->getContents();
_size = reference->getSize();
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
QVector<QRgb> delta = decodeVoxelColor(reference->getEncodedDelta(), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
if (delta.isEmpty()) {
return;
}
if (offsetX == 0) {
_contents = delta;
_size = sizeX;
return;
}
int minX = offsetX - 1;
int minY = offsetY - 1;
int minZ = offsetZ - 1;
const QRgb* src = delta.constData();
int destStride = _size * EDGE_COUNT;
int destStride2 = _size * destStride;
QRgb* planeDest = _contents.data() + minZ * destStride2 + minY * destStride + minX * EDGE_COUNT;
int srcStride = sizeX * EDGE_COUNT;
int length = srcStride * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeDest += destStride2) {
QRgb* dest = planeDest;
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
memcpy(dest, src, length);
}
}
}
void VoxelHermiteData::write(Bitstream& out) {
QMutexLocker locker(&_encodedMutex);
if (_encoded.isEmpty()) {
_encoded = encodeVoxelColor(0, 0, 0, _size, _size, _size, _contents);
}
out << _encoded.size();
out.writeAligned(_encoded);
}
void VoxelHermiteData::writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference) {
if (!reference || reference->getSize() != _size) {
write(out);
return;
}
QMutexLocker locker(&reference->getEncodedDeltaMutex());
if (reference->getEncodedDelta().isEmpty() || reference->getDeltaData() != this) {
int minX = _size, minY = _size, minZ = _size;
int maxX = -1, maxY = -1, maxZ = -1;
const QRgb* src = _contents.constData();
const QRgb* ref = reference->getContents().constData();
for (int z = 0; z < _size; z++) {
bool differenceZ = false;
for (int y = 0; y < _size; y++) {
bool differenceY = false;
for (int x = 0; x < _size; x++, src += EDGE_COUNT, ref += EDGE_COUNT) {
if (src[0] != ref[0] || src[1] != ref[1] || src[2] != ref[2]) {
minX = qMin(minX, x);
maxX = qMax(maxX, x);
differenceY = differenceZ = true;
}
}
if (differenceY) {
minY = qMin(minY, y);
maxY = qMax(maxY, y);
}
}
if (differenceZ) {
minZ = qMin(minZ, z);
maxZ = qMax(maxZ, z);
}
}
QVector<QRgb> delta;
int sizeX = 0, sizeY = 0, sizeZ = 0;
if (maxX >= minX) {
sizeX = maxX - minX + 1;
sizeY = maxY - minY + 1;
sizeZ = maxZ - minZ + 1;
delta = QVector<QRgb>(sizeX * sizeY * sizeZ * EDGE_COUNT, 0);
QRgb* dest = delta.data();
int srcStride = _size * EDGE_COUNT;
int srcStride2 = _size * srcStride;
const QRgb* planeSrc = _contents.constData() + minZ * srcStride2 + minY * srcStride + minX * EDGE_COUNT;
int destStride = sizeX * EDGE_COUNT;
int length = destStride * sizeof(QRgb);
for (int z = 0; z < sizeZ; z++, planeSrc += srcStride2) {
src = planeSrc;
for (int y = 0; y < sizeY; y++, src += srcStride, dest += destStride) {
memcpy(dest, src, length);
}
}
}
reference->setEncodedDelta(encodeVoxelColor(minX + 1, minY + 1, minZ + 1, sizeX, sizeY, sizeZ, delta));
reference->setDeltaData(DataBlockPointer(this));
}
out << reference->getEncodedDelta().size();
out.writeAligned(reference->getEncodedDelta());
}
void VoxelHermiteData::read(Bitstream& in, int bytes) {
int offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ;
_contents = decodeVoxelColor(_encoded = in.readAligned(bytes), offsetX, offsetY, offsetZ, sizeX, sizeY, sizeZ);
_size = sizeX;
}
VoxelHermiteAttribute::VoxelHermiteAttribute(const QString& name) :
InlineAttribute<VoxelHermiteDataPointer>(name) {
}
void VoxelHermiteAttribute::read(Bitstream& in, void*& value, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
} else {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(in, size));
}
}
void VoxelHermiteAttribute::write(Bitstream& out, void* value, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
if (data) {
data->write(out);
} else {
out << 0;
}
}
void VoxelHermiteAttribute::readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
int size;
in >> size;
if (size == 0) {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer();
} else {
*(VoxelHermiteDataPointer*)&value = VoxelHermiteDataPointer(new VoxelHermiteData(
in, size, decodeInline<VoxelHermiteDataPointer>(reference)));
}
}
void VoxelHermiteAttribute::writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const {
if (!isLeaf) {
return;
}
VoxelHermiteDataPointer data = decodeInline<VoxelHermiteDataPointer>(value);
if (data) {
data->writeDelta(out, decodeInline<VoxelHermiteDataPointer>(reference));
} else {
out << 0;
}
}
bool VoxelHermiteAttribute::merge(void*& parent, void* children[], bool postRead) const {
int maxSize = 0;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelHermiteDataPointer pointer = decodeInline<VoxelHermiteDataPointer>(children[i]);
if (pointer) {
maxSize = qMax(maxSize, pointer->getSize());
}
}
if (maxSize == 0) {
*(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer();
return true;
}
int size = maxSize;
int area = size * size;
QVector<QRgb> contents(area * size * VoxelHermiteData::EDGE_COUNT);
int halfSize = size / 2;
int halfSizeComplement = size - halfSize;
for (int i = 0; i < MERGE_COUNT; i++) {
VoxelHermiteDataPointer child = decodeInline<VoxelHermiteDataPointer>(children[i]);
if (!child) {
continue;
}
const QVector<QRgb>& childContents = child->getContents();
int childSize = child->getSize();
int childArea = childSize * childSize;
const int INDEX_MASK = 1;
int xIndex = i & INDEX_MASK;
const int Y_SHIFT = 1;
int yIndex = (i >> Y_SHIFT) & INDEX_MASK;
int Z_SHIFT = 2;
int zIndex = (i >> Z_SHIFT) & INDEX_MASK;
QRgb* dest = contents.data() + ((zIndex * halfSize * area) + (yIndex * halfSize * size) + (xIndex * halfSize)) *
VoxelHermiteData::EDGE_COUNT;
const QRgb* src = childContents.data();
int offsets[VoxelHermiteData::EDGE_COUNT];
if (childSize == size) {
// simple case: one destination value for four child values
for (int z = 0; z < halfSizeComplement; z++) {
offsets[2] = (z == halfSize) ? 0 : (childArea * VoxelHermiteData::EDGE_COUNT);
for (int y = 0; y < halfSizeComplement; y++) {
offsets[1] = (y == halfSize) ? 0 : (childSize * VoxelHermiteData::EDGE_COUNT);
for (QRgb* end = dest + halfSizeComplement * VoxelHermiteData::EDGE_COUNT; dest != end;
dest += VoxelHermiteData::EDGE_COUNT) {
offsets[0] = (dest == end - VoxelHermiteData::EDGE_COUNT) ? 0 : VoxelHermiteData::EDGE_COUNT;
for (int i = 0; i < VoxelHermiteData::EDGE_COUNT; i++) {
QRgb v0 = src[i], v1 = src[i + offsets[i]];
glm::vec3 n0 = unpackNormal(v0), n1 = unpackNormal(v1);
float l0 = glm::length(n0), l1 = glm::length(n1);
float lengthTotal = l0 + l1;
if (lengthTotal == 0.0f) {
dest[i] = qRgba(0, 0, 0, 0);
continue;
}
glm::vec3 combinedNormal = n0 + n1;
float combinedLength = glm::length(combinedNormal);
if (combinedLength > 0.0f) {
combinedNormal /= combinedLength;
}
float combinedOffset = qAlpha(v0) * 0.5f * l0 + (qAlpha(v1) + EIGHT_BIT_MAXIMUM) * 0.5f * l1;
dest[i] = packNormal(combinedNormal, combinedOffset / lengthTotal);
}
src += (VoxelHermiteData::EDGE_COUNT + offsets[0]);
}
dest += (halfSize * VoxelHermiteData::EDGE_COUNT);
src += offsets[1];
}
dest += (halfSize * size * VoxelHermiteData::EDGE_COUNT);
src += offsets[2];
}
} else {
// more complex: N destination values for four child values
// ...
}
}
*(VoxelHermiteDataPointer*)&parent = VoxelHermiteDataPointer(new VoxelHermiteData(contents, size));
return false;
}
SharedObjectAttribute::SharedObjectAttribute(const QString& name, const QMetaObject* metaObject,
const SharedObjectPointer& defaultValue) :
InlineAttribute<SharedObjectPointer>(name, defaultValue),

View file

@ -30,16 +30,10 @@ class QScriptValue;
class Attribute;
class DataBlock;
class HeightfieldColorData;
class HeightfieldHeightData;
class HeightfieldMaterialData;
class MetavoxelData;
class MetavoxelLOD;
class MetavoxelNode;
class MetavoxelStreamState;
class VoxelColorData;
class VoxelHermiteData;
class VoxelMaterialData;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
@ -88,24 +82,6 @@ public:
/// Returns a reference to the standard SharedObjectSet "spanners" attribute.
const AttributePointer& getSpannersAttribute() const { return _spannersAttribute; }
/// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute.
const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; }
/// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute.
const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; }
/// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute.
const AttributePointer& getHeightfieldMaterialAttribute() const { return _heightfieldMaterialAttribute; }
/// Returns a reference to the standard VoxelColorDataPointer "voxelColor" attribute.
const AttributePointer& getVoxelColorAttribute() const { return _voxelColorAttribute; }
/// Returns a reference to the standard VoxelMaterialDataPointer "voxelMaterial" attribute.
const AttributePointer& getVoxelMaterialAttribute() const { return _voxelMaterialAttribute; }
/// Returns a reference to the standard VoxelHermiteDataPointer "voxelHermite" attribute.
const AttributePointer& getVoxelHermiteAttribute() const { return _voxelHermiteAttribute; }
private:
static QScriptValue getAttribute(QScriptContext* context, QScriptEngine* engine);
@ -116,12 +92,6 @@ private:
AttributePointer _guideAttribute;
AttributePointer _rendererAttribute;
AttributePointer _spannersAttribute;
AttributePointer _heightfieldAttribute;
AttributePointer _heightfieldColorAttribute;
AttributePointer _heightfieldMaterialAttribute;
AttributePointer _voxelColorAttribute;
AttributePointer _voxelMaterialAttribute;
AttributePointer _voxelHermiteAttribute;
};
/// Converts a value to a void pointer.
@ -355,213 +325,6 @@ public:
Q_INVOKABLE FloatAttribute(const QString& name = QString());
};
/// Packs a normal into an RGB value.
QRgb packNormal(const glm::vec3& normal);
/// Packs a normal (plus extra alpha value) into an RGBA value.
QRgb packNormal(const glm::vec3& normal, int alpha);
/// Unpacks a normal from an RGB value.
glm::vec3 unpackNormal(QRgb value);
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
/// Base class for blocks of data.
class DataBlock : public QSharedData {
public:
static const int COLOR_BYTES = 3;
virtual ~DataBlock();
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
const DataBlockPointer& getDeltaData() const { return _deltaData; }
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; }
protected:
QByteArray _encoded;
QMutex _encodedMutex;
DataBlockPointer _deltaData;
QByteArray _encodedDelta;
QMutex _encodedDeltaMutex;
class EncodedSubdivision {
public:
DataBlockPointer ancestor;
QByteArray data;
};
QVector<EncodedSubdivision> _encodedSubdivisions;
QMutex _encodedSubdivisionsMutex;
};
/// Contains the description of a material.
class MaterialObject : public SharedObject {
Q_OBJECT
Q_PROPERTY(QUrl diffuse MEMBER _diffuse)
Q_PROPERTY(float scaleS MEMBER _scaleS)
Q_PROPERTY(float scaleT MEMBER _scaleT)
public:
Q_INVOKABLE MaterialObject();
const QUrl& getDiffuse() const { return _diffuse; }
float getScaleS() const { return _scaleS; }
float getScaleT() const { return _scaleT; }
private:
QUrl _diffuse;
float _scaleS;
float _scaleT;
};
/// Utility method for editing: given a material pointer and a list of materials, returns the corresponding material index,
/// creating a new entry in the list if necessary.
uchar getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials, QByteArray& contents);
/// Utility method for editing: removes any unused materials from the supplied list.
void clearUnusedMaterials(QVector<SharedObjectPointer>& materials, const QByteArray& contents);
typedef QExplicitlySharedDataPointer<VoxelColorData> VoxelColorDataPointer;
/// Contains a block of voxel color data.
class VoxelColorData : public DataBlock {
public:
VoxelColorData(const QVector<QRgb>& contents, int size);
VoxelColorData(Bitstream& in, int bytes);
VoxelColorData(Bitstream& in, int bytes, const VoxelColorDataPointer& reference);
const QVector<QRgb>& getContents() const { return _contents; }
int getSize() const { return _size; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelColorDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<QRgb> _contents;
int _size;
};
/// An attribute that stores voxel colors.
class VoxelColorAttribute : public InlineAttribute<VoxelColorDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelColorAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<VoxelMaterialData> VoxelMaterialDataPointer;
/// Contains a block of voxel material data.
class VoxelMaterialData : public DataBlock {
public:
VoxelMaterialData(const QByteArray& contents, int size,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
VoxelMaterialData(Bitstream& in, int bytes);
VoxelMaterialData(Bitstream& in, int bytes, const VoxelMaterialDataPointer& reference);
const QByteArray& getContents() const { return _contents; }
int getSize() const { return _size; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelMaterialDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
int _size;
QVector<SharedObjectPointer> _materials;
};
/// An attribute that stores voxel materials.
class VoxelMaterialAttribute : public InlineAttribute<VoxelMaterialDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelMaterialAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<VoxelHermiteData> VoxelHermiteDataPointer;
/// Contains a block of voxel Hermite data (positions and normals at edge crossings).
class VoxelHermiteData : public DataBlock {
public:
static const int EDGE_COUNT = 3;
VoxelHermiteData(const QVector<QRgb>& contents, int size);
VoxelHermiteData(Bitstream& in, int bytes);
VoxelHermiteData(Bitstream& in, int bytes, const VoxelHermiteDataPointer& reference);
const QVector<QRgb>& getContents() const { return _contents; }
int getSize() const { return _size; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const VoxelHermiteDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<QRgb> _contents;
int _size;
};
/// An attribute that stores voxel Hermite data.
class VoxelHermiteAttribute : public InlineAttribute<VoxelHermiteDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE VoxelHermiteAttribute(const QString& name = QString());
virtual void read(Bitstream& in, void*& value, bool isLeaf) const;
virtual void write(Bitstream& out, void* value, bool isLeaf) const;
virtual void readDelta(Bitstream& in, void*& value, void* reference, bool isLeaf) const;
virtual void writeDelta(Bitstream& out, void* value, void* reference, bool isLeaf) const;
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
/// An attribute that takes the form of QObjects of a given meta-type (a subclass of SharedObject).
class SharedObjectAttribute : public InlineAttribute<SharedObjectPointer> {
Q_OBJECT

View file

@ -2432,12 +2432,13 @@ void MappedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
}
QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const {
bool reread = (object != NULL);
if (!object && _metaObject) {
object = _metaObject->newInstance();
}
foreach (const StreamerPropertyPair& property, _properties) {
QVariant value = property.first->read(in);
if (property.second.isValid() && object) {
if (property.second.isValid() && object && !reread) {
property.second.write(object, value);
}
}
@ -2445,6 +2446,7 @@ QObject* MappedObjectStreamer::read(Bitstream& in, QObject* object) const {
}
QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
bool reread = (object != NULL);
if (!object && _metaObject) {
object = _metaObject->newInstance();
}
@ -2452,7 +2454,7 @@ QObject* MappedObjectStreamer::readRawDelta(Bitstream& in, const QObject* refere
QVariant value;
property.first->readDelta(in, value, (property.second.isValid() && reference &&
reference->metaObject() == _metaObject) ? property.second.read(reference) : QVariant());
if (property.second.isValid() && object) {
if (property.second.isValid() && object && !reread) {
property.second.write(object, value);
}
}
@ -2475,13 +2477,13 @@ void SharedObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
QObject* SharedObjectStreamer::read(Bitstream& in, QObject* object) const {
QObject* result = MappedObjectStreamer::read(in, object);
static_cast<SharedObject*>(result)->readExtra(in);
static_cast<SharedObject*>(result)->readExtra(in, object != NULL);
return result;
}
QObject* SharedObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
QObject* result = MappedObjectStreamer::readRawDelta(in, reference, object);
static_cast<SharedObject*>(result)->readExtraDelta(in, static_cast<const SharedObject*>(reference));
static_cast<SharedObject*>(result)->readExtraDelta(in, static_cast<const SharedObject*>(reference), object != NULL);
return result;
}
@ -2592,6 +2594,7 @@ void GenericObjectStreamer::writeRawDelta(Bitstream& out, const QObject* object,
}
QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const {
bool reread = (object != NULL);
if (!object) {
object = new GenericSharedObject(_weakSelf);
}
@ -2599,11 +2602,14 @@ QObject* GenericObjectStreamer::read(Bitstream& in, QObject* object) const {
foreach (const StreamerNamePair& property, _properties) {
values.append(property.first->read(in));
}
static_cast<GenericSharedObject*>(object)->setValues(values);
if (!reread) {
static_cast<GenericSharedObject*>(object)->setValues(values);
}
return object;
}
QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* reference, QObject* object) const {
bool reread = (object != NULL);
if (!object) {
object = new GenericSharedObject(_weakSelf);
}
@ -2615,7 +2621,9 @@ QObject* GenericObjectStreamer::readRawDelta(Bitstream& in, const QObject* refer
static_cast<const GenericSharedObject*>(reference)->getValues().at(i) : QVariant());
values.append(value);
}
static_cast<GenericSharedObject*>(object)->setValues(values);
if (!reread) {
static_cast<GenericSharedObject*>(object)->setValues(values);
}
return object;
}

View file

@ -715,8 +715,9 @@ void ReliableChannel::endMessage() {
quint32 length = _buffer.pos() - _messageLengthPlaceholder;
_buffer.writeBytes(_messageLengthPlaceholder, sizeof(quint32), (const char*)&length);
_messageReceivedOffset = getBytesWritten();
_messageSize = length;
pruneOutgoingMessageStats();
_outgoingMessageStats.append(OffsetSizePair(getBytesWritten(), length));
}
void ReliableChannel::sendMessage(const QVariant& message) {
@ -725,12 +726,14 @@ void ReliableChannel::sendMessage(const QVariant& message) {
endMessage();
}
bool ReliableChannel::getMessageSendProgress(int& sent, int& total) const {
if (!_messagesEnabled || _offset >= _messageReceivedOffset) {
bool ReliableChannel::getMessageSendProgress(int& sent, int& total) {
pruneOutgoingMessageStats();
if (!_messagesEnabled || _outgoingMessageStats.isEmpty()) {
return false;
}
sent = qMax(0, _messageSize - (_messageReceivedOffset - _offset));
total = _messageSize;
const OffsetSizePair& stat = _outgoingMessageStats.first();
sent = qMax(0, stat.second - (stat.first - _offset));
total = stat.second;
return true;
}
@ -770,8 +773,7 @@ ReliableChannel::ReliableChannel(DatagramSequencer* sequencer, int index, bool o
_offset(0),
_writePosition(0),
_writePositionResetPacketNumber(0),
_messagesEnabled(true),
_messageReceivedOffset(0) {
_messagesEnabled(true) {
_buffer.open(output ? QIODevice::WriteOnly : QIODevice::ReadOnly);
_dataStream.setByteOrder(QDataStream::LittleEndian);
@ -942,3 +944,9 @@ void ReliableChannel::readData(QDataStream& in) {
}
}
void ReliableChannel::pruneOutgoingMessageStats() {
while (!_outgoingMessageStats.isEmpty() && _offset >= _outgoingMessageStats.first().first) {
_outgoingMessageStats.removeFirst();
}
}

View file

@ -386,7 +386,7 @@ public:
/// Determines the number of bytes uploaded towards the currently pending message.
/// \return true if there is a message pending, in which case the sent and total arguments will be set
bool getMessageSendProgress(int& sent, int& total) const;
bool getMessageSendProgress(int& sent, int& total);
/// Determines the number of bytes downloaded towards the currently pending message.
/// \return true if there is a message pending, in which case the received and total arguments will be set
@ -418,6 +418,8 @@ private:
void readData(QDataStream& in);
void pruneOutgoingMessageStats();
int _index;
bool _output;
CircularBuffer _buffer;
@ -432,6 +434,10 @@ private:
SpanList _acknowledged;
bool _messagesEnabled;
int _messageLengthPlaceholder; ///< the location in the buffer of the message length for the current message
typedef QPair<int, int> OffsetSizePair;
QVector<OffsetSizePair> _outgoingMessageStats;
int _messageReceivedOffset; ///< when reached, indicates that the most recent sent message has been received
int _messageSize; ///< the size of the most recent sent message; only valid when _messageReceivedOffset has been set
};

View file

@ -29,7 +29,9 @@ MetavoxelLOD::MetavoxelLOD(const glm::vec3& position, float threshold) :
}
bool MetavoxelLOD::shouldSubdivide(const glm::vec3& minimum, float size, float multiplier) const {
return size >= glm::distance(position, minimum + glm::vec3(size, size, size) * 0.5f) * threshold * multiplier;
float halfSize = size * 0.5f;
return size >= (glm::distance(position, minimum + glm::vec3(halfSize, halfSize, halfSize)) - halfSize) *
threshold * multiplier;
}
bool MetavoxelLOD::becameSubdivided(const glm::vec3& minimum, float size,
@ -57,7 +59,9 @@ bool MetavoxelLOD::becameSubdividedOrCollapsed(const glm::vec3& minimum, float s
}
bool MetavoxelLOD::shouldSubdivide(const glm::vec2& minimum, float size, float multiplier) const {
return size >= glm::distance(glm::vec2(position), minimum + glm::vec2(size, size) * 0.5f) * threshold * multiplier;
float halfSize = size * 0.5f;
return size >= (glm::distance(glm::vec2(position), minimum + glm::vec2(halfSize, halfSize)) - halfSize) *
threshold * multiplier;
}
bool MetavoxelLOD::becameSubdivided(const glm::vec2& minimum, float size,
@ -1188,67 +1192,6 @@ void MetavoxelNode::writeSpanners(MetavoxelStreamState& state) const {
}
}
void MetavoxelNode::writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const {
SharedObjectSet oldSet = decodeInline<SharedObjectSet>(reference.getAttributeValue());
SharedObjectSet newSet = decodeInline<SharedObjectSet>(_attributeValue);
foreach (const SharedObjectPointer& object, oldSet) {
if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !newSet.contains(object)) {
state.base.stream << object;
}
}
foreach (const SharedObjectPointer& object, newSet) {
if (static_cast<Spanner*>(object.data())->testAndSetVisited(state.base.visit) && !oldSet.contains(object)) {
state.base.stream << object;
}
}
if (isLeaf() || !state.shouldSubdivide()) {
if (!reference.isLeaf() && state.shouldSubdivideReference()) {
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
reference._children[i]->writeSpanners(nextState);
}
}
return;
}
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
if (reference.isLeaf() || !state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
_children[i]->writeSpanners(nextState);
}
return;
}
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
if (_children[i] != reference._children[i]) {
_children[i]->writeSpannerDelta(*reference._children[i], nextState);
} else if (nextState.becameSubdivided()) {
_children[i]->writeSpannerSubdivision(nextState);
}
}
}
void MetavoxelNode::writeSpannerSubdivision(MetavoxelStreamState& state) const {
if (!isLeaf()) {
MetavoxelStreamState nextState = { state.base, glm::vec3(), state.size * 0.5f };
if (!state.shouldSubdivideReference()) {
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
_children[i]->writeSpanners(nextState);
}
} else {
for (int i = 0; i < CHILD_COUNT; i++) {
nextState.setMinimum(state.minimum, i);
if (nextState.becameSubdivided()) {
_children[i]->writeSpannerSubdivision(nextState);
}
}
}
}
}
void MetavoxelNode::decrementReferenceCount(const AttributePointer& attribute) {
if (!_referenceCount.deref()) {
destroy(attribute);
@ -1621,8 +1564,9 @@ static inline bool defaultGuideToChildren(MetavoxelVisitation& visitation, int e
bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
visitation.visitor->getLOD().threshold;
float halfSize = visitation.info.size * 0.5f;
visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum +
glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold;
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
visitation.visitor->getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();
@ -1651,8 +1595,9 @@ bool DefaultMetavoxelGuide::guide(MetavoxelVisitation& visitation) {
bool DefaultMetavoxelGuide::guideToDifferent(MetavoxelVisitation& visitation) {
// save the core of the LOD calculation; we'll reuse it to determine whether to subdivide each attribute
visitation.info.lodBase = glm::distance(visitation.visitor->getLOD().position, visitation.info.getCenter()) *
visitation.visitor->getLOD().threshold;
float halfSize = visitation.info.size * 0.5f;
visitation.info.lodBase = (glm::distance(visitation.visitor->getLOD().position, visitation.info.minimum +
glm::vec3(halfSize, halfSize, halfSize)) - halfSize) * visitation.visitor->getLOD().threshold;
visitation.info.isLODLeaf = (visitation.info.size < visitation.info.lodBase *
visitation.visitor->getMinimumLODThresholdMultiplier());
visitation.info.isLeaf = visitation.info.isLODLeaf || visitation.allInputNodesLeaves();

View file

@ -239,9 +239,7 @@ public:
void writeSubdivided(MetavoxelStreamState& state, const MetavoxelStreamState& ancestorState, void* ancestorValue) const;
void writeSpanners(MetavoxelStreamState& state) const;
void writeSpannerDelta(const MetavoxelNode& reference, MetavoxelStreamState& state) const;
void writeSpannerSubdivision(MetavoxelStreamState& state) const;
/// Increments the node's reference count.
void incrementReferenceCount() { _referenceCount.ref(); }

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/transform.hpp>
#include "MetavoxelMessages.h"
#include "Spanner.h"
@ -146,10 +148,13 @@ void SetDataEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects
data.set(minimum, this->data, blend);
}
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius, float height) :
PaintHeightfieldHeightEdit::PaintHeightfieldHeightEdit(const glm::vec3& position, float radius,
float height, bool set, bool erase) :
position(position),
radius(radius),
height(height) {
height(height),
set(set),
erase(erase) {
}
void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
@ -161,7 +166,7 @@ void PaintHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObje
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height);
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintHeight(position, radius, height, set, erase);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
@ -173,473 +178,100 @@ MaterialEdit::MaterialEdit(const SharedObjectPointer& material, const QColor& av
averageColor(averageColor) {
}
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
HeightfieldMaterialSpannerEdit::HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner,
const SharedObjectPointer& material, const QColor& averageColor, bool paint, bool voxelize) :
MaterialEdit(material, averageColor),
spanner(spanner),
paint(paint),
voxelize(voxelize) {
}
class SpannerProjectionFetchVisitor : public SpannerVisitor {
public:
SpannerProjectionFetchVisitor(const Box& bounds, QVector<SharedObjectPointer>& results);
virtual bool visit(Spanner* spanner);
private:
const Box& _bounds;
QVector<SharedObjectPointer>& _results;
float _closestDistance;
};
SpannerProjectionFetchVisitor::SpannerProjectionFetchVisitor(const Box& bounds, QVector<SharedObjectPointer>& results) :
SpannerVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getSpannersAttribute()),
_bounds(bounds),
_results(results),
_closestDistance(FLT_MAX) {
}
bool SpannerProjectionFetchVisitor::visit(Spanner* spanner) {
Heightfield* heightfield = qobject_cast<Heightfield*>(spanner);
if (!heightfield) {
return true;
}
glm::mat4 transform = glm::scale(1.0f / glm::vec3(heightfield->getScale(),
heightfield->getScale() * heightfield->getAspectY(),
heightfield->getScale() * heightfield->getAspectZ())) *
glm::mat4_cast(glm::inverse(heightfield->getRotation())) * glm::translate(-heightfield->getTranslation());
Box transformedBounds = transform * _bounds;
if (transformedBounds.maximum.x < 0.0f || transformedBounds.maximum.z < 0.0f ||
transformedBounds.minimum.x > 1.0f || transformedBounds.minimum.z > 1.0f) {
return true;
}
float distance = qMin(glm::abs(transformedBounds.minimum.y), glm::abs(transformedBounds.maximum.y));
if (distance < _closestDistance) {
_results.clear();
_results.append(spanner);
_closestDistance = distance;
}
return true;
}
void HeightfieldMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// make sure the color meets our transparency requirements
QColor color = averageColor;
if (paint) {
color.setAlphaF(1.0f);
} else if (color.alphaF() < 0.5f) {
color = QColor(0, 0, 0, 0);
}
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
static_cast<Spanner*>(spanner.data())->getBounds(), results);
// if there's nothing intersecting directly, find the closest heightfield that intersects the projection
if (results.isEmpty()) {
SpannerProjectionFetchVisitor visitor(static_cast<Spanner*>(spanner.data())->getBounds(), results);
data.guide(visitor);
}
foreach (const SharedObjectPointer& result, results) {
Spanner* newResult = static_cast<Spanner*>(result.data())->setMaterial(spanner, material, color, paint, voxelize);
if (newResult != result) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newResult);
}
}
}
FillHeightfieldHeightEdit::FillHeightfieldHeightEdit(const glm::vec3& position, float radius) :
position(position),
radius(radius) {
}
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
glm::vec3 extents(radius, radius, radius);
void FillHeightfieldHeightEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
glm::vec3 extents = glm::vec3(radius, radius, radius);
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(),
Box(position - extents, position + extents), results);
foreach (const SharedObjectPointer& spanner, results) {
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->paintMaterial(position, radius, material, averageColor);
Spanner* newSpanner = static_cast<Spanner*>(spanner.data())->fillHeight(position, radius);
if (newSpanner != spanner) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), spanner, newSpanner);
}
}
}
const int VOXEL_BLOCK_SIZE = 16;
const int VOXEL_BLOCK_SAMPLES = VOXEL_BLOCK_SIZE + 1;
const int VOXEL_BLOCK_AREA = VOXEL_BLOCK_SAMPLES * VOXEL_BLOCK_SAMPLES;
const int VOXEL_BLOCK_VOLUME = VOXEL_BLOCK_AREA * VOXEL_BLOCK_SAMPLES;
VoxelMaterialSpannerEdit::VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner,
const SharedObjectPointer& material, const QColor& averageColor) :
MaterialEdit(material, averageColor),
spanner(spanner) {
}
class VoxelMaterialSpannerEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialSpannerEditVisitor(Spanner* spanner, const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
Spanner* _spanner;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelMaterialSpannerEditVisitor::VoxelMaterialSpannerEditVisitor(Spanner* spanner,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelHermiteAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
_spanner(spanner),
_material(material),
_color(color),
_blockSize(spanner->getVoxelizationGranularity() * VOXEL_BLOCK_SIZE) {
}
int VoxelMaterialSpannerEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_spanner->getBounds())) {
return STOP_RECURSION;
}
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
QVector<QRgb> oldColorContents;
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
if (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
oldColorContents = colorPointer->getContents();
} else {
oldColorContents = QVector<QRgb>(VOXEL_BLOCK_VOLUME);
}
QVector<QRgb> hermiteContents;
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
if (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) {
hermiteContents = hermitePointer->getContents();
} else {
hermiteContents = QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
}
QByteArray materialContents;
QVector<SharedObjectPointer> materials;
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
materialContents = materialPointer->getContents();
materials = materialPointer->getMaterials();
} else {
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
}
float scale = VOXEL_BLOCK_SIZE / info.size;
QVector<QRgb> colorContents = oldColorContents;
Box overlap = info.getBounds().getIntersection(_spanner->getBounds());
overlap.minimum = (overlap.minimum - info.minimum) * scale;
overlap.maximum = (overlap.maximum - info.minimum) * scale;
int minX = glm::ceil(overlap.minimum.x);
int minY = glm::ceil(overlap.minimum.y);
int minZ = glm::ceil(overlap.minimum.z);
int sizeX = (int)overlap.maximum.x - minX + 1;
int sizeY = (int)overlap.maximum.y - minY + 1;
int sizeZ = (int)overlap.maximum.z - minZ + 1;
bool flipped = false;
float step = 1.0f / scale;
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
if (_spanner->hasOwnColors()) {
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
*destX = _spanner->getColorAt(position);
}
}
}
}
} else {
QRgb rgb = _color.rgba();
flipped = (qAlpha(rgb) == 0);
for (QRgb* destZ = colorContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
*destX = rgb;
}
}
}
}
}
// if there are no visible colors, we can clear everything
bool foundOpaque = false;
for (const QRgb* src = colorContents.constData(), *end = src + colorContents.size(); src != end; src++) {
if (qAlpha(*src) != 0) {
foundOpaque = true;
break;
}
}
if (!foundOpaque) {
info.outputValues[0] = AttributeValue(_outputs.at(0));
info.outputValues[1] = AttributeValue(_outputs.at(1));
info.outputValues[2] = AttributeValue(_outputs.at(2));
return STOP_RECURSION;
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
int hermiteArea = VOXEL_BLOCK_AREA * VoxelHermiteData::EDGE_COUNT;
int hermiteSamples = VOXEL_BLOCK_SAMPLES * VoxelHermiteData::EDGE_COUNT;
int hermiteMinX = minX, hermiteMinY = minY, hermiteMinZ = minZ;
int hermiteSizeX = sizeX, hermiteSizeY = sizeY, hermiteSizeZ = sizeZ;
if (minX > 0) {
hermiteMinX--;
hermiteSizeX++;
}
if (minY > 0) {
hermiteMinY--;
hermiteSizeY++;
}
if (minZ > 0) {
hermiteMinZ--;
hermiteSizeZ++;
}
const int EIGHT_BIT_MAXIMUM = 255;
QRgb* hermiteDestZ = hermiteContents.data() + hermiteMinZ * hermiteArea + hermiteMinY * hermiteSamples +
hermiteMinX * VoxelHermiteData::EDGE_COUNT;
for (int z = hermiteMinZ, hermiteMaxZ = z + hermiteSizeZ - 1; z <= hermiteMaxZ; z++, hermiteDestZ += hermiteArea) {
QRgb* hermiteDestY = hermiteDestZ;
for (int y = hermiteMinY, hermiteMaxY = y + hermiteSizeY - 1; y <= hermiteMaxY; y++, hermiteDestY += hermiteSamples) {
QRgb* hermiteDestX = hermiteDestY;
for (int x = hermiteMinX, hermiteMaxX = x + hermiteSizeX - 1; x <= hermiteMaxX; x++,
hermiteDestX += VoxelHermiteData::EDGE_COUNT) {
// at each intersected non-terminal edge, we check for a transition and, if one is detected, we assign the
// crossing and normal values based on intersection with the sphere
float distance;
glm::vec3 normal;
if (x != VOXEL_BLOCK_SIZE) {
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
int alpha1 = qAlpha(color[1]);
if (alpha0 != alpha1) {
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
info.minimum + glm::vec3(x + 1, y, z) * step, distance, normal)) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[1]) == alpha1) {
int alpha = distance * EIGHT_BIT_MAXIMUM;
if (normal.x < 0.0f ? alpha <= qAlpha(hermiteDestX[0]) : alpha >= qAlpha(hermiteDestX[0])) {
hermiteDestX[0] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[0] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM);
}
}
} else {
hermiteDestX[0] = 0x0;
}
} else {
hermiteDestX[0] = 0x0;
}
if (y != VOXEL_BLOCK_SIZE) {
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
int alpha2 = qAlpha(color[VOXEL_BLOCK_SAMPLES]);
if (alpha0 != alpha2) {
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
info.minimum + glm::vec3(x, y + 1, z) * step, distance, normal)) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_SAMPLES]) == alpha2) {
int alpha = distance * EIGHT_BIT_MAXIMUM;
if (normal.y < 0.0f ? alpha <= qAlpha(hermiteDestX[1]) : alpha >= qAlpha(hermiteDestX[1])) {
hermiteDestX[1] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[1] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM);
}
}
} else {
hermiteDestX[1] = 0x0;
}
} else {
hermiteDestX[1] = 0x0;
}
if (z != VOXEL_BLOCK_SIZE) {
int offset = z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
const QRgb* color = colorContents.constData() + offset;
int alpha0 = qAlpha(color[0]);
int alpha4 = qAlpha(color[VOXEL_BLOCK_AREA]);
if (alpha0 != alpha4) {
if (_spanner->intersects(info.minimum + glm::vec3(x, y, z) * step,
info.minimum + glm::vec3(x, y, z + 1) * step, distance, normal)) {
const QRgb* oldColor = oldColorContents.constData() + offset;
if (qAlpha(oldColor[0]) == alpha0 && qAlpha(oldColor[VOXEL_BLOCK_AREA]) == alpha4) {
int alpha = distance * EIGHT_BIT_MAXIMUM;
if (normal.z < 0.0f ? alpha <= qAlpha(hermiteDestX[2]) : alpha >= qAlpha(hermiteDestX[2])) {
hermiteDestX[2] = packNormal(flipped ? -normal : normal, alpha);
}
} else {
hermiteDestX[2] = packNormal(flipped ? -normal : normal, distance * EIGHT_BIT_MAXIMUM);
}
}
} else {
hermiteDestX[2] = 0x0;
}
} else {
hermiteDestX[2] = 0x0;
}
}
}
}
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
if (_spanner->hasOwnMaterials()) {
QHash<int, int> materialMap;
position.z = info.minimum.z + minZ * step;
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
int material = _spanner->getMaterialAt(position);
if (material != 0) {
int& mapping = materialMap[material];
if (mapping == 0) {
mapping = getMaterialIndex(_spanner->getMaterials().at(material - 1), materials,
materialContents);
}
material = mapping;
}
*destX = material;
}
}
}
}
} else {
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
position.z = info.minimum.z + minZ * step;
for (uchar* destZ = (uchar*)materialContents.data() + minZ * VOXEL_BLOCK_AREA + minY * VOXEL_BLOCK_SAMPLES + minX,
*endZ = destZ + sizeZ * VOXEL_BLOCK_AREA; destZ != endZ; destZ += VOXEL_BLOCK_AREA, position.z += step) {
position.y = info.minimum.y + minY * step;
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y += step) {
position.x = info.minimum.x + minX * step;
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x += step) {
if (_spanner->contains(position)) {
*destX = materialIndex;
}
}
}
}
}
clearUnusedMaterials(materials, materialContents);
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, VOXEL_BLOCK_SAMPLES, materials));
info.outputValues[2] = AttributeValue(_inputs.at(2), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
return STOP_RECURSION;
}
void VoxelMaterialSpannerEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
Spanner* spanner = static_cast<Spanner*>(this->spanner.data());
while (!data.getBounds().contains(spanner->getBounds())) {
data.expand();
}
// make sure it's either 100% transparent or 100% opaque
QColor color = averageColor;
color.setAlphaF(color.alphaF() > 0.5f ? 1.0f : 0.0f);
// find the bounds of all voxel nodes intersected
float nodeSize = VOXEL_BLOCK_SIZE * glm::pow(2.0f, glm::floor(
glm::log(spanner->getVoxelizationGranularity()) / glm::log(2.0f)));
Box bounds(glm::floor(spanner->getBounds().minimum / nodeSize) * nodeSize,
glm::ceil(spanner->getBounds().maximum / nodeSize) * nodeSize);
// expand to include edges
Box expandedBounds = bounds;
float increment = nodeSize / VOXEL_BLOCK_SIZE;
expandedBounds.maximum.x += increment;
expandedBounds.maximum.z += increment;
// get all intersecting spanners
QVector<SharedObjectPointer> results;
data.getIntersecting(AttributeRegistry::getInstance()->getSpannersAttribute(), expandedBounds, results);
// clear/voxelize as appropriate
SharedObjectPointer heightfield;
foreach (const SharedObjectPointer& result, results) {
Spanner* newSpanner = static_cast<Spanner*>(result.data())->clearAndFetchHeight(bounds, heightfield);
if (newSpanner != result) {
data.replace(AttributeRegistry::getInstance()->getSpannersAttribute(), result, newSpanner);
}
}
// voxelize the fetched heightfield, if any
if (heightfield) {
VoxelMaterialSpannerEditVisitor visitor(static_cast<Spanner*>(heightfield.data()), material, color);
data.guide(visitor);
}
VoxelMaterialSpannerEditVisitor visitor(spanner, material, color);
data.guide(visitor);
}
PaintVoxelMaterialEdit::PaintVoxelMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
MaterialEdit(material, averageColor),
position(position),
radius(radius) {
}
class PaintVoxelMaterialEditVisitor : public MetavoxelVisitor {
public:
PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
glm::vec3 _position;
float _radius;
SharedObjectPointer _material;
QColor _color;
Box _bounds;
};
PaintVoxelMaterialEditVisitor::PaintVoxelMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getVoxelColorAttribute() <<
AttributeRegistry::getInstance()->getVoxelMaterialAttribute()),
_position(position),
_radius(radius),
_material(material),
_color(color) {
glm::vec3 extents(_radius, _radius, _radius);
_bounds = Box(_position - extents, _position + extents);
}
int PaintVoxelMaterialEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
VoxelMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<VoxelMaterialDataPointer>();
if (!(colorPointer && materialPointer && colorPointer->getSize() == materialPointer->getSize())) {
return STOP_RECURSION;
}
QVector<QRgb> colorContents = colorPointer->getContents();
QByteArray materialContents = materialPointer->getContents();
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
Box overlap = info.getBounds().getIntersection(_bounds);
int size = colorPointer->getSize();
int area = size * size;
float scale = (size - 1.0f) / info.size;
overlap.minimum = (overlap.minimum - info.minimum) * scale;
overlap.maximum = (overlap.maximum - info.minimum) * scale;
int minX = glm::ceil(overlap.minimum.x);
int minY = glm::ceil(overlap.minimum.y);
int minZ = glm::ceil(overlap.minimum.z);
int sizeX = (int)overlap.maximum.x - minX + 1;
int sizeY = (int)overlap.maximum.y - minY + 1;
int sizeZ = (int)overlap.maximum.z - minZ + 1;
QRgb rgb = _color.rgba();
float step = 1.0f / scale;
glm::vec3 position(0.0f, 0.0f, info.minimum.z + minZ * step);
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
QRgb* colorData = colorContents.data();
uchar* materialData = (uchar*)materialContents.data();
for (int destZ = minZ * area + minY * size + minX, endZ = destZ + sizeZ * area; destZ != endZ;
destZ += area, position.z += step) {
position.y = info.minimum.y + minY * step;
for (int destY = destZ, endY = destY + sizeY * size; destY != endY; destY += size, position.y += step) {
position.x = info.minimum.x + minX * step;
for (int destX = destY, endX = destX + sizeX; destX != endX; destX++, position.x += step) {
QRgb& color = colorData[destX];
if (qAlpha(color) != 0 && glm::distance(position, _position) <= _radius) {
color = rgb;
materialData[destX] = materialIndex;
}
}
}
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, size));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
clearUnusedMaterials(materials, materialContents);
VoxelMaterialDataPointer newMaterialPointer(new VoxelMaterialData(materialContents, size, materials));
info.outputValues[1] = AttributeValue(_inputs.at(1), encodeInline<VoxelMaterialDataPointer>(newMaterialPointer));
return STOP_RECURSION;
}
void PaintVoxelMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// make sure it's 100% opaque
QColor color = averageColor;
color.setAlphaF(1.0f);
PaintVoxelMaterialEditVisitor visitor(position, radius, material, color);
data.guide(visitor);
}

View file

@ -203,8 +203,11 @@ public:
STREAM glm::vec3 position;
STREAM float radius;
STREAM float height;
STREAM bool set;
STREAM bool erase;
PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f, float height = 0.0f);
PaintHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
float height = 0.0f, bool set = false, bool erase = false);
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
@ -225,54 +228,39 @@ public:
DECLARE_STREAMABLE_METATYPE(MaterialEdit)
/// An edit that sets a region of a heightfield material.
class PaintHeightfieldMaterialEdit : STREAM public MaterialEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit)
/// An edit that sets the materials of voxels within a spanner to a value.
class VoxelMaterialSpannerEdit : STREAM public MaterialEdit {
/// An edit that sets the materials of a heightfield within a spanner to a value.
class HeightfieldMaterialSpannerEdit : STREAM public MaterialEdit {
STREAMABLE
public:
STREAM SharedObjectPointer spanner;
STREAM bool paint;
STREAM bool voxelize;
VoxelMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(),
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
HeightfieldMaterialSpannerEdit(const SharedObjectPointer& spanner = SharedObjectPointer(),
const SharedObjectPointer& material = SharedObjectPointer(),
const QColor& averageColor = QColor(), bool paint = false, bool voxelize = false);
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSpannerEdit)
DECLARE_STREAMABLE_METATYPE(HeightfieldMaterialSpannerEdit)
/// An edit that sets a region of a voxel material.
class PaintVoxelMaterialEdit : STREAM public MaterialEdit {
/// An edit that fills a region of a heightfield height.
class FillHeightfieldHeightEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
PaintVoxelMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
FillHeightfieldHeightEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f);
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(PaintVoxelMaterialEdit)
DECLARE_STREAMABLE_METATYPE(FillHeightfieldHeightEdit)
#endif // hifi_MetavoxelMessages_h

View file

@ -135,7 +135,7 @@ void SharedObject::writeExtra(Bitstream& out) const {
// nothing by default
}
void SharedObject::readExtra(Bitstream& in) {
void SharedObject::readExtra(Bitstream& in, bool reread) {
// nothing by default
}
@ -143,7 +143,7 @@ void SharedObject::writeExtraDelta(Bitstream& out, const SharedObject* reference
// nothing by default
}
void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference) {
void SharedObject::readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread) {
// nothing by default
}

View file

@ -84,13 +84,15 @@ public:
virtual void writeExtra(Bitstream& out) const;
/// Reads the non-property contents of this object from the specified stream.
virtual void readExtra(Bitstream& in);
/// \param reread if true, reread the contents from the stream but don't reapply them
virtual void readExtra(Bitstream& in, bool reread = false);
/// Writes the delta-encoded non-property contents of this object to the specified stream.
virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const;
/// Reads the delta-encoded non-property contents of this object from the specified stream.
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference);
/// \param reread if true, reread the contents from the stream but don't reapply them
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread = false);
/// Writes the subdivision of the contents of this object (preceeded by a
/// reference to the object itself) to the specified stream if necessary.

File diff suppressed because it is too large Load diff

View file

@ -18,11 +18,13 @@
#include "MetavoxelUtil.h"
class AbstractHeightfieldNodeRenderer;
class DataBlock;
class Heightfield;
class HeightfieldColor;
class HeightfieldHeight;
class HeightfieldMaterial;
class HeightfieldNode;
class HeightfieldStack;
class SpannerRenderer;
/// An object that spans multiple octree cells.
@ -71,19 +73,20 @@ public:
/// Finds the intersection between the described ray and this spanner.
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
/// Attempts to paint on the spanner.
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
const QColor& color);
/// Attempts to modify the spanner's height.
/// \param set whether to set the height as opposed to raising/lowering it
/// \param erase whether to erase height values
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase);
/// Attempts to clear and fetch part of the spanner's height.
/// \param heightfield the heightfield to populate
/// Attempts to fill the spanner's height (adding removing volumetric information).
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
virtual Spanner* fillHeight(const glm::vec3& position, float radius);
/// Attempts to "sculpt" or "paint," etc., with the supplied spanner.
/// \return the modified spanner, or this if no modification was performed
virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material,
const QColor& color, bool paint, bool voxelize);
/// Checks whether this spanner has its own colors.
virtual bool hasOwnColors() const;
@ -292,6 +295,42 @@ private:
QUrl _url;
};
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
/// Base class for blocks of data.
class DataBlock : public QSharedData {
public:
static const int COLOR_BYTES = 3;
virtual ~DataBlock();
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
const DataBlockPointer& getDeltaData() const { return _deltaData; }
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
QMutex& getEncodedDeltaMutex() { return _encodedDeltaMutex; }
protected:
QByteArray _encoded;
QMutex _encodedMutex;
DataBlockPointer _deltaData;
QByteArray _encodedDelta;
QMutex _encodedDeltaMutex;
class EncodedSubdivision {
public:
DataBlockPointer ancestor;
QByteArray data;
};
QVector<EncodedSubdivision> _encodedSubdivisions;
QMutex _encodedSubdivisionsMutex;
};
/// Base class for heightfield data blocks.
class HeightfieldData : public DataBlock {
public:
@ -466,6 +505,126 @@ Bitstream& operator>>(Bitstream& in, HeightfieldMaterialPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldMaterialPointer& value, const HeightfieldMaterialPointer& reference);
/// Contains the description of a material.
class MaterialObject : public SharedObject {
Q_OBJECT
Q_PROPERTY(QUrl diffuse MEMBER _diffuse)
Q_PROPERTY(float scaleS MEMBER _scaleS)
Q_PROPERTY(float scaleT MEMBER _scaleT)
public:
Q_INVOKABLE MaterialObject();
const QUrl& getDiffuse() const { return _diffuse; }
float getScaleS() const { return _scaleS; }
float getScaleT() const { return _scaleT; }
private:
QUrl _diffuse;
float _scaleS;
float _scaleT;
};
/// Finds a material index for the supplied material in the provided list, adding an entry if necessary. Returns -1
/// on failure (no room to add new material).
int getMaterialIndex(const SharedObjectPointer& material, QVector<SharedObjectPointer>& materials);
typedef QExplicitlySharedDataPointer<HeightfieldStack> HeightfieldStackPointer;
/// A single column within a stack block.
class StackArray : public QByteArray {
public:
#pragma pack(push, 1)
/// A single entry within the array.
class Entry {
public:
quint32 color;
uchar material;
quint32 hermiteX;
quint32 hermiteY;
quint32 hermiteZ;
Entry();
bool isSet() const { return qAlpha(color) != 0; }
bool isZero() const;
bool isMergeable(const Entry& other) const;
void setHermiteX(const glm::vec3& normal, float position);
float getHermiteX(glm::vec3& normal) const;
void setHermiteY(const glm::vec3& normal, float position);
float getHermiteY(glm::vec3& normal) const;
void setHermiteZ(const glm::vec3& normal, float position);
float getHermiteZ(glm::vec3& normal) const;
};
#pragma pack(pop)
static int getSize(int entries) { return (entries == 0) ? 0 : sizeof(quint16) + sizeof(Entry) * entries; }
StackArray() : QByteArray() { }
StackArray(int entries) : QByteArray(getSize(entries), 0) { }
StackArray(const QByteArray& other) : QByteArray(other) { }
StackArray(const char* src, int bytes) : QByteArray(src, bytes) { }
int getPosition() const { return *(const quint16*)constData(); }
void setPosition(int position) { *(quint16*)data() = position; }
quint16& getPositionRef() { return *(quint16*)data(); }
int getEntryCount() const { return isEmpty() ? 0 : (size() - sizeof(quint16)) / sizeof(Entry); }
Entry* getEntryData() { return (Entry*)(data() + sizeof(quint16)); }
const Entry* getEntryData() const { return (const Entry*)(constData() + sizeof(quint16)); }
int getEntryAlpha(int y, float heightfieldHeight = 0.0f) const;
Entry& getEntry(int y, float heightfieldHeight = 0.0f);
const Entry& getEntry(int y, float heightfieldHeight = 0.0f) const;
void getExtents(int& minimumY, int& maximumY) const;
bool hasSetEntries() const;
void removeEntries(int position, int count) { remove(sizeof(quint16) + position * sizeof(Entry), count * sizeof(Entry)); }
};
/// A block of stack data associated with a heightfield.
class HeightfieldStack : public HeightfieldData {
public:
HeightfieldStack(int width, const QVector<StackArray>& contents, const QVector<SharedObjectPointer>& materials);
HeightfieldStack(Bitstream& in, int bytes);
HeightfieldStack(Bitstream& in, int bytes, const HeightfieldStackPointer& reference);
QVector<StackArray>& getContents() { return _contents; }
QVector<SharedObjectPointer>& getMaterials() { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldStackPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<StackArray> _contents;
QVector<SharedObjectPointer> _materials;
};
Q_DECLARE_METATYPE(HeightfieldStackPointer)
Bitstream& operator<<(Bitstream& out, const HeightfieldStackPointer& value);
Bitstream& operator>>(Bitstream& in, HeightfieldStackPointer& value);
template<> void Bitstream::writeRawDelta(const HeightfieldStackPointer& value, const HeightfieldStackPointer& reference);
template<> void Bitstream::readRawDelta(HeightfieldStackPointer& value, const HeightfieldStackPointer& reference);
typedef QExplicitlySharedDataPointer<HeightfieldNode> HeightfieldNodePointer;
/// Holds the base state used in streaming heightfield data.
@ -499,14 +658,15 @@ public:
HeightfieldNode(const HeightfieldHeightPointer& height = HeightfieldHeightPointer(),
const HeightfieldColorPointer& color = HeightfieldColorPointer(),
const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer());
const HeightfieldMaterialPointer& material = HeightfieldMaterialPointer(),
const HeightfieldStackPointer& stack = HeightfieldStackPointer());
HeightfieldNode(const HeightfieldNode& other);
~HeightfieldNode();
void setContents(const HeightfieldHeightPointer& height, const HeightfieldColorPointer& color,
const HeightfieldMaterialPointer& material);
const HeightfieldMaterialPointer& material, const HeightfieldStackPointer& stack);
void setHeight(const HeightfieldHeightPointer& height) { _height = height; }
const HeightfieldHeightPointer& getHeight() const { return _height; }
@ -517,6 +677,9 @@ public:
void setMaterial(const HeightfieldMaterialPointer& material) { _material = material; }
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
void setStack(const HeightfieldStackPointer& stack) { _stack = stack; }
const HeightfieldStackPointer& getStack() const { return _stack; }
void setRenderer(AbstractHeightfieldNodeRenderer* renderer) { _renderer = renderer; }
AbstractHeightfieldNodeRenderer* getRenderer() const { return _renderer; }
@ -525,21 +688,25 @@ public:
void setChild(int index, const HeightfieldNodePointer& child) { _children[index] = child; }
const HeightfieldNodePointer& getChild(int index) const { return _children[index]; }
float getHeight(const glm::vec3& location) const;
bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
void getRangeAfterHeightPaint(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& position, float radius, float height, float& minimum, float& maximum) const;
HeightfieldNode* paintMaterial(const glm::vec3& position, const glm::vec3& radius, const SharedObjectPointer& material,
const QColor& color);
void getRangeAfterHeightPaint(const glm::vec3& position, const glm::vec3& radius,
float height, int& minimum, int& maximum) const;
HeightfieldNode* paintHeight(const glm::vec3& position, const glm::vec3& radius, float height,
HeightfieldNode* paintHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& position, float radius, float height, bool set, bool erase,
float normalizeScale, float normalizeOffset);
HeightfieldNode* fillHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& position, float radius);
void getRangeAfterEdit(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const Box& editBounds, float& minimum, float& maximum) const;
HeightfieldNode* setMaterial(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
Spanner* spanner, const SharedObjectPointer& material, const QColor& color, bool paint, bool voxelize,
float normalizeScale, float normalizeOffset);
HeightfieldNode* clearAndFetchHeight(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const Box& bounds, SharedObjectPointer& heightfield);
void read(HeightfieldStreamState& state);
void write(HeightfieldStreamState& state) const;
@ -563,9 +730,16 @@ private:
QRgb getColorAt(const glm::vec3& location) const;
int getMaterialAt(const glm::vec3& location) const;
void maybeRenormalize(const glm::vec3& scale, float normalizeScale, float normalizeOffset, int innerStackWidth,
QVector<quint16>& heightContents, QVector<StackArray>& stackContents);
bool findHeightfieldRayIntersection(const glm::vec3& origin, const glm::vec3& direction,
float boundsDistance, float& distance) const;
HeightfieldHeightPointer _height;
HeightfieldColorPointer _color;
HeightfieldMaterialPointer _material;
HeightfieldStackPointer _stack;
HeightfieldNodePointer _children[CHILD_COUNT];
@ -577,6 +751,9 @@ class AbstractHeightfieldNodeRenderer {
public:
virtual ~AbstractHeightfieldNodeRenderer();
virtual bool findRayIntersection(const glm::vec3& translation, const glm::quat& rotation, const glm::vec3& scale,
const glm::vec3& origin, const glm::vec3& direction, float boundsDistance, float& distance) const;
};
/// A heightfield represented as a spanner.
@ -588,8 +765,9 @@ class Heightfield : public Transformable {
Q_PROPERTY(HeightfieldColorPointer color MEMBER _color WRITE setColor NOTIFY colorChanged STORED false)
Q_PROPERTY(HeightfieldMaterialPointer material MEMBER _material WRITE setMaterial NOTIFY materialChanged STORED false
DESIGNABLE false)
Q_PROPERTY(HeightfieldNodePointer root MEMBER _root WRITE setRoot NOTIFY rootChanged STORED false DESIGNABLE false)
Q_PROPERTY(HeightfieldStackPointer stack MEMBER _stack WRITE setStack NOTIFY stackChanged STORED false
DESIGNABLE false)
public:
Q_INVOKABLE Heightfield();
@ -609,7 +787,10 @@ public:
void setMaterial(const HeightfieldMaterialPointer& material);
const HeightfieldMaterialPointer& getMaterial() const { return _material; }
void setRoot(const HeightfieldNodePointer& root);
void setStack(const HeightfieldStackPointer& stack);
const HeightfieldStackPointer& getStack() const { return _stack; }
void setRoot(const HeightfieldNodePointer& root) { _root = root; }
const HeightfieldNodePointer& getRoot() const { return _root; }
MetavoxelLOD transformLOD(const MetavoxelLOD& lod) const;
@ -622,13 +803,13 @@ public:
virtual bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance) const;
virtual Spanner* paintMaterial(const glm::vec3& position, float radius, const SharedObjectPointer& material,
const QColor& color);
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height);
virtual Spanner* paintHeight(const glm::vec3& position, float radius, float height, bool set, bool erase);
virtual Spanner* clearAndFetchHeight(const Box& bounds, SharedObjectPointer& heightfield);
virtual Spanner* fillHeight(const glm::vec3& position, float radius);
virtual Spanner* setMaterial(const SharedObjectPointer& spanner, const SharedObjectPointer& material,
const QColor& color, bool paint, bool voxelize);
virtual bool hasOwnColors() const;
virtual bool hasOwnMaterials() const;
virtual QRgb getColorAt(const glm::vec3& point);
@ -639,9 +820,9 @@ public:
virtual bool intersects(const glm::vec3& start, const glm::vec3& end, float& distance, glm::vec3& normal);
virtual void writeExtra(Bitstream& out) const;
virtual void readExtra(Bitstream& in);
virtual void readExtra(Bitstream& in, bool reread);
virtual void writeExtraDelta(Bitstream& out, const SharedObject* reference) const;
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference);
virtual void readExtraDelta(Bitstream& in, const SharedObject* reference, bool reread);
virtual void maybeWriteSubdivision(Bitstream& out);
virtual SharedObject* readSubdivision(Bitstream& in);
@ -652,7 +833,7 @@ signals:
void heightChanged(const HeightfieldHeightPointer& height);
void colorChanged(const HeightfieldColorPointer& color);
void materialChanged(const HeightfieldMaterialPointer& material);
void rootChanged(const HeightfieldNodePointer& root);
void stackChanged(const HeightfieldStackPointer& stack);
protected:
@ -665,12 +846,15 @@ private slots:
private:
Heightfield* prepareEdit(float minimumValue, float maximumValue, float& normalizeScale, float& normalizeOffset);
float _aspectY;
float _aspectZ;
HeightfieldHeightPointer _height;
HeightfieldColorPointer _color;
HeightfieldMaterialPointer _material;
HeightfieldStackPointer _stack;
HeightfieldNodePointer _root;
};

View file

@ -158,7 +158,8 @@ void AccountManager::setAuthURL(const QUrl& authURL) {
void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart) {
QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap) {
QMetaObject::invokeMethod(this, "invokedRequest",
Q_ARG(const QString&, path),
@ -166,13 +167,15 @@ void AccountManager::authenticatedRequest(const QString& path, QNetworkAccessMan
Q_ARG(QNetworkAccessManager::Operation, operation),
Q_ARG(const JSONCallbackParameters&, callbackParams),
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,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart) {
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap) {
QMetaObject::invokeMethod(this, "invokedRequest",
Q_ARG(const QString&, path),
@ -180,14 +183,16 @@ void AccountManager::unauthenticatedRequest(const QString& path, QNetworkAccessM
Q_ARG(QNetworkAccessManager::Operation, operation),
Q_ARG(const JSONCallbackParameters&, callbackParams),
Q_ARG(const QByteArray&, dataByteArray),
Q_ARG(QHttpMultiPart*, dataMultiPart));
Q_ARG(QHttpMultiPart*, dataMultiPart),
Q_ARG(QVariantMap, propertyMap));
}
void AccountManager::invokedRequest(const QString& path,
bool requiresAuthentication,
QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart) {
const QByteArray& dataByteArray, QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap) {
QNetworkAccessManager& networkAccessManager = NetworkAccessManager::getInstance();
@ -235,7 +240,9 @@ void AccountManager::invokedRequest(const QString& path,
} else {
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 {
networkRequest.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
if (operation == QNetworkAccessManager::PostOperation) {
@ -255,6 +262,14 @@ void AccountManager::invokedRequest(const QString& path,
}
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 we have information for a callback, insert the callbackParams into our local map
_pendingCallbackMap.insert(networkReply, callbackParams);

View file

@ -48,13 +48,15 @@ public:
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
const QByteArray& dataByteArray = QByteArray(),
QHttpMultiPart* dataMultiPart = NULL);
QHttpMultiPart* dataMultiPart = NULL,
const QVariantMap& propertyMap = QVariantMap());
void unauthenticatedRequest(const QString& path,
QNetworkAccessManager::Operation operation = QNetworkAccessManager::GetOperation,
const JSONCallbackParameters& callbackParams = JSONCallbackParameters(),
const QByteArray& dataByteArray = QByteArray(),
QHttpMultiPart* dataMultiPart = NULL);
QHttpMultiPart* dataMultiPart = NULL,
const QVariantMap& propertyMap = QVariantMap()) ;
const QUrl& getAuthURL() const { return _authURL; }
void setAuthURL(const QUrl& authURL);
@ -109,7 +111,8 @@ private:
QNetworkAccessManager::Operation operation,
const JSONCallbackParameters& callbackParams,
const QByteArray& dataByteArray,
QHttpMultiPart* dataMultiPart);
QHttpMultiPart* dataMultiPart,
const QVariantMap& propertyMap);
QUrl _authURL;
QMap<QNetworkReply*, JSONCallbackParameters> _pendingCallbackMap;

View file

@ -119,14 +119,16 @@ bool AddressManager::handleUrl(const QUrl& lookupUrl) {
if (!handleUsername(lookupUrl.authority())) {
// we're assuming this is either a network address or global place name
// check if it is a network address first
if (!handleNetworkAddress(lookupUrl.host()
if (handleNetworkAddress(lookupUrl.host()
+ (lookupUrl.port() == -1 ? "" : ":" + QString::number(lookupUrl.port())))) {
// we may have a path that defines a relative viewpoint - if so we should jump to that now
handleRelativeViewpoint(lookupUrl.path());
} else {
// wasn't an address - lookup the place name
attemptPlaceNameLookup(lookupUrl.host());
// 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());
}
// we may have a path that defines a relative viewpoint - if so we should jump to that now
handleRelativeViewpoint(lookupUrl.path());
}
return true;
@ -164,12 +166,14 @@ void AddressManager::handleAPIResponse(QNetworkReply& requestReply) {
QJsonObject responseObject = QJsonDocument::fromJson(requestReply.readAll()).object();
QJsonObject dataObject = responseObject["data"].toObject();
goToAddressFromObject(dataObject.toVariantMap());
goToAddressFromObject(dataObject.toVariantMap(), requestReply);
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_USER_LOCATION_KEY = "location";
@ -203,6 +207,8 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
if (domainObject.contains(DOMAIN_NETWORK_ADDRESS_KEY)) {
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);
} else {
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();
QUuid domainID(domainIDString);
qDebug() << "Possible domain change required to connect to domain with ID" << domainID
<< "via ice-server at" << iceServerAddress;
emit possibleDomainChangeRequiredViaICEForID(iceServerAddress, domainID);
}
@ -223,18 +232,29 @@ void AddressManager::goToAddressFromObject(const QVariantMap& dataObject) {
QString newRootPlaceName = rootMap[PLACE_NAME_KEY].toString();
setRootPlaceName(newRootPlaceName);
// take the path that came back
const QString PLACE_PATH_KEY = "path";
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
// check if we had a path to override the path returned
QString overridePath = reply.property(OVERRIDE_PATH_KEY).toString();
bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY);
if (!returnedPath.isEmpty()) {
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) {
qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
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
const QString PLACE_PATH_KEY = "path";
QString returnedPath = locationMap[PLACE_PATH_KEY].toString();
bool shouldFaceViewpoint = locationMap.contains(LOCATION_API_ONLINE_KEY);
if (!returnedPath.isEmpty()) {
// try to parse this returned path as a viewpoint, that's the only thing it could be for now
if (!handleRelativeViewpoint(returnedPath, shouldFaceViewpoint)) {
qDebug() << "Received a location path that was could not be handled as a viewpoint -" << returnedPath;
}
}
}
} else {
qDebug() << "Received an address manager API response with no domain key. Cannot parse.";
qDebug() << locationMap;
@ -260,12 +280,21 @@ void AddressManager::handleAPIError(QNetworkReply& errorReply) {
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
QString placeName = QUrl::toPercentEncoding(lookupString);
QVariantMap requestParams;
if (!overridePath.isEmpty()) {
requestParams.insert(OVERRIDE_PATH_KEY, overridePath);
}
AccountManager::getInstance().unauthenticatedRequest(GET_PLACE.arg(placeName),
QNetworkAccessManager::GetOperation,
apiCallbackParameters());
apiCallbackParameters(),
QByteArray(),
NULL,
requestParams);
}
bool AddressManager::handleNetworkAddress(const QString& lookupString) {
@ -389,6 +418,8 @@ void AddressManager::setDomainInfo(const QString& hostname, quint16 port) {
_rootPlaceName = hostname;
_rootPlaceID = QUuid();
qDebug() << "Possible domain change required to connect to domain at" << hostname << "on" << port;
emit possibleDomainChangeRequired(hostname, port);
}

View file

@ -47,7 +47,7 @@ public:
const QString& getRootPlaceName() const { return _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 setOrientationGetter(OrientationGetter orientationGetter) { _orientationGetter = orientationGetter; }
@ -57,7 +57,7 @@ public:
public slots:
void handleLookupString(const QString& lookupString);
void goToUser(const QString& username);
void goToAddressFromObject(const QVariantMap& addressMap);
void goToAddressFromObject(const QVariantMap& addressMap, const QNetworkReply& reply);
void storeCurrentAddress();

View file

@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType type) {
case PacketTypeAudioStreamStats:
return 1;
case PacketTypeMetavoxelData:
return 10;
return 12;
default:
return 0;
}

View file

@ -12,8 +12,6 @@
#ifndef hifi_BulletUtil_h
#define hifi_BulletUtil_h
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
@ -34,5 +32,4 @@ inline btQuaternion glmToBullet(const glm::quat& g) {
return btQuaternion(g.x, g.y, g.z, g.w);
}
#endif // USE_BULLET_PHYSICS
#endif // hifi_BulletUtil_h

View file

@ -12,9 +12,7 @@
#include <EntityItem.h>
#include <EntityEditPacketSender.h>
#ifdef USE_BULLET_PHYSICS
#include "BulletUtil.h"
#endif // USE_BULLET_PHYSICS
#include "EntityMotionState.h"
#include "SimpleEntityKinematicController.h"
@ -62,7 +60,6 @@ void EntityMotionState::addKinematicController() {
}
}
#ifdef USE_BULLET_PHYSICS
// This callback is invoked by the physics simulation in two cases:
// (1) when the RigidBody is first added to the world
// (irregardless of MotionType: STATIC, DYNAMIC, or KINEMATIC)
@ -93,10 +90,8 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) {
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
EntityMotionState::enqueueOutgoingEntity(_entity);
}
#endif // USE_BULLET_PHYSICS
void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
#ifdef USE_BULLET_PHYSICS
if (flags & (EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY)) {
if (flags & EntityItem::DIRTY_POSITION) {
_sentPosition = _entity->getPositionInMeters() - ObjectMotionState::getWorldOffset();
@ -132,11 +127,9 @@ void EntityMotionState::updateObjectEasy(uint32_t flags, uint32_t frame) {
_body->updateInertiaTensor();
}
_body->activate();
#endif // USE_BULLET_PHYSICS
};
void EntityMotionState::updateObjectVelocities() {
#ifdef USE_BULLET_PHYSICS
if (_body) {
_sentVelocity = _entity->getVelocityInMeters();
setVelocity(_sentVelocity);
@ -150,7 +143,6 @@ void EntityMotionState::updateObjectVelocities() {
_body->setActivationState(ACTIVE_TAG);
}
#endif // USE_BULLET_PHYSICS
}
void EntityMotionState::computeShapeInfo(ShapeInfo& shapeInfo) {
@ -162,7 +154,6 @@ float EntityMotionState::computeMass(const ShapeInfo& shapeInfo) const {
}
void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_t frame) {
#ifdef USE_BULLET_PHYSICS
if (!_entity->isKnownID()) {
return; // never update entities that are unknown
}
@ -232,12 +223,11 @@ void EntityMotionState::sendUpdate(OctreeEditPacketSender* packetSender, uint32_
_outgoingPacketFlags = DIRTY_PHYSICS_FLAGS;
_sentFrame = frame;
}
#endif // USE_BULLET_PHYSICS
}
uint32_t EntityMotionState::getIncomingDirtyFlags() const {
uint32_t dirtyFlags = _entity->getDirtyFlags();
#ifdef USE_BULLET_PHYSICS
// we add DIRTY_MOTION_TYPE if the body's motion type disagrees with entity velocity settings
int bodyFlags = _body->getCollisionFlags();
bool isMoving = _entity->isMoving();
@ -245,6 +235,5 @@ uint32_t EntityMotionState::getIncomingDirtyFlags() const {
(bodyFlags & btCollisionObject::CF_KINEMATIC_OBJECT && !isMoving)) {
dirtyFlags |= EntityItem::DIRTY_MOTION_TYPE;
}
#endif // USE_BULLET_PHYSICS
return dirtyFlags;
}

View file

@ -14,18 +14,8 @@
#include <AACube.h>
#include "ObjectMotionState.h"
#ifndef USE_BULLET_PHYSICS
// ObjectMotionState stubbery
#include "KinematicController.h"
class ObjectMotionState {
public:
// so that this stub implementation is not completely empty we give the class a data member
KinematicController* _kinematicController;
bool _stubData;
};
#endif // USE_BULLET_PHYSICS
#include "ObjectMotionState.h"
class EntityItem;
@ -52,13 +42,11 @@ public:
// virtual override for ObjectMotionState
void addKinematicController();
#ifdef USE_BULLET_PHYSICS
// this relays incoming position/rotation to the RigidBody
void getWorldTransform(btTransform& worldTrans) const;
// this relays outgoing position/rotation to the EntityItem
void setWorldTransform(const btTransform& worldTrans);
#endif // USE_BULLET_PHYSICS
// these relay incoming values to the RigidBody
void updateObjectEasy(uint32_t flags, uint32_t frame);

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifdef USE_BULLET_PHYSICS
#include <math.h>
#include "BulletUtil.h"
@ -18,14 +16,6 @@
#include "ObjectMotionState.h"
#include "PhysicsEngine.h"
const float MIN_DENSITY = 200.0f;
const float DEFAULT_DENSITY = 1000.0f;
const float MAX_DENSITY = 20000.0f;
const float MIN_VOLUME = 0.001f;
const float DEFAULT_VOLUME = 1.0f;
const float MAX_VOLUME = 1000000.0f;
const float DEFAULT_FRICTION = 0.5f;
const float MAX_FRICTION = 10.0f;
@ -180,5 +170,3 @@ void ObjectMotionState::removeKinematicController() {
_kinematicController = NULL;
}
}
#endif // USE_BULLET_PHYSICS

View file

@ -12,8 +12,13 @@
#ifndef hifi_ObjectMotionState_h
#define hifi_ObjectMotionState_h
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include <EntityItem.h>
#include "ShapeInfo.h"
enum MotionType {
MOTION_TYPE_STATIC, // no motion
MOTION_TYPE_DYNAMIC, // motion according to physical laws
@ -32,13 +37,6 @@ const uint32_t DIRTY_PHYSICS_FLAGS = HARD_DIRTY_PHYSICS_FLAGS | EASY_DIRTY_PHYSI
// These are the outgoing flags that the PhysicsEngine can affect:
const uint32_t OUTGOING_DIRTY_PHYSICS_FLAGS = EntityItem::DIRTY_POSITION | EntityItem::DIRTY_VELOCITY;
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
#include <EntityItem.h> // for EntityItem::DIRTY_FOO bitmasks
#include "ShapeInfo.h"
class OctreeEditPacketSender;
class KinematicController;
@ -117,5 +115,4 @@ protected:
KinematicController* _kinematicController = NULL;
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ObjectMotionState_h

View file

@ -10,7 +10,8 @@
//
#include "PhysicsEngine.h"
#include "ShapeInfoUtil.h"
#include "ThreadSafeDynamicsWorld.h"
static uint32_t _frameCount;
@ -19,11 +20,6 @@ uint32_t PhysicsEngine::getFrameCount() {
return _frameCount;
}
#ifdef USE_BULLET_PHYSICS
#include "ShapeInfoUtil.h"
#include "ThreadSafeDynamicsWorld.h"
PhysicsEngine::PhysicsEngine(const glm::vec3& offset)
: _collisionConfig(NULL),
_collisionDispatcher(NULL),
@ -64,14 +60,17 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
assert(entity);
void* physicsInfo = entity->getPhysicsInfo();
if (!physicsInfo) {
EntityMotionState* motionState = new EntityMotionState(entity);
if (addObject(motionState)) {
ShapeInfo shapeInfo;
entity->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) {
EntityMotionState* motionState = new EntityMotionState(entity);
entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState);
addObject(shapeInfo, shape, motionState);
} else {
// 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";
delete motionState;
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
}
}
}
@ -248,59 +247,53 @@ void PhysicsEngine::stepSimulation() {
// CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing
// 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);
ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) {
btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f;
btRigidBody* body = NULL;
switch(motionState->computeMotionType()) {
case MOTION_TYPE_KINEMATIC: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
body->updateInertiaTensor();
motionState->_body = body;
motionState->addKinematicController();
const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec
const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec
body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD);
break;
}
case MOTION_TYPE_DYNAMIC: {
mass = motionState->computeMass(shapeInfo);
shape->calculateLocalInertia(mass, inertia);
body = new btRigidBody(mass, motionState, shape, inertia);
body->updateInertiaTensor();
motionState->_body = body;
motionState->updateObjectVelocities();
// NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds.
// (the 2 seconds is determined by: static btRigidBody::gDeactivationTime
const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
const float DYNAMIC_ANGULAR_VELOCITY_THRESHOLD = 0.087266f; // ~5 deg/sec
body->setSleepingThresholds(DYNAMIC_LINEAR_VELOCITY_THRESHOLD, DYNAMIC_ANGULAR_VELOCITY_THRESHOLD);
break;
}
case MOTION_TYPE_STATIC:
default: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
body->updateInertiaTensor();
motionState->_body = body;
break;
}
btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f;
btRigidBody* body = NULL;
switch(motionState->computeMotionType()) {
case MOTION_TYPE_KINEMATIC: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT);
body->updateInertiaTensor();
motionState->_body = body;
motionState->addKinematicController();
const float KINEMATIC_LINEAR_VELOCITY_THRESHOLD = 0.01f; // 1 cm/sec
const float KINEMATIC_ANGULAR_VELOCITY_THRESHOLD = 0.01f; // ~1 deg/sec
body->setSleepingThresholds(KINEMATIC_LINEAR_VELOCITY_THRESHOLD, KINEMATIC_ANGULAR_VELOCITY_THRESHOLD);
break;
}
case MOTION_TYPE_DYNAMIC: {
mass = motionState->computeMass(shapeInfo);
shape->calculateLocalInertia(mass, inertia);
body = new btRigidBody(mass, motionState, shape, inertia);
body->updateInertiaTensor();
motionState->_body = body;
motionState->updateObjectVelocities();
// NOTE: Bullet will deactivate any object whose velocity is below these thresholds for longer than 2 seconds.
// (the 2 seconds is determined by: static btRigidBody::gDeactivationTime
const float DYNAMIC_LINEAR_VELOCITY_THRESHOLD = 0.05f; // 5 cm/sec
const float DYNAMIC_ANGULAR_VELOCITY_THRESHOLD = 0.087266f; // ~5 deg/sec
body->setSleepingThresholds(DYNAMIC_LINEAR_VELOCITY_THRESHOLD, DYNAMIC_ANGULAR_VELOCITY_THRESHOLD);
break;
}
case MOTION_TYPE_STATIC:
default: {
body = new btRigidBody(mass, motionState, shape, inertia);
body->setCollisionFlags(btCollisionObject::CF_STATIC_OBJECT);
body->updateInertiaTensor();
motionState->_body = body;
break;
}
// wtf?
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
_dynamicsWorld->addRigidBody(body);
return true;
}
return false;
body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction);
body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
_dynamicsWorld->addRigidBody(body);
}
bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {
@ -411,5 +404,3 @@ void PhysicsEngine::updateObjectHard(btRigidBody* body, ObjectMotionState* motio
body->activate();
}
#endif // USE_BULLET_PHYSICS

View file

@ -16,8 +16,6 @@
const float PHYSICS_ENGINE_FIXED_SUBSTEP = 1.0f / 60.0f;
#ifdef USE_BULLET_PHYSICS
#include <QSet>
#include <btBulletDynamicsCommon.h>
@ -61,7 +59,7 @@ public:
/// \param motionState pointer to Object's MotionState
/// \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
/// \return true if Object removed
@ -96,11 +94,4 @@ private:
EntityEditPacketSender* _entityPacketSender;
};
#else // USE_BULLET_PHYSICS
// PhysicsEngine stubbery until Bullet is required
class PhysicsEngine {
public:
static uint32_t getFrameCount();
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_PhysicsEngine_h

View file

@ -15,9 +15,6 @@
#include "ShapeInfoUtil.h"
#include "BulletUtil.h"
#ifdef USE_BULLET_PHYSICS
int ShapeInfoUtil::toBulletShapeType(int shapeInfoType) {
int bulletShapeType = INVALID_SHAPE_PROXYTYPE;
switch(shapeInfoType) {
@ -168,5 +165,3 @@ DoubleHashKey ShapeInfoUtil::computeHash(const ShapeInfo& info) {
key._hash2 = (int)hash;
return key;
}
#endif // USE_BULLET_PHYSICS

View file

@ -12,8 +12,6 @@
#ifndef hifi_ShapeInfoUtil_h
#define hifi_ShapeInfoUtil_h
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <glm/glm.hpp>
@ -35,5 +33,4 @@ namespace ShapeInfoUtil {
int fromBulletShapeType(int bulletShapeType);
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ShapeInfoUtil_h

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifdef USE_BULLET_PHYSICS
#include <glm/gtx/norm.hpp>
#include "ShapeInfoUtil.h"
@ -104,6 +102,3 @@ int ShapeManager::getNumReferences(const ShapeInfo& info) const {
}
return -1;
}
#endif // USE_BULLET_PHYSICS

View file

@ -12,8 +12,6 @@
#ifndef hifi_ShapeManager_h
#define hifi_ShapeManager_h
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <LinearMath/btHashMap.h>
@ -52,5 +50,4 @@ private:
btAlignedObjectArray<DoubleHashKey> _pendingGarbage;
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ShapeManager_h

View file

@ -17,7 +17,6 @@
#include "ThreadSafeDynamicsWorld.h"
#ifdef USE_BULLET_PHYSICS
ThreadSafeDynamicsWorld::ThreadSafeDynamicsWorld(
btDispatcher* dispatcher,
btBroadphaseInterface* pairCache,
@ -82,4 +81,3 @@ int ThreadSafeDynamicsWorld::stepSimulation( btScalar timeStep, int maxSubSteps,
return subSteps;
}
#endif // USE_BULLET_PHYSICS

View file

@ -18,7 +18,6 @@
#ifndef hifi_ThreadSafeDynamicsWorld_h
#define hifi_ThreadSafeDynamicsWorld_h
#ifdef USE_BULLET_PHYSICS
#include <BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h>
ATTRIBUTE_ALIGNED16(class) ThreadSafeDynamicsWorld : public btDiscreteDynamicsWorld {
@ -40,13 +39,4 @@ public:
float getLocalTimeAccumulation() const { return m_localTime; }
};
#else // USE_BULLET_PHYSICS
// stubbery for ThreadSafeDynamicsWorld when Bullet not available
class ThreadSafeDynamicsWorld {
public:
ThreadSafeDynamicsWorld() {}
};
#endif // USE_BULLET_PHYSICS
#endif // hifi_ThreadSafeDynamicsWorld_h

View file

@ -262,7 +262,7 @@ bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direc
return false; // origin below plane
}
float divisor = glm::dot(normal, direction);
if (divisor > -EPSILON) {
if (divisor >= 0.0f) {
return false;
}
float t = dividend / divisor;
@ -490,4 +490,4 @@ void PolygonClip::copyCleanArray(int& lengthA, glm::vec2* vertexArrayA, int& len
vertexArrayA[i] = vertexArrayB[i];
}
}
}
}

View file

@ -16,7 +16,6 @@
#include "BulletUtilTests.h"
#ifdef USE_BULLET_PHYSICS
void BulletUtilTests::fromBulletToGLM() {
btVector3 bV(1.23f, 4.56f, 7.89f);
glm::vec3 gV = bulletToGLM(bV);
@ -101,14 +100,3 @@ void BulletUtilTests::runAllTests() {
fromBulletToGLM();
fromGLMToBullet();
}
#else // USE_BULLET_PHYSICS
void BulletUtilTests::fromBulletToGLM() {
}
void BulletUtilTests::fromGLMToBullet() {
}
void BulletUtilTests::runAllTests() {
}
#endif // USE_BULLET_PHYSICS

View file

@ -11,10 +11,8 @@
#include <iostream>
#ifdef USE_BULLET_PHYSICS
#include <btBulletDynamicsCommon.h>
#include <LinearMath/btHashMap.h>
#endif // USE_BULLET_PHYSICS
#include <DoubleHashKey.h>
#include <ShapeInfo.h>
@ -24,7 +22,6 @@
#include "ShapeInfoTests.h"
void ShapeInfoTests::testHashFunctions() {
#ifdef USE_BULLET_PHYSICS
int maxTests = 10000000;
ShapeInfo info;
btHashMap<btHashInt, int> hashes;
@ -135,11 +132,9 @@ void ShapeInfoTests::testHashFunctions() {
for (int i = 0; i < 32; ++i) {
std::cout << "bit 0x" << std::hex << masks[i] << std::dec << " = " << bits[i] << std::endl;
}
#endif // USE_BULLET_PHYSICS
}
void ShapeInfoTests::testBoxShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
glm::vec3 halfExtents(1.23f, 4.56f, 7.89f);
info.setBox(halfExtents);
@ -165,11 +160,9 @@ void ShapeInfoTests::testBoxShape() {
}
delete shape;
#endif // USE_BULLET_PHYSICS
}
void ShapeInfoTests::testSphereShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
float radius = 1.23f;
info.setSphere(radius);
@ -191,11 +184,9 @@ void ShapeInfoTests::testSphereShape() {
}
delete shape;
#endif // USE_BULLET_PHYSICS
}
void ShapeInfoTests::testCylinderShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
@ -218,11 +209,9 @@ void ShapeInfoTests::testCylinderShape() {
}
delete shape;
#endif // USE_BULLET_PHYSICS
}
void ShapeInfoTests::testCapsuleShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
@ -245,7 +234,6 @@ void ShapeInfoTests::testCapsuleShape() {
}
delete shape;
#endif // USE_BULLET_PHYSICS
}
void ShapeInfoTests::runAllTests() {

View file

@ -17,7 +17,6 @@
#include "ShapeManagerTests.h"
void ShapeManagerTests::testShapeAccounting() {
#ifdef USE_BULLET_PHYSICS
ShapeManager shapeManager;
ShapeInfo info;
info.setBox(glm::vec3(1.0f, 1.0f, 1.0f));
@ -118,11 +117,9 @@ void ShapeManagerTests::testShapeAccounting() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected refcount = 1 for shape but found refcount = " << numReferences << std::endl;
}
#endif // USE_BULLET_PHYSICS
}
void ShapeManagerTests::addManyShapes() {
#ifdef USE_BULLET_PHYSICS
ShapeManager shapeManager;
int numSizes = 100;
@ -152,11 +149,9 @@ void ShapeManagerTests::addManyShapes() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: expected numShapes = " << numSizes << " but found numShapes = " << numShapes << std::endl;
}
#endif // USE_BULLET_PHYSICS
}
void ShapeManagerTests::addBoxShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
glm::vec3 halfExtents(1.23f, 4.56f, 7.89f);
info.setBox(halfExtents);
@ -172,11 +167,9 @@ void ShapeManagerTests::addBoxShape() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: Box ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl;
}
#endif // USE_BULLET_PHYSICS
}
void ShapeManagerTests::addSphereShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
float radius = 1.23f;
info.setSphere(radius);
@ -192,11 +185,9 @@ void ShapeManagerTests::addSphereShape() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: Sphere ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl;
}
#endif // USE_BULLET_PHYSICS
}
void ShapeManagerTests::addCylinderShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
@ -213,11 +204,9 @@ void ShapeManagerTests::addCylinderShape() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: Cylinder ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl;
}
#endif // USE_BULLET_PHYSICS
}
void ShapeManagerTests::addCapsuleShape() {
#ifdef USE_BULLET_PHYSICS
ShapeInfo info;
float radius = 1.23f;
float height = 4.56f;
@ -234,7 +223,6 @@ void ShapeManagerTests::addCapsuleShape() {
std::cout << __FILE__ << ":" << __LINE__
<< " ERROR: Capsule ShapeInfo --> shape --> ShapeInfo --> shape did not work" << std::endl;
}
#endif // USE_BULLET_PHYSICS
}
void ShapeManagerTests::runAllTests() {