This commit is contained in:
Philip Rosedale 2014-09-05 15:04:55 -07:00
commit e5945c5b5a
57 changed files with 4376 additions and 1602 deletions

View file

@ -141,7 +141,7 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream*
float weakChannelAmplitudeRatio = 1.0f;
bool shouldAttenuate = (streamToAdd != listeningNodeStream);
if (shouldAttenuate) {
// if the two stream pointers do not match then these are different streams
@ -306,40 +306,61 @@ int AudioMixer::addStreamToMixForListeningNodeWithStream(PositionalAudioStream*
}
if (_enableFilter && shouldAttenuate) {
glm::vec3 relativePosition = streamToAdd->getPosition() - listeningNodeStream->getPosition();
if (relativePosition.z < 0) { // if the source is behind us
glm::quat inverseOrientation = glm::inverse(listeningNodeStream->getOrientation());
glm::vec3 rotatedSourcePosition = inverseOrientation * relativePosition;
// project the rotated source position vector onto the XZ plane
rotatedSourcePosition.y = 0.0f;
// produce an oriented angle about the y-axis
bearingRelativeAngleToSource = glm::orientedAngle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(rotatedSourcePosition),
glm::vec3(0.0f, 1.0f, 0.0f));
// if the source is in the range (-pi/2,+pi/2) (e.g, -Z from the listener's perspective
if (bearingRelativeAngleToSource < -PI_OVER_TWO || bearingRelativeAngleToSource > PI_OVER_TWO)
{
AudioFilterHSF1s& penumbraFilter = streamToAdd->getFilter();
// calculate penumbra angle
float headPenumbraAngle = glm::angle(glm::vec3(0.0f, 0.0f, -1.0f),
glm::normalize(relativePosition));
const float FULL_POWER = 1.0f;
const float SQUARE_ROOT_OF_TWO_OVER_TWO = 0.71f;
const float HALF_POWER = SQUARE_ROOT_OF_TWO_OVER_TWO;
const float QUARTER_POWER = HALF_POWER * HALF_POWER;
if (relativePosition.x < 0) {
headPenumbraAngle *= -1.0f; // [-pi/2,+pi/2]
}
const float SQUARE_ROOT_OF_TWO_OVER_TWO = 0.71f; // half power
const float ONE_OVER_TWO_PI = 1.0f / TWO_PI;
const float FILTER_CUTOFF_FREQUENCY_HZ = 4000.0f;
const float FILTER_CUTOFF_FREQUENCY_HZ = 1000.0f;
// calculate the updated gain, frequency and slope. this will be tuned over time.
const float penumbraFilterGainL = (-1.0f * ONE_OVER_TWO_PI * headPenumbraAngle) + SQUARE_ROOT_OF_TWO_OVER_TWO;
const float penumbraFilterGainR = (+1.0f * ONE_OVER_TWO_PI * headPenumbraAngle) + SQUARE_ROOT_OF_TWO_OVER_TWO;
// calculate the updated gain, frequency and slope.
const float penumbraFilterFrequency = FILTER_CUTOFF_FREQUENCY_HZ; // constant frequency
const float penumbraFilterSlope = SQUARE_ROOT_OF_TWO_OVER_TWO; // constant slope
qDebug() << "penumbra gainL="
<< penumbraFilterGainL
<< "penumbra gainR="
<< penumbraFilterGainR
<< "penumbraAngle="
<< headPenumbraAngle;
const float penumbraFilterGainL = (bearingRelativeAngleToSource <= -PI_OVER_TWO) ?
((+1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + FULL_POWER) :
((+1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER);
const float penumbraFilterGainR = (bearingRelativeAngleToSource <= -PI_OVER_TWO) ?
((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource + PI_OVER_TWO)) + HALF_POWER) :
((-1.0 * ONE_OVER_TWO_PI * (bearingRelativeAngleToSource - PI)) + HALF_POWER);
float distanceBetween = glm::length(relativePosition);
#if 0
qDebug() << "avatar="
<< listeningNodeStream
<< "gainL="
<< penumbraFilterGainL
<< "gainR="
<< penumbraFilterGainR
<< "angle="
<< bearingRelativeAngleToSource
<< "dist="
<< distanceBetween;
#endif
// set the gain on both filter channels
penumbraFilter.setParameters(0, 0, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainL, penumbraFilterSlope);
penumbraFilter.setParameters(0, 1, SAMPLE_RATE, penumbraFilterFrequency, penumbraFilterGainR, penumbraFilterSlope);
penumbraFilter.render(_clientSamples, _clientSamples, NETWORK_BUFFER_LENGTH_SAMPLES_STEREO / 2);
}
}

View file

@ -11,7 +11,7 @@
// Set the following variables to the right value
var NUM_AC = 3; // This is the number of AC. Their ID need to be unique and between 0 (included) and NUM_AC (excluded)
var NAMES = new Array("Arnold", "Jeff"); // ACs names ordered by IDs (Default name is "ACx", x = ID + 1))
var NAMES = new Array("Craig", "Clement", "Jeff"); // ACs names ordered by IDs (Default name is "ACx", x = ID + 1))
// Those variables MUST be common to every scripts
var controlVoxelSize = 0.25;
@ -40,9 +40,10 @@ var windowDimensions = Controller.getViewportDimensions();
var TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/";
var ALPHA_ON = 1.0;
var ALPHA_OFF = 0.7;
var COLOR_TOOL_BAR = { red: 128, green: 128, blue: 128 };
var COLOR_MASTER = { red: 200, green: 200, blue: 200 };
var TEXT_HEIGHT = 10;
var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 };
var COLOR_MASTER = { red: 0, green: 0, blue: 0 };
var TEXT_HEIGHT = 12;
var TEXT_MARGIN = 3;
var toolBars = new Array();
var nameOverlays = new Array();
@ -52,76 +53,87 @@ var playLoopIcon = new Array();
var stopIcon = new Array();
setupToolBars();
function setupToolBars() {
if (toolBars.length > 0) {
print("Multiple calls to Recorder.js:setupToolBars()");
return;
}
Tool.IMAGE_HEIGHT /= 2;
Tool.IMAGE_WIDTH /= 2;
for (i = 0; i <= NUM_AC; i++) {
toolBars.push(new ToolBar(0, 0, ToolBar.HORIZONTAL));
nameOverlays.push(Overlays.addOverlay("text", {
font: { size: TEXT_HEIGHT },
text: (i === NUM_AC) ? "Master" :
((i < NAMES.length) ? NAMES[i] :
"AC" + (i + 1)),
x: 0, y: 0,
width: 0,
height: 0,
alpha: 1.0,
visible: true
}));
toolBars[i].setBack((i === NUM_AC) ? COLOR_MASTER : COLOR_TOOL_BAR, ALPHA_OFF);
onOffIcon.push(toolBars[i].addTool({
imageURL: TOOL_ICON_URL + "models-tool.svg",
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, true, false));
playIcon.push(null);
playLoopIcon.push(null);
stopIcon.push(null);
imageURL: TOOL_ICON_URL + "ac-on-off.svg",
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
x: 0, y: 0,
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, true, true));
playIcon[i] = toolBars[i].addTool({
imageURL: TOOL_ICON_URL + "play.svg",
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_OFF,
visible: true
}, false);
var playLoopWidthFactor = 1.65;
playLoopIcon[i] = toolBars[i].addTool({
imageURL: TOOL_ICON_URL + "play-and-loop.svg",
subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: playLoopWidthFactor * Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_OFF,
visible: true
}, false);
stopIcon[i] = toolBars[i].addTool({
imageURL: TOOL_ICON_URL + "recording-stop.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_OFF,
visible: true
}, false);
nameOverlays.push(Overlays.addOverlay("text", {
backgroundColor: { red: 0, green: 0, blue: 0 },
font: { size: TEXT_HEIGHT },
text: (i === NUM_AC) ? "Master" : i + ". " +
((i < NAMES.length) ? NAMES[i] :
"AC" + i),
x: 0, y: 0,
width: toolBars[i].width + ToolBar.SPACING,
height: TEXT_HEIGHT + TEXT_MARGIN,
leftMargin: TEXT_MARGIN,
topMargin: TEXT_MARGIN,
alpha: ALPHA_OFF,
visible: true
}));
}
}
function sendCommand(id, action) {
if (action === SHOW && toolBars[id].numberOfTools() === 1) {
toolBars[id].selectTool(onOffIcon[id], true);
playIcon[id] = toolBars[id].addTool({
imageURL: TOOL_ICON_URL + "play.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, false);
playLoopIcon[id] = toolBars[id].addTool({
imageURL: TOOL_ICON_URL + "play-loop.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, false);
stopIcon[id] = toolBars[id].addTool({
imageURL: TOOL_ICON_URL + "stop.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, false);
toolBars[id].setBack((id === NUM_AC) ? COLOR_MASTER : COLOR_TOOL_BAR, ALPHA_OFF);
} else if (action === HIDE && toolBars[id].numberOfTools() != 1) {
if (action === SHOW) {
toolBars[id].selectTool(onOffIcon[id], false);
toolBars[id].removeLastTool();
toolBars[id].removeLastTool();
toolBars[id].removeLastTool();
toolBars[id].setBack(null);
}
toolBars[id].setAlpha(ALPHA_ON, playIcon[id]);
toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]);
toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]);
} else if (action === HIDE) {
toolBars[id].selectTool(onOffIcon[id], true);
toolBars[id].setAlpha(ALPHA_OFF, playIcon[id]);
toolBars[id].setAlpha(ALPHA_OFF, playLoopIcon[id]);
toolBars[id].setAlpha(ALPHA_OFF, stopIcon[id]);
} else if (toolBars[id].toolSelected(onOffIcon[id])) {
return;
}
if (id === toolBars.length - 1) {
for (i = 0; i < NUM_AC; i++) {
@ -141,55 +153,56 @@ function sendCommand(id, action) {
function mousePressEvent(event) {
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
// Check master control
var i = toolBars.length - 1;
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay)) {
if (toolBars[i].toolSelected(onOffIcon[i])) {
sendCommand(i, SHOW);
} else {
sendCommand(i, HIDE);
}
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay)) {
sendCommand(i, PLAY);
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
sendCommand(i, PLAY_LOOP);
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
sendCommand(i, STOP);
} else {
// Check individual controls
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
if (toolBars[i].toolSelected(onOffIcon[i])) {
sendCommand(i, SHOW);
} else {
sendCommand(i, HIDE);
}
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
sendCommand(i, PLAY);
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
sendCommand(i, PLAY_LOOP);
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
sendCommand(i, STOP);
} else {
// Check individual controls
for (i = 0; i < NUM_AC; i++) {
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay)) {
if (toolBars[i].toolSelected(onOffIcon[i])) {
sendCommand(i, SHOW);
} else {
sendCommand(i, HIDE);
}
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay)) {
sendCommand(i, PLAY);
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
sendCommand(i, PLAY_LOOP);
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
sendCommand(i, STOP);
} else {
}
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
if (toolBars[i].toolSelected(onOffIcon[i], false)) {
sendCommand(i, SHOW);
} else {
sendCommand(i, HIDE);
}
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
sendCommand(i, PLAY);
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
sendCommand(i, PLAY_LOOP);
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
sendCommand(i, STOP);
} else {
}
}
}
}
}
function moveUI() {
var relative = { x: 70, y: 400 };
var textSize = TEXT_HEIGHT + 2 * TEXT_MARGIN;
var relative = { x: 70, y: 75 + (NUM_AC) * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize) };
for (i = 0; i <= NUM_AC; i++) {
toolBars[i].move(relative.x,
windowDimensions.y - relative.y +
i * (Tool.IMAGE_HEIGHT + 2 * ToolBar.SPACING + TEXT_HEIGHT));
windowDimensions.y - relative.y +
i * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize));
Overlays.editOverlay(nameOverlays[i], {
x: relative.x,
y: windowDimensions.y - relative.y +
i * (Tool.IMAGE_HEIGHT + 2 * ToolBar.SPACING + TEXT_HEIGHT) -
ToolBar.SPACING - 2 * TEXT_HEIGHT
});
x: toolBars[i].x - ToolBar.SPACING,
y: toolBars[i].y - textSize
});
}
}

View file

@ -13,7 +13,6 @@ Script.include("toolBars.js");
var recordingFile = "recording.rec";
var playFromCurrentLocation = true;
var loop = true;
var windowDimensions = Controller.getViewportDimensions();
var TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/";
@ -21,69 +20,91 @@ var ALPHA_ON = 1.0;
var ALPHA_OFF = 0.7;
var COLOR_ON = { red: 128, green: 0, blue: 0 };
var COLOR_OFF = { red: 128, green: 128, blue: 128 };
Tool.IMAGE_WIDTH *= 0.7;
Tool.IMAGE_HEIGHT *= 0.7;
var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 };
var toolBar = null;
var recordIcon;
var playIcon;
var playLoopIcon;
var saveIcon;
var loadIcon;
var spacing;
var timerOffset;
setupToolBar();
var timer = null;
setupTimer();
var watchStop = false;
function setupToolBar() {
if (toolBar != null) {
print("Multiple calls to Recorder.js:setupToolBar()");
return;
}
Tool.IMAGE_HEIGHT /= 2;
Tool.IMAGE_WIDTH /= 2;
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
toolBar.setBack(COLOR_OFF, ALPHA_OFF);
recordIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "record.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, false);
playIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "play.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, false, false);
saveIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "save.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, false, false);
loadIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "load.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: ALPHA_ON,
visible: true
}, false, false);
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
recordIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "recording-record.svg",
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
x: 0, y: 0,
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: MyAvatar.isPlaying() ? ALPHA_OFF : ALPHA_ON,
visible: true
}, true, !MyAvatar.isRecording());
var playLoopWidthFactor = 1.65;
playIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "play-pause.svg",
width: playLoopWidthFactor * Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON,
visible: true
}, false);
playLoopIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "play-and-loop.svg",
subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
width: playLoopWidthFactor * Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON,
visible: true
}, false);
timerOffset = toolBar.width;
spacing = toolBar.addSpacing(0);
saveIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "recording-save.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: (MyAvatar.isRecording() || MyAvatar.isPlaying() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON,
visible: true
}, false);
loadIcon = toolBar.addTool({
imageURL: TOOL_ICON_URL + "recording-upload.svg",
width: Tool.IMAGE_WIDTH,
height: Tool.IMAGE_HEIGHT,
alpha: (MyAvatar.isRecording() || MyAvatar.isPlaying()) ? ALPHA_OFF : ALPHA_ON,
visible: true
}, false);
}
function setupTimer() {
timer = Overlays.addOverlay("text", {
font: { size: 20 },
font: { size: 15 },
text: (0.00).toFixed(3),
backgroundColor: COLOR_OFF,
x: 0, y: 0,
width: 100,
height: 100,
width: 0,
height: 0,
alpha: 1.0,
visible: true
});
@ -92,7 +113,8 @@ function setupTimer() {
function updateTimer() {
var text = "";
if (MyAvatar.isRecording()) {
text = formatTime(MyAvatar.recorderElapsed())
text = formatTime(MyAvatar.recorderElapsed());
} else {
text = formatTime(MyAvatar.playerElapsed()) + " / " +
formatTime(MyAvatar.playerLength());
@ -101,6 +123,7 @@ function updateTimer() {
Overlays.editOverlay(timer, {
text: text
})
toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing);
}
function formatTime(time) {
@ -127,54 +150,86 @@ function formatTime(time) {
}
function moveUI() {
var relative = { x: 30, y: 90 };
var relative = { x: 70, y: 40 };
toolBar.move(relative.x,
windowDimensions.y - relative.y);
Overlays.editOverlay(timer, {
x: relative.x - 10,
y: windowDimensions.y - relative.y - 35,
width: 0,
height: 0
x: relative.x + timerOffset - ToolBar.SPACING,
y: windowDimensions.y - relative.y - ToolBar.SPACING
});
}
function mousePressEvent(event) {
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
if (recordIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isPlaying()) {
if (!MyAvatar.isRecording()) {
MyAvatar.startRecording();
toolBar.setBack(COLOR_ON, ALPHA_ON);
} else {
MyAvatar.stopRecording();
MyAvatar.loadLastRecording();
toolBar.setBack(COLOR_OFF, ALPHA_OFF);
}
} else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
if (MyAvatar.isPlaying()) {
MyAvatar.stopPlaying();
} else {
MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation);
MyAvatar.setPlayerLoop(loop);
MyAvatar.startPlaying(true);
}
} else if (saveIcon === toolBar.clicked(clickedOverlay)) {
if (!MyAvatar.isRecording()) {
recordingFile = Window.save("Save recording to file", ".", "*.rec");
if (recordingFile != null) {
if (recordIcon === toolBar.clicked(clickedOverlay, false) && !MyAvatar.isPlaying()) {
if (!MyAvatar.isRecording()) {
MyAvatar.startRecording();
toolBar.selectTool(recordIcon, false);
toolBar.setAlpha(ALPHA_OFF, playIcon);
toolBar.setAlpha(ALPHA_OFF, playLoopIcon);
toolBar.setAlpha(ALPHA_OFF, saveIcon);
toolBar.setAlpha(ALPHA_OFF, loadIcon);
} else {
MyAvatar.stopRecording();
toolBar.selectTool(recordIcon, true );
MyAvatar.loadLastRecording();
toolBar.setAlpha(ALPHA_ON, playIcon);
toolBar.setAlpha(ALPHA_ON, playLoopIcon);
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
}
} else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
if (MyAvatar.isPlaying()) {
MyAvatar.stopPlaying();
toolBar.setAlpha(ALPHA_ON, recordIcon);
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
} else if (MyAvatar.playerLength() > 0) {
MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation);
MyAvatar.setPlayerLoop(false);
MyAvatar.startPlaying();
toolBar.setAlpha(ALPHA_OFF, recordIcon);
toolBar.setAlpha(ALPHA_OFF, saveIcon);
toolBar.setAlpha(ALPHA_OFF, loadIcon);
watchStop = true;
}
} else if (playLoopIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
if (MyAvatar.isPlaying()) {
MyAvatar.stopPlaying();
toolBar.setAlpha(ALPHA_ON, recordIcon);
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
} else if (MyAvatar.playerLength() > 0) {
MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation);
MyAvatar.setPlayerLoop(true);
MyAvatar.startPlaying();
toolBar.setAlpha(ALPHA_OFF, recordIcon);
toolBar.setAlpha(ALPHA_OFF, saveIcon);
toolBar.setAlpha(ALPHA_OFF, loadIcon);
}
} else if (saveIcon === toolBar.clicked(clickedOverlay)) {
if (!MyAvatar.isRecording() && !MyAvatar.isPlaying() && MyAvatar.playerLength() != 0) {
recordingFile = Window.save("Save recording to file", ".", "*.rec");
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
MyAvatar.saveRecording(recordingFile);
}
}
} else if (loadIcon === toolBar.clicked(clickedOverlay)) {
if (!MyAvatar.isRecording()) {
recordingFile = Window.browse("Load recorcding from file", ".", "*.rec");
if (recordingFile != "null") {
MyAvatar.loadRecording(recordingFile);
}
}
} else {
}
}
}
} else if (loadIcon === toolBar.clicked(clickedOverlay)) {
if (!MyAvatar.isRecording() && !MyAvatar.isPlaying()) {
recordingFile = Window.browse("Load recorcding from file", ".", "*.rec");
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
MyAvatar.loadRecording(recordingFile);
}
if (MyAvatar.playerLength() > 0) {
toolBar.setAlpha(ALPHA_ON, playIcon);
toolBar.setAlpha(ALPHA_ON, playLoopIcon);
toolBar.setAlpha(ALPHA_ON, saveIcon);
}
}
} else {
}
}
function update() {
@ -186,6 +241,13 @@ function update() {
}
updateTimer();
if (watchStop && !MyAvatar.isPlaying()) {
watchStop = false;
toolBar.setAlpha(ALPHA_ON, recordIcon);
toolBar.setAlpha(ALPHA_ON, saveIcon);
toolBar.setAlpha(ALPHA_ON, loadIcon);
}
}
function scriptEnding() {

View file

@ -2862,7 +2862,7 @@ function handeMenuEvent(menuItem) {
properties.gravity.z = array[index++].value;
properties.lifetime = array[index++].value; // give ourselves that many more seconds
if (properties.type == "Box") {
if (properties.type == "Box" || properties.type == "Sphere") {
properties.color.red = array[index++].value;
properties.color.green = array[index++].value;
properties.color.blue = array[index++].value;

View file

@ -1,230 +1,241 @@
//
// butterflyFlockTest1.js
//
//
// Created by Adrian McCarlie on August 2, 2014
// Modified by Brad Hefta-Gaub to use Entities on Sept. 3, 2014
// Copyright 2014 High Fidelity, Inc.
//
// This sample script creates a swarm of butterfly entities that fly around the avatar.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
function getRandomFloat(min, max) {
return Math.random() * (max - min) + min;
}
// Multiply vector by scalar
function vScalarMult(v, s) {
var rval = { x: v.x * s, y: v.y * s, z: v.z * s };
return rval;
}
function printVector(v) {
print(v.x + ", " + v.y + ", " + v.z + "\n");
}
// Create a random vector with individual lengths between a,b
function randVector(a, b) {
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
return rval;
}
// Returns a vector which is fraction of the way between a and b
function vInterpolate(a, b, fraction) {
var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction };
return rval;
}
var startTimeInSeconds = new Date().getTime() / 1000;
var lifeTime = 60; // lifetime of the butterflies in seconds!
var range = 1.0; // Over what distance in meters do you want the flock to fly around
var frame = 0;
var CHANCE_OF_MOVING = 0.9;
var BUTTERFLY_GRAVITY = 0;//-0.06;
var BUTTERFLY_FLAP_SPEED = 1.0;
var BUTTERFLY_VELOCITY = 0.55;
var myPosition = MyAvatar.position;
var pitch = 0.0;//experimental
var yaw = 0.0;//experimental
var roll = 0.0; //experimental
var rotation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll);//experimental
// This is our butterfly object
function defineButterfly(entityID, targetPosition) {
this.entityID = entityID;
this.previousFlapOffset = 0;
this.targetPosition = targetPosition;
this.moving = false;
}
// Array of butterflies
var butterflies = [];
var numButterflies = 20;
function addButterfly() {
// Decide the size of butterfly
var color = { red: 100, green: 100, blue: 100 };
var size = 0;
var which = Math.random();
if (which < 0.2) {
size = 0.08;
} else if (which < 0.4) {
size = 0.09;
} else if (which < 0.6) {
size = 0.8;
} else if (which < 0.8) {
size = 0.8;
} else {
size = 0.8;
}
myPosition = MyAvatar.position;
// if ( frame < numButterflies){
// myPosition = {x: myPosition.x, y: myPosition.y, z: myPosition.z };
// }
var properties = {
type: "Model",
lifetime: lifeTime,
position: Vec3.sum(randVector(-range, range), myPosition),
velocity: { x: 0, y: 0.0, z: 0 },
gravity: { x: 0, y: 1.0, z: 0 },
damping: 0.1,
radius : size,
color: color,
rotation: rotation,
//animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly6.fbx",
//animationIsPlaying: true,
modelURL: "http://business.ozblog.me/objects/butterfly/newButterfly6.fbx"
};
properties.position.z = properties.position.z+1;
butterflies.push(new defineButterfly(Entities.addEntity(properties), properties.position));
}
// Generate the butterflies
for (var i = 0; i < numButterflies; i++) {
addButterfly();
}
// Main update function
function updateButterflies(deltaTime) {
// Check to see if we've been running long enough that our butterflies are dead
var nowTimeInSeconds = new Date().getTime() / 1000;
if ((nowTimeInSeconds - startTimeInSeconds) >= lifeTime) {
// print("our butterflies are dying, stop our script");
Script.stop();
return;
}
frame++;
// Only update every third frame
if ((frame % 3) == 0) {
myPosition = MyAvatar.position;
// Update all the butterflies
for (var i = 0; i < numButterflies; i++) {
entityID = butterflies[i].entityID;
var properties = Entities.getEntityProperties(entityID);
if (properties.position.y > myPosition.y + getRandomFloat(0.0,0.3)){ //0.3 //ceiling
properties.gravity.y = - 3.0;
properties.damping.y = 1.0;
properties.velocity.y = 0;
properties.velocity.x = properties.velocity.x;
properties.velocity.z = properties.velocity.z;
if (properties.velocity.x < 0.5){
butterflies[i].moving = false;
}
if (properties.velocity.z < 0.5){
butterflies[i].moving = false;
}
}
if (properties.velocity.y <= -0.2) {
properties.velocity.y = 0.22;
properties.velocity.x = properties.velocity.x;
properties.velocity.z = properties.velocity.z;
}
if (properties.position.y < myPosition.y - getRandomFloat(0.0,0.3)) { //-0.3 // floor
properties.velocity.y = 0.9;
properties.gravity.y = - 4.0;
properties.velocity.x = properties.velocity.x;
properties.velocity.z = properties.velocity.z;
if (properties.velocity.x < 0.5){
butterflies[i].moving = false;
}
if (properties.velocity.z < 0.5){
butterflies[i].moving = false;
}
}
// Begin movement by getting a target
if (butterflies[i].moving == false) {
if (Math.random() < CHANCE_OF_MOVING) {
var targetPosition = Vec3.sum(randVector(-range, range), myPosition);
if (targetPosition.x < 0) {
targetPosition.x = 0;
}
if (targetPosition.y < 0) {
targetPosition.y = 0;
}
if (targetPosition.z < 0) {
targetPosition.z = 0;
}
if (targetPosition.x > TREE_SCALE) {
targetPosition.x = TREE_SCALE;
}
if (targetPosition.y > TREE_SCALE) {
targetPosition.y = TREE_SCALE;
}
if (targetPosition.z > TREE_SCALE) {
targetPosition.z = TREE_SCALE;
}
butterflies[i].targetPosition = targetPosition;
butterflies[i].moving = true;
}
}
// If we are moving, move towards the target
if (butterflies[i].moving) {
var holding = properties.velocity.y;
var desiredVelocity = Vec3.subtract(butterflies[i].targetPosition, properties.position);
desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BUTTERFLY_VELOCITY);
properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2);
properties.velocity.y = holding ;
// If we are near the target, we should get a new target
if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (properties.radius / 1.0)) {
butterflies[i].moving = false;
}
var yawRads = Math.atan2(properties.velocity.z, properties.velocity.x);
yawRads = yawRads + Math.PI / 2.0;
var newOrientation = Quat.fromPitchYawRollRadians(0.0, yawRads, 0.0);
properties.rotation = newOrientation;
}
// Use a cosine wave offset to make it look like its flapping.
var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (properties.radius);
properties.position.y = properties.position.y + (offset - butterflies[i].previousFlapOffset);
// Change position relative to previous offset.
butterflies[i].previousFlapOffset = offset;
Entities.editEntity(entityID, properties);
}
}
}
// register the call back so it fires before each data send
//
// butterflyFlockTest1.js
//
//
// Created by Adrian McCarlie on August 2, 2014
// Modified by Brad Hefta-Gaub to use Entities on Sept. 3, 2014
// Copyright 2014 High Fidelity, Inc.
//
// This sample script creates a swarm of butterfly entities that fly around the avatar.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
function getRandomFloat(min, max) {
return Math.random() * (max - min) + min;
}
// Multiply vector by scalar
function vScalarMult(v, s) {
var rval = { x: v.x * s, y: v.y * s, z: v.z * s };
return rval;
}
function printVector(v) {
print(v.x + ", " + v.y + ", " + v.z + "\n");
}
// Create a random vector with individual lengths between a,b
function randVector(a, b) {
var rval = { x: a + Math.random() * (b - a), y: a + Math.random() * (b - a), z: a + Math.random() * (b - a) };
return rval;
}
// Returns a vector which is fraction of the way between a and b
function vInterpolate(a, b, fraction) {
var rval = { x: a.x + (b.x - a.x) * fraction, y: a.y + (b.y - a.y) * fraction, z: a.z + (b.z - a.z) * fraction };
return rval;
}
var startTimeInSeconds = new Date().getTime() / 1000;
var lifeTime = 60; // lifetime of the butterflies in seconds!
var range = 1.0; // Over what distance in meters do you want the flock to fly around
var frame = 0;
var CHANCE_OF_MOVING = 0.9;
var BUTTERFLY_GRAVITY = 0;//-0.06;
var BUTTERFLY_FLAP_SPEED = 1.0;
var BUTTERFLY_VELOCITY = 0.55;
var DISTANCE_IN_FRONT_OF_ME = 1.5;
var DISTANCE_ABOVE_ME = 1.5;
var flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME),
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME)));
// set these pitch, yaw, roll to the needed values to orient the model as you want it
var pitchInDegrees = 270.0;
var yawInDegrees = 0.0;
var rollInDegrees = 0.0;
var pitchInRadians = pitchInDegrees / 180.0 * Math.PI;
var yawInRadians = yawInDegrees / 180.0 * Math.PI;
var rollInRadians = rollInDegrees / 180.0 * Math.PI;
var rotation = Quat.fromPitchYawRollDegrees(pitchInDegrees, yawInDegrees, rollInDegrees);//experimental
// This is our butterfly object
function defineButterfly(entityID, targetPosition) {
this.entityID = entityID;
this.previousFlapOffset = 0;
this.targetPosition = targetPosition;
this.moving = false;
}
// Array of butterflies
var butterflies = [];
var numButterflies = 20;
function addButterfly() {
// Decide the size of butterfly
var color = { red: 100, green: 100, blue: 100 };
var size = 0;
var which = Math.random();
if (which < 0.2) {
size = 0.08;
} else if (which < 0.4) {
size = 0.09;
} else if (which < 0.6) {
size = 0.8;
} else if (which < 0.8) {
size = 0.8;
} else {
size = 0.8;
}
flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME),
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME)));
var properties = {
type: "Model",
lifetime: lifeTime,
position: Vec3.sum(randVector(-range, range), flockPosition),
velocity: { x: 0, y: 0.0, z: 0 },
gravity: { x: 0, y: 1.0, z: 0 },
damping: 0.1,
radius : size,
color: color,
rotation: rotation,
animationURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx",
animationIsPlaying: true,
modelURL: "http://business.ozblog.me/objects/butterfly/newButterfly2.fbx"
};
properties.position.z = properties.position.z+1;
butterflies.push(new defineButterfly(Entities.addEntity(properties), properties.position));
}
// Generate the butterflies
for (var i = 0; i < numButterflies; i++) {
addButterfly();
}
// Main update function
function updateButterflies(deltaTime) {
// Check to see if we've been running long enough that our butterflies are dead
var nowTimeInSeconds = new Date().getTime() / 1000;
if ((nowTimeInSeconds - startTimeInSeconds) >= lifeTime) {
// print("our butterflies are dying, stop our script");
Script.stop();
return;
}
frame++;
// Only update every third frame
if ((frame % 3) == 0) {
flockPosition = Vec3.sum(MyAvatar.position,Vec3.sum(
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_ABOVE_ME),
Vec3.multiply(Quat.getFront(MyAvatar.orientation), DISTANCE_IN_FRONT_OF_ME)));
// Update all the butterflies
for (var i = 0; i < numButterflies; i++) {
entityID = butterflies[i].entityID;
var properties = Entities.getEntityProperties(entityID);
if (properties.position.y > flockPosition.y + getRandomFloat(0.0,0.3)){ //0.3 //ceiling
properties.gravity.y = - 3.0;
properties.damping.y = 1.0;
properties.velocity.y = 0;
properties.velocity.x = properties.velocity.x;
properties.velocity.z = properties.velocity.z;
if (properties.velocity.x < 0.5){
butterflies[i].moving = false;
}
if (properties.velocity.z < 0.5){
butterflies[i].moving = false;
}
}
if (properties.velocity.y <= -0.2) {
properties.velocity.y = 0.22;
properties.velocity.x = properties.velocity.x;
properties.velocity.z = properties.velocity.z;
}
if (properties.position.y < flockPosition.y - getRandomFloat(0.0,0.3)) { //-0.3 // floor
properties.velocity.y = 0.9;
properties.gravity.y = - 4.0;
properties.velocity.x = properties.velocity.x;
properties.velocity.z = properties.velocity.z;
if (properties.velocity.x < 0.5){
butterflies[i].moving = false;
}
if (properties.velocity.z < 0.5){
butterflies[i].moving = false;
}
}
// Begin movement by getting a target
if (butterflies[i].moving == false) {
if (Math.random() < CHANCE_OF_MOVING) {
var targetPosition = Vec3.sum(randVector(-range, range), flockPosition);
if (targetPosition.x < 0) {
targetPosition.x = 0;
}
if (targetPosition.y < 0) {
targetPosition.y = 0;
}
if (targetPosition.z < 0) {
targetPosition.z = 0;
}
if (targetPosition.x > TREE_SCALE) {
targetPosition.x = TREE_SCALE;
}
if (targetPosition.y > TREE_SCALE) {
targetPosition.y = TREE_SCALE;
}
if (targetPosition.z > TREE_SCALE) {
targetPosition.z = TREE_SCALE;
}
butterflies[i].targetPosition = targetPosition;
butterflies[i].moving = true;
}
}
// If we are moving, move towards the target
if (butterflies[i].moving) {
var holding = properties.velocity.y;
var desiredVelocity = Vec3.subtract(butterflies[i].targetPosition, properties.position);
desiredVelocity = vScalarMult(Vec3.normalize(desiredVelocity), BUTTERFLY_VELOCITY);
properties.velocity = vInterpolate(properties.velocity, desiredVelocity, 0.2);
properties.velocity.y = holding ;
// If we are near the target, we should get a new target
if (Vec3.length(Vec3.subtract(properties.position, butterflies[i].targetPosition)) < (properties.radius / 1.0)) {
butterflies[i].moving = false;
}
var yawRads = Math.atan2(properties.velocity.z, properties.velocity.x);
yawRads = yawRads + Math.PI / 2.0;
var newOrientation = Quat.fromPitchYawRollRadians(pitchInRadians, yawRads, rollInRadians);
properties.rotation = newOrientation;
}
// Use a cosine wave offset to make it look like its flapping.
var offset = Math.cos(nowTimeInSeconds * BUTTERFLY_FLAP_SPEED) * (properties.radius);
properties.position.y = properties.position.y + (offset - butterflies[i].previousFlapOffset);
// Change position relative to previous offset.
butterflies[i].previousFlapOffset = offset;
Entities.editEntity(entityID, properties);
}
}
}
// register the call back so it fires before each data send
Script.update.connect(updateButterflies);

View file

@ -61,11 +61,10 @@ Overlay2D = function(properties, overlay) { // overlay is an optionnal variable
}
this.clicked = function(clickedOverlay) {
return (overlay == clickedOverlay ? true : false);
return overlay === clickedOverlay;
}
this.cleanup = function() {
print("Cleanup");
Overlays.deleteOverlay(overlay);
}
}
@ -112,9 +111,9 @@ Tool = function(properties, selectable, selected) { // selectable and selected a
this.select(selected);
this.baseClicked = this.clicked;
this.clicked = function(clickedOverlay) {
this.clicked = function(clickedOverlay, update) {
if (this.baseClicked(clickedOverlay)) {
if (selectable) {
if (selectable && update) {
this.toggle();
}
return true;
@ -141,6 +140,7 @@ ToolBar = function(x, y, direction) {
alpha: 1.0,
visible: false
});
this.spacing = [];
this.addTool = function(properties, selectable, selected) {
if (direction == ToolBar.HORIZONTAL) {
@ -154,16 +154,56 @@ ToolBar = function(x, y, direction) {
this.width = Math.max(properties.width, this.width);
this.height += properties.height + ToolBar.SPACING;
}
if (this.back != null) {
Overlays.editOverlay(this.back, {
width: this.width + 2 * ToolBar.SPACING,
height: this.height + 2 * ToolBar.SPACING
width: this.width +
((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING,
height: this.height +
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
});
}
this.tools.push(new Tool(properties, selectable, selected));
return ((this.tools.length) - 1);
}
this.addSpacing = function(size) {
if (direction == ToolBar.HORIZONTAL) {
this.width += size;
} else {
this.height += size;
}
this.spacing[this.tools.length] = size;
return (this.tools.length);
}
this.changeSpacing = function(size, id) {
if (this.spacing[id] === null) {
this.spacing[id] = 0;
}
var diff = size - this.spacing[id];
this.spacing[id] = size;
var dx = (direction == ToolBar.HORIZONTAL) ? diff : 0;
var dy = (direction == ToolBar.VERTICAL) ? diff : 0;
this.width += dx;
this.height += dy;
for(i = id; i < this.tools.length; i++) {
this.tools[i].move(this.tools[i].x() + dx,
this.tools[i].y() + dy);
}
if (this.back != null) {
Overlays.editOverlay(this.back, {
width: this.width +
((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING,
height: this.height +
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
});
}
}
this.removeLastTool = function() {
this.tools.pop().cleanup();
@ -209,18 +249,22 @@ ToolBar = function(x, y, direction) {
this.tools[tool].setAlpha(alpha);
}
}
this.setBack = function(color, alpha) {
if (color == null) {
Overlays.editOverlay(this.back, {
visible: false
});
visible: false
});
} else {
Overlays.editOverlay(this.back, {
visible: true,
backgroundColor: color,
alpha: alpha
})
width: this.width +
((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING,
height: this.height +
((direction == ToolBar.VERTICAL) ? 1 : 2) * ToolBar.SPACING,
visible: true,
backgroundColor: color,
alpha: alpha
});
}
}
@ -233,9 +277,13 @@ ToolBar = function(x, y, direction) {
}
}
this.clicked = function(clickedOverlay) {
this.clicked = function(clickedOverlay, update) {
if(typeof(update) === 'undefined') {
update = true;
}
for(var tool in this.tools) {
if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay)) {
if (this.tools[tool].visible() && this.tools[tool].clicked(clickedOverlay, update)) {
return parseInt(tool);
}
}

View file

@ -0,0 +1,26 @@
#version 120
//
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal texture
uniform sampler2D normalMap;
void main(void) {
// compute the base color based on OpenGL lighting model
vec4 normal = texture2D(normalMap, gl_TexCoord[0].st);
gl_FragColor = vec4((texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor +
gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * max(0.0, dot(normal * 2.0 -
vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position)))).rgb, normal.a);
}

View file

@ -1,10 +1,10 @@
#version 120
//
// metavoxel_heightfield.frag
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 7/28/14.
// Created by Andrzej Kapolka on 9/3/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
@ -14,6 +14,12 @@
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal texture
uniform sampler2D normalMap;
// the depth texture
uniform sampler2D depthMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
@ -21,28 +27,39 @@ uniform sampler2DShadow shadowMap;
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
uniform float shadowScale;
// the interpolated position
varying vec4 position;
// the distance to the near clip plane
uniform float near;
// the interpolated normal
varying vec4 normal;
// scale factor for depth: (far - near) / far
uniform float depthScale;
// offset for depth texture coordinates
uniform vec2 depthTexCoordOffset;
// scale for depth texture coordinates
uniform vec2 depthTexCoordScale;
void main(void) {
// compute the view space position using the depth
float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0);
vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0);
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
dot(gl_EyePlaneR[shadowIndex], position));
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
// compute the color based on OpenGL lighting model, use the alpha from the normal map
vec4 normal = texture2D(normalMap, gl_TexCoord[0].st);
float diffuse = dot(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor +
gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = vec4(baseColor.rgb, normal.a);
}

View file

@ -0,0 +1,60 @@
#version 120
//
// directional_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/3/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the normal texture
uniform sampler2D normalMap;
// the depth texture
uniform sampler2D depthMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
uniform float shadowScale;
// the distance to the near clip plane
uniform float near;
// scale factor for depth: (far - near) / far
uniform float depthScale;
// offset for depth texture coordinates
uniform vec2 depthTexCoordOffset;
// scale for depth texture coordinates
uniform vec2 depthTexCoordScale;
void main(void) {
// compute the view space position using the depth
float z = near / (texture2D(depthMap, gl_TexCoord[0].st).r * depthScale - 1.0);
vec4 position = vec4((depthTexCoordOffset + gl_TexCoord[0].st * depthTexCoordScale) * z, z, 1.0);
// compute the corresponding texture coordinates
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position));
// compute the color based on OpenGL lighting model, use the alpha from the normal map
vec4 normal = texture2D(normalMap, gl_TexCoord[0].st);
float diffuse = dot(normal * 2.0 - vec4(1.0, 1.0, 1.0, 2.0), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor +
gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = vec4(baseColor.rgb, normal.a);
}

View file

@ -1,25 +0,0 @@
#version 120
//
// metavoxel_heightfield.frag
// fragment shader
//
// Created by Andrzej Kapolka on 7/28/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position)));
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
}

View file

@ -1,51 +0,0 @@
#version 120
//
// metavoxel_heighfield.vert
// vertex shader
//
// Created by Andrzej Kapolka on 7/28/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the height texture
uniform sampler2D heightMap;
// the distance between height points in texture space
uniform float heightScale;
// the scale between height and color textures
uniform float colorScale;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
vec2 heightCoord = gl_MultiTexCoord0.st;
float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r -
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r;
float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r -
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
// add the height to the position
float height = texture2D(heightMap, heightCoord).r;
position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
gl_Position = gl_ProjectionMatrix * position;
// the zero height should be invisible
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
// pass along the scaled/offset texture coordinates
gl_TexCoord[0] = (gl_MultiTexCoord0 - vec4(heightScale, heightScale, 0.0, 0.0)) * colorScale;
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
}

View file

@ -14,7 +14,11 @@
// the diffuse texture
uniform sampler2D diffuseMap;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
gl_FragColor = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
gl_FragData[0] = gl_Color * texture2D(diffuseMap, gl_TexCoord[0].st);
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
}

View file

@ -20,9 +20,20 @@ uniform float heightScale;
// the scale between height and color textures
uniform float colorScale;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
vec2 heightCoord = gl_MultiTexCoord0.st;
float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r -
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r;
float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r -
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
// add the height to the position
float height = texture2D(heightMap, gl_MultiTexCoord0.st).r;
float height = texture2D(heightMap, heightCoord).r;
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
// the zero height should be invisible

View file

@ -1,21 +0,0 @@
#version 120
//
// metavoxel_heightfield_light.frag
// fragment shader
//
// Created by Andrzej Kapolka on 8/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalize(normal), gl_LightSource[0].position)));
}

View file

@ -1,45 +0,0 @@
#version 120
//
// metavoxel_heighfield_light.vert
// vertex shader
//
// Created by Andrzej Kapolka on 8/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the height texture
uniform sampler2D heightMap;
// the distance between height points in texture space
uniform float heightScale;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
vec2 heightCoord = gl_MultiTexCoord0.st;
float deltaX = texture2D(heightMap, heightCoord - vec2(heightScale, 0.0)).r -
texture2D(heightMap, heightCoord + vec2(heightScale, 0.0)).r;
float deltaZ = texture2D(heightMap, heightCoord - vec2(0.0, heightScale)).r -
texture2D(heightMap, heightCoord + vec2(0.0, heightScale)).r;
normal = normalize(gl_ModelViewMatrix * vec4(deltaX, heightScale, deltaZ, 0.0));
// add the height to the position
float height = texture2D(heightMap, heightCoord).r;
position = gl_ModelViewMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
gl_Position = gl_ProjectionMatrix * position;
// the zero height should be invisible
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
// and the shadow texture coordinates
gl_TexCoord[1] = vec4(dot(gl_EyePlaneS[0], position), dot(gl_EyePlaneT[0], position), dot(gl_EyePlaneR[0], position), 1.0);
}

View file

@ -1,44 +0,0 @@
#version 120
//
// metavoxel_heightfield_light_cascaded_shadow_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 8/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the shadow texture
uniform sampler2DShadow shadowMap;
// the distances to the cascade sections
uniform vec3 shadowDistances;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated position
varying vec4 position;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the index of the cascade to use and the corresponding texture coordinates
int shadowIndex = int(dot(step(vec3(position.z), shadowDistances), vec3(1.0, 1.0, 1.0)));
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
dot(gl_EyePlaneR[shadowIndex], position));
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, shadowTexCoord + vec3(shadowScale, shadowScale, 0.0)).r);
gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
}

View file

@ -1,33 +0,0 @@
#version 120
//
// metavoxel_heightfield_light_shadow_map.frag
// fragment shader
//
// Created by Andrzej Kapolka on 8/20/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
gl_FragColor = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
}

View file

@ -1,37 +0,0 @@
#version 120
//
// metavoxel_heightfield.frag
// fragment shader
//
// Created by Andrzej Kapolka on 7/28/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the diffuse texture
uniform sampler2D diffuseMap;
// the shadow texture
uniform sampler2DShadow shadowMap;
// the inverse of the size of the shadow map
const float shadowScale = 1.0 / 2048.0;
// the interpolated normal
varying vec4 normal;
void main(void) {
// compute the base color based on OpenGL lighting model
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
float facingLight = step(0.0, diffuse) * 0.25 *
(shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(-shadowScale, shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, -shadowScale, 0.0)).r +
shadow2D(shadowMap, gl_TexCoord[1].stp + vec3(shadowScale, shadowScale, 0.0)).r);
vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient +
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
}

View file

@ -0,0 +1,21 @@
#version 120
//
// metavoxel_voxel_base.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/4/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the interpolated normal
varying vec4 normal;
void main(void) {
// store the interpolated color and normal
gl_FragData[0] = gl_Color;
gl_FragData[1] = normalize(normal) * 0.5 + vec4(0.5, 0.5, 0.5, 1.0);
}

View file

@ -0,0 +1,26 @@
#version 120
//
// metavoxel_voxel_base.vert
// vertex shader
//
// Created by Andrzej Kapolka on 9/4/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the interpolated normal
varying vec4 normal;
void main(void) {
// transform and store the normal for interpolation
normal = vec4(normalize(gl_NormalMatrix * gl_Normal), 0.0);
// use the fixed-function position
gl_Position = ftransform();
// copy the color for interpolation
gl_FrontColor = vec4(gl_Color.rgb, 0.0);
}

View file

@ -0,0 +1,29 @@
#version 120
//
// metavoxel_voxel_splat.frag
// fragment shader
//
// Created by Andrzej Kapolka on 9/4/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the number of splats per pass
const int SPLAT_COUNT = 4;
// the splat textures
uniform sampler2D diffuseMaps[SPLAT_COUNT];
// alpha values for the four splat textures
varying vec4 alphaValues;
void main(void) {
// blend the splat textures
gl_FragColor = (texture2D(diffuseMaps[0], gl_TexCoord[0].st) * alphaValues.x +
texture2D(diffuseMaps[1], gl_TexCoord[1].st) * alphaValues.y +
texture2D(diffuseMaps[2], gl_TexCoord[2].st) * alphaValues.z +
texture2D(diffuseMaps[3], gl_TexCoord[3].st) * alphaValues.w);
}

View file

@ -0,0 +1,62 @@
#version 120
//
// metavoxel_voxel_splat.vert
// vertex shader
//
// Created by Andrzej Kapolka on 9/4/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
// the splat textures scales on the S axis
uniform vec4 splatTextureScalesS;
// the splat texture scales on the T axis
uniform vec4 splatTextureScalesT;
// the lower bounds of the values corresponding to the splat textures
uniform vec4 textureValueMinima;
// the upper bounds of the values corresponding to the splat textures
uniform vec4 textureValueMaxima;
// the materials to apply to the vertex
attribute vec4 materials;
// the weights of each material
attribute vec4 materialWeights;
// alpha values for the four splat textures
varying vec4 alphaValues;
void main(void) {
// use the fixed-function position
gl_Position = ftransform();
// pass along the scaled/offset texture coordinates
vec4 textureSpacePosition = vec4(gl_Vertex.xz, 0.0, 1.0);
gl_TexCoord[0] = textureSpacePosition * vec4(splatTextureScalesS[0], splatTextureScalesT[0], 0.0, 1.0);
gl_TexCoord[1] = textureSpacePosition * vec4(splatTextureScalesS[1], splatTextureScalesT[1], 0.0, 1.0);
gl_TexCoord[2] = textureSpacePosition * vec4(splatTextureScalesS[2], splatTextureScalesT[2], 0.0, 1.0);
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
// compute the alpha values for each texture
float value = materials[0];
vec4 valueVector = vec4(value, value, value, value);
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[0];
value = materials[1];
valueVector = vec4(value, value, value, value);
alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[1];
value = materials[2];
valueVector = vec4(value, value, value, value);
alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[2];
value = materials[3];
valueVector = vec4(value, value, value, value);
alphaValues += step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima) * materialWeights[3];
}

View file

@ -1315,7 +1315,7 @@ void Application::dropEvent(QDropEvent *event) {
const QMimeData *mimeData = event->mimeData();
foreach (QUrl url, mimeData->urls()) {
if (url.url().toLower().endsWith(SNAPSHOT_EXTENSION)) {
snapshotPath = url.url().remove("file://");
snapshotPath = url.toLocalFile();
break;
}
}
@ -3932,6 +3932,7 @@ void Application::setPreviousScriptLocation(const QString& previousScriptLocatio
_previousScriptLocation = previousScriptLocation;
QMutexLocker locker(&_settingsMutex);
_settings->setValue("LastScriptLocation", _previousScriptLocation);
bumpSettings();
}
void Application::loadDialog() {

View file

@ -725,6 +725,9 @@ void Menu::loadSettings(QSettings* settings) {
Application::getInstance()->updateWindowTitle();
NodeList::getInstance()->loadData(settings);
// notify that a settings has changed
connect(&NodeList::getInstance()->getDomainHandler(), &DomainHandler::hostnameChanged, this, &Menu::bumpSettings);
// MyAvatar caches some menu options, so we have to update them whenever we load settings.
// TODO: cache more settings in MyAvatar that are checked with very high frequency.
MyAvatar* myAvatar = Application::getInstance()->getAvatar();
@ -894,6 +897,8 @@ void Menu::handleViewFrustumOffsetKeyModifier(int key) {
default:
break;
}
bumpSettings();
}
void Menu::addDisabledActionAndSeparator(QMenu* destinationMenu, const QString& actionName, int menuItemLocation) {
@ -1000,7 +1005,7 @@ QAction* Menu::addCheckableActionToQMenuAndActionHash(QMenu* destinationMenu,
QAction::NoRole, menuItemLocation);
action->setCheckable(true);
action->setChecked(checked);
connect(action, SIGNAL(changed()), Application::getInstance(), SLOT(bumpSettings()));
connect(action, SIGNAL(changed()), this, SLOT(bumpSettings()));
return action;
}
@ -1042,6 +1047,10 @@ void Menu::aboutApp() {
InfoView::forcedShow();
}
void Menu::bumpSettings() {
Application::getInstance()->bumpSettings();
}
void sendFakeEnterEvent() {
QPoint lastCursorPosition = QCursor::pos();
QGLWidget* glWidget = Application::getInstance()->getGLWidget();
@ -1115,6 +1124,8 @@ void Menu::changePrivateKey() {
// pull the private key from the dialog
_walletPrivateKey = privateKeyDialog.textValue().toUtf8();
}
bumpSettings();
sendFakeEnterEvent();
}
@ -1637,10 +1648,12 @@ void Menu::resetLODAdjust() {
void Menu::setVoxelSizeScale(float sizeScale) {
_voxelSizeScale = sizeScale;
bumpSettings();
}
void Menu::setBoundaryLevelAdjust(int boundaryLevelAdjust) {
_boundaryLevelAdjust = boundaryLevelAdjust;
bumpSettings();
}
void Menu::lodTools() {
@ -1930,5 +1943,6 @@ QString Menu::getSnapshotsLocation() const {
void Menu::setScriptsLocation(const QString& scriptsLocation) {
_scriptsLocation = scriptsLocation;
bumpSettings();
emit scriptLocationChanged(scriptsLocation);
}

View file

@ -92,20 +92,20 @@ public:
const InboundAudioStream::Settings& getReceivedAudioStreamSettings() const { return _receivedAudioStreamSettings; }
void setReceivedAudioStreamSettings(const InboundAudioStream::Settings& receivedAudioStreamSettings) { _receivedAudioStreamSettings = receivedAudioStreamSettings; }
float getFieldOfView() const { return _fieldOfView; }
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; }
void setFieldOfView(float fieldOfView) { _fieldOfView = fieldOfView; bumpSettings(); }
float getRealWorldFieldOfView() const { return _realWorldFieldOfView; }
void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; }
void setRealWorldFieldOfView(float realWorldFieldOfView) { _realWorldFieldOfView = realWorldFieldOfView; bumpSettings(); }
float getOculusUIAngularSize() const { return _oculusUIAngularSize; }
void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; }
void setOculusUIAngularSize(float oculusUIAngularSize) { _oculusUIAngularSize = oculusUIAngularSize; bumpSettings(); }
float getSixenseReticleMoveSpeed() const { return _sixenseReticleMoveSpeed; }
void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; }
void setSixenseReticleMoveSpeed(float sixenseReticleMoveSpeed) { _sixenseReticleMoveSpeed = sixenseReticleMoveSpeed; bumpSettings(); }
bool getInvertSixenseButtons() const { return _invertSixenseButtons; }
void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; }
void setInvertSixenseButtons(bool invertSixenseButtons) { _invertSixenseButtons = invertSixenseButtons; bumpSettings(); }
float getFaceshiftEyeDeflection() const { return _faceshiftEyeDeflection; }
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; }
void setFaceshiftEyeDeflection(float faceshiftEyeDeflection) { _faceshiftEyeDeflection = faceshiftEyeDeflection; bumpSettings(); }
QString getSnapshotsLocation() const;
void setSnapshotsLocation(QString snapshotsLocation) { _snapshotsLocation = snapshotsLocation; }
void setSnapshotsLocation(QString snapshotsLocation) { _snapshotsLocation = snapshotsLocation; bumpSettings(); }
const QString& getScriptsLocation() const { return _scriptsLocation; }
void setScriptsLocation(const QString& scriptsLocation);
@ -128,13 +128,13 @@ public:
void resetLODAdjust();
void setVoxelSizeScale(float sizeScale);
float getVoxelSizeScale() const { return _voxelSizeScale; }
void setAutomaticAvatarLOD(bool automaticAvatarLOD) { _automaticAvatarLOD = automaticAvatarLOD; }
void setAutomaticAvatarLOD(bool automaticAvatarLOD) { _automaticAvatarLOD = automaticAvatarLOD; bumpSettings(); }
bool getAutomaticAvatarLOD() const { return _automaticAvatarLOD; }
void setAvatarLODDecreaseFPS(float avatarLODDecreaseFPS) { _avatarLODDecreaseFPS = avatarLODDecreaseFPS; }
void setAvatarLODDecreaseFPS(float avatarLODDecreaseFPS) { _avatarLODDecreaseFPS = avatarLODDecreaseFPS; bumpSettings(); }
float getAvatarLODDecreaseFPS() const { return _avatarLODDecreaseFPS; }
void setAvatarLODIncreaseFPS(float avatarLODIncreaseFPS) { _avatarLODIncreaseFPS = avatarLODIncreaseFPS; }
void setAvatarLODIncreaseFPS(float avatarLODIncreaseFPS) { _avatarLODIncreaseFPS = avatarLODIncreaseFPS; bumpSettings(); }
float getAvatarLODIncreaseFPS() const { return _avatarLODIncreaseFPS; }
void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; }
void setAvatarLODDistanceMultiplier(float multiplier) { _avatarLODDistanceMultiplier = multiplier; bumpSettings(); }
float getAvatarLODDistanceMultiplier() const { return _avatarLODDistanceMultiplier; }
void setBoundaryLevelAdjust(int boundaryLevelAdjust);
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
@ -145,7 +145,7 @@ public:
// User Tweakable PPS from Voxel Server
int getMaxVoxelPacketsPerSecond() const { return _maxVoxelPacketsPerSecond; }
void setMaxVoxelPacketsPerSecond(int maxVoxelPacketsPerSecond) { _maxVoxelPacketsPerSecond = maxVoxelPacketsPerSecond; }
void setMaxVoxelPacketsPerSecond(int maxVoxelPacketsPerSecond) { _maxVoxelPacketsPerSecond = maxVoxelPacketsPerSecond; bumpSettings(); }
QAction* addActionToQMenuAndActionHash(QMenu* destinationMenu,
const QString& actionName,
@ -206,6 +206,7 @@ public slots:
private slots:
void aboutApp();
void bumpSettings();
void editPreferences();
void editAttachments();
void editAnimations();

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,7 @@ public:
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; }
void simulate(float deltaTime);
void render();
@ -51,6 +52,12 @@ public:
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
void noteNeedToLight() { _needToLight = true; }
signals:
void rendering();
protected:
virtual MetavoxelClient* createClient(const SharedNodePointer& node);
@ -59,12 +66,33 @@ private:
void guideToAugmented(MetavoxelVisitor& visitor, bool render = false);
class LightLocations {
public:
int shadowDistances;
int shadowScale;
int nearLocation;
int depthScale;
int depthTexCoordOffset;
int depthTexCoordScale;
};
static void loadLightProgram(const char* name, ProgramObject& program, LightLocations& locations);
AttributePointer _pointBufferAttribute;
AttributePointer _heightfieldBufferAttribute;
AttributePointer _voxelBufferAttribute;
MetavoxelLOD _lod;
QReadWriteLock _lodLock;
Frustum _frustum;
bool _needToLight;
ProgramObject _directionalLight;
LightLocations _directionalLightLocations;
ProgramObject _directionalLightShadowMap;
LightLocations _directionalLightShadowMapLocations;
ProgramObject _directionalLightCascadedShadowMap;
LightLocations _directionalLightCascadedShadowMapLocations;
};
/// Describes contents of a point in a point buffer.
@ -143,8 +171,8 @@ public:
static const int HEIGHT_EXTENSION;
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height,
const QByteArray& color, const QByteArray& texture = QByteArray(),
const QVector<SharedObjectPointer>& textures = QVector<SharedObjectPointer>());
const QByteArray& color, const QByteArray& material = QByteArray(),
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
~HeightfieldBuffer();
const glm::vec3& getTranslation() const { return _translation; }
@ -159,10 +187,10 @@ public:
QByteArray& getColor() { return _color; }
const QByteArray& getColor() const { return _color; }
QByteArray& getTexture() { return _texture; }
const QByteArray& getTexture() const { return _texture; }
QByteArray& getMaterial() { return _material; }
const QByteArray& getMaterial() const { return _material; }
const QVector<SharedObjectPointer>& getTextures() const { return _textures; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
QByteArray getUnextendedHeight() const;
QByteArray getUnextendedColor() const;
@ -183,11 +211,11 @@ private:
Box _colorBounds;
QByteArray _height;
QByteArray _color;
QByteArray _texture;
QVector<SharedObjectPointer> _textures;
QByteArray _material;
QVector<SharedObjectPointer> _materials;
GLuint _heightTextureID;
GLuint _colorTextureID;
GLuint _textureTextureID;
GLuint _materialTextureID;
QVector<NetworkTexturePointer> _networkTextures;
int _heightSize;
float _heightIncrement;
@ -212,6 +240,37 @@ private:
QVector<BufferDataPointer> _buffers;
};
/// Describes contents of a vertex in a voxel buffer.
class VoxelPoint {
public:
glm::vec3 vertex;
quint8 color[3];
char normal[3];
quint8 materials[4];
quint8 materialWeights[4];
};
/// Contains the information necessary to render a voxel block.
class VoxelBuffer : public BufferData {
public:
VoxelBuffer(const QVector<VoxelPoint>& vertices, const QVector<int>& indices,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
virtual void render(bool cursor = false);
private:
QVector<VoxelPoint> _vertices;
QVector<int> _indices;
int _vertexCount;
int _indexCount;
QOpenGLBuffer _vertexBuffer;
QOpenGLBuffer _indexBuffer;
QVector<SharedObjectPointer> _materials;
QVector<NetworkTexturePointer> _networkTextures;
};
/// A client-side attribute that stores renderable buffers.
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
Q_OBJECT
@ -233,42 +292,33 @@ public:
static void init();
static ProgramObject& getHeightfieldProgram() { return _heightfieldProgram; }
static int getHeightScaleLocation() { return _heightScaleLocation; }
static int getColorScaleLocation() { return _colorScaleLocation; }
static ProgramObject& getShadowMapHeightfieldProgram() { return _shadowMapHeightfieldProgram; }
static int getShadowMapHeightScaleLocation() { return _shadowMapHeightScaleLocation; }
static int getShadowMapColorScaleLocation() { return _shadowMapColorScaleLocation; }
static ProgramObject& getCascadedShadowMapHeightfieldProgram() { return _cascadedShadowMapHeightfieldProgram; }
static int getCascadedShadowMapHeightScaleLocation() { return _cascadedShadowMapHeightScaleLocation; }
static int getCascadedShadowMapColorScaleLocation() { return _cascadedShadowMapColorScaleLocation; }
static ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; }
static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; }
static int getBaseColorScaleLocation() { return _baseColorScaleLocation; }
class SplatLocations {
public:
int heightScale;
int textureScale;
int splatTextureOffset;
int splatTextureScalesS;
int splatTextureScalesT;
int textureValueMinima;
int textureValueMaxima;
int materials;
int materialWeights;
};
static ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; }
static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; }
static int getSplatTextureScaleLocation() { return _splatTextureScaleLocation; }
static int getSplatTextureOffsetLocation() { return _splatTextureOffsetLocation; }
static int getSplatTextureScalesSLocation() { return _splatTextureScalesSLocation; }
static int getSplatTextureScalesTLocation() { return _splatTextureScalesTLocation; }
static int getSplatTextureValueMinimaLocation() { return _splatTextureValueMinimaLocation; }
static int getSplatTextureValueMaximaLocation() { return _splatTextureValueMaximaLocation; }
static ProgramObject& getLightHeightfieldProgram() { return _lightHeightfieldProgram; }
static int getLightHeightScaleLocation() { return _lightHeightScaleLocation; }
static ProgramObject& getShadowLightHeightfieldProgram() { return _shadowLightHeightfieldProgram; }
static int getShadowLightHeightScaleLocation() { return _shadowLightHeightScaleLocation; }
static ProgramObject& getCascadedShadowLightHeightfieldProgram() { return _cascadedShadowLightHeightfieldProgram; }
static int getCascadedShadowLightHeightScaleLocation() { return _cascadedShadowLightHeightScaleLocation; }
static const SplatLocations& getSplatHeightfieldLocations() { return _splatHeightfieldLocations; }
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
static ProgramObject& getBaseVoxelProgram() { return _baseVoxelProgram; }
static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; }
static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; }
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
@ -277,27 +327,18 @@ public:
private:
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
static ProgramObject _pointProgram;
static int _pointScaleLocation;
static ProgramObject _heightfieldProgram;
static int _heightScaleLocation;
static int _colorScaleLocation;
static ProgramObject _shadowMapHeightfieldProgram;
static int _shadowMapHeightScaleLocation;
static int _shadowMapColorScaleLocation;
static ProgramObject _cascadedShadowMapHeightfieldProgram;
static int _cascadedShadowMapHeightScaleLocation;
static int _cascadedShadowMapColorScaleLocation;
static int _shadowDistancesLocation;
static ProgramObject _baseHeightfieldProgram;
static int _baseHeightScaleLocation;
static int _baseColorScaleLocation;
static ProgramObject _splatHeightfieldProgram;
static SplatLocations _splatHeightfieldLocations;
static int _splatHeightScaleLocation;
static int _splatTextureScaleLocation;
static int _splatTextureOffsetLocation;
@ -306,17 +347,11 @@ private:
static int _splatTextureValueMinimaLocation;
static int _splatTextureValueMaximaLocation;
static ProgramObject _lightHeightfieldProgram;
static int _lightHeightScaleLocation;
static ProgramObject _shadowLightHeightfieldProgram;
static int _shadowLightHeightScaleLocation;
static ProgramObject _cascadedShadowLightHeightfieldProgram;
static int _cascadedShadowLightHeightScaleLocation;
static int _shadowLightDistancesLocation;
static ProgramObject _heightfieldCursorProgram;
static ProgramObject _baseVoxelProgram;
static ProgramObject _splatVoxelProgram;
static SplatLocations _splatVoxelLocations;
};
/// Base class for spanner renderers; provides clipping.

View file

@ -79,7 +79,8 @@ MyAvatar::MyAvatar() :
_lookAtTargetAvatar(),
_shouldRender(true),
_billboardValid(false),
_physicsSimulation()
_physicsSimulation(),
_voxelShapeManager()
{
ShapeCollider::initDispatchTable();
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
@ -90,11 +91,11 @@ MyAvatar::MyAvatar() :
_skeletonModel.setEnableShapes(true);
Ragdoll* ragdoll = _skeletonModel.buildRagdoll();
_physicsSimulation.setRagdoll(ragdoll);
_physicsSimulation.addEntity(&_voxelShapeManager);
}
MyAvatar::~MyAvatar() {
_physicsSimulation.setRagdoll(NULL);
_physicsSimulation.setEntity(NULL);
_physicsSimulation.clear();
_lookAtTargetAvatar.clear();
}
@ -1486,112 +1487,125 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
static CollisionList myCollisions(64);
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
const float MAX_VOXEL_COLLISION_SPEED = 100.0f;
float speed = glm::length(_velocity);
if (speed > MAX_VOXEL_COLLISION_SPEED) {
// don't even bother to try to collide against voxles when moving very fast
_trapDuration = 0.0f;
return;
}
bool isTrapped = false;
myCollisions.clear();
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) {
const float VOXEL_ELASTICITY = 0.0f;
const float VOXEL_DAMPING = 0.0f;
float capsuleRadius = boundingShape.getRadius();
float capsuleHalfHeight = boundingShape.getHalfHeight();
const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight;
const float MIN_STEP_HEIGHT = 0.0f;
glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
float highestStep = 0.0f;
float lowestStep = MAX_STEP_HEIGHT;
glm::vec3 floorPoint;
glm::vec3 stepPenetration(0.0f);
glm::vec3 totalPenetration(0.0f);
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) {
// We use a multiple of the avatar's boundingRadius as the size of the cube of interest.
float cubeScale = 4.0f * getBoundingRadius();
glm::vec3 corner = getPosition() - glm::vec3(0.5f * cubeScale);
AACube boundingCube(corner, cubeScale);
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
glm::vec3 cubeCenter = collision->_vecData;
float cubeSide = collision->_floatData;
float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection);
float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection);
const float MAX_TRAP_PERIOD = 0.125f;
if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) {
isTrapped = true;
if (_trapDuration > MAX_TRAP_PERIOD) {
float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection);
if (distance < 0.0f) {
distance = fabsf(distance) + 0.5f * cubeSide;
}
distance += capsuleRadius + capsuleHalfHeight;
totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection);
continue;
}
} else if (_trapDuration > MAX_TRAP_PERIOD) {
// we're trapped, ignore this collision
continue;
}
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
if (glm::dot(collision->_penetration, _velocity) >= 0.0f) {
glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection;
float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase);
if (stepHeight > highestStep) {
highestStep = stepHeight;
stepPenetration = collision->_penetration;
}
if (stepHeight < lowestStep) {
lowestStep = stepHeight;
floorPoint = collision->_contactPoint - collision->_penetration;
}
}
// query the VoxelTree for cubes that touch avatar's boundingCube
CubeList cubes;
if (Application::getInstance()->getVoxelTree()->findContentInCube(boundingCube, cubes)) {
_voxelShapeManager.updateVoxels(cubes);
}
if (lowestStep < MAX_STEP_HEIGHT) {
_lastFloorContactPoint = floorPoint;
}
float penetrationLength = glm::length(totalPenetration);
if (penetrationLength < EPSILON) {
} else {
const float MAX_VOXEL_COLLISION_SPEED = 100.0f;
float speed = glm::length(_velocity);
if (speed > MAX_VOXEL_COLLISION_SPEED) {
// don't even bother to try to collide against voxles when moving very fast
_trapDuration = 0.0f;
return;
}
float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection);
if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) {
// we're colliding against an edge
glm::vec3 targetVelocity = _motorVelocity;
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
// rotate _motorVelocity into world frame
glm::quat rotation = getHead()->getCameraOrientation();
targetVelocity = rotation * _motorVelocity;
}
if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) {
// we're puhing into the edge, so we want to lift
// remove unhelpful horizontal component of the step's penetration
totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection);
// further adjust penetration to help lift
float liftSpeed = glm::max(MAX_WALKING_SPEED, speed);
float thisStep = glm::min(liftSpeed * deltaTime, highestStep);
float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep;
if (extraStep > 0.0f) {
totalPenetration -= extraStep * _worldUpDirection;
bool isTrapped = false;
myCollisions.clear();
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) {
const float VOXEL_ELASTICITY = 0.0f;
const float VOXEL_DAMPING = 0.0f;
float capsuleRadius = boundingShape.getRadius();
float capsuleHalfHeight = boundingShape.getHalfHeight();
const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight;
const float MIN_STEP_HEIGHT = 0.0f;
glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
float highestStep = 0.0f;
float lowestStep = MAX_STEP_HEIGHT;
glm::vec3 floorPoint;
glm::vec3 stepPenetration(0.0f);
glm::vec3 totalPenetration(0.0f);
for (int i = 0; i < myCollisions.size(); ++i) {
CollisionInfo* collision = myCollisions[i];
glm::vec3 cubeCenter = collision->_vecData;
float cubeSide = collision->_floatData;
float verticalDepth = glm::dot(collision->_penetration, _worldUpDirection);
float horizontalDepth = glm::length(collision->_penetration - verticalDepth * _worldUpDirection);
const float MAX_TRAP_PERIOD = 0.125f;
if (horizontalDepth > capsuleRadius || fabsf(verticalDepth) > MAX_STEP_HEIGHT) {
isTrapped = true;
if (_trapDuration > MAX_TRAP_PERIOD) {
float distance = glm::dot(boundingShape.getTranslation() - cubeCenter, _worldUpDirection);
if (distance < 0.0f) {
distance = fabsf(distance) + 0.5f * cubeSide;
}
distance += capsuleRadius + capsuleHalfHeight;
totalPenetration = addPenetrations(totalPenetration, - distance * _worldUpDirection);
continue;
}
} else if (_trapDuration > MAX_TRAP_PERIOD) {
// we're trapped, ignore this collision
continue;
}
totalPenetration = addPenetrations(totalPenetration, collision->_penetration);
if (glm::dot(collision->_penetration, _velocity) >= 0.0f) {
glm::vec3 cubeTop = cubeCenter + (0.5f * cubeSide) * _worldUpDirection;
float stepHeight = glm::dot(_worldUpDirection, cubeTop - footBase);
if (stepHeight > highestStep) {
highestStep = stepHeight;
stepPenetration = collision->_penetration;
}
if (stepHeight < lowestStep) {
lowestStep = stepHeight;
floorPoint = collision->_contactPoint - collision->_penetration;
}
}
}
if (lowestStep < MAX_STEP_HEIGHT) {
_lastFloorContactPoint = floorPoint;
}
float penetrationLength = glm::length(totalPenetration);
if (penetrationLength < EPSILON) {
_trapDuration = 0.0f;
return;
}
float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection);
if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) {
// we're colliding against an edge
glm::vec3 targetVelocity = _motorVelocity;
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
// rotate _motorVelocity into world frame
glm::quat rotation = getHead()->getCameraOrientation();
targetVelocity = rotation * _motorVelocity;
}
if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) {
// we're puhing into the edge, so we want to lift
// remove unhelpful horizontal component of the step's penetration
totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection);
// further adjust penetration to help lift
float liftSpeed = glm::max(MAX_WALKING_SPEED, speed);
float thisStep = glm::min(liftSpeed * deltaTime, highestStep);
float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep;
if (extraStep > 0.0f) {
totalPenetration -= extraStep * _worldUpDirection;
}
_position -= totalPenetration;
} else {
// we're not pushing into the edge, so let the avatar fall
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
_position -= totalPenetration;
} else {
// we're not pushing into the edge, so let the avatar fall
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
} else {
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
}
// Don't make a collision sound against voxlels by default -- too annoying when walking
//const float VOXEL_COLLISION_FREQUENCY = 0.5f;
//updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
}
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
// Don't make a collision sound against voxlels by default -- too annoying when walking
//const float VOXEL_COLLISION_FREQUENCY = 0.5f;
//updateCollisionSound(myCollisions[0]->_penetration, deltaTime, VOXEL_COLLISION_FREQUENCY);
}
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
}
}
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {
@ -1957,6 +1971,9 @@ void MyAvatar::updateMotionBehaviorsFromMenu() {
} else {
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
}
if (!(_collisionGroups | COLLISION_GROUP_VOXELS)) {
_voxelShapeManager.clearShapes();
}
}
void MyAvatar::renderAttachments(RenderMode renderMode) {

View file

@ -17,6 +17,7 @@
#include <PhysicsSimulation.h>
#include "Avatar.h"
#include "VoxelShapeManager.h"
class ModelItemID;
@ -214,6 +215,7 @@ private:
QList<AnimationHandlePointer> _animationHandles;
PhysicsSimulation _physicsSimulation;
VoxelShapeManager _voxelShapeManager;
RecorderPointer _recorder;

View file

@ -759,7 +759,9 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
float capsuleRadius = 0.5f * sqrtf(0.5f * (diagonal.x * diagonal.x + diagonal.z * diagonal.z));
_boundingShape.setRadius(capsuleRadius);
_boundingShape.setHalfHeight(0.5f * diagonal.y - capsuleRadius);
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum);
glm::vec3 rootPosition = _jointStates[geometry.rootJointIndex].getPosition();
_boundingShapeLocalOffset = 0.5f * (totalExtents.maximum + totalExtents.minimum) - rootPosition;
_boundingRadius = 0.5f * glm::length(diagonal);
}

View file

@ -0,0 +1,99 @@
//
// VoxelShapeManager.cpp
// interface/src/avatar
//
// Created by Andrew Meadows on 2014.09.02
// Copyright 2012 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <glm/gtx/norm.hpp>
#include <AACubeShape.h>
#include <PhysicsSimulation.h>
#include <SharedUtil.h>
#include "VoxelShapeManager.h"
VoxelShapeManager::VoxelShapeManager() : PhysicsEntity(), _lastSimulationTranslation(0.0f) {
}
VoxelShapeManager::~VoxelShapeManager() {
clearShapes();
}
void VoxelShapeManager::stepForward(float deltaTime) {
PhysicsSimulation* simulation = getSimulation();
if (simulation) {
glm::vec3 simulationOrigin = simulation->getTranslation();
if (glm::distance2(_lastSimulationTranslation, simulationOrigin) > EPSILON) {
VoxelPool::const_iterator voxelItr = _voxels.constBegin();
while (voxelItr != _voxels.constEnd()) {
// the shape's position is stored in the simulation-frame
const VoxelInfo& voxel = voxelItr.value();
voxel._shape->setTranslation(voxel._cube.calcCenter() - simulationOrigin);
++voxelItr;
}
_lastSimulationTranslation = simulationOrigin;
}
}
}
void VoxelShapeManager::buildShapes() {
// the shapes are owned by the elements of _voxels,
// so _shapes is constructed by harvesting them from _voxels
_shapes.clear();
VoxelPool::const_iterator voxelItr = _voxels.constBegin();
while (voxelItr != _voxels.constEnd()) {
_shapes.push_back(voxelItr.value()._shape);
++voxelItr;
}
}
void VoxelShapeManager::clearShapes() {
PhysicsEntity::clearShapes();
_voxels.clear();
}
void VoxelShapeManager::updateVoxels(CubeList& cubes) {
PhysicsSimulation* simulation = getSimulation();
if (!simulation) {
return;
}
int numChanges = 0;
VoxelPool::iterator voxelItr = _voxels.begin();
while (voxelItr != _voxels.end()) {
// look for this voxel in cubes
CubeList::iterator cubeItr = cubes.find(voxelItr.key());
if (cubeItr == cubes.end()) {
// did not find it --> remove the voxel
simulation->removeShape(voxelItr.value()._shape);
voxelItr = _voxels.erase(voxelItr);
++numChanges;
} else {
// found it --> remove the cube
cubes.erase(cubeItr);
voxelItr++;
}
}
// add remaining cubes to _voxels
glm::vec3 simulationOrigin = simulation->getTranslation();
CubeList::const_iterator cubeItr = cubes.constBegin();
while (cubeItr != cubes.constEnd()) {
AACube cube = cubeItr.value();
AACubeShape* shape = new AACubeShape(cube.getScale(), cube.calcCenter() - simulationOrigin);
shape->setEntity(this);
VoxelInfo voxel = {cube, shape };
_voxels.insert(cubeItr.key(), voxel);
++numChanges;
++cubeItr;
}
if (numChanges > 0) {
buildShapes();
}
}

View file

@ -0,0 +1,51 @@
//
// VoxelShapeManager.h
// interface/src/avatar
//
// Created by Andrew Meadows on 2014.09.02
// Copyright 2012 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#ifndef hifi_VoxelShapeManager_h
#define hifi_VoxelShapeManager_h
#include <QHash>
#include <AACube.h>
#include <PhysicsEntity.h>
#include <Octree.h>
#include "VoxelShapeManager.h"
class AACubeShape;
class VoxelInfo{
public:
AACube _cube;
AACubeShape* _shape;
};
typedef QHash<quint64, VoxelInfo> VoxelPool;
class VoxelShapeManager : public PhysicsEntity {
public:
VoxelShapeManager();
~VoxelShapeManager();
void stepForward(float deltaTime);
void buildShapes();
void clearShapes();
/// \param cubes list of AACubes representing all of the voxels that should be in this VoxelShapeManager
void updateVoxels(CubeList& cubes);
private:
glm::vec3 _lastSimulationTranslation;
VoxelPool _voxels;
};
#endif // hifi_VoxelShapeManager_h

View file

@ -29,6 +29,7 @@ TextureCache::TextureCache() :
_whiteTextureID(0),
_blueTextureID(0),
_primaryDepthTextureID(0),
_primaryNormalTextureID(0),
_primaryFramebufferObject(NULL),
_secondaryFramebufferObject(NULL),
_tertiaryFramebufferObject(NULL),
@ -46,6 +47,7 @@ TextureCache::~TextureCache() {
}
if (_primaryFramebufferObject) {
glDeleteTextures(1, &_primaryDepthTextureID);
glDeleteTextures(1, &_primaryNormalTextureID);
}
if (_primaryFramebufferObject) {
@ -71,6 +73,8 @@ void TextureCache::setFrameBufferSize(QSize frameBufferSize) {
_primaryFramebufferObject = NULL;
glDeleteTextures(1, &_primaryDepthTextureID);
_primaryDepthTextureID = 0;
glDeleteTextures(1, &_primaryNormalTextureID);
_primaryNormalTextureID = 0;
}
if (_secondaryFramebufferObject) {
@ -205,15 +209,22 @@ QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() {
glGenTextures(1, &_primaryDepthTextureID);
glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(),
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glGenTextures(1, &_primaryNormalTextureID);
glBindTexture(GL_TEXTURE_2D, _primaryNormalTextureID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _frameBufferSize.width(), _frameBufferSize.height(),
0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
_primaryFramebufferObject->bind();
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _primaryDepthTextureID, 0);
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, _primaryNormalTextureID, 0);
_primaryFramebufferObject->release();
}
return _primaryFramebufferObject;
@ -225,6 +236,12 @@ GLuint TextureCache::getPrimaryDepthTextureID() {
return _primaryDepthTextureID;
}
GLuint TextureCache::getPrimaryNormalTextureID() {
// ensure that the primary framebuffer object is initialized before returning the normal texture id
getPrimaryFramebufferObject();
return _primaryNormalTextureID;
}
QOpenGLFramebufferObject* TextureCache::getSecondaryFramebufferObject() {
if (!_secondaryFramebufferObject) {
_secondaryFramebufferObject = createFramebufferObject();
@ -278,6 +295,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
delete _primaryFramebufferObject;
_primaryFramebufferObject = NULL;
glDeleteTextures(1, &_primaryDepthTextureID);
glDeleteTextures(1, &_primaryNormalTextureID);
}
if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) {
delete _secondaryFramebufferObject;

View file

@ -61,6 +61,9 @@ public:
/// Returns the ID of the primary framebuffer object's depth texture. This contains the Z buffer used in rendering.
GLuint getPrimaryDepthTextureID();
/// Returns the ID of the primary framebuffer object's normal texture.
GLuint getPrimaryNormalTextureID();
/// Returns a pointer to the secondary framebuffer object, used as an additional render target when performing full
/// screen effects.
QOpenGLFramebufferObject* getSecondaryFramebufferObject();
@ -95,6 +98,7 @@ private:
QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures;
GLuint _primaryDepthTextureID;
GLuint _primaryNormalTextureID;
QOpenGLFramebufferObject* _primaryFramebufferObject;
QOpenGLFramebufferObject* _secondaryFramebufferObject;
QOpenGLFramebufferObject* _tertiaryFramebufferObject;

View file

@ -124,6 +124,10 @@ void ChatWindow::showEvent(QShowEvent* event) {
ui->messagePlainTextEdit->setFocus();
}
Application::processEvents();
scrollToBottom();
#ifdef HAVE_QXMPP
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
if (xmppClient.isConnected()) {

View file

@ -66,13 +66,16 @@ MetavoxelEditor::MetavoxelEditor() :
attributeLayout->addLayout(attributeButtonLayout);
QPushButton* newAttribute = new QPushButton("New...");
attributeButtonLayout->addWidget(newAttribute);
attributeButtonLayout->addWidget(newAttribute, 1);
connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute()));
attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"));
attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"), 1);
_deleteAttribute->setEnabled(false);
connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute()));
attributeButtonLayout->addWidget(_showAll = new QCheckBox("Show All"));
connect(_showAll, SIGNAL(clicked()), SLOT(updateAttributes()));
QFormLayout* formLayout = new QFormLayout();
topLayout->addLayout(formLayout);
@ -116,11 +119,15 @@ MetavoxelEditor::MetavoxelEditor() :
addTool(new RemoveSpannerTool(this));
addTool(new ClearSpannersTool(this));
addTool(new SetSpannerTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new EraseHeightfieldTool(this));
addTool(new HeightfieldHeightBrushTool(this));
addTool(new HeightfieldColorBrushTool(this));
addTool(new HeightfieldTextureBrushTool(this));
addTool(new HeightfieldMaterialBrushTool(this));
addTool(new ImportHeightfieldTool(this));
addTool(new EraseHeightfieldTool(this));
addTool(new VoxelColorBoxTool(this));
addTool(new VoxelMaterialBoxTool(this));
addTool(new VoxelColorSphereTool(this));
addTool(new VoxelMaterialSphereTool(this));
updateAttributes();
@ -200,7 +207,7 @@ void MetavoxelEditor::selectedAttributeChanged() {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected);
foreach (MetavoxelTool* tool, _tools) {
if (tool->appliesTo(attribute)) {
if (tool->appliesTo(attribute) && (tool->isUserFacing() || _showAll->isChecked())) {
_toolBox->addItem(tool->objectName(), QVariant::fromValue(tool));
}
}
@ -214,6 +221,7 @@ void MetavoxelEditor::selectedAttributeChanged() {
editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
_valueArea->setWidget(editor);
}
updateTool();
}
void MetavoxelEditor::createNewAttribute() {
@ -271,6 +279,35 @@ void MetavoxelEditor::alignGridPosition() {
_gridPosition->setValue(step * floor(_gridPosition->value() / step));
}
void MetavoxelEditor::updateAttributes(const QString& select) {
// remember the selection in order to preserve it
QString selected = select.isNull() ? getSelectedAttribute() : select;
_attributes->clear();
// sort the names for consistent ordering
QList<QString> names;
if (_showAll->isChecked()) {
names = AttributeRegistry::getInstance()->getAttributes().keys();
} else {
foreach (const AttributePointer& attribute, AttributeRegistry::getInstance()->getAttributes()) {
if (attribute->isUserFacing()) {
names.append(attribute->getName());
}
}
}
qSort(names);
foreach (const QString& name, names) {
QListWidgetItem* item = new QListWidgetItem(name);
_attributes->addItem(item);
if (name == selected || selected.isNull()) {
item->setSelected(true);
selected = name;
}
}
}
void MetavoxelEditor::updateTool() {
MetavoxelTool* active = getActiveTool();
foreach (MetavoxelTool* tool, _tools) {
@ -335,25 +372,6 @@ void MetavoxelEditor::addTool(MetavoxelTool* tool) {
layout()->addWidget(tool);
}
void MetavoxelEditor::updateAttributes(const QString& select) {
// remember the selection in order to preserve it
QString selected = select.isNull() ? getSelectedAttribute() : select;
_attributes->clear();
// sort the names for consistent ordering
QList<QString> names = AttributeRegistry::getInstance()->getAttributes().keys();
qSort(names);
foreach (const QString& name, names) {
QListWidgetItem* item = new QListWidgetItem(name);
_attributes->addItem(item);
if (name == selected || selected.isNull()) {
item->setSelected(true);
selected = name;
}
}
}
MetavoxelTool* MetavoxelEditor::getActiveTool() const {
int index = _toolBox->currentIndex();
return (index == -1) ? NULL : static_cast<MetavoxelTool*>(_toolBox->itemData(index).value<QObject*>());
@ -361,9 +379,10 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const {
ProgramObject MetavoxelEditor::_gridProgram;
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue) :
MetavoxelTool::MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
_editor(editor),
_usesValue(usesValue) {
_usesValue(usesValue),
_userFacing(userFacing) {
QVBoxLayout* layout = new QVBoxLayout();
setLayout(layout);
@ -385,13 +404,13 @@ void MetavoxelTool::render() {
// nothing by default
}
BoxSetTool::BoxSetTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Set Value (Box)") {
BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
MetavoxelTool(editor, name, usesValue, userFacing) {
resetState();
}
void BoxSetTool::render() {
void BoxTool::render() {
if (Application::getInstance()->isMouseHidden()) {
resetState();
return;
@ -457,7 +476,7 @@ void BoxSetTool::render() {
glTranslatef(0.5f, 0.5f, 0.5f);
if (_state != HOVERING_STATE) {
const float BOX_ALPHA = 0.25f;
QColor color = _editor->getValue().value<QColor>();
QColor color = getColor();
if (color.isValid()) {
glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA);
} else {
@ -476,7 +495,7 @@ void BoxSetTool::render() {
glPopMatrix();
}
bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
bool BoxTool::eventFilter(QObject* watched, QEvent* event) {
switch (_state) {
case HOVERING_STATE:
if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) {
@ -515,12 +534,20 @@ bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
return false;
}
void BoxSetTool::resetState() {
void BoxTool::resetState() {
_state = HOVERING_STATE;
_startPosition = INVALID_VECTOR;
_height = 0.0f;
}
BoxSetTool::BoxSetTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Value (Box)", true, false) {
}
QColor BoxSetTool::getColor() {
return _editor->getValue().value<QColor>();
}
void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
if (!attribute) {
@ -533,7 +560,7 @@ void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum)
}
GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) :
MetavoxelTool(editor, "Set Value (Global)") {
MetavoxelTool(editor, "Set Value (Global)", true, false) {
QPushButton* button = new QPushButton("Apply");
layout()->addWidget(button);
@ -944,11 +971,9 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
_form->addRow("Color:", _color = new QPushButton());
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
}
void ImportHeightfieldTool::render() {
HeightfieldTool::render();
_preview.render(_translation->getValue(), _translation->getSingleStep());
connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering,
this, &ImportHeightfieldTool::renderPreview);
}
void ImportHeightfieldTool::apply() {
@ -966,7 +991,7 @@ void ImportHeightfieldTool::apply() {
QByteArray color;
if (buffer->getColor().isEmpty()) {
const int WHITE_VALUE = 0xFF;
color = QByteArray(height.size() * HeightfieldData::COLOR_BYTES, WHITE_VALUE);
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
} else {
color = buffer->getUnextendedColor();
}
@ -975,10 +1000,10 @@ void ImportHeightfieldTool::apply() {
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
int size = glm::sqrt(height.size()) + HeightfieldBuffer::SHARED_EDGE;
QByteArray texture(size * size, 0);
HeightfieldTextureDataPointer texturePointer(new HeightfieldTextureData(texture));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), encodeInline(texturePointer))));
QByteArray material(size * size, 0);
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue(
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), encodeInline(materialPointer))));
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
_translation->getValue() + buffer->getTranslation() * scale, data)) };
@ -1032,22 +1057,22 @@ void ImportHeightfieldTool::updatePreview() {
int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI);
int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ);
for (int y = 0; y < rows; y++) {
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * HeightfieldData::COLOR_BYTES;
uchar* src = _heightImage.scanLine(extendedI + y) + extendedJ * DataBlock::COLOR_BYTES;
char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
for (int x = 0; x < columns; x++) {
*dest++ = *src;
src += HeightfieldData::COLOR_BYTES;
src += DataBlock::COLOR_BYTES;
}
}
QByteArray color;
if (!_colorImage.isNull()) {
color = QByteArray(colorSize * colorSize * HeightfieldData::COLOR_BYTES, 0);
color = QByteArray(colorSize * colorSize * DataBlock::COLOR_BYTES, 0);
rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
for (int y = 0; y < rows; y++) {
memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES,
_colorImage.scanLine(i + y) + j * HeightfieldData::COLOR_BYTES,
columns * HeightfieldData::COLOR_BYTES);
memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES,
_colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES,
columns * DataBlock::COLOR_BYTES);
}
}
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
@ -1057,6 +1082,12 @@ void ImportHeightfieldTool::updatePreview() {
_preview.setBuffers(buffers);
}
void ImportHeightfieldTool::renderPreview() {
if (isVisible()) {
_preview.render(_translation->getValue(), _translation->getSingleStep());
}
}
EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) :
HeightfieldTool(editor, "Erase Heightfield") {
@ -1177,25 +1208,190 @@ QVariant HeightfieldColorBrushTool::createEdit(bool alternate) {
alternate ? QColor() : _color->getColor()));
}
HeightfieldTextureBrushTool::HeightfieldTextureBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Texture Brush") {
HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* editor) :
HeightfieldBrushTool(editor, "Material Brush") {
_form->addRow(_textureEditor = new SharedObjectEditor(&HeightfieldTexture::staticMetaObject, false));
connect(_textureEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldTextureBrushTool::updateTexture);
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldMaterialBrushTool::updateTexture);
}
QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) {
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
if (alternate) {
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
} else {
SharedObjectPointer texture = _textureEditor->getObject();
_textureEditor->detachObject();
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), texture,
SharedObjectPointer material = _materialEditor->getObject();
_materialEditor->detachObject();
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), material,
_texture ? _texture->getAverageColor() : QColor()));
}
}
void HeightfieldTextureBrushTool::updateTexture() {
HeightfieldTexture* texture = static_cast<HeightfieldTexture*>(_textureEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL());
void HeightfieldMaterialBrushTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}
VoxelColorBoxTool::VoxelColorBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Voxel Color (Box)", false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
widget->setLayout(form);
layout()->addWidget(widget);
form->addRow("Color:", _color = new QColorEditor(this));
}
bool VoxelColorBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelColorBoxTool::getColor() {
return _color->getColor();
}
void VoxelColorBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorBoxEdit(Box(minimum, maximum),
_editor->getGridSpacing(), _color->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelMaterialBoxTool::VoxelMaterialBoxTool(MetavoxelEditor* editor) :
BoxTool(editor, "Set Voxel Material (Box)", false) {
QWidget* widget = new QWidget();
QFormLayout* form = new QFormLayout();
widget->setLayout(form);
layout()->addWidget(widget);
form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialBoxTool::updateTexture);
}
bool VoxelMaterialBoxTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelMaterialBoxTool::getColor() {
return _texture ? _texture->getAverageColor() : QColor();
}
void VoxelMaterialBoxTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
SharedObjectPointer material = _materialEditor->getObject();
_materialEditor->detachObject();
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialBoxEdit(Box(minimum, maximum),
_editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
void VoxelMaterialBoxTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}
SphereTool::SphereTool(MetavoxelEditor* editor, const QString& name) :
MetavoxelTool(editor, name, false, true) {
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(1.0);
}
void SphereTool::render() {
if (Application::getInstance()->isMouseHidden()) {
return;
}
glm::quat rotation = _editor->getGridRotation();
glm::quat inverseRotation = glm::inverse(rotation);
glm::vec3 rayOrigin = inverseRotation * Application::getInstance()->getMouseRayOrigin();
glm::vec3 rayDirection = inverseRotation * Application::getInstance()->getMouseRayDirection();
float position = _editor->getGridPosition();
if (glm::abs(rayDirection.z) < EPSILON) {
return;
}
float distance = (position - rayOrigin.z) / rayDirection.z;
_position = Application::getInstance()->getMouseRayOrigin() +
Application::getInstance()->getMouseRayDirection() * distance;
glPushMatrix();
glTranslatef(_position.x, _position.y, _position.z);
const float CURSOR_ALPHA = 0.5f;
QColor color = getColor();
glColor4f(color.redF(), color.greenF(), color.blueF(), CURSOR_ALPHA);
glEnable(GL_CULL_FACE);
glutSolidSphere(_radius->value(), 10, 10);
glDisable(GL_CULL_FACE);
glPopMatrix();
}
bool SphereTool::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) {
applyValue(_position, _radius->value());
return true;
}
return false;
}
VoxelColorSphereTool::VoxelColorSphereTool(MetavoxelEditor* editor) :
SphereTool(editor, "Set Voxel Color (Sphere)") {
_form->addRow("Color:", _color = new QColorEditor(this));
}
bool VoxelColorSphereTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelColorSphereTool::getColor() {
return _color->getColor();
}
void VoxelColorSphereTool::applyValue(const glm::vec3& position, float radius) {
MetavoxelEditMessage message = { QVariant::fromValue(VoxelColorSphereEdit(position, radius,
_editor->getGridSpacing(), _color->getColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
VoxelMaterialSphereTool::VoxelMaterialSphereTool(MetavoxelEditor* editor) :
SphereTool(editor, "Set Voxel Material (Sphere)") {
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &VoxelMaterialSphereTool::updateTexture);
}
bool VoxelMaterialSphereTool::appliesTo(const AttributePointer& attribute) const {
return attribute->inherits("VoxelColorAttribute");
}
QColor VoxelMaterialSphereTool::getColor() {
return _texture ? _texture->getAverageColor() : QColor();
}
void VoxelMaterialSphereTool::applyValue(const glm::vec3& position, float radius) {
SharedObjectPointer material = _materialEditor->getObject();
_materialEditor->detachObject();
MetavoxelEditMessage message = { QVariant::fromValue(VoxelMaterialSphereEdit(position, radius,
_editor->getGridSpacing(), material, _texture ? _texture->getAverageColor() : QColor())) };
Application::getInstance()->getMetavoxels()->applyEdit(message, true);
}
void VoxelMaterialSphereTool::updateTexture() {
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
_texture = Application::getInstance()->getTextureCache()->getTexture(material->getDiffuse(), SPLAT_TEXTURE);
}

View file

@ -57,6 +57,7 @@ private slots:
void deleteSelectedAttribute();
void centerGridPosition();
void alignGridPosition();
void updateAttributes(const QString& select = QString());
void updateTool();
void simulate(float deltaTime);
@ -65,11 +66,11 @@ private slots:
private:
void addTool(MetavoxelTool* tool);
void updateAttributes(const QString& select = QString());
MetavoxelTool* getActiveTool() const;
QListWidget* _attributes;
QPushButton* _deleteAttribute;
QCheckBox* _showAll;
QComboBox* _gridPlane;
QDoubleSpinBox* _gridSpacing;
@ -90,10 +91,12 @@ class MetavoxelTool : public QWidget {
public:
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true);
MetavoxelTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
bool getUsesValue() const { return _usesValue; }
bool isUserFacing() const { return _userFacing; }
virtual bool appliesTo(const AttributePointer& attribute) const;
virtual void simulate(float deltaTime);
@ -105,24 +108,30 @@ protected:
MetavoxelEditor* _editor;
bool _usesValue;
bool _userFacing;
};
/// Allows setting the value of a region by dragging out a box.
class BoxSetTool : public MetavoxelTool {
/// Base class for tools that allow dragging out a 3D box.
class BoxTool : public MetavoxelTool {
Q_OBJECT
public:
BoxSetTool(MetavoxelEditor* editor);
BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QColor getColor() = 0;
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum) = 0;
private:
void resetState();
void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE };
@ -134,6 +143,21 @@ private:
float _height; ///< the selection height
};
/// Allows setting the value of a region by dragging out a box.
class BoxSetTool : public BoxTool {
Q_OBJECT
public:
BoxSetTool(MetavoxelEditor* editor);
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
};
/// Allows setting the value across the entire space.
class GlobalSetTool : public MetavoxelTool {
Q_OBJECT
@ -259,8 +283,6 @@ public:
ImportHeightfieldTool(MetavoxelEditor* editor);
virtual void render();
protected:
virtual void apply();
@ -270,6 +292,7 @@ private slots:
void selectHeightFile();
void selectColorFile();
void updatePreview();
void renderPreview();
private:
@ -363,12 +386,12 @@ private:
};
/// Allows texturing parts of the heightfield.
class HeightfieldTextureBrushTool : public HeightfieldBrushTool {
class HeightfieldMaterialBrushTool : public HeightfieldBrushTool {
Q_OBJECT
public:
HeightfieldTextureBrushTool(MetavoxelEditor* editor);
HeightfieldMaterialBrushTool(MetavoxelEditor* editor);
protected:
@ -380,7 +403,125 @@ private slots:
private:
SharedObjectEditor* _textureEditor;
SharedObjectEditor* _materialEditor;
QSharedPointer<NetworkTexture> _texture;
};
/// Allows setting voxel colors by dragging out a box.
class VoxelColorBoxTool : public BoxTool {
Q_OBJECT
public:
VoxelColorBoxTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
private:
QColorEditor* _color;
};
/// Allows setting voxel materials by dragging out a box.
class VoxelMaterialBoxTool : public BoxTool {
Q_OBJECT
public:
VoxelMaterialBoxTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
private slots:
void updateTexture();
private:
SharedObjectEditor* _materialEditor;
QSharedPointer<NetworkTexture> _texture;
};
/// Base class for tools based on a sphere brush.
class SphereTool : public MetavoxelTool {
Q_OBJECT
public:
SphereTool(MetavoxelEditor* editor, const QString& name);
virtual void render();
virtual bool eventFilter(QObject* watched, QEvent* event);
protected:
virtual QColor getColor() = 0;
virtual void applyValue(const glm::vec3& position, float radius) = 0;
QFormLayout* _form;
QDoubleSpinBox* _radius;
glm::vec3 _position;
};
/// Allows setting voxel colors by moving a sphere around.
class VoxelColorSphereTool : public SphereTool {
Q_OBJECT
public:
VoxelColorSphereTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& position, float radius);
private:
QColorEditor* _color;
};
/// Allows setting voxel materials by moving a sphere around.
class VoxelMaterialSphereTool : public SphereTool {
Q_OBJECT
public:
VoxelMaterialSphereTool(MetavoxelEditor* editor);
virtual bool appliesTo(const AttributePointer& attribute) const;
protected:
virtual QColor getColor();
virtual void applyValue(const glm::vec3& position, float radius);
private slots:
void updateTexture();
private:
SharedObjectEditor* _materialEditor;
QSharedPointer<NetworkTexture> _texture;
};

View file

@ -210,7 +210,6 @@ void PreferencesDialog::savePreferences() {
if (shouldDispatchIdentityPacket) {
myAvatar->sendIdentityPacket();
Application::getInstance()->bumpSettings();
}
if (!Menu::getInstance()->isOptionChecked(MenuOption::DisableActivityLogger)
@ -263,4 +262,6 @@ void PreferencesDialog::savePreferences() {
Application::getInstance()->resizeGL(Application::getInstance()->getGLWidget()->width(),
Application::getInstance()->getGLWidget()->height());
Application::getInstance()->bumpSettings();
}

View file

@ -88,11 +88,13 @@ bool RearMirrorTools::mousePressEvent(int x, int y) {
if (_headZoomIconRect.contains(x, y)) {
_zoomLevel = HEAD;
Application::getInstance()->bumpSettings();
return true;
}
if (_bodyZoomIconRect.contains(x, y)) {
_zoomLevel = BODY;
Application::getInstance()->bumpSettings();
return true;
}

View file

@ -32,7 +32,7 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, EntityItemI
}
// use MAX_PACKET_SIZE since it's static and guaranteed to be larger than _maxPacketSize
static unsigned char bufferOut[MAX_PACKET_SIZE];
unsigned char bufferOut[MAX_PACKET_SIZE];
int sizeOut = 0;
if (EntityItemProperties::encodeEntityEditPacket(type, modelID, properties, &bufferOut[0], _maxPacketSize, sizeOut)) {
@ -45,7 +45,7 @@ void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityI
return; // bail early
}
// use MAX_PACKET_SIZE since it's static and guaranteed to be larger than _maxPacketSize
static unsigned char bufferOut[MAX_PACKET_SIZE];
unsigned char bufferOut[MAX_PACKET_SIZE];
size_t sizeOut = 0;
if (EntityItemProperties::encodeEraseEntityMessage(entityItemID, &bufferOut[0], _maxPacketSize, sizeOut)) {
queueOctreeEditMessage(PacketTypeEntityErase, bufferOut, sizeOut);

View file

@ -50,8 +50,6 @@ public:
EntityItemID convertToCreatorTokenVersion() const;
// these methods allow you to create models, and later edit them.
//static uint32_t getIDfromCreatorTokenID(uint32_t creatorTokenID);
static EntityItemID getIDfromCreatorTokenID(uint32_t creatorTokenID);
static uint32_t getNextCreatorTokenID();
static void handleAddEntityResponse(const QByteArray& packet);

View file

@ -596,8 +596,14 @@ bool EntityItemProperties::encodeEntityEditPacket(PacketType command, EntityItem
packetData->endSubTree();
const unsigned char* finalizedData = packetData->getFinalizedData();
int finalizedSize = packetData->getFinalizedSize();
memcpy(bufferOut, finalizedData, finalizedSize);
sizeOut = finalizedSize;
if (finalizedSize <= sizeIn) {
memcpy(bufferOut, finalizedData, finalizedSize);
sizeOut = finalizedSize;
} else {
qDebug() << "ERROR - encoded edit message doesn't fit in output buffer.";
sizeOut = 0;
success = false;
}
} else {
packetData->discardSubTree();
sizeOut = 0;
@ -747,8 +753,13 @@ bool EntityItemProperties::encodeEraseEntityMessage(const EntityItemID& entityIt
unsigned char* outputBuffer, size_t maxLength, size_t& outputLength) {
unsigned char* copyAt = outputBuffer;
uint16_t numberOfIds = 1; // only one entity ID in this message
if (maxLength < sizeof(numberOfIds) + NUM_BYTES_RFC4122_UUID) {
qDebug() << "ERROR - encodeEraseEntityMessage() called with buffer that is too small!";
outputLength = 0;
return false;
}
memcpy(copyAt, &numberOfIds, sizeof(numberOfIds));
copyAt += sizeof(numberOfIds);
outputLength = sizeof(numberOfIds);

View file

@ -24,7 +24,7 @@
QMap<EntityTypes::EntityType, QString> EntityTypes::_typeToNameMap;
QMap<QString, EntityTypes::EntityType> EntityTypes::_nameToTypeMap;
EntityTypeFactory EntityTypes::_factories[EntityTypes::LAST];
EntityTypeFactory EntityTypes::_factories[EntityTypes::LAST + 1];
bool EntityTypes::_factoriesInitialized = false;
const QString ENTITY_TYPE_NAME_UNKNOWN = "Unknown";
@ -58,8 +58,11 @@ bool EntityTypes::registerEntityType(EntityType entityType, const char* name, En
memset(&_factories,0,sizeof(_factories));
_factoriesInitialized = true;
}
_factories[entityType] = factoryMethod;
return true;
if (entityType >= 0 && entityType <= LAST) {
_factories[entityType] = factoryMethod;
return true;
}
return false;
}
EntityItem* EntityTypes::constructEntityItem(EntityType entityType, const EntityItemID& entityID, const EntityItemProperties& properties) {

View file

@ -45,7 +45,7 @@ public:
private:
static QMap<EntityType, QString> _typeToNameMap;
static QMap<QString, EntityTypes::EntityType> _nameToTypeMap;
static EntityTypeFactory _factories[LAST];
static EntityTypeFactory _factories[LAST + 1];
static bool _factoriesInitialized;
};

File diff suppressed because it is too large Load diff

View file

@ -29,14 +29,17 @@ class QScriptEngine;
class QScriptValue;
class Attribute;
class DataBlock;
class HeightfieldColorData;
class HeightfieldData;
class HeightfieldHeightData;
class HeightfieldTextureData;
class HeightfieldMaterialData;
class MetavoxelData;
class MetavoxelLOD;
class MetavoxelNode;
class MetavoxelStreamState;
class VoxelColorData;
class VoxelHermiteData;
class VoxelMaterialData;
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
@ -100,14 +103,23 @@ public:
/// Returns a reference to the standard "spannerMask" attribute.
const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; }
/// Returns a reference to the standard HeightfieldDataPointer "heightfield" attribute.
/// Returns a reference to the standard HeightfieldHeightDataPointer "heightfield" attribute.
const AttributePointer& getHeightfieldAttribute() const { return _heightfieldAttribute; }
/// Returns a reference to the standard HeightfieldDataPointer "heightfieldColor" attribute.
/// Returns a reference to the standard HeightfieldColorDataPointer "heightfieldColor" attribute.
const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; }
/// Returns a reference to the standard HeightfieldDataPointer "heightfieldTexture" attribute.
const AttributePointer& getHeightfieldTextureAttribute() const { return _heightfieldTextureAttribute; }
/// 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:
@ -126,7 +138,10 @@ private:
AttributePointer _spannerMaskAttribute;
AttributePointer _heightfieldAttribute;
AttributePointer _heightfieldColorAttribute;
AttributePointer _heightfieldTextureAttribute;
AttributePointer _heightfieldMaterialAttribute;
AttributePointer _voxelColorAttribute;
AttributePointer _voxelMaterialAttribute;
AttributePointer _voxelHermiteAttribute;
};
/// Converts a value to a void pointer.
@ -206,6 +221,7 @@ Q_DECLARE_METATYPE(OwnedAttributeValue)
class Attribute : public SharedObject {
Q_OBJECT
Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier)
Q_PROPERTY(bool userFacing MEMBER _userFacing)
public:
@ -219,6 +235,9 @@ public:
float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; }
void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; }
bool isUserFacing() const { return _userFacing; }
void setUserFacing(bool userFacing) { _userFacing = userFacing; }
void* create() const { return create(getDefaultValue()); }
virtual void* create(void* copy) const = 0;
virtual void destroy(void* value) const = 0;
@ -283,6 +302,7 @@ public:
private:
float _lodThresholdMultiplier;
bool _userFacing;
};
/// A simple attribute class that stores its values inline.
@ -396,6 +416,9 @@ public:
/// 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);
@ -435,21 +458,18 @@ public:
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
};
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
/// Contains a block of heightfield data.
class HeightfieldData : public QSharedData {
/// Base class for blocks of data.
class DataBlock : public QSharedData {
public:
static const int COLOR_BYTES = 3;
HeightfieldData(const QByteArray& contents = QByteArray());
virtual ~HeightfieldData();
virtual ~DataBlock();
const QByteArray& getContents() const { return _contents; }
void setDeltaData(const HeightfieldDataPointer& deltaData) { _deltaData = deltaData; }
const HeightfieldDataPointer& getDeltaData() const { return _deltaData; }
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; }
@ -458,17 +478,16 @@ public:
protected:
QByteArray _contents;
QByteArray _encoded;
QMutex _encodedMutex;
HeightfieldDataPointer _deltaData;
DataBlockPointer _deltaData;
QByteArray _encodedDelta;
QMutex _encodedDeltaMutex;
class EncodedSubdivision {
public:
HeightfieldDataPointer ancestor;
DataBlockPointer ancestor;
QByteArray data;
};
QVector<EncodedSubdivision> _encodedSubdivisions;
@ -478,7 +497,7 @@ protected:
typedef QExplicitlySharedDataPointer<HeightfieldHeightData> HeightfieldHeightDataPointer;
/// Contains a block of heightfield height data.
class HeightfieldHeightData : public HeightfieldData {
class HeightfieldHeightData : public DataBlock {
public:
HeightfieldHeightData(const QByteArray& contents);
@ -487,6 +506,8 @@ public:
HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor,
const glm::vec3& minimum, float size);
const QByteArray& getContents() const { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor,
@ -496,75 +517,8 @@ private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
};
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
/// Contains a block of heightfield color data.
class HeightfieldColorData : public HeightfieldData {
public:
HeightfieldColorData(const QByteArray& contents);
HeightfieldColorData(Bitstream& in, int bytes);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
};
typedef QExplicitlySharedDataPointer<HeightfieldTextureData> HeightfieldTextureDataPointer;
/// Contains a block of heightfield texture data.
class HeightfieldTextureData : public HeightfieldData {
public:
HeightfieldTextureData(const QByteArray& contents,
const QVector<SharedObjectPointer>& textures = QVector<SharedObjectPointer>());
HeightfieldTextureData(Bitstream& in, int bytes);
HeightfieldTextureData(Bitstream& in, int bytes, const HeightfieldTextureDataPointer& reference);
const QVector<SharedObjectPointer>& getTextures() const { return _textures; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldTextureDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QVector<SharedObjectPointer> _textures;
};
/// Contains the description of a heightfield texture.
class HeightfieldTexture : public SharedObject {
Q_OBJECT
Q_PROPERTY(QUrl url MEMBER _url)
Q_PROPERTY(float scaleS MEMBER _scaleS)
Q_PROPERTY(float scaleT MEMBER _scaleT)
public:
Q_INVOKABLE HeightfieldTexture();
const QUrl& getURL() const { return _url; }
float getScaleS() const { return _scaleS; }
float getScaleT() const { return _scaleT; }
private:
QUrl _url;
float _scaleS;
float _scaleT;
QByteArray _contents;
};
/// An attribute that stores heightfield data.
@ -584,6 +538,33 @@ public:
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
typedef QExplicitlySharedDataPointer<HeightfieldColorData> HeightfieldColorDataPointer;
/// Contains a block of heightfield color data.
class HeightfieldColorData : public DataBlock {
public:
HeightfieldColorData(const QByteArray& contents);
HeightfieldColorData(Bitstream& in, int bytes);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& reference);
HeightfieldColorData(Bitstream& in, int bytes, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
const QByteArray& getContents() const { return _contents; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldColorDataPointer& reference);
void writeSubdivided(Bitstream& out, const HeightfieldColorDataPointer& ancestor,
const glm::vec3& minimum, float size);
private:
void read(Bitstream& in, int bytes);
void set(const QImage& image);
QByteArray _contents;
};
/// An attribute that stores heightfield colors.
class HeightfieldColorAttribute : public InlineAttribute<HeightfieldColorDataPointer> {
Q_OBJECT
@ -601,13 +582,194 @@ public:
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
};
/// An attribute that stores heightfield textures.
class HeightfieldTextureAttribute : public InlineAttribute<HeightfieldTextureDataPointer> {
typedef QExplicitlySharedDataPointer<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
/// Contains a block of heightfield material data.
class HeightfieldMaterialData : public DataBlock {
public:
HeightfieldMaterialData(const QByteArray& contents,
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
HeightfieldMaterialData(Bitstream& in, int bytes);
HeightfieldMaterialData(Bitstream& in, int bytes, const HeightfieldMaterialDataPointer& reference);
const QByteArray& getContents() const { return _contents; }
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
void write(Bitstream& out);
void writeDelta(Bitstream& out, const HeightfieldMaterialDataPointer& reference);
private:
void read(Bitstream& in, int bytes);
QByteArray _contents;
QVector<SharedObjectPointer> _materials;
};
/// 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;
};
/// An attribute that stores heightfield materials.
class HeightfieldMaterialAttribute : public InlineAttribute<HeightfieldMaterialDataPointer> {
Q_OBJECT
public:
Q_INVOKABLE HeightfieldTextureAttribute(const QString& name = QString());
Q_INVOKABLE HeightfieldMaterialAttribute(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<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;

View file

@ -414,119 +414,36 @@ PaintHeightfieldColorEdit::PaintHeightfieldColorEdit(const glm::vec3& position,
color(color) {
}
class PaintHeightfieldColorEditVisitor : public MetavoxelVisitor {
class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit);
PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldColorEdit _edit;
glm::vec3 _position;
float _radius;
SharedObjectPointer _material;
QColor _color;
Box _bounds;
};
PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
_edit(edit) {
PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& color) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute()),
_position(position),
_radius(radius),
_material(material),
_color(color) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
}
static void paintColor(MetavoxelInfo& info, int index, const glm::vec3& position, float radius, const QColor& color) {
HeightfieldColorDataPointer pointer = info.inputValues.at(index).getInlineValue<HeightfieldColorDataPointer>();
if (!pointer) {
return;
}
QByteArray contents(pointer->getContents());
int size = glm::sqrt((float)contents.size() / HeightfieldData::COLOR_BYTES);
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (position - info.minimum) * heightScale;
float scaledRadius = radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
int stride = size * HeightfieldData::COLOR_BYTES;
char* lineDest = contents.data() + (int)z * stride + (int)startX * HeightfieldData::COLOR_BYTES;
float squaredRadius = scaledRadius * scaledRadius;
char red = color.red(), green = color.green(), blue = color.blue();
bool changed = false;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
char* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest += HeightfieldData::COLOR_BYTES) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
changed = true;
}
}
lineDest += stride;
}
if (changed) {
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[index] = AttributeValue(info.inputValues.at(index).getAttribute(),
encodeInline<HeightfieldColorDataPointer>(newPointer));
}
}
int PaintHeightfieldColorEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
paintColor(info, 0, _edit.position, _edit.radius, _edit.color);
return STOP_RECURSION;
}
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldColorEditVisitor visitor(*this);
data.guide(visitor);
}
PaintHeightfieldTextureEdit::PaintHeightfieldTextureEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& texture, const QColor& averageColor) :
position(position),
radius(radius),
texture(texture),
averageColor(averageColor) {
}
class PaintHeightfieldTextureEditVisitor : public MetavoxelVisitor {
public:
PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit);
virtual int visit(MetavoxelInfo& info);
private:
PaintHeightfieldTextureEdit _edit;
Box _bounds;
};
PaintHeightfieldTextureEditVisitor::PaintHeightfieldTextureEditVisitor(const PaintHeightfieldTextureEdit& edit) :
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), QVector<AttributePointer>() <<
AttributeRegistry::getInstance()->getHeightfieldTextureAttribute() <<
AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
_edit(edit) {
glm::vec3 extents(_edit.radius, _edit.radius, _edit.radius);
_bounds = Box(_edit.position - extents, _edit.position + extents);
glm::vec3 extents(_radius, _radius, _radius);
_bounds = Box(_position - extents, _position + extents);
}
static QHash<uchar, int> countIndices(const QByteArray& contents) {
@ -539,106 +456,624 @@ static QHash<uchar, int> countIndices(const QByteArray& contents) {
return counts;
}
int PaintHeightfieldTextureEditVisitor::visit(MetavoxelInfo& info) {
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, 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();
}
}
int PaintHeightfieldMaterialEditVisitor::visit(MetavoxelInfo& info) {
if (!info.getBounds().intersects(_bounds)) {
return STOP_RECURSION;
}
if (!info.isLeaf) {
return DEFAULT_ORDER;
}
HeightfieldTextureDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldTextureDataPointer>();
if (!pointer) {
return STOP_RECURSION;
}
QVector<SharedObjectPointer> textures = pointer->getTextures();
QByteArray contents(pointer->getContents());
uchar textureIndex = 0;
if (_edit.texture && static_cast<HeightfieldTexture*>(_edit.texture.data())->getURL().isValid()) {
// first look for a matching existing texture, noting the first reusable slot
int firstEmptyIndex = -1;
for (int i = 0; i < textures.size(); i++) {
const SharedObjectPointer& texture = textures.at(i);
if (texture) {
if (texture->equals(_edit.texture.data())) {
textureIndex = i + 1;
break;
HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<HeightfieldColorDataPointer>();
if (colorPointer) {
QByteArray contents(colorPointer->getContents());
int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
int highest = size - 1;
float heightScale = size / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
int stride = size * DataBlock::COLOR_BYTES;
char* lineDest = contents.data() + (int)z * stride + (int)startX * DataBlock::COLOR_BYTES;
float squaredRadius = scaledRadius * scaledRadius;
char red = _color.red(), green = _color.green(), blue = _color.blue();
bool changed = false;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
char* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest += DataBlock::COLOR_BYTES) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
dest[0] = red;
dest[1] = green;
dest[2] = blue;
changed = true;
}
} else if (firstEmptyIndex == -1) {
firstEmptyIndex = i;
}
lineDest += stride;
}
// if nothing found, use the first empty slot or append
if (textureIndex == 0) {
if (firstEmptyIndex != -1) {
textures[firstEmptyIndex] = _edit.texture;
textureIndex = firstEmptyIndex + 1;
} else if (textures.size() < EIGHT_BIT_MAXIMUM) {
textures.append(_edit.texture);
textureIndex = textures.size();
} else {
// last resort: find the least-used texture and remove it
QHash<uchar, int> counts = countIndices(contents);
int lowestCount = INT_MAX;
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
if (it.value() < lowestCount) {
textureIndex = it.key();
lowestCount = it.value();
}
if (changed) {
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<HeightfieldColorDataPointer>(newPointer));
}
}
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<HeightfieldMaterialDataPointer>();
if (materialPointer) {
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
QByteArray contents(materialPointer->getContents());
uchar materialIndex = getMaterialIndex(_material, materials, contents);
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_position - info.minimum) * heightScale;
float scaledRadius = _radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
bool changed = false;
QHash<uchar, int> counts;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
uchar* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
*dest = materialIndex;
changed = true;
}
contents.replace((char)textureIndex, (char)0);
}
lineDest += size;
}
if (changed) {
clearUnusedMaterials(materials, contents);
HeightfieldMaterialDataPointer newPointer(new HeightfieldMaterialData(contents, materials));
info.outputValues[1] = AttributeValue(_outputs.at(1), encodeInline<HeightfieldMaterialDataPointer>(newPointer));
}
}
int size = glm::sqrt((float)contents.size());
int highest = size - 1;
float heightScale = highest / info.size;
glm::vec3 center = (_edit.position - info.minimum) * heightScale;
float scaledRadius = _edit.radius * heightScale;
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
glm::vec3 start = glm::floor(center - extents);
glm::vec3 end = glm::ceil(center + extents);
// paint all points within the radius
float z = qMax(start.z, 0.0f);
float startX = qMax(start.x, 0.0f), endX = qMin(end.x, (float)highest);
uchar* lineDest = (uchar*)contents.data() + (int)z * size + (int)startX;
float squaredRadius = scaledRadius * scaledRadius;
bool changed = false;
QHash<uchar, int> counts;
for (float endZ = qMin(end.z, (float)highest); z <= endZ; z += 1.0f) {
uchar* dest = lineDest;
for (float x = startX; x <= endX; x += 1.0f, dest++) {
float dx = x - center.x, dz = z - center.z;
if (dx * dx + dz * dz <= squaredRadius) {
*dest = textureIndex;
changed = true;
}
}
lineDest += size;
}
if (changed) {
// clear any unused textures
QHash<uchar, int> counts = countIndices(contents);
for (int i = 0; i < textures.size(); i++) {
if (counts.value(i + 1) == 0) {
textures[i] = SharedObjectPointer();
}
}
while (!(textures.isEmpty() || textures.last())) {
textures.removeLast();
}
HeightfieldTextureDataPointer newPointer(new HeightfieldTextureData(contents, textures));
info.outputValues[0] = AttributeValue(_outputs.at(0), encodeInline<HeightfieldTextureDataPointer>(newPointer));
}
paintColor(info, 1, _edit.position, _edit.radius, _edit.averageColor);
return STOP_RECURSION;
}
void PaintHeightfieldTextureEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldTextureEditVisitor visitor(*this);
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldMaterialEditVisitor visitor(position, radius, SharedObjectPointer(), color);
data.guide(visitor);
}
PaintHeightfieldMaterialEdit::PaintHeightfieldMaterialEdit(const glm::vec3& position, float radius,
const SharedObjectPointer& material, const QColor& averageColor) :
position(position),
radius(radius),
material(material),
averageColor(averageColor) {
}
void PaintHeightfieldMaterialEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
PaintHeightfieldMaterialEditVisitor visitor(position, radius, material, averageColor);
data.guide(visitor);
}
VoxelColorBoxEdit::VoxelColorBoxEdit(const Box& region, float granularity, const QColor& color) :
region(region),
granularity(granularity),
color(color) {
}
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;
class VoxelMaterialBoxEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialBoxEditVisitor(const Box& region, float granularity,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
Box _region;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelMaterialBoxEditVisitor::VoxelMaterialBoxEditVisitor(const Box& region, float granularity,
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()),
_region(region),
_material(material),
_color(color),
_blockSize(granularity * VOXEL_BLOCK_SIZE) {
}
int VoxelMaterialBoxEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_region)) {
return STOP_RECURSION;
}
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
Box overlap = info.getBounds().getIntersection(_region);
float scale = VOXEL_BLOCK_SIZE / 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();
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) {
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) {
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++) {
*destX = rgb;
}
}
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
QVector<QRgb> hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
hermitePointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
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 NORMAL_MAX = 127;
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) {
// internal edges are set to zero; border edges (when non-terminal) are set to the intersection values
hermiteDestX[0] = 0x0;
if ((x == hermiteMinX || x == hermiteMaxX) && x != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[1])) {
hermiteDestX[0] = qRgba(alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0, 0,
((x == hermiteMinX ? overlap.minimum.x : overlap.maximum.x) - x) * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[1] = 0x0;
if ((y == hermiteMinY || y == hermiteMaxY) && y != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) {
hermiteDestX[1] = qRgba(0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX, 0,
((y == hermiteMinY ? overlap.minimum.y : overlap.maximum.y) - y) * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[2] = 0x0;
if ((z == hermiteMinZ || z == hermiteMaxZ) && z != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) {
hermiteDestX[2] = qRgba(0, 0, alpha0 == 0 ? -NORMAL_MAX : NORMAL_MAX,
((z == hermiteMinZ ? overlap.minimum.z : overlap.maximum.z) - z) * EIGHT_BIT_MAXIMUM);
}
}
}
}
}
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
QByteArray materialContents;
QVector<SharedObjectPointer> materials;
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
materialContents = materialPointer->getContents();
materials = materialPointer->getMaterials();
} else {
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
}
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
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) {
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY; destY += VOXEL_BLOCK_SAMPLES) {
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++) {
*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 VoxelColorBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
data.expand();
}
VoxelMaterialBoxEditVisitor visitor(region, granularity, SharedObjectPointer(), color);
data.guide(visitor);
}
VoxelMaterialBoxEdit::VoxelMaterialBoxEdit(const Box& region, float granularity,
const SharedObjectPointer& material, const QColor& averageColor) :
region(region),
granularity(granularity),
material(material),
averageColor(averageColor) {
}
void VoxelMaterialBoxEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
while (!data.getBounds().contains(region)) {
data.expand();
}
VoxelMaterialBoxEditVisitor visitor(region, granularity, material, averageColor);
data.guide(visitor);
}
VoxelColorSphereEdit::VoxelColorSphereEdit(const glm::vec3& center, float radius, float granularity, const QColor& color) :
center(center),
radius(radius),
granularity(granularity),
color(color) {
}
class VoxelMaterialSphereEditVisitor : public MetavoxelVisitor {
public:
VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds, float granularity,
const SharedObjectPointer& material, const QColor& color);
virtual int visit(MetavoxelInfo& info);
private:
glm::vec3 _center;
float _radius;
Box _bounds;
SharedObjectPointer _material;
QColor _color;
float _blockSize;
};
VoxelMaterialSphereEditVisitor::VoxelMaterialSphereEditVisitor(const glm::vec3& center, float radius, const Box& bounds,
float granularity, 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()),
_center(center),
_radius(radius),
_bounds(bounds),
_material(material),
_color(color),
_blockSize(granularity * VOXEL_BLOCK_SIZE) {
}
int VoxelMaterialSphereEditVisitor::visit(MetavoxelInfo& info) {
Box bounds = info.getBounds();
if (!bounds.intersects(_bounds)) {
return STOP_RECURSION;
}
if (info.size > _blockSize) {
return DEFAULT_ORDER;
}
VoxelColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<VoxelColorDataPointer>();
QVector<QRgb> colorContents = (colorPointer && colorPointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
colorPointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME);
Box overlap = info.getBounds().getIntersection(_bounds);
float scale = VOXEL_BLOCK_SIZE / 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;
glm::vec3 relativeCenter = (_center - info.minimum) * scale;
float relativeRadius = _radius * scale;
float relativeRadiusSquared = relativeRadius * relativeRadius;
QRgb rgb = _color.rgba();
glm::vec3 position(0.0f, 0.0f, minZ);
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++) {
position.y = minY;
for (QRgb* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y++) {
position.x = minX;
for (QRgb* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) {
if (glm::distance(relativeCenter, position) <= relativeRadius) {
*destX = rgb;
}
}
}
}
VoxelColorDataPointer newColorPointer(new VoxelColorData(colorContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
encodeInline<VoxelColorDataPointer>(newColorPointer));
VoxelHermiteDataPointer hermitePointer = info.inputValues.at(1).getInlineValue<VoxelHermiteDataPointer>();
QVector<QRgb> hermiteContents = (hermitePointer && hermitePointer->getSize() == VOXEL_BLOCK_SAMPLES) ?
hermitePointer->getContents() : QVector<QRgb>(VOXEL_BLOCK_VOLUME * VoxelHermiteData::EDGE_COUNT);
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++;
}
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
hermiteDestX[0] = 0x0;
glm::vec3 offset(x - relativeCenter.x, y - relativeCenter.y, z - relativeCenter.z);
if (x != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[1])) {
float radicand = relativeRadiusSquared - offset.y * offset.y - offset.z * offset.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.x - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.x + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(0.0f, 1.0f, 0.0f);
}
hermiteDestX[0] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[1] = 0x0;
if (y != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_SAMPLES])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.z * offset.z;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.y - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.y + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[1] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
hermiteDestX[2] = 0x0;
if (z != VOXEL_BLOCK_SIZE) {
const QRgb* color = colorContents.constData() + z * VOXEL_BLOCK_AREA + y * VOXEL_BLOCK_SAMPLES + x;
int alpha0 = qAlpha(color[0]);
if (alpha0 != qAlpha(color[VOXEL_BLOCK_AREA])) {
float radicand = relativeRadiusSquared - offset.x * offset.x - offset.y * offset.y;
float parameter = 0.5f;
if (radicand >= 0.0f) {
float root = glm::sqrt(radicand);
parameter = -offset.z - root;
if (parameter < 0.0f || parameter > 1.0f) {
parameter = glm::clamp(-offset.z + root, 0.0f, 1.0f);
}
}
glm::vec3 normal = offset + glm::vec3(parameter, 0.0f, 0.0f);
float length = glm::length(normal);
if (length > EPSILON) {
normal /= length;
} else {
normal = glm::vec3(1.0f, 0.0f, 0.0f);
}
hermiteDestX[2] = packNormal(normal, parameter * EIGHT_BIT_MAXIMUM);
}
}
}
}
}
VoxelHermiteDataPointer newHermitePointer(new VoxelHermiteData(hermiteContents, VOXEL_BLOCK_SAMPLES));
info.outputValues[1] = AttributeValue(info.inputValues.at(1).getAttribute(),
encodeInline<VoxelHermiteDataPointer>(newHermitePointer));
VoxelMaterialDataPointer materialPointer = info.inputValues.at(2).getInlineValue<VoxelMaterialDataPointer>();
QByteArray materialContents;
QVector<SharedObjectPointer> materials;
if (materialPointer && materialPointer->getSize() == VOXEL_BLOCK_SAMPLES) {
materialContents = materialPointer->getContents();
materials = materialPointer->getMaterials();
} else {
materialContents = QByteArray(VOXEL_BLOCK_VOLUME, 0);
}
uchar materialIndex = getMaterialIndex(_material, materials, materialContents);
position.z = minZ;
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++) {
position.y = minY;
for (uchar* destY = destZ, *endY = destY + sizeY * VOXEL_BLOCK_SAMPLES; destY != endY;
destY += VOXEL_BLOCK_SAMPLES, position.y++) {
position.x = minX;
for (uchar* destX = destY, *endX = destX + sizeX; destX != endX; destX++, position.x++) {
if (glm::distance(relativeCenter, position) <= relativeRadius) {
*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 VoxelColorSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
glm::vec3 extents(radius, radius, radius);
Box bounds(center - extents, center + extents);
while (!data.getBounds().contains(bounds)) {
data.expand();
}
VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, SharedObjectPointer(), color);
data.guide(visitor);
}
VoxelMaterialSphereEdit::VoxelMaterialSphereEdit(const glm::vec3& center, float radius, float granularity,
const SharedObjectPointer& material, const QColor& averageColor) :
center(center),
radius(radius),
granularity(granularity),
material(material),
averageColor(averageColor) {
}
void VoxelMaterialSphereEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
// expand to fit the entire edit
glm::vec3 extents(radius, radius, radius);
Box bounds(center - extents, center + extents);
while (!data.getBounds().contains(bounds)) {
data.expand();
}
VoxelMaterialSphereEditVisitor visitor(center, radius, bounds, granularity, material, averageColor);
data.guide(visitor);
}

View file

@ -241,23 +241,98 @@ public:
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit)
/// An edit that sets a region of a heightfield texture.
class PaintHeightfieldTextureEdit : public MetavoxelEdit {
/// An edit that sets a region of a heightfield material.
class PaintHeightfieldMaterialEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 position;
STREAM float radius;
STREAM SharedObjectPointer texture;
STREAM SharedObjectPointer material;
STREAM QColor averageColor;
PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
const SharedObjectPointer& texture = SharedObjectPointer(), const QColor& averageColor = QColor());
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(PaintHeightfieldTextureEdit)
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldMaterialEdit)
/// An edit that sets the color of voxels within a box to a value.
class VoxelColorBoxEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM Box region;
STREAM float granularity;
STREAM QColor color;
VoxelColorBoxEdit(const Box& region = Box(), float granularity = 0.0f, const QColor& color = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelColorBoxEdit)
/// An edit that sets the materials of voxels within a box to a value.
class VoxelMaterialBoxEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM Box region;
STREAM float granularity;
STREAM SharedObjectPointer material;
STREAM QColor averageColor;
VoxelMaterialBoxEdit(const Box& region = Box(), float granularity = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelMaterialBoxEdit)
/// An edit that sets the color of voxels within a sphere to a value.
class VoxelColorSphereEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 center;
STREAM float radius;
STREAM float granularity;
STREAM QColor color;
VoxelColorSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f,
float granularity = 0.0f, const QColor& color = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelColorSphereEdit)
/// An edit that sets the materials of voxels within a sphere to a value.
class VoxelMaterialSphereEdit : public MetavoxelEdit {
STREAMABLE
public:
STREAM glm::vec3 center;
STREAM float radius;
STREAM float granularity;
STREAM SharedObjectPointer material;
STREAM QColor averageColor;
VoxelMaterialSphereEdit(const glm::vec3& center = glm::vec3(), float radius = 0.0f, float granularity = 0.0f,
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
};
DECLARE_STREAMABLE_METATYPE(VoxelMaterialSphereEdit)
#endif // hifi_MetavoxelMessages_h

View file

@ -343,6 +343,10 @@ void NodeList::sendDomainServerCheckIn() {
PacketType domainPacketType = !_domainHandler.isConnected()
? PacketTypeDomainConnectRequest : PacketTypeDomainListRequest;
if (!_domainHandler.isConnected()) {
qDebug() << "Sending connect request to domain-server at" << _domainHandler.getHostname();
}
// construct the DS check in packet
QUuid packetUUID = _sessionUUID;

View file

@ -19,6 +19,7 @@
#include <fstream> // to load voxels from file
#include <QDebug>
#include <QVector>
#include <GeometryUtil.h>
#include <OctalCode.h>
@ -744,6 +745,12 @@ public:
bool found;
};
class ContentArgs {
public:
AACube cube;
CubeList* cubes;
};
bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
@ -786,6 +793,39 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) {
return false;
}
quint64 cubeListHashKey(const glm::vec3& point) {
// NOTE: TREE_SCALE = 16384 (15 bits) and multiplier is 1024 (11 bits),
// so each component (26 bits) uses more than its alloted 21 bits.
// however we don't expect to span huge cubes so it is ok if we wrap
// (every 2^21 / 2^10 = 2048 meters).
const uint BITS_PER_COMPONENT = 21;
const quint64 MAX_SCALED_COMPONENT = 2097152; // 2^21
const float RESOLUTION_PER_METER = 1024.0f; // 2^10
return (quint64)(point.x * RESOLUTION_PER_METER) % MAX_SCALED_COMPONENT +
(((quint64)(point.y * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << BITS_PER_COMPONENT) +
(((quint64)(point.z * RESOLUTION_PER_METER)) % MAX_SCALED_COMPONENT << 2 * BITS_PER_COMPONENT);
}
bool findContentInCubeOp(OctreeElement* element, void* extraData) {
ContentArgs* args = static_cast<ContentArgs*>(extraData);
// coarse check against bounds
AACube cube = element->getAACube();
cube.scale(TREE_SCALE);
if (!cube.touches(args->cube)) {
return false;
}
if (!element->isLeaf()) {
return true; // recurse on children
}
if (element->hasContent()) {
// NOTE: the voxel's center is unique so we use it as the input for the key
args->cubes->insert(cubeListHashKey(cube.calcCenter()), cube);
return true;
}
return false;
}
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
glm::vec3& penetration, Octree::lockType lockType, bool* accurateResult) {
@ -854,6 +894,16 @@ bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions,
return args.found;
}
bool Octree::findContentInCube(const AACube& cube, CubeList& cubes) {
if (!tryLockForRead()) {
return false;
}
ContentArgs args = { cube, &cubes };
recurseTreeWithOperation(findContentInCubeOp, &args);
unlock();
return true;
}
class GetElementEnclosingArgs {
public:
OctreeElement* element;

View file

@ -33,6 +33,7 @@ class Shape;
#include <CollisionInfo.h>
#include <QHash>
#include <QObject>
#include <QReadWriteLock>
@ -47,6 +48,7 @@ public:
// Callback function, for recuseTreeWithOperation
typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData);
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
typedef QHash<quint64, AACube> CubeList;
const bool NO_EXISTS_BITS = false;
const bool WANT_EXISTS_BITS = true;
@ -308,6 +310,8 @@ public:
bool findShapeCollisions(const Shape* shape, CollisionList& collisions,
Octree::lockType = Octree::TryLock, bool* accurateResult = NULL);
bool findContentInCube(const AACube& cube, CubeList& cubes);
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);

View file

@ -33,6 +33,8 @@ public:
PhysicsEntity();
virtual ~PhysicsEntity();
virtual void stepForward(float deltaTime) { }
void setTranslation(const glm::vec3& translation);
void setRotation(const glm::quat& rotation);

View file

@ -30,6 +30,10 @@ PhysicsSimulation::PhysicsSimulation() : _translation(0.0f), _frameCount(0), _en
}
PhysicsSimulation::~PhysicsSimulation() {
clear();
}
void PhysicsSimulation::clear() {
// entities have a backpointer to this simulator that must be cleaned up
int numEntities = _otherEntities.size();
for (int i = 0; i < numEntities; ++i) {
@ -43,6 +47,9 @@ PhysicsSimulation::~PhysicsSimulation() {
// but Ragdolls do not
_ragdoll = NULL;
_otherRagdolls.clear();
// contacts have backpointers to shapes so we clear them
_contacts.clear();
}
void PhysicsSimulation::setRagdoll(Ragdoll* ragdoll) {
@ -134,6 +141,18 @@ void PhysicsSimulation::removeShapes(const PhysicsEntity* entity) {
}
}
void PhysicsSimulation::removeShape(const Shape* shape) {
// remove data structures with pointers to shape
QMap<quint64, ContactPoint>::iterator itr = _contacts.begin();
while (itr != _contacts.end()) {
if (shape == itr.value().getShapeA() || shape == itr.value().getShapeB()) {
itr = _contacts.erase(itr);
} else {
++itr;
}
}
}
const float OTHER_RAGDOLL_MASS_SCALE = 10.0f;
bool PhysicsSimulation::addRagdoll(Ragdoll* doll) {
@ -195,7 +214,7 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter
quint64 startTime = now;
quint64 expiry = startTime + maxUsec;
moveRagdolls(deltaTime);
integrate(deltaTime);
enforceContacts();
int numDolls = _otherRagdolls.size();
{
@ -238,8 +257,12 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter
pruneContacts();
}
void PhysicsSimulation::moveRagdolls(float deltaTime) {
void PhysicsSimulation::integrate(float deltaTime) {
PerformanceTimer perfTimer("integrate");
int numEntities = _otherEntities.size();
for (int i = 0; i < numEntities; ++i) {
_otherEntities[i]->stepForward(deltaTime);
}
_ragdoll->stepForward(deltaTime);
int numDolls = _otherRagdolls.size();
for (int i = 0; i < numDolls; ++i) {

View file

@ -27,6 +27,8 @@ public:
PhysicsSimulation();
~PhysicsSimulation();
void clear();
void setTranslation(const glm::vec3& translation) { _translation = translation; }
const glm::vec3& getTranslation() const { return _translation; }
@ -39,6 +41,7 @@ public:
void removeEntity(PhysicsEntity* entity);
void removeShapes(const PhysicsEntity* entity);
void removeShape(const Shape* shape);
/// \return true if doll was added to or is already in the list
bool addRagdoll(Ragdoll* doll);
@ -52,7 +55,7 @@ public:
void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec);
protected:
void moveRagdolls(float deltaTime);
void integrate(float deltaTime);
/// \return true if main ragdoll collides with other avatar
bool computeCollisions();

View file

@ -81,17 +81,22 @@ public:
protected:
// these ctors are protected (used by derived classes only)
Shape(Type type) : _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(0.f), _rotation() {
Shape(Type type) : _type(type), _owningEntity(NULL),
_boundingRadius(0.f), _translation(0.f),
_rotation(), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}
Shape(Type type, const glm::vec3& position)
: _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation() {
Shape(Type type, const glm::vec3& position) :
_type(type), _owningEntity(NULL),
_boundingRadius(0.f), _translation(position),
_rotation(), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}
Shape(Type type, const glm::vec3& position, const glm::quat& rotation)
: _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation(rotation) {
Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL),
_boundingRadius(0.f), _translation(position),
_rotation(rotation), _mass(MAX_SHAPE_MASS) {
_id = getNextID();
}

View file

@ -676,8 +676,7 @@ CollisionInfo* sphereVsAACubeHelper(const glm::vec3& sphereCenter, float sphereR
// sphereCenter is touching cube surface, so we can't use the difference between those two
// points to compute the penetration direction. Instead we use the unitary components of
// cubeContact.
direction = cubeContact / halfCubeSide;
glm::modf(BA, direction);
glm::modf(cubeContact / halfCubeSide, direction);
lengthDirection = glm::length(direction);
} else if (lengthDirection > sphereRadius) {
collisions.deleteLastCollision();

View file

@ -27,5 +27,5 @@ bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void*
}
}
#endif
return true;
return false;
}