mirror of
https://github.com/overte-org/overte.git
synced 2025-08-13 01:36:15 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi into temp0bis
This commit is contained in:
commit
33dcbded22
61 changed files with 4192 additions and 4494 deletions
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
93
cmake/modules/FindBullet.cmake
Normal file
93
cmake/modules/FindBullet.cmake
Normal 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
197
examples/billiards.js
Normal file
|
@ -0,0 +1,197 @@
|
|||
// Pool Table
|
||||
var tableParts = [];
|
||||
var balls = [];
|
||||
|
||||
var LENGTH = 2.84;
|
||||
var WIDTH = 1.42;
|
||||
var HEIGHT = 0.80;
|
||||
var SCALE = 2.0;
|
||||
var BALL_SIZE = 0.05715;
|
||||
var BUMPER_WIDTH = 0.15;
|
||||
var BUMPER_HEIGHT = BALL_SIZE * 2.0;
|
||||
var HOLE_SIZE = BALL_SIZE;
|
||||
var DROP_HEIGHT = BALL_SIZE * 3.0;
|
||||
var GRAVITY = -9.8;
|
||||
var BALL_GAP = 0.001;
|
||||
|
||||
var startStroke = 0;
|
||||
|
||||
HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/";
|
||||
var screenSize = Controller.getViewportDimensions();
|
||||
var reticle = Overlays.addOverlay("image", {
|
||||
x: screenSize.x / 2 - 16,
|
||||
y: screenSize.y / 2 - 16,
|
||||
width: 32,
|
||||
height: 32,
|
||||
imageURL: HIFI_PUBLIC_BUCKET + "images/reticle.png",
|
||||
color: { red: 255, green: 255, blue: 255},
|
||||
alpha: 1
|
||||
});
|
||||
|
||||
function makeTable(pos) {
|
||||
// Top
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: pos,
|
||||
dimensions: { x: LENGTH * SCALE, y: HEIGHT, z: WIDTH * SCALE },
|
||||
color: { red: 0, green: 255, blue: 0 } }));
|
||||
// Long Bumpers
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x - LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x + LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z - (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x - LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x + LENGTH / 2.0,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z + (WIDTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE },
|
||||
dimensions: { x: (LENGTH - 3.0 * HOLE_SIZE) * SCALE / 2.0, y: BUMPER_HEIGHT, z: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
// End bumpers
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x + (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z },
|
||||
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
|
||||
tableParts.push(Entities.addEntity(
|
||||
{ type: "Box",
|
||||
position: { x: pos.x - (LENGTH / 2.0 + BUMPER_WIDTH / 2.0) * SCALE,
|
||||
y: pos.y + (HEIGHT / 2.0 + BUMPER_HEIGHT / 2.0),
|
||||
z: pos.z },
|
||||
dimensions: { z: (WIDTH - 2.0 * HOLE_SIZE) * SCALE, y: BUMPER_HEIGHT, x: BUMPER_WIDTH * SCALE },
|
||||
color: { red: 237, green: 201, blue: 175 } }));
|
||||
|
||||
}
|
||||
|
||||
function makeBalls(pos) {
|
||||
var colors = [{ red: 255, green: 255, blue: 0}, // Yellow
|
||||
{ red: 0, green: 0, blue: 255}, // Blue
|
||||
{ red: 255, green: 0, blue: 0}, // Red
|
||||
{ red: 128, green: 0, blue: 128}, // Purple
|
||||
{ red: 255, green: 165, blue: 0}, // Orange
|
||||
{ red: 0, green: 255, blue: 0}, // Green
|
||||
{ red: 128, green: 0, blue: 0}, // Maroon
|
||||
{ red: 0, green: 0, blue: 0}, // Black
|
||||
{ red: 255, green: 255, blue: 224}, // Light Yellow
|
||||
{ red: 173, green: 216, blue: 230}, // Light Blue
|
||||
{ red: 205, green: 92, blue: 92}, // Indian Red
|
||||
{ red: 218, green: 112, blue: 214}, // Orchid
|
||||
{ red: 218, green: 165, blue: 32}, // GoldenRod
|
||||
{ red: 255, green: 99, blue: 71}, // Tomato
|
||||
{ red: 128, green: 128, blue: 128}]; // Gray
|
||||
|
||||
// Object balls
|
||||
var ballPosition = { x: pos.x + (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z };
|
||||
for (var row = 1; row <= 5; row++) {
|
||||
ballPosition.z = pos.z - ((row - 1.0) / 2.0 * (BALL_SIZE + BALL_GAP) * SCALE);
|
||||
for (var spot = 0; spot < row; spot++) {
|
||||
balls.push(Entities.addEntity(
|
||||
{ type: "Sphere",
|
||||
position: ballPosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
color: colors[balls.length],
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.40,
|
||||
collisionsWillMove: true }));
|
||||
ballPosition.z += (BALL_SIZE + BALL_GAP) * SCALE;
|
||||
}
|
||||
ballPosition.x += (BALL_GAP + Math.sqrt(3.0) / 2.0 * BALL_SIZE) * SCALE;
|
||||
}
|
||||
// Cue Ball
|
||||
ballPosition = { x: pos.x - (LENGTH / 4.0) * SCALE, y: pos.y + DROP_HEIGHT, z: pos.z };
|
||||
balls.push(Entities.addEntity(
|
||||
{ type: "Sphere",
|
||||
position: ballPosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
gravity: { x: 0, y: GRAVITY, z: 0 },
|
||||
ignoreCollisions: false,
|
||||
damping: 0.40,
|
||||
collisionsWillMove: true }));
|
||||
}
|
||||
|
||||
function shootCue(velocity) {
|
||||
var DISTANCE_FROM_CAMERA = BALL_SIZE * 5.0 * SCALE;
|
||||
var camera = Camera.getPosition();
|
||||
var forwardVector = Quat.getFront(Camera.getOrientation());
|
||||
var cuePosition = Vec3.sum(camera, Vec3.multiply(forwardVector, DISTANCE_FROM_CAMERA));
|
||||
var velocity = Vec3.multiply(forwardVector, velocity);
|
||||
var BULLET_LIFETIME = 3.0;
|
||||
var BULLET_GRAVITY = 0.0;
|
||||
bulletID = Entities.addEntity(
|
||||
{ type: "Sphere",
|
||||
position: cuePosition,
|
||||
dimensions: { x: BALL_SIZE * SCALE, y: BALL_SIZE * SCALE, z: BALL_SIZE * SCALE },
|
||||
color: { red: 255, green: 255, blue: 255 },
|
||||
velocity: velocity,
|
||||
lifetime: BULLET_LIFETIME,
|
||||
gravity: { x: 0, y: BULLET_GRAVITY, z: 0 },
|
||||
damping: 0.10,
|
||||
density: 1000,
|
||||
ignoreCollisions: false,
|
||||
collisionsWillMove: true
|
||||
});
|
||||
}
|
||||
|
||||
function keyReleaseEvent(event) {
|
||||
if ((startStroke > 0) && event.text == "SPACE") {
|
||||
var endTime = new Date().getTime();
|
||||
var delta = endTime - startStroke;
|
||||
shootCue(delta / 100.0);
|
||||
startStroke = 0;
|
||||
}
|
||||
}
|
||||
|
||||
function keyPressEvent(event) {
|
||||
// Fire a cue ball
|
||||
if ((startStroke == 0) && (event.text == "SPACE")) {
|
||||
startStroke = new Date().getTime();
|
||||
}
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
for (var i = 0; i < tableParts.length; i++) {
|
||||
if (!tableParts[i].isKnownID) {
|
||||
tableParts[i] = Entities.identifyEntity(tableParts[i]);
|
||||
}
|
||||
Entities.deleteEntity(tableParts[i]);
|
||||
}
|
||||
for (var i = 0; i < balls.length; i++) {
|
||||
if (!balls[i].isKnownID) {
|
||||
balls[i] = Entities.identifyEntity(balls[i]);
|
||||
}
|
||||
Entities.deleteEntity(balls[i]);
|
||||
}
|
||||
Overlays.deleteOverlay(reticle);
|
||||
}
|
||||
|
||||
var tableCenter = Vec3.sum(MyAvatar.position, Vec3.multiply(4.0, Quat.getFront(Camera.getOrientation())));
|
||||
|
||||
makeTable(tableCenter);
|
||||
makeBalls(tableCenter);
|
||||
|
||||
Script.scriptEnding.connect(cleanup);
|
||||
Controller.keyPressEvent.connect(keyPressEvent);
|
||||
Controller.keyReleaseEvent.connect(keyReleaseEvent);
|
|
@ -44,10 +44,16 @@ var entityListTool = EntityListTool();
|
|||
|
||||
var hasShownPropertiesTool = false;
|
||||
|
||||
var 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") {
|
||||
|
|
|
@ -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 = ' ▾';
|
||||
var DESC_STRING = ' ▴';
|
||||
|
||||
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"> ▾</span></th>
|
||||
<th id="entity-url" data-sort="url">URL <span class="sort-order" style="display: none"> ▾</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
1
examples/html/list.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -5,9 +5,10 @@ body {
|
|||
margin: 0;
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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],
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -463,9 +463,7 @@ private:
|
|||
bool _justStarted;
|
||||
Stars _stars;
|
||||
|
||||
#ifdef USE_BULLET_PHYSICS
|
||||
PhysicsEngine _physicsEngine;
|
||||
#endif // USE_BULLET_PHYSICS
|
||||
|
||||
EntityTreeRenderer _entities;
|
||||
EntityCollisionSystem _entityCollisionSystem;
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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(); }
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ PacketVersion versionForPacketType(PacketType type) {
|
|||
case PacketTypeAudioStreamStats:
|
||||
return 1;
|
||||
case PacketTypeMetavoxelData:
|
||||
return 10;
|
||||
return 12;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
Loading…
Reference in a new issue