mirror of
https://github.com/overte-org/overte.git
synced 2025-04-19 15:43:50 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
e5945c5b5a
57 changed files with 4376 additions and 1602 deletions
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
26
interface/resources/shaders/directional_light.frag
Normal file
26
interface/resources/shaders/directional_light.frag
Normal 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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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));
|
||||
}
|
|
@ -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);
|
||||
}
|
21
interface/resources/shaders/metavoxel_voxel_base.frag
Normal file
21
interface/resources/shaders/metavoxel_voxel_base.frag
Normal 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);
|
||||
}
|
26
interface/resources/shaders/metavoxel_voxel_base.vert
Normal file
26
interface/resources/shaders/metavoxel_voxel_base.vert
Normal 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);
|
||||
}
|
29
interface/resources/shaders/metavoxel_voxel_splat.frag
Normal file
29
interface/resources/shaders/metavoxel_voxel_splat.frag
Normal 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);
|
||||
}
|
62
interface/resources/shaders/metavoxel_voxel_splat.vert
Normal file
62
interface/resources/shaders/metavoxel_voxel_splat.vert
Normal 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];
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
99
interface/src/avatar/VoxelShapeManager.cpp
Normal file
99
interface/src/avatar/VoxelShapeManager.cpp
Normal 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();
|
||||
}
|
||||
}
|
51
interface/src/avatar/VoxelShapeManager.h
Normal file
51
interface/src/avatar/VoxelShapeManager.h
Normal 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
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -27,5 +27,5 @@ bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void*
|
|||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue