mirror of
https://github.com/Armored-Dragon/overte.git
synced 2025-03-11 16:13:16 +01:00
merge upstream/master into andrew/inerita
This commit is contained in:
commit
ff09a0d41b
41 changed files with 4023 additions and 1322 deletions
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
// Set the following variables to the right value
|
// 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 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
|
// Those variables MUST be common to every scripts
|
||||||
var controlVoxelSize = 0.25;
|
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 TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/";
|
||||||
var ALPHA_ON = 1.0;
|
var ALPHA_ON = 1.0;
|
||||||
var ALPHA_OFF = 0.7;
|
var ALPHA_OFF = 0.7;
|
||||||
var COLOR_TOOL_BAR = { red: 128, green: 128, blue: 128 };
|
var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 };
|
||||||
var COLOR_MASTER = { red: 200, green: 200, blue: 200 };
|
var COLOR_MASTER = { red: 0, green: 0, blue: 0 };
|
||||||
var TEXT_HEIGHT = 10;
|
var TEXT_HEIGHT = 12;
|
||||||
|
var TEXT_MARGIN = 3;
|
||||||
|
|
||||||
var toolBars = new Array();
|
var toolBars = new Array();
|
||||||
var nameOverlays = new Array();
|
var nameOverlays = new Array();
|
||||||
|
@ -52,76 +53,87 @@ var playLoopIcon = new Array();
|
||||||
var stopIcon = new Array();
|
var stopIcon = new Array();
|
||||||
setupToolBars();
|
setupToolBars();
|
||||||
|
|
||||||
|
|
||||||
function setupToolBars() {
|
function setupToolBars() {
|
||||||
if (toolBars.length > 0) {
|
if (toolBars.length > 0) {
|
||||||
print("Multiple calls to Recorder.js:setupToolBars()");
|
print("Multiple calls to Recorder.js:setupToolBars()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Tool.IMAGE_HEIGHT /= 2;
|
||||||
|
Tool.IMAGE_WIDTH /= 2;
|
||||||
|
|
||||||
for (i = 0; i <= NUM_AC; i++) {
|
for (i = 0; i <= NUM_AC; i++) {
|
||||||
toolBars.push(new ToolBar(0, 0, ToolBar.HORIZONTAL));
|
toolBars.push(new ToolBar(0, 0, ToolBar.HORIZONTAL));
|
||||||
nameOverlays.push(Overlays.addOverlay("text", {
|
toolBars[i].setBack((i === NUM_AC) ? COLOR_MASTER : COLOR_TOOL_BAR, ALPHA_OFF);
|
||||||
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
|
|
||||||
}));
|
|
||||||
|
|
||||||
onOffIcon.push(toolBars[i].addTool({
|
onOffIcon.push(toolBars[i].addTool({
|
||||||
imageURL: TOOL_ICON_URL + "models-tool.svg",
|
imageURL: TOOL_ICON_URL + "ac-on-off.svg",
|
||||||
subImage: { x: 0, y: Tool.IMAGE_WIDTH, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||||
width: Tool.IMAGE_WIDTH,
|
x: 0, y: 0,
|
||||||
height: Tool.IMAGE_HEIGHT,
|
width: Tool.IMAGE_WIDTH,
|
||||||
alpha: ALPHA_ON,
|
height: Tool.IMAGE_HEIGHT,
|
||||||
visible: true
|
alpha: ALPHA_ON,
|
||||||
}, true, false));
|
visible: true
|
||||||
playIcon.push(null);
|
}, true, true));
|
||||||
playLoopIcon.push(null);
|
|
||||||
stopIcon.push(null);
|
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) {
|
function sendCommand(id, action) {
|
||||||
if (action === SHOW && toolBars[id].numberOfTools() === 1) {
|
if (action === SHOW) {
|
||||||
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) {
|
|
||||||
toolBars[id].selectTool(onOffIcon[id], false);
|
toolBars[id].selectTool(onOffIcon[id], false);
|
||||||
toolBars[id].removeLastTool();
|
toolBars[id].setAlpha(ALPHA_ON, playIcon[id]);
|
||||||
toolBars[id].removeLastTool();
|
toolBars[id].setAlpha(ALPHA_ON, playLoopIcon[id]);
|
||||||
toolBars[id].removeLastTool();
|
toolBars[id].setAlpha(ALPHA_ON, stopIcon[id]);
|
||||||
toolBars[id].setBack(null);
|
} 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) {
|
if (id === toolBars.length - 1) {
|
||||||
for (i = 0; i < NUM_AC; i++) {
|
for (i = 0; i < NUM_AC; i++) {
|
||||||
|
@ -141,55 +153,56 @@ function sendCommand(id, action) {
|
||||||
|
|
||||||
function mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||||
|
|
||||||
// Check master control
|
// Check master control
|
||||||
var i = toolBars.length - 1;
|
var i = toolBars.length - 1;
|
||||||
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
if (toolBars[i].toolSelected(onOffIcon[i])) {
|
if (toolBars[i].toolSelected(onOffIcon[i])) {
|
||||||
sendCommand(i, SHOW);
|
sendCommand(i, SHOW);
|
||||||
} else {
|
} else {
|
||||||
sendCommand(i, HIDE);
|
sendCommand(i, HIDE);
|
||||||
}
|
}
|
||||||
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, PLAY);
|
sendCommand(i, PLAY);
|
||||||
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, PLAY_LOOP);
|
sendCommand(i, PLAY_LOOP);
|
||||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, STOP);
|
sendCommand(i, STOP);
|
||||||
} else {
|
} else {
|
||||||
// Check individual controls
|
// Check individual controls
|
||||||
for (i = 0; i < NUM_AC; i++) {
|
for (i = 0; i < NUM_AC; i++) {
|
||||||
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
if (onOffIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
if (toolBars[i].toolSelected(onOffIcon[i])) {
|
if (toolBars[i].toolSelected(onOffIcon[i], false)) {
|
||||||
sendCommand(i, SHOW);
|
sendCommand(i, SHOW);
|
||||||
} else {
|
} else {
|
||||||
sendCommand(i, HIDE);
|
sendCommand(i, HIDE);
|
||||||
}
|
}
|
||||||
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
} else if (playIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, PLAY);
|
sendCommand(i, PLAY);
|
||||||
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
} else if (playLoopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, PLAY_LOOP);
|
sendCommand(i, PLAY_LOOP);
|
||||||
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay)) {
|
} else if (stopIcon[i] === toolBars[i].clicked(clickedOverlay, false)) {
|
||||||
sendCommand(i, STOP);
|
sendCommand(i, STOP);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveUI() {
|
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++) {
|
for (i = 0; i <= NUM_AC; i++) {
|
||||||
toolBars[i].move(relative.x,
|
toolBars[i].move(relative.x,
|
||||||
windowDimensions.y - relative.y +
|
windowDimensions.y - relative.y +
|
||||||
i * (Tool.IMAGE_HEIGHT + 2 * ToolBar.SPACING + TEXT_HEIGHT));
|
i * (Tool.IMAGE_HEIGHT + ToolBar.SPACING + textSize));
|
||||||
|
|
||||||
Overlays.editOverlay(nameOverlays[i], {
|
Overlays.editOverlay(nameOverlays[i], {
|
||||||
x: relative.x,
|
x: toolBars[i].x - ToolBar.SPACING,
|
||||||
y: windowDimensions.y - relative.y +
|
y: toolBars[i].y - textSize
|
||||||
i * (Tool.IMAGE_HEIGHT + 2 * ToolBar.SPACING + TEXT_HEIGHT) -
|
});
|
||||||
ToolBar.SPACING - 2 * TEXT_HEIGHT
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ Script.include("toolBars.js");
|
||||||
|
|
||||||
var recordingFile = "recording.rec";
|
var recordingFile = "recording.rec";
|
||||||
var playFromCurrentLocation = true;
|
var playFromCurrentLocation = true;
|
||||||
var loop = true;
|
|
||||||
|
|
||||||
var windowDimensions = Controller.getViewportDimensions();
|
var windowDimensions = Controller.getViewportDimensions();
|
||||||
var TOOL_ICON_URL = "http://s3-us-west-1.amazonaws.com/highfidelity-public/images/tools/";
|
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 ALPHA_OFF = 0.7;
|
||||||
var COLOR_ON = { red: 128, green: 0, blue: 0 };
|
var COLOR_ON = { red: 128, green: 0, blue: 0 };
|
||||||
var COLOR_OFF = { red: 128, green: 128, blue: 128 };
|
var COLOR_OFF = { red: 128, green: 128, blue: 128 };
|
||||||
Tool.IMAGE_WIDTH *= 0.7;
|
var COLOR_TOOL_BAR = { red: 0, green: 0, blue: 0 };
|
||||||
Tool.IMAGE_HEIGHT *= 0.7;
|
|
||||||
|
|
||||||
var toolBar = null;
|
var toolBar = null;
|
||||||
var recordIcon;
|
var recordIcon;
|
||||||
var playIcon;
|
var playIcon;
|
||||||
|
var playLoopIcon;
|
||||||
var saveIcon;
|
var saveIcon;
|
||||||
var loadIcon;
|
var loadIcon;
|
||||||
|
var spacing;
|
||||||
|
var timerOffset;
|
||||||
setupToolBar();
|
setupToolBar();
|
||||||
|
|
||||||
var timer = null;
|
var timer = null;
|
||||||
setupTimer();
|
setupTimer();
|
||||||
|
|
||||||
|
var watchStop = false;
|
||||||
|
|
||||||
function setupToolBar() {
|
function setupToolBar() {
|
||||||
if (toolBar != null) {
|
if (toolBar != null) {
|
||||||
print("Multiple calls to Recorder.js:setupToolBar()");
|
print("Multiple calls to Recorder.js:setupToolBar()");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
Tool.IMAGE_HEIGHT /= 2;
|
||||||
|
Tool.IMAGE_WIDTH /= 2;
|
||||||
|
|
||||||
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
toolBar = new ToolBar(0, 0, ToolBar.HORIZONTAL);
|
||||||
toolBar.setBack(COLOR_OFF, ALPHA_OFF);
|
|
||||||
|
toolBar.setBack(COLOR_TOOL_BAR, ALPHA_OFF);
|
||||||
recordIcon = toolBar.addTool({
|
|
||||||
imageURL: TOOL_ICON_URL + "record.svg",
|
recordIcon = toolBar.addTool({
|
||||||
width: Tool.IMAGE_WIDTH,
|
imageURL: TOOL_ICON_URL + "recording-record.svg",
|
||||||
height: Tool.IMAGE_HEIGHT,
|
subImage: { x: 0, y: 0, width: Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||||
alpha: ALPHA_ON,
|
x: 0, y: 0,
|
||||||
visible: true
|
width: Tool.IMAGE_WIDTH,
|
||||||
}, false);
|
height: Tool.IMAGE_HEIGHT,
|
||||||
|
alpha: MyAvatar.isPlaying() ? ALPHA_OFF : ALPHA_ON,
|
||||||
playIcon = toolBar.addTool({
|
visible: true
|
||||||
imageURL: TOOL_ICON_URL + "play.svg",
|
}, true, !MyAvatar.isRecording());
|
||||||
width: Tool.IMAGE_WIDTH,
|
|
||||||
height: Tool.IMAGE_HEIGHT,
|
var playLoopWidthFactor = 1.65;
|
||||||
alpha: ALPHA_ON,
|
playIcon = toolBar.addTool({
|
||||||
visible: true
|
imageURL: TOOL_ICON_URL + "play-pause.svg",
|
||||||
}, false, false);
|
width: playLoopWidthFactor * Tool.IMAGE_WIDTH,
|
||||||
|
height: Tool.IMAGE_HEIGHT,
|
||||||
saveIcon = toolBar.addTool({
|
alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON,
|
||||||
imageURL: TOOL_ICON_URL + "save.svg",
|
visible: true
|
||||||
width: Tool.IMAGE_WIDTH,
|
}, false);
|
||||||
height: Tool.IMAGE_HEIGHT,
|
|
||||||
alpha: ALPHA_ON,
|
playLoopIcon = toolBar.addTool({
|
||||||
visible: true
|
imageURL: TOOL_ICON_URL + "play-and-loop.svg",
|
||||||
}, false, false);
|
subImage: { x: 0, y: 0, width: playLoopWidthFactor * Tool.IMAGE_WIDTH, height: Tool.IMAGE_HEIGHT },
|
||||||
|
width: playLoopWidthFactor * Tool.IMAGE_WIDTH,
|
||||||
loadIcon = toolBar.addTool({
|
height: Tool.IMAGE_HEIGHT,
|
||||||
imageURL: TOOL_ICON_URL + "load.svg",
|
alpha: (MyAvatar.isRecording() || MyAvatar.playerLength() === 0) ? ALPHA_OFF : ALPHA_ON,
|
||||||
width: Tool.IMAGE_WIDTH,
|
visible: true
|
||||||
height: Tool.IMAGE_HEIGHT,
|
}, false);
|
||||||
alpha: ALPHA_ON,
|
|
||||||
visible: true
|
timerOffset = toolBar.width;
|
||||||
}, false, false);
|
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() {
|
function setupTimer() {
|
||||||
timer = Overlays.addOverlay("text", {
|
timer = Overlays.addOverlay("text", {
|
||||||
font: { size: 20 },
|
font: { size: 15 },
|
||||||
text: (0.00).toFixed(3),
|
text: (0.00).toFixed(3),
|
||||||
backgroundColor: COLOR_OFF,
|
backgroundColor: COLOR_OFF,
|
||||||
x: 0, y: 0,
|
x: 0, y: 0,
|
||||||
width: 100,
|
width: 0,
|
||||||
height: 100,
|
height: 0,
|
||||||
alpha: 1.0,
|
alpha: 1.0,
|
||||||
visible: true
|
visible: true
|
||||||
});
|
});
|
||||||
|
@ -92,7 +113,8 @@ function setupTimer() {
|
||||||
function updateTimer() {
|
function updateTimer() {
|
||||||
var text = "";
|
var text = "";
|
||||||
if (MyAvatar.isRecording()) {
|
if (MyAvatar.isRecording()) {
|
||||||
text = formatTime(MyAvatar.recorderElapsed())
|
text = formatTime(MyAvatar.recorderElapsed());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
text = formatTime(MyAvatar.playerElapsed()) + " / " +
|
text = formatTime(MyAvatar.playerElapsed()) + " / " +
|
||||||
formatTime(MyAvatar.playerLength());
|
formatTime(MyAvatar.playerLength());
|
||||||
|
@ -101,6 +123,7 @@ function updateTimer() {
|
||||||
Overlays.editOverlay(timer, {
|
Overlays.editOverlay(timer, {
|
||||||
text: text
|
text: text
|
||||||
})
|
})
|
||||||
|
toolBar.changeSpacing(text.length * 8 + ((MyAvatar.isRecording()) ? 15 : 0), spacing);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatTime(time) {
|
function formatTime(time) {
|
||||||
|
@ -127,54 +150,86 @@ function formatTime(time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function moveUI() {
|
function moveUI() {
|
||||||
var relative = { x: 30, y: 90 };
|
var relative = { x: 70, y: 40 };
|
||||||
toolBar.move(relative.x,
|
toolBar.move(relative.x,
|
||||||
windowDimensions.y - relative.y);
|
windowDimensions.y - relative.y);
|
||||||
Overlays.editOverlay(timer, {
|
Overlays.editOverlay(timer, {
|
||||||
x: relative.x - 10,
|
x: relative.x + timerOffset - ToolBar.SPACING,
|
||||||
y: windowDimensions.y - relative.y - 35,
|
y: windowDimensions.y - relative.y - ToolBar.SPACING
|
||||||
width: 0,
|
|
||||||
height: 0
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function mousePressEvent(event) {
|
function mousePressEvent(event) {
|
||||||
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
clickedOverlay = Overlays.getOverlayAtPoint({ x: event.x, y: event.y });
|
||||||
|
|
||||||
if (recordIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isPlaying()) {
|
if (recordIcon === toolBar.clicked(clickedOverlay, false) && !MyAvatar.isPlaying()) {
|
||||||
if (!MyAvatar.isRecording()) {
|
if (!MyAvatar.isRecording()) {
|
||||||
MyAvatar.startRecording();
|
MyAvatar.startRecording();
|
||||||
toolBar.setBack(COLOR_ON, ALPHA_ON);
|
toolBar.selectTool(recordIcon, false);
|
||||||
} else {
|
toolBar.setAlpha(ALPHA_OFF, playIcon);
|
||||||
MyAvatar.stopRecording();
|
toolBar.setAlpha(ALPHA_OFF, playLoopIcon);
|
||||||
MyAvatar.loadLastRecording();
|
toolBar.setAlpha(ALPHA_OFF, saveIcon);
|
||||||
toolBar.setBack(COLOR_OFF, ALPHA_OFF);
|
toolBar.setAlpha(ALPHA_OFF, loadIcon);
|
||||||
}
|
} else {
|
||||||
} else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
|
MyAvatar.stopRecording();
|
||||||
if (MyAvatar.isPlaying()) {
|
toolBar.selectTool(recordIcon, true );
|
||||||
MyAvatar.stopPlaying();
|
MyAvatar.loadLastRecording();
|
||||||
} else {
|
toolBar.setAlpha(ALPHA_ON, playIcon);
|
||||||
MyAvatar.setPlayFromCurrentLocation(playFromCurrentLocation);
|
toolBar.setAlpha(ALPHA_ON, playLoopIcon);
|
||||||
MyAvatar.setPlayerLoop(loop);
|
toolBar.setAlpha(ALPHA_ON, saveIcon);
|
||||||
MyAvatar.startPlaying(true);
|
toolBar.setAlpha(ALPHA_ON, loadIcon);
|
||||||
}
|
}
|
||||||
} else if (saveIcon === toolBar.clicked(clickedOverlay)) {
|
} else if (playIcon === toolBar.clicked(clickedOverlay) && !MyAvatar.isRecording()) {
|
||||||
if (!MyAvatar.isRecording()) {
|
if (MyAvatar.isPlaying()) {
|
||||||
recordingFile = Window.save("Save recording to file", ".", "*.rec");
|
MyAvatar.stopPlaying();
|
||||||
if (recordingFile != null) {
|
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);
|
MyAvatar.saveRecording(recordingFile);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (loadIcon === toolBar.clicked(clickedOverlay)) {
|
} else if (loadIcon === toolBar.clicked(clickedOverlay)) {
|
||||||
if (!MyAvatar.isRecording()) {
|
if (!MyAvatar.isRecording() && !MyAvatar.isPlaying()) {
|
||||||
recordingFile = Window.browse("Load recorcding from file", ".", "*.rec");
|
recordingFile = Window.browse("Load recorcding from file", ".", "*.rec");
|
||||||
if (recordingFile != "null") {
|
if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) {
|
||||||
MyAvatar.loadRecording(recordingFile);
|
MyAvatar.loadRecording(recordingFile);
|
||||||
}
|
}
|
||||||
}
|
if (MyAvatar.playerLength() > 0) {
|
||||||
} else {
|
toolBar.setAlpha(ALPHA_ON, playIcon);
|
||||||
|
toolBar.setAlpha(ALPHA_ON, playLoopIcon);
|
||||||
}
|
toolBar.setAlpha(ALPHA_ON, saveIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function update() {
|
function update() {
|
||||||
|
@ -186,6 +241,13 @@ function update() {
|
||||||
}
|
}
|
||||||
|
|
||||||
updateTimer();
|
updateTimer();
|
||||||
|
|
||||||
|
if (watchStop && !MyAvatar.isPlaying()) {
|
||||||
|
watchStop = false;
|
||||||
|
toolBar.setAlpha(ALPHA_ON, recordIcon);
|
||||||
|
toolBar.setAlpha(ALPHA_ON, saveIcon);
|
||||||
|
toolBar.setAlpha(ALPHA_ON, loadIcon);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function scriptEnding() {
|
function scriptEnding() {
|
||||||
|
|
|
@ -61,11 +61,10 @@ Overlay2D = function(properties, overlay) { // overlay is an optionnal variable
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clicked = function(clickedOverlay) {
|
this.clicked = function(clickedOverlay) {
|
||||||
return (overlay == clickedOverlay ? true : false);
|
return overlay === clickedOverlay;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.cleanup = function() {
|
this.cleanup = function() {
|
||||||
print("Cleanup");
|
|
||||||
Overlays.deleteOverlay(overlay);
|
Overlays.deleteOverlay(overlay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -112,9 +111,9 @@ Tool = function(properties, selectable, selected) { // selectable and selected a
|
||||||
this.select(selected);
|
this.select(selected);
|
||||||
|
|
||||||
this.baseClicked = this.clicked;
|
this.baseClicked = this.clicked;
|
||||||
this.clicked = function(clickedOverlay) {
|
this.clicked = function(clickedOverlay, update) {
|
||||||
if (this.baseClicked(clickedOverlay)) {
|
if (this.baseClicked(clickedOverlay)) {
|
||||||
if (selectable) {
|
if (selectable && update) {
|
||||||
this.toggle();
|
this.toggle();
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -141,6 +140,7 @@ ToolBar = function(x, y, direction) {
|
||||||
alpha: 1.0,
|
alpha: 1.0,
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
|
this.spacing = [];
|
||||||
|
|
||||||
this.addTool = function(properties, selectable, selected) {
|
this.addTool = function(properties, selectable, selected) {
|
||||||
if (direction == ToolBar.HORIZONTAL) {
|
if (direction == ToolBar.HORIZONTAL) {
|
||||||
|
@ -154,16 +154,56 @@ ToolBar = function(x, y, direction) {
|
||||||
this.width = Math.max(properties.width, this.width);
|
this.width = Math.max(properties.width, this.width);
|
||||||
this.height += properties.height + ToolBar.SPACING;
|
this.height += properties.height + ToolBar.SPACING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.back != null) {
|
if (this.back != null) {
|
||||||
Overlays.editOverlay(this.back, {
|
Overlays.editOverlay(this.back, {
|
||||||
width: this.width + 2 * ToolBar.SPACING,
|
width: this.width +
|
||||||
height: this.height + 2 * ToolBar.SPACING
|
((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));
|
this.tools.push(new Tool(properties, selectable, selected));
|
||||||
return ((this.tools.length) - 1);
|
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.removeLastTool = function() {
|
||||||
this.tools.pop().cleanup();
|
this.tools.pop().cleanup();
|
||||||
|
@ -209,18 +249,22 @@ ToolBar = function(x, y, direction) {
|
||||||
this.tools[tool].setAlpha(alpha);
|
this.tools[tool].setAlpha(alpha);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setBack = function(color, alpha) {
|
this.setBack = function(color, alpha) {
|
||||||
if (color == null) {
|
if (color == null) {
|
||||||
Overlays.editOverlay(this.back, {
|
Overlays.editOverlay(this.back, {
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
Overlays.editOverlay(this.back, {
|
Overlays.editOverlay(this.back, {
|
||||||
visible: true,
|
width: this.width +
|
||||||
backgroundColor: color,
|
((direction == ToolBar.HORIZONTAL) ? 1 : 2) * ToolBar.SPACING,
|
||||||
alpha: alpha
|
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) {
|
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);
|
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
|
#version 120
|
||||||
|
|
||||||
//
|
//
|
||||||
// metavoxel_heightfield.frag
|
// directional_light.frag
|
||||||
// fragment shader
|
// fragment shader
|
||||||
//
|
//
|
||||||
// Created by Andrzej Kapolka on 7/28/14.
|
// Created by Andrzej Kapolka on 9/3/14.
|
||||||
// Copyright 2014 High Fidelity, Inc.
|
// Copyright 2014 High Fidelity, Inc.
|
||||||
//
|
//
|
||||||
// Distributed under the Apache License, Version 2.0.
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
@ -14,6 +14,12 @@
|
||||||
// the diffuse texture
|
// the diffuse texture
|
||||||
uniform sampler2D diffuseMap;
|
uniform sampler2D diffuseMap;
|
||||||
|
|
||||||
|
// the normal texture
|
||||||
|
uniform sampler2D normalMap;
|
||||||
|
|
||||||
|
// the depth texture
|
||||||
|
uniform sampler2D depthMap;
|
||||||
|
|
||||||
// the shadow texture
|
// the shadow texture
|
||||||
uniform sampler2DShadow shadowMap;
|
uniform sampler2DShadow shadowMap;
|
||||||
|
|
||||||
|
@ -21,28 +27,39 @@ uniform sampler2DShadow shadowMap;
|
||||||
uniform vec3 shadowDistances;
|
uniform vec3 shadowDistances;
|
||||||
|
|
||||||
// the inverse of the size of the shadow map
|
// the inverse of the size of the shadow map
|
||||||
const float shadowScale = 1.0 / 2048.0;
|
uniform float shadowScale;
|
||||||
|
|
||||||
// the interpolated position
|
// the distance to the near clip plane
|
||||||
varying vec4 position;
|
uniform float near;
|
||||||
|
|
||||||
// the interpolated normal
|
// scale factor for depth: (far - near) / far
|
||||||
varying vec4 normal;
|
uniform float depthScale;
|
||||||
|
|
||||||
|
// offset for depth texture coordinates
|
||||||
|
uniform vec2 depthTexCoordOffset;
|
||||||
|
|
||||||
|
// scale for depth texture coordinates
|
||||||
|
uniform vec2 depthTexCoordScale;
|
||||||
|
|
||||||
void main(void) {
|
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
|
// 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)));
|
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),
|
vec3 shadowTexCoord = vec3(dot(gl_EyePlaneS[shadowIndex], position), dot(gl_EyePlaneT[shadowIndex], position),
|
||||||
dot(gl_EyePlaneR[shadowIndex], position));
|
dot(gl_EyePlaneR[shadowIndex], position));
|
||||||
|
|
||||||
// compute the base color based on OpenGL lighting model
|
// compute the color based on OpenGL lighting model, use the alpha from the normal map
|
||||||
float diffuse = dot(normalize(normal), gl_LightSource[0].position);
|
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 *
|
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 +
|
||||||
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 +
|
vec4 baseColor = texture2D(diffuseMap, gl_TexCoord[0].st) * (gl_FrontLightModelProduct.sceneColor +
|
||||||
gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
gl_FrontLightProduct[0].ambient + gl_FrontLightProduct[0].diffuse * (diffuse * facingLight));
|
||||||
gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st);
|
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
|
// the diffuse texture
|
||||||
uniform sampler2D diffuseMap;
|
uniform sampler2D diffuseMap;
|
||||||
|
|
||||||
|
// the interpolated normal
|
||||||
|
varying vec4 normal;
|
||||||
|
|
||||||
void main(void) {
|
void main(void) {
|
||||||
// compute the base color based on OpenGL lighting model
|
// 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
|
// the scale between height and color textures
|
||||||
uniform float colorScale;
|
uniform float colorScale;
|
||||||
|
|
||||||
|
// the interpolated normal
|
||||||
|
varying vec4 normal;
|
||||||
|
|
||||||
void main(void) {
|
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
|
// 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));
|
gl_Position = gl_ModelViewProjectionMatrix * (gl_Vertex + vec4(0.0, height, 0.0, 0.0));
|
||||||
|
|
||||||
// the zero height should be invisible
|
// 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];
|
||||||
|
}
|
File diff suppressed because it is too large
Load diff
|
@ -39,6 +39,7 @@ public:
|
||||||
|
|
||||||
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
|
const AttributePointer& getPointBufferAttribute() { return _pointBufferAttribute; }
|
||||||
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
|
const AttributePointer& getHeightfieldBufferAttribute() { return _heightfieldBufferAttribute; }
|
||||||
|
const AttributePointer& getVoxelBufferAttribute() { return _voxelBufferAttribute; }
|
||||||
|
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
void render();
|
void render();
|
||||||
|
@ -51,6 +52,12 @@ public:
|
||||||
|
|
||||||
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
|
Q_INVOKABLE void deleteTextures(int heightID, int colorID, int textureID);
|
||||||
|
|
||||||
|
void noteNeedToLight() { _needToLight = true; }
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void rendering();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual MetavoxelClient* createClient(const SharedNodePointer& node);
|
virtual MetavoxelClient* createClient(const SharedNodePointer& node);
|
||||||
|
@ -59,12 +66,33 @@ private:
|
||||||
|
|
||||||
void guideToAugmented(MetavoxelVisitor& visitor, bool render = false);
|
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 _pointBufferAttribute;
|
||||||
AttributePointer _heightfieldBufferAttribute;
|
AttributePointer _heightfieldBufferAttribute;
|
||||||
|
AttributePointer _voxelBufferAttribute;
|
||||||
|
|
||||||
MetavoxelLOD _lod;
|
MetavoxelLOD _lod;
|
||||||
QReadWriteLock _lodLock;
|
QReadWriteLock _lodLock;
|
||||||
Frustum _frustum;
|
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.
|
/// Describes contents of a point in a point buffer.
|
||||||
|
@ -143,8 +171,8 @@ public:
|
||||||
static const int HEIGHT_EXTENSION;
|
static const int HEIGHT_EXTENSION;
|
||||||
|
|
||||||
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height,
|
HeightfieldBuffer(const glm::vec3& translation, float scale, const QByteArray& height,
|
||||||
const QByteArray& color, const QByteArray& texture = QByteArray(),
|
const QByteArray& color, const QByteArray& material = QByteArray(),
|
||||||
const QVector<SharedObjectPointer>& textures = QVector<SharedObjectPointer>());
|
const QVector<SharedObjectPointer>& materials = QVector<SharedObjectPointer>());
|
||||||
~HeightfieldBuffer();
|
~HeightfieldBuffer();
|
||||||
|
|
||||||
const glm::vec3& getTranslation() const { return _translation; }
|
const glm::vec3& getTranslation() const { return _translation; }
|
||||||
|
@ -159,10 +187,10 @@ public:
|
||||||
QByteArray& getColor() { return _color; }
|
QByteArray& getColor() { return _color; }
|
||||||
const QByteArray& getColor() const { return _color; }
|
const QByteArray& getColor() const { return _color; }
|
||||||
|
|
||||||
QByteArray& getTexture() { return _texture; }
|
QByteArray& getMaterial() { return _material; }
|
||||||
const QByteArray& getTexture() const { return _texture; }
|
const QByteArray& getMaterial() const { return _material; }
|
||||||
|
|
||||||
const QVector<SharedObjectPointer>& getTextures() const { return _textures; }
|
const QVector<SharedObjectPointer>& getMaterials() const { return _materials; }
|
||||||
|
|
||||||
QByteArray getUnextendedHeight() const;
|
QByteArray getUnextendedHeight() const;
|
||||||
QByteArray getUnextendedColor() const;
|
QByteArray getUnextendedColor() const;
|
||||||
|
@ -183,11 +211,11 @@ private:
|
||||||
Box _colorBounds;
|
Box _colorBounds;
|
||||||
QByteArray _height;
|
QByteArray _height;
|
||||||
QByteArray _color;
|
QByteArray _color;
|
||||||
QByteArray _texture;
|
QByteArray _material;
|
||||||
QVector<SharedObjectPointer> _textures;
|
QVector<SharedObjectPointer> _materials;
|
||||||
GLuint _heightTextureID;
|
GLuint _heightTextureID;
|
||||||
GLuint _colorTextureID;
|
GLuint _colorTextureID;
|
||||||
GLuint _textureTextureID;
|
GLuint _materialTextureID;
|
||||||
QVector<NetworkTexturePointer> _networkTextures;
|
QVector<NetworkTexturePointer> _networkTextures;
|
||||||
int _heightSize;
|
int _heightSize;
|
||||||
float _heightIncrement;
|
float _heightIncrement;
|
||||||
|
@ -212,6 +240,37 @@ private:
|
||||||
QVector<BufferDataPointer> _buffers;
|
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.
|
/// A client-side attribute that stores renderable buffers.
|
||||||
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
|
class BufferDataAttribute : public InlineAttribute<BufferDataPointer> {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -233,42 +292,33 @@ public:
|
||||||
|
|
||||||
static void init();
|
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 ProgramObject& getBaseHeightfieldProgram() { return _baseHeightfieldProgram; }
|
||||||
static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; }
|
static int getBaseHeightScaleLocation() { return _baseHeightScaleLocation; }
|
||||||
static int getBaseColorScaleLocation() { return _baseColorScaleLocation; }
|
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 ProgramObject& getSplatHeightfieldProgram() { return _splatHeightfieldProgram; }
|
||||||
static int getSplatHeightScaleLocation() { return _splatHeightScaleLocation; }
|
static const SplatLocations& getSplatHeightfieldLocations() { return _splatHeightfieldLocations; }
|
||||||
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 ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
|
static ProgramObject& getHeightfieldCursorProgram() { return _heightfieldCursorProgram; }
|
||||||
|
|
||||||
|
static ProgramObject& getBaseVoxelProgram() { return _baseVoxelProgram; }
|
||||||
|
|
||||||
|
static ProgramObject& getSplatVoxelProgram() { return _splatVoxelProgram; }
|
||||||
|
static const SplatLocations& getSplatVoxelLocations() { return _splatVoxelLocations; }
|
||||||
|
|
||||||
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
Q_INVOKABLE DefaultMetavoxelRendererImplementation();
|
||||||
|
|
||||||
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
virtual void augment(MetavoxelData& data, const MetavoxelData& previous, MetavoxelInfo& info, const MetavoxelLOD& lod);
|
||||||
|
@ -277,27 +327,18 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
static void loadSplatProgram(const char* type, ProgramObject& program, SplatLocations& locations);
|
||||||
|
|
||||||
static ProgramObject _pointProgram;
|
static ProgramObject _pointProgram;
|
||||||
static int _pointScaleLocation;
|
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 ProgramObject _baseHeightfieldProgram;
|
||||||
static int _baseHeightScaleLocation;
|
static int _baseHeightScaleLocation;
|
||||||
static int _baseColorScaleLocation;
|
static int _baseColorScaleLocation;
|
||||||
|
|
||||||
static ProgramObject _splatHeightfieldProgram;
|
static ProgramObject _splatHeightfieldProgram;
|
||||||
|
static SplatLocations _splatHeightfieldLocations;
|
||||||
|
|
||||||
static int _splatHeightScaleLocation;
|
static int _splatHeightScaleLocation;
|
||||||
static int _splatTextureScaleLocation;
|
static int _splatTextureScaleLocation;
|
||||||
static int _splatTextureOffsetLocation;
|
static int _splatTextureOffsetLocation;
|
||||||
|
@ -306,17 +347,11 @@ private:
|
||||||
static int _splatTextureValueMinimaLocation;
|
static int _splatTextureValueMinimaLocation;
|
||||||
static int _splatTextureValueMaximaLocation;
|
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 _heightfieldCursorProgram;
|
||||||
|
|
||||||
|
static ProgramObject _baseVoxelProgram;
|
||||||
|
static ProgramObject _splatVoxelProgram;
|
||||||
|
static SplatLocations _splatVoxelLocations;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Base class for spanner renderers; provides clipping.
|
/// Base class for spanner renderers; provides clipping.
|
||||||
|
|
|
@ -78,7 +78,8 @@ MyAvatar::MyAvatar() :
|
||||||
_lookAtTargetAvatar(),
|
_lookAtTargetAvatar(),
|
||||||
_shouldRender(true),
|
_shouldRender(true),
|
||||||
_billboardValid(false),
|
_billboardValid(false),
|
||||||
_physicsSimulation()
|
_physicsSimulation(),
|
||||||
|
_voxelShapeManager()
|
||||||
{
|
{
|
||||||
ShapeCollider::initDispatchTable();
|
ShapeCollider::initDispatchTable();
|
||||||
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
for (int i = 0; i < MAX_DRIVE_KEYS; i++) {
|
||||||
|
@ -89,11 +90,11 @@ MyAvatar::MyAvatar() :
|
||||||
_skeletonModel.setEnableShapes(true);
|
_skeletonModel.setEnableShapes(true);
|
||||||
Ragdoll* ragdoll = _skeletonModel.buildRagdoll();
|
Ragdoll* ragdoll = _skeletonModel.buildRagdoll();
|
||||||
_physicsSimulation.setRagdoll(ragdoll);
|
_physicsSimulation.setRagdoll(ragdoll);
|
||||||
|
_physicsSimulation.addEntity(&_voxelShapeManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
MyAvatar::~MyAvatar() {
|
MyAvatar::~MyAvatar() {
|
||||||
_physicsSimulation.setRagdoll(NULL);
|
_physicsSimulation.clear();
|
||||||
_physicsSimulation.setEntity(NULL);
|
|
||||||
_lookAtTargetAvatar.clear();
|
_lookAtTargetAvatar.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1366,112 +1367,125 @@ void MyAvatar::updateCollisionWithEnvironment(float deltaTime, float radius) {
|
||||||
static CollisionList myCollisions(64);
|
static CollisionList myCollisions(64);
|
||||||
|
|
||||||
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
void MyAvatar::updateCollisionWithVoxels(float deltaTime, float radius) {
|
||||||
const float MAX_VOXEL_COLLISION_SPEED = 100.0f;
|
if (Menu::getInstance()->isOptionChecked(MenuOption::CollideAsRagdoll)) {
|
||||||
float speed = glm::length(_velocity);
|
// We use a multiple of the avatar's boundingRadius as the size of the cube of interest.
|
||||||
if (speed > MAX_VOXEL_COLLISION_SPEED) {
|
float cubeScale = 4.0f * getBoundingRadius();
|
||||||
// don't even bother to try to collide against voxles when moving very fast
|
glm::vec3 corner = getPosition() - glm::vec3(0.5f * cubeScale);
|
||||||
_trapDuration = 0.0f;
|
AACube boundingCube(corner, cubeScale);
|
||||||
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);
|
|
||||||
|
|
||||||
for (int i = 0; i < myCollisions.size(); ++i) {
|
// query the VoxelTree for cubes that touch avatar's boundingCube
|
||||||
CollisionInfo* collision = myCollisions[i];
|
CubeList cubes;
|
||||||
glm::vec3 cubeCenter = collision->_vecData;
|
if (Application::getInstance()->getVoxelTree()->findContentInCube(boundingCube, cubes)) {
|
||||||
float cubeSide = collision->_floatData;
|
_voxelShapeManager.updateVoxels(cubes);
|
||||||
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) {
|
} else {
|
||||||
_lastFloorContactPoint = floorPoint;
|
const float MAX_VOXEL_COLLISION_SPEED = 100.0f;
|
||||||
}
|
float speed = glm::length(_velocity);
|
||||||
|
if (speed > MAX_VOXEL_COLLISION_SPEED) {
|
||||||
float penetrationLength = glm::length(totalPenetration);
|
// don't even bother to try to collide against voxles when moving very fast
|
||||||
if (penetrationLength < EPSILON) {
|
|
||||||
_trapDuration = 0.0f;
|
_trapDuration = 0.0f;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float verticalPenetration = glm::dot(totalPenetration, _worldUpDirection);
|
bool isTrapped = false;
|
||||||
if (highestStep > MIN_STEP_HEIGHT && highestStep < MAX_STEP_HEIGHT && verticalPenetration <= 0.0f) {
|
myCollisions.clear();
|
||||||
// we're colliding against an edge
|
const CapsuleShape& boundingShape = _skeletonModel.getBoundingShape();
|
||||||
glm::vec3 targetVelocity = _motorVelocity;
|
if (Application::getInstance()->getVoxelTree()->findShapeCollisions(&boundingShape, myCollisions, Octree::TryLock)) {
|
||||||
if (_motionBehaviors & AVATAR_MOTION_MOTOR_USE_LOCAL_FRAME) {
|
const float VOXEL_ELASTICITY = 0.0f;
|
||||||
// rotate _motorVelocity into world frame
|
const float VOXEL_DAMPING = 0.0f;
|
||||||
glm::quat rotation = getHead()->getCameraOrientation();
|
float capsuleRadius = boundingShape.getRadius();
|
||||||
targetVelocity = rotation * _motorVelocity;
|
float capsuleHalfHeight = boundingShape.getHalfHeight();
|
||||||
}
|
const float MAX_STEP_HEIGHT = capsuleRadius + capsuleHalfHeight;
|
||||||
if (_wasPushing && glm::dot(targetVelocity, totalPenetration) > EPSILON) {
|
const float MIN_STEP_HEIGHT = 0.0f;
|
||||||
// we're puhing into the edge, so we want to lift
|
glm::vec3 footBase = boundingShape.getTranslation() - (capsuleRadius + capsuleHalfHeight) * _worldUpDirection;
|
||||||
|
float highestStep = 0.0f;
|
||||||
// remove unhelpful horizontal component of the step's penetration
|
float lowestStep = MAX_STEP_HEIGHT;
|
||||||
totalPenetration -= stepPenetration - (glm::dot(stepPenetration, _worldUpDirection) * _worldUpDirection);
|
glm::vec3 floorPoint;
|
||||||
|
glm::vec3 stepPenetration(0.0f);
|
||||||
// further adjust penetration to help lift
|
glm::vec3 totalPenetration(0.0f);
|
||||||
float liftSpeed = glm::max(MAX_WALKING_SPEED, speed);
|
|
||||||
float thisStep = glm::min(liftSpeed * deltaTime, highestStep);
|
for (int i = 0; i < myCollisions.size(); ++i) {
|
||||||
float extraStep = glm::dot(totalPenetration, _worldUpDirection) + thisStep;
|
CollisionInfo* collision = myCollisions[i];
|
||||||
if (extraStep > 0.0f) {
|
glm::vec3 cubeCenter = collision->_vecData;
|
||||||
totalPenetration -= extraStep * _worldUpDirection;
|
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 {
|
} else {
|
||||||
// we're not pushing into the edge, so let the avatar fall
|
|
||||||
applyHardCollision(totalPenetration, VOXEL_ELASTICITY, VOXEL_DAMPING);
|
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);
|
||||||
// Don't make a collision sound against voxlels by default -- too annoying when walking
|
}
|
||||||
//const float VOXEL_COLLISION_FREQUENCY = 0.5f;
|
_trapDuration = isTrapped ? _trapDuration + deltaTime : 0.0f;
|
||||||
//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) {
|
void MyAvatar::applyHardCollision(const glm::vec3& penetration, float elasticity, float damping) {
|
||||||
|
@ -1837,6 +1851,9 @@ void MyAvatar::updateMotionBehaviorsFromMenu() {
|
||||||
} else {
|
} else {
|
||||||
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
_motionBehaviors &= ~AVATAR_MOTION_STAND_ON_NEARBY_FLOORS;
|
||||||
}
|
}
|
||||||
|
if (!(_collisionGroups | COLLISION_GROUP_VOXELS)) {
|
||||||
|
_voxelShapeManager.clearShapes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MyAvatar::renderAttachments(RenderMode renderMode) {
|
void MyAvatar::renderAttachments(RenderMode renderMode) {
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include <PhysicsSimulation.h>
|
#include <PhysicsSimulation.h>
|
||||||
|
|
||||||
#include "Avatar.h"
|
#include "Avatar.h"
|
||||||
|
#include "VoxelShapeManager.h"
|
||||||
|
|
||||||
class ModelItemID;
|
class ModelItemID;
|
||||||
|
|
||||||
|
@ -213,6 +214,7 @@ private:
|
||||||
|
|
||||||
QList<AnimationHandlePointer> _animationHandles;
|
QList<AnimationHandlePointer> _animationHandles;
|
||||||
PhysicsSimulation _physicsSimulation;
|
PhysicsSimulation _physicsSimulation;
|
||||||
|
VoxelShapeManager _voxelShapeManager;
|
||||||
|
|
||||||
RecorderPointer _recorder;
|
RecorderPointer _recorder;
|
||||||
|
|
||||||
|
|
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),
|
_whiteTextureID(0),
|
||||||
_blueTextureID(0),
|
_blueTextureID(0),
|
||||||
_primaryDepthTextureID(0),
|
_primaryDepthTextureID(0),
|
||||||
|
_primaryNormalTextureID(0),
|
||||||
_primaryFramebufferObject(NULL),
|
_primaryFramebufferObject(NULL),
|
||||||
_secondaryFramebufferObject(NULL),
|
_secondaryFramebufferObject(NULL),
|
||||||
_tertiaryFramebufferObject(NULL),
|
_tertiaryFramebufferObject(NULL),
|
||||||
|
@ -46,6 +47,7 @@ TextureCache::~TextureCache() {
|
||||||
}
|
}
|
||||||
if (_primaryFramebufferObject) {
|
if (_primaryFramebufferObject) {
|
||||||
glDeleteTextures(1, &_primaryDepthTextureID);
|
glDeleteTextures(1, &_primaryDepthTextureID);
|
||||||
|
glDeleteTextures(1, &_primaryNormalTextureID);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_primaryFramebufferObject) {
|
if (_primaryFramebufferObject) {
|
||||||
|
@ -71,6 +73,8 @@ void TextureCache::setFrameBufferSize(QSize frameBufferSize) {
|
||||||
_primaryFramebufferObject = NULL;
|
_primaryFramebufferObject = NULL;
|
||||||
glDeleteTextures(1, &_primaryDepthTextureID);
|
glDeleteTextures(1, &_primaryDepthTextureID);
|
||||||
_primaryDepthTextureID = 0;
|
_primaryDepthTextureID = 0;
|
||||||
|
glDeleteTextures(1, &_primaryNormalTextureID);
|
||||||
|
_primaryNormalTextureID = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_secondaryFramebufferObject) {
|
if (_secondaryFramebufferObject) {
|
||||||
|
@ -205,15 +209,22 @@ QOpenGLFramebufferObject* TextureCache::getPrimaryFramebufferObject() {
|
||||||
|
|
||||||
glGenTextures(1, &_primaryDepthTextureID);
|
glGenTextures(1, &_primaryDepthTextureID);
|
||||||
glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID);
|
glBindTexture(GL_TEXTURE_2D, _primaryDepthTextureID);
|
||||||
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(),
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, _frameBufferSize.width(), _frameBufferSize.height(),
|
||||||
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
|
0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, 0);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
|
|
||||||
_primaryFramebufferObject->bind();
|
_primaryFramebufferObject->bind();
|
||||||
glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, _primaryDepthTextureID, 0);
|
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();
|
_primaryFramebufferObject->release();
|
||||||
}
|
}
|
||||||
return _primaryFramebufferObject;
|
return _primaryFramebufferObject;
|
||||||
|
@ -225,6 +236,12 @@ GLuint TextureCache::getPrimaryDepthTextureID() {
|
||||||
return _primaryDepthTextureID;
|
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() {
|
QOpenGLFramebufferObject* TextureCache::getSecondaryFramebufferObject() {
|
||||||
if (!_secondaryFramebufferObject) {
|
if (!_secondaryFramebufferObject) {
|
||||||
_secondaryFramebufferObject = createFramebufferObject();
|
_secondaryFramebufferObject = createFramebufferObject();
|
||||||
|
@ -278,6 +295,7 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
|
||||||
delete _primaryFramebufferObject;
|
delete _primaryFramebufferObject;
|
||||||
_primaryFramebufferObject = NULL;
|
_primaryFramebufferObject = NULL;
|
||||||
glDeleteTextures(1, &_primaryDepthTextureID);
|
glDeleteTextures(1, &_primaryDepthTextureID);
|
||||||
|
glDeleteTextures(1, &_primaryNormalTextureID);
|
||||||
}
|
}
|
||||||
if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) {
|
if (_secondaryFramebufferObject && _secondaryFramebufferObject->size() != size) {
|
||||||
delete _secondaryFramebufferObject;
|
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.
|
/// Returns the ID of the primary framebuffer object's depth texture. This contains the Z buffer used in rendering.
|
||||||
GLuint getPrimaryDepthTextureID();
|
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
|
/// Returns a pointer to the secondary framebuffer object, used as an additional render target when performing full
|
||||||
/// screen effects.
|
/// screen effects.
|
||||||
QOpenGLFramebufferObject* getSecondaryFramebufferObject();
|
QOpenGLFramebufferObject* getSecondaryFramebufferObject();
|
||||||
|
@ -95,6 +98,7 @@ private:
|
||||||
QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures;
|
QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures;
|
||||||
|
|
||||||
GLuint _primaryDepthTextureID;
|
GLuint _primaryDepthTextureID;
|
||||||
|
GLuint _primaryNormalTextureID;
|
||||||
QOpenGLFramebufferObject* _primaryFramebufferObject;
|
QOpenGLFramebufferObject* _primaryFramebufferObject;
|
||||||
QOpenGLFramebufferObject* _secondaryFramebufferObject;
|
QOpenGLFramebufferObject* _secondaryFramebufferObject;
|
||||||
QOpenGLFramebufferObject* _tertiaryFramebufferObject;
|
QOpenGLFramebufferObject* _tertiaryFramebufferObject;
|
||||||
|
|
|
@ -66,13 +66,16 @@ MetavoxelEditor::MetavoxelEditor() :
|
||||||
attributeLayout->addLayout(attributeButtonLayout);
|
attributeLayout->addLayout(attributeButtonLayout);
|
||||||
|
|
||||||
QPushButton* newAttribute = new QPushButton("New...");
|
QPushButton* newAttribute = new QPushButton("New...");
|
||||||
attributeButtonLayout->addWidget(newAttribute);
|
attributeButtonLayout->addWidget(newAttribute, 1);
|
||||||
connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute()));
|
connect(newAttribute, SIGNAL(clicked()), SLOT(createNewAttribute()));
|
||||||
|
|
||||||
attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"));
|
attributeButtonLayout->addWidget(_deleteAttribute = new QPushButton("Delete"), 1);
|
||||||
_deleteAttribute->setEnabled(false);
|
_deleteAttribute->setEnabled(false);
|
||||||
connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute()));
|
connect(_deleteAttribute, SIGNAL(clicked()), SLOT(deleteSelectedAttribute()));
|
||||||
|
|
||||||
|
attributeButtonLayout->addWidget(_showAll = new QCheckBox("Show All"));
|
||||||
|
connect(_showAll, SIGNAL(clicked()), SLOT(updateAttributes()));
|
||||||
|
|
||||||
QFormLayout* formLayout = new QFormLayout();
|
QFormLayout* formLayout = new QFormLayout();
|
||||||
topLayout->addLayout(formLayout);
|
topLayout->addLayout(formLayout);
|
||||||
|
|
||||||
|
@ -116,11 +119,15 @@ MetavoxelEditor::MetavoxelEditor() :
|
||||||
addTool(new RemoveSpannerTool(this));
|
addTool(new RemoveSpannerTool(this));
|
||||||
addTool(new ClearSpannersTool(this));
|
addTool(new ClearSpannersTool(this));
|
||||||
addTool(new SetSpannerTool(this));
|
addTool(new SetSpannerTool(this));
|
||||||
addTool(new ImportHeightfieldTool(this));
|
|
||||||
addTool(new EraseHeightfieldTool(this));
|
|
||||||
addTool(new HeightfieldHeightBrushTool(this));
|
addTool(new HeightfieldHeightBrushTool(this));
|
||||||
addTool(new HeightfieldColorBrushTool(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();
|
updateAttributes();
|
||||||
|
|
||||||
|
@ -200,7 +207,7 @@ void MetavoxelEditor::selectedAttributeChanged() {
|
||||||
|
|
||||||
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected);
|
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(selected);
|
||||||
foreach (MetavoxelTool* tool, _tools) {
|
foreach (MetavoxelTool* tool, _tools) {
|
||||||
if (tool->appliesTo(attribute)) {
|
if (tool->appliesTo(attribute) && (tool->isUserFacing() || _showAll->isChecked())) {
|
||||||
_toolBox->addItem(tool->objectName(), QVariant::fromValue(tool));
|
_toolBox->addItem(tool->objectName(), QVariant::fromValue(tool));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -214,6 +221,7 @@ void MetavoxelEditor::selectedAttributeChanged() {
|
||||||
editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
editor->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
||||||
_valueArea->setWidget(editor);
|
_valueArea->setWidget(editor);
|
||||||
}
|
}
|
||||||
|
updateTool();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MetavoxelEditor::createNewAttribute() {
|
void MetavoxelEditor::createNewAttribute() {
|
||||||
|
@ -271,6 +279,35 @@ void MetavoxelEditor::alignGridPosition() {
|
||||||
_gridPosition->setValue(step * floor(_gridPosition->value() / step));
|
_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() {
|
void MetavoxelEditor::updateTool() {
|
||||||
MetavoxelTool* active = getActiveTool();
|
MetavoxelTool* active = getActiveTool();
|
||||||
foreach (MetavoxelTool* tool, _tools) {
|
foreach (MetavoxelTool* tool, _tools) {
|
||||||
|
@ -335,25 +372,6 @@ void MetavoxelEditor::addTool(MetavoxelTool* tool) {
|
||||||
layout()->addWidget(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 {
|
MetavoxelTool* MetavoxelEditor::getActiveTool() const {
|
||||||
int index = _toolBox->currentIndex();
|
int index = _toolBox->currentIndex();
|
||||||
return (index == -1) ? NULL : static_cast<MetavoxelTool*>(_toolBox->itemData(index).value<QObject*>());
|
return (index == -1) ? NULL : static_cast<MetavoxelTool*>(_toolBox->itemData(index).value<QObject*>());
|
||||||
|
@ -361,9 +379,10 @@ MetavoxelTool* MetavoxelEditor::getActiveTool() const {
|
||||||
|
|
||||||
ProgramObject MetavoxelEditor::_gridProgram;
|
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),
|
_editor(editor),
|
||||||
_usesValue(usesValue) {
|
_usesValue(usesValue),
|
||||||
|
_userFacing(userFacing) {
|
||||||
|
|
||||||
QVBoxLayout* layout = new QVBoxLayout();
|
QVBoxLayout* layout = new QVBoxLayout();
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
@ -385,13 +404,13 @@ void MetavoxelTool::render() {
|
||||||
// nothing by default
|
// nothing by default
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxSetTool::BoxSetTool(MetavoxelEditor* editor) :
|
BoxTool::BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue, bool userFacing) :
|
||||||
MetavoxelTool(editor, "Set Value (Box)") {
|
MetavoxelTool(editor, name, usesValue, userFacing) {
|
||||||
|
|
||||||
resetState();
|
resetState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BoxSetTool::render() {
|
void BoxTool::render() {
|
||||||
if (Application::getInstance()->isMouseHidden()) {
|
if (Application::getInstance()->isMouseHidden()) {
|
||||||
resetState();
|
resetState();
|
||||||
return;
|
return;
|
||||||
|
@ -457,7 +476,7 @@ void BoxSetTool::render() {
|
||||||
glTranslatef(0.5f, 0.5f, 0.5f);
|
glTranslatef(0.5f, 0.5f, 0.5f);
|
||||||
if (_state != HOVERING_STATE) {
|
if (_state != HOVERING_STATE) {
|
||||||
const float BOX_ALPHA = 0.25f;
|
const float BOX_ALPHA = 0.25f;
|
||||||
QColor color = _editor->getValue().value<QColor>();
|
QColor color = getColor();
|
||||||
if (color.isValid()) {
|
if (color.isValid()) {
|
||||||
glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA);
|
glColor4f(color.redF(), color.greenF(), color.blueF(), BOX_ALPHA);
|
||||||
} else {
|
} else {
|
||||||
|
@ -476,7 +495,7 @@ void BoxSetTool::render() {
|
||||||
glPopMatrix();
|
glPopMatrix();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
|
bool BoxTool::eventFilter(QObject* watched, QEvent* event) {
|
||||||
switch (_state) {
|
switch (_state) {
|
||||||
case HOVERING_STATE:
|
case HOVERING_STATE:
|
||||||
if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) {
|
if (event->type() == QEvent::MouseButtonPress && _startPosition != INVALID_VECTOR) {
|
||||||
|
@ -515,12 +534,20 @@ bool BoxSetTool::eventFilter(QObject* watched, QEvent* event) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BoxSetTool::resetState() {
|
void BoxTool::resetState() {
|
||||||
_state = HOVERING_STATE;
|
_state = HOVERING_STATE;
|
||||||
_startPosition = INVALID_VECTOR;
|
_startPosition = INVALID_VECTOR;
|
||||||
_height = 0.0f;
|
_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) {
|
void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum) {
|
||||||
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
|
AttributePointer attribute = AttributeRegistry::getInstance()->getAttribute(_editor->getSelectedAttribute());
|
||||||
if (!attribute) {
|
if (!attribute) {
|
||||||
|
@ -533,7 +560,7 @@ void BoxSetTool::applyValue(const glm::vec3& minimum, const glm::vec3& maximum)
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) :
|
GlobalSetTool::GlobalSetTool(MetavoxelEditor* editor) :
|
||||||
MetavoxelTool(editor, "Set Value (Global)") {
|
MetavoxelTool(editor, "Set Value (Global)", true, false) {
|
||||||
|
|
||||||
QPushButton* button = new QPushButton("Apply");
|
QPushButton* button = new QPushButton("Apply");
|
||||||
layout()->addWidget(button);
|
layout()->addWidget(button);
|
||||||
|
@ -944,11 +971,9 @@ ImportHeightfieldTool::ImportHeightfieldTool(MetavoxelEditor* editor) :
|
||||||
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
|
connect(_height, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectHeightFile);
|
||||||
_form->addRow("Color:", _color = new QPushButton());
|
_form->addRow("Color:", _color = new QPushButton());
|
||||||
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
|
connect(_color, &QAbstractButton::clicked, this, &ImportHeightfieldTool::selectColorFile);
|
||||||
}
|
|
||||||
|
connect(Application::getInstance()->getMetavoxels(), &MetavoxelSystem::rendering,
|
||||||
void ImportHeightfieldTool::render() {
|
this, &ImportHeightfieldTool::renderPreview);
|
||||||
HeightfieldTool::render();
|
|
||||||
_preview.render(_translation->getValue(), _translation->getSingleStep());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImportHeightfieldTool::apply() {
|
void ImportHeightfieldTool::apply() {
|
||||||
|
@ -966,7 +991,7 @@ void ImportHeightfieldTool::apply() {
|
||||||
QByteArray color;
|
QByteArray color;
|
||||||
if (buffer->getColor().isEmpty()) {
|
if (buffer->getColor().isEmpty()) {
|
||||||
const int WHITE_VALUE = 0xFF;
|
const int WHITE_VALUE = 0xFF;
|
||||||
color = QByteArray(height.size() * HeightfieldData::COLOR_BYTES, WHITE_VALUE);
|
color = QByteArray(height.size() * DataBlock::COLOR_BYTES, WHITE_VALUE);
|
||||||
} else {
|
} else {
|
||||||
color = buffer->getUnextendedColor();
|
color = buffer->getUnextendedColor();
|
||||||
}
|
}
|
||||||
|
@ -975,10 +1000,10 @@ void ImportHeightfieldTool::apply() {
|
||||||
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
AttributeRegistry::getInstance()->getHeightfieldColorAttribute(), encodeInline(colorPointer))));
|
||||||
|
|
||||||
int size = glm::sqrt(height.size()) + HeightfieldBuffer::SHARED_EDGE;
|
int size = glm::sqrt(height.size()) + HeightfieldBuffer::SHARED_EDGE;
|
||||||
QByteArray texture(size * size, 0);
|
QByteArray material(size * size, 0);
|
||||||
HeightfieldTextureDataPointer texturePointer(new HeightfieldTextureData(texture));
|
HeightfieldMaterialDataPointer materialPointer(new HeightfieldMaterialData(material));
|
||||||
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), new MetavoxelNode(AttributeValue(
|
data.setRoot(AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), new MetavoxelNode(AttributeValue(
|
||||||
AttributeRegistry::getInstance()->getHeightfieldTextureAttribute(), encodeInline(texturePointer))));
|
AttributeRegistry::getInstance()->getHeightfieldMaterialAttribute(), encodeInline(materialPointer))));
|
||||||
|
|
||||||
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
MetavoxelEditMessage message = { QVariant::fromValue(SetDataEdit(
|
||||||
_translation->getValue() + buffer->getTranslation() * scale, data)) };
|
_translation->getValue() + buffer->getTranslation() * scale, data)) };
|
||||||
|
@ -1032,22 +1057,22 @@ void ImportHeightfieldTool::updatePreview() {
|
||||||
int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI);
|
int rows = qMin(heightSize - offsetY, _heightImage.height() - extendedI);
|
||||||
int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ);
|
int columns = qMin(heightSize - offsetX, _heightImage.width() - extendedJ);
|
||||||
for (int y = 0; y < rows; y++) {
|
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;
|
char* dest = height.data() + (y + offsetY) * heightSize + offsetX;
|
||||||
for (int x = 0; x < columns; x++) {
|
for (int x = 0; x < columns; x++) {
|
||||||
*dest++ = *src;
|
*dest++ = *src;
|
||||||
src += HeightfieldData::COLOR_BYTES;
|
src += DataBlock::COLOR_BYTES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
QByteArray color;
|
QByteArray color;
|
||||||
if (!_colorImage.isNull()) {
|
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));
|
rows = qMax(0, qMin(colorSize, _colorImage.height() - i));
|
||||||
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
|
columns = qMax(0, qMin(colorSize, _colorImage.width() - j));
|
||||||
for (int y = 0; y < rows; y++) {
|
for (int y = 0; y < rows; y++) {
|
||||||
memcpy(color.data() + y * colorSize * HeightfieldData::COLOR_BYTES,
|
memcpy(color.data() + y * colorSize * DataBlock::COLOR_BYTES,
|
||||||
_colorImage.scanLine(i + y) + j * HeightfieldData::COLOR_BYTES,
|
_colorImage.scanLine(i + y) + j * DataBlock::COLOR_BYTES,
|
||||||
columns * HeightfieldData::COLOR_BYTES);
|
columns * DataBlock::COLOR_BYTES);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
buffers.append(BufferDataPointer(new HeightfieldBuffer(glm::vec3(x, 0.0f, z), 1.0f, height, color)));
|
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);
|
_preview.setBuffers(buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ImportHeightfieldTool::renderPreview() {
|
||||||
|
if (isVisible()) {
|
||||||
|
_preview.render(_translation->getValue(), _translation->getSingleStep());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) :
|
EraseHeightfieldTool::EraseHeightfieldTool(MetavoxelEditor* editor) :
|
||||||
HeightfieldTool(editor, "Erase Heightfield") {
|
HeightfieldTool(editor, "Erase Heightfield") {
|
||||||
|
|
||||||
|
@ -1177,25 +1208,190 @@ QVariant HeightfieldColorBrushTool::createEdit(bool alternate) {
|
||||||
alternate ? QColor() : _color->getColor()));
|
alternate ? QColor() : _color->getColor()));
|
||||||
}
|
}
|
||||||
|
|
||||||
HeightfieldTextureBrushTool::HeightfieldTextureBrushTool(MetavoxelEditor* editor) :
|
HeightfieldMaterialBrushTool::HeightfieldMaterialBrushTool(MetavoxelEditor* editor) :
|
||||||
HeightfieldBrushTool(editor, "Texture Brush") {
|
HeightfieldBrushTool(editor, "Material Brush") {
|
||||||
|
|
||||||
_form->addRow(_textureEditor = new SharedObjectEditor(&HeightfieldTexture::staticMetaObject, false));
|
_form->addRow(_materialEditor = new SharedObjectEditor(&MaterialObject::staticMetaObject, false));
|
||||||
connect(_textureEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldTextureBrushTool::updateTexture);
|
connect(_materialEditor, &SharedObjectEditor::objectChanged, this, &HeightfieldMaterialBrushTool::updateTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
QVariant HeightfieldTextureBrushTool::createEdit(bool alternate) {
|
QVariant HeightfieldMaterialBrushTool::createEdit(bool alternate) {
|
||||||
if (alternate) {
|
if (alternate) {
|
||||||
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), SharedObjectPointer(), QColor()));
|
||||||
} else {
|
} else {
|
||||||
SharedObjectPointer texture = _textureEditor->getObject();
|
SharedObjectPointer material = _materialEditor->getObject();
|
||||||
_textureEditor->detachObject();
|
_materialEditor->detachObject();
|
||||||
return QVariant::fromValue(PaintHeightfieldTextureEdit(_position, _radius->value(), texture,
|
return QVariant::fromValue(PaintHeightfieldMaterialEdit(_position, _radius->value(), material,
|
||||||
_texture ? _texture->getAverageColor() : QColor()));
|
_texture ? _texture->getAverageColor() : QColor()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HeightfieldTextureBrushTool::updateTexture() {
|
void HeightfieldMaterialBrushTool::updateTexture() {
|
||||||
HeightfieldTexture* texture = static_cast<HeightfieldTexture*>(_textureEditor->getObject().data());
|
MaterialObject* material = static_cast<MaterialObject*>(_materialEditor->getObject().data());
|
||||||
_texture = Application::getInstance()->getTextureCache()->getTexture(texture->getURL());
|
_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 deleteSelectedAttribute();
|
||||||
void centerGridPosition();
|
void centerGridPosition();
|
||||||
void alignGridPosition();
|
void alignGridPosition();
|
||||||
|
void updateAttributes(const QString& select = QString());
|
||||||
void updateTool();
|
void updateTool();
|
||||||
|
|
||||||
void simulate(float deltaTime);
|
void simulate(float deltaTime);
|
||||||
|
@ -65,11 +66,11 @@ private slots:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
void addTool(MetavoxelTool* tool);
|
void addTool(MetavoxelTool* tool);
|
||||||
void updateAttributes(const QString& select = QString());
|
|
||||||
MetavoxelTool* getActiveTool() const;
|
MetavoxelTool* getActiveTool() const;
|
||||||
|
|
||||||
QListWidget* _attributes;
|
QListWidget* _attributes;
|
||||||
QPushButton* _deleteAttribute;
|
QPushButton* _deleteAttribute;
|
||||||
|
QCheckBox* _showAll;
|
||||||
|
|
||||||
QComboBox* _gridPlane;
|
QComboBox* _gridPlane;
|
||||||
QDoubleSpinBox* _gridSpacing;
|
QDoubleSpinBox* _gridSpacing;
|
||||||
|
@ -90,10 +91,12 @@ class MetavoxelTool : public QWidget {
|
||||||
|
|
||||||
public:
|
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 getUsesValue() const { return _usesValue; }
|
||||||
|
|
||||||
|
bool isUserFacing() const { return _userFacing; }
|
||||||
|
|
||||||
virtual bool appliesTo(const AttributePointer& attribute) const;
|
virtual bool appliesTo(const AttributePointer& attribute) const;
|
||||||
|
|
||||||
virtual void simulate(float deltaTime);
|
virtual void simulate(float deltaTime);
|
||||||
|
@ -105,24 +108,30 @@ protected:
|
||||||
|
|
||||||
MetavoxelEditor* _editor;
|
MetavoxelEditor* _editor;
|
||||||
bool _usesValue;
|
bool _usesValue;
|
||||||
|
bool _userFacing;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Allows setting the value of a region by dragging out a box.
|
/// Base class for tools that allow dragging out a 3D box.
|
||||||
class BoxSetTool : public MetavoxelTool {
|
class BoxTool : public MetavoxelTool {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
BoxSetTool(MetavoxelEditor* editor);
|
BoxTool(MetavoxelEditor* editor, const QString& name, bool usesValue = true, bool userFacing = true);
|
||||||
|
|
||||||
virtual void render();
|
virtual void render();
|
||||||
|
|
||||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
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:
|
private:
|
||||||
|
|
||||||
void resetState();
|
void resetState();
|
||||||
void applyValue(const glm::vec3& minimum, const glm::vec3& maximum);
|
|
||||||
|
|
||||||
enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE };
|
enum State { HOVERING_STATE, DRAGGING_STATE, RAISING_STATE };
|
||||||
|
|
||||||
|
@ -134,6 +143,21 @@ private:
|
||||||
float _height; ///< the selection height
|
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.
|
/// Allows setting the value across the entire space.
|
||||||
class GlobalSetTool : public MetavoxelTool {
|
class GlobalSetTool : public MetavoxelTool {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -259,8 +283,6 @@ public:
|
||||||
|
|
||||||
ImportHeightfieldTool(MetavoxelEditor* editor);
|
ImportHeightfieldTool(MetavoxelEditor* editor);
|
||||||
|
|
||||||
virtual void render();
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void apply();
|
virtual void apply();
|
||||||
|
@ -270,6 +292,7 @@ private slots:
|
||||||
void selectHeightFile();
|
void selectHeightFile();
|
||||||
void selectColorFile();
|
void selectColorFile();
|
||||||
void updatePreview();
|
void updatePreview();
|
||||||
|
void renderPreview();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -363,12 +386,12 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Allows texturing parts of the heightfield.
|
/// Allows texturing parts of the heightfield.
|
||||||
class HeightfieldTextureBrushTool : public HeightfieldBrushTool {
|
class HeightfieldMaterialBrushTool : public HeightfieldBrushTool {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HeightfieldTextureBrushTool(MetavoxelEditor* editor);
|
HeightfieldMaterialBrushTool(MetavoxelEditor* editor);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
@ -380,7 +403,125 @@ private slots:
|
||||||
|
|
||||||
private:
|
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;
|
QSharedPointer<NetworkTexture> _texture;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -29,14 +29,17 @@ class QScriptEngine;
|
||||||
class QScriptValue;
|
class QScriptValue;
|
||||||
|
|
||||||
class Attribute;
|
class Attribute;
|
||||||
|
class DataBlock;
|
||||||
class HeightfieldColorData;
|
class HeightfieldColorData;
|
||||||
class HeightfieldData;
|
|
||||||
class HeightfieldHeightData;
|
class HeightfieldHeightData;
|
||||||
class HeightfieldTextureData;
|
class HeightfieldMaterialData;
|
||||||
class MetavoxelData;
|
class MetavoxelData;
|
||||||
class MetavoxelLOD;
|
class MetavoxelLOD;
|
||||||
class MetavoxelNode;
|
class MetavoxelNode;
|
||||||
class MetavoxelStreamState;
|
class MetavoxelStreamState;
|
||||||
|
class VoxelColorData;
|
||||||
|
class VoxelHermiteData;
|
||||||
|
class VoxelMaterialData;
|
||||||
|
|
||||||
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
typedef SharedObjectPointerTemplate<Attribute> AttributePointer;
|
||||||
|
|
||||||
|
@ -100,14 +103,23 @@ public:
|
||||||
/// Returns a reference to the standard "spannerMask" attribute.
|
/// Returns a reference to the standard "spannerMask" attribute.
|
||||||
const AttributePointer& getSpannerMaskAttribute() const { return _spannerMaskAttribute; }
|
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; }
|
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; }
|
const AttributePointer& getHeightfieldColorAttribute() const { return _heightfieldColorAttribute; }
|
||||||
|
|
||||||
/// Returns a reference to the standard HeightfieldDataPointer "heightfieldTexture" attribute.
|
/// Returns a reference to the standard HeightfieldMaterialDataPointer "heightfieldMaterial" attribute.
|
||||||
const AttributePointer& getHeightfieldTextureAttribute() const { return _heightfieldTextureAttribute; }
|
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:
|
private:
|
||||||
|
|
||||||
|
@ -126,7 +138,10 @@ private:
|
||||||
AttributePointer _spannerMaskAttribute;
|
AttributePointer _spannerMaskAttribute;
|
||||||
AttributePointer _heightfieldAttribute;
|
AttributePointer _heightfieldAttribute;
|
||||||
AttributePointer _heightfieldColorAttribute;
|
AttributePointer _heightfieldColorAttribute;
|
||||||
AttributePointer _heightfieldTextureAttribute;
|
AttributePointer _heightfieldMaterialAttribute;
|
||||||
|
AttributePointer _voxelColorAttribute;
|
||||||
|
AttributePointer _voxelMaterialAttribute;
|
||||||
|
AttributePointer _voxelHermiteAttribute;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Converts a value to a void pointer.
|
/// Converts a value to a void pointer.
|
||||||
|
@ -206,6 +221,7 @@ Q_DECLARE_METATYPE(OwnedAttributeValue)
|
||||||
class Attribute : public SharedObject {
|
class Attribute : public SharedObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier)
|
Q_PROPERTY(float lodThresholdMultiplier MEMBER _lodThresholdMultiplier)
|
||||||
|
Q_PROPERTY(bool userFacing MEMBER _userFacing)
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -219,6 +235,9 @@ public:
|
||||||
float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; }
|
float getLODThresholdMultiplier() const { return _lodThresholdMultiplier; }
|
||||||
void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; }
|
void setLODThresholdMultiplier(float multiplier) { _lodThresholdMultiplier = multiplier; }
|
||||||
|
|
||||||
|
bool isUserFacing() const { return _userFacing; }
|
||||||
|
void setUserFacing(bool userFacing) { _userFacing = userFacing; }
|
||||||
|
|
||||||
void* create() const { return create(getDefaultValue()); }
|
void* create() const { return create(getDefaultValue()); }
|
||||||
virtual void* create(void* copy) const = 0;
|
virtual void* create(void* copy) const = 0;
|
||||||
virtual void destroy(void* value) const = 0;
|
virtual void destroy(void* value) const = 0;
|
||||||
|
@ -283,6 +302,7 @@ public:
|
||||||
private:
|
private:
|
||||||
|
|
||||||
float _lodThresholdMultiplier;
|
float _lodThresholdMultiplier;
|
||||||
|
bool _userFacing;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A simple attribute class that stores its values inline.
|
/// A simple attribute class that stores its values inline.
|
||||||
|
@ -396,6 +416,9 @@ public:
|
||||||
/// Packs a normal into an RGB value.
|
/// Packs a normal into an RGB value.
|
||||||
QRgb packNormal(const glm::vec3& normal);
|
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.
|
/// Unpacks a normal from an RGB value.
|
||||||
glm::vec3 unpackNormal(QRgb value);
|
glm::vec3 unpackNormal(QRgb value);
|
||||||
|
|
||||||
|
@ -435,21 +458,18 @@ public:
|
||||||
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
virtual AttributeValue inherit(const AttributeValue& parentValue) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef QExplicitlySharedDataPointer<HeightfieldData> HeightfieldDataPointer;
|
typedef QExplicitlySharedDataPointer<DataBlock> DataBlockPointer;
|
||||||
|
|
||||||
/// Contains a block of heightfield data.
|
/// Base class for blocks of data.
|
||||||
class HeightfieldData : public QSharedData {
|
class DataBlock : public QSharedData {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
static const int COLOR_BYTES = 3;
|
static const int COLOR_BYTES = 3;
|
||||||
|
|
||||||
HeightfieldData(const QByteArray& contents = QByteArray());
|
virtual ~DataBlock();
|
||||||
virtual ~HeightfieldData();
|
|
||||||
|
|
||||||
const QByteArray& getContents() const { return _contents; }
|
void setDeltaData(const DataBlockPointer& deltaData) { _deltaData = deltaData; }
|
||||||
|
const DataBlockPointer& getDeltaData() const { return _deltaData; }
|
||||||
void setDeltaData(const HeightfieldDataPointer& deltaData) { _deltaData = deltaData; }
|
|
||||||
const HeightfieldDataPointer& getDeltaData() const { return _deltaData; }
|
|
||||||
|
|
||||||
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
|
void setEncodedDelta(const QByteArray& encodedDelta) { _encodedDelta = encodedDelta; }
|
||||||
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
|
const QByteArray& getEncodedDelta() const { return _encodedDelta; }
|
||||||
|
@ -458,17 +478,16 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
QByteArray _contents;
|
|
||||||
QByteArray _encoded;
|
QByteArray _encoded;
|
||||||
QMutex _encodedMutex;
|
QMutex _encodedMutex;
|
||||||
|
|
||||||
HeightfieldDataPointer _deltaData;
|
DataBlockPointer _deltaData;
|
||||||
QByteArray _encodedDelta;
|
QByteArray _encodedDelta;
|
||||||
QMutex _encodedDeltaMutex;
|
QMutex _encodedDeltaMutex;
|
||||||
|
|
||||||
class EncodedSubdivision {
|
class EncodedSubdivision {
|
||||||
public:
|
public:
|
||||||
HeightfieldDataPointer ancestor;
|
DataBlockPointer ancestor;
|
||||||
QByteArray data;
|
QByteArray data;
|
||||||
};
|
};
|
||||||
QVector<EncodedSubdivision> _encodedSubdivisions;
|
QVector<EncodedSubdivision> _encodedSubdivisions;
|
||||||
|
@ -478,7 +497,7 @@ protected:
|
||||||
typedef QExplicitlySharedDataPointer<HeightfieldHeightData> HeightfieldHeightDataPointer;
|
typedef QExplicitlySharedDataPointer<HeightfieldHeightData> HeightfieldHeightDataPointer;
|
||||||
|
|
||||||
/// Contains a block of heightfield height data.
|
/// Contains a block of heightfield height data.
|
||||||
class HeightfieldHeightData : public HeightfieldData {
|
class HeightfieldHeightData : public DataBlock {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
HeightfieldHeightData(const QByteArray& contents);
|
HeightfieldHeightData(const QByteArray& contents);
|
||||||
|
@ -487,6 +506,8 @@ public:
|
||||||
HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor,
|
HeightfieldHeightData(Bitstream& in, int bytes, const HeightfieldHeightDataPointer& ancestor,
|
||||||
const glm::vec3& minimum, float size);
|
const glm::vec3& minimum, float size);
|
||||||
|
|
||||||
|
const QByteArray& getContents() const { return _contents; }
|
||||||
|
|
||||||
void write(Bitstream& out);
|
void write(Bitstream& out);
|
||||||
void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference);
|
void writeDelta(Bitstream& out, const HeightfieldHeightDataPointer& reference);
|
||||||
void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor,
|
void writeSubdivided(Bitstream& out, const HeightfieldHeightDataPointer& ancestor,
|
||||||
|
@ -496,75 +517,8 @@ private:
|
||||||
|
|
||||||
void read(Bitstream& in, int bytes);
|
void read(Bitstream& in, int bytes);
|
||||||
void set(const QImage& image);
|
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);
|
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;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An attribute that stores heightfield data.
|
/// An attribute that stores heightfield data.
|
||||||
|
@ -584,6 +538,33 @@ public:
|
||||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
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.
|
/// An attribute that stores heightfield colors.
|
||||||
class HeightfieldColorAttribute : public InlineAttribute<HeightfieldColorDataPointer> {
|
class HeightfieldColorAttribute : public InlineAttribute<HeightfieldColorDataPointer> {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -601,13 +582,194 @@ public:
|
||||||
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
virtual bool merge(void*& parent, void* children[], bool postRead = false) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// An attribute that stores heightfield textures.
|
typedef QExplicitlySharedDataPointer<HeightfieldMaterialData> HeightfieldMaterialDataPointer;
|
||||||
class HeightfieldTextureAttribute : public InlineAttribute<HeightfieldTextureDataPointer> {
|
|
||||||
|
/// 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
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
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 read(Bitstream& in, void*& value, bool isLeaf) const;
|
||||||
virtual void write(Bitstream& out, 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) {
|
color(color) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class PaintHeightfieldColorEditVisitor : public MetavoxelVisitor {
|
class PaintHeightfieldMaterialEditVisitor : public MetavoxelVisitor {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit);
|
PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
|
||||||
|
const SharedObjectPointer& material, const QColor& color);
|
||||||
|
|
||||||
virtual int visit(MetavoxelInfo& info);
|
virtual int visit(MetavoxelInfo& info);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
PaintHeightfieldColorEdit _edit;
|
glm::vec3 _position;
|
||||||
|
float _radius;
|
||||||
|
SharedObjectPointer _material;
|
||||||
|
QColor _color;
|
||||||
Box _bounds;
|
Box _bounds;
|
||||||
};
|
};
|
||||||
|
|
||||||
PaintHeightfieldColorEditVisitor::PaintHeightfieldColorEditVisitor(const PaintHeightfieldColorEdit& edit) :
|
PaintHeightfieldMaterialEditVisitor::PaintHeightfieldMaterialEditVisitor(const glm::vec3& position, float radius,
|
||||||
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute(),
|
const SharedObjectPointer& material, const QColor& color) :
|
||||||
QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute()),
|
MetavoxelVisitor(QVector<AttributePointer>() << AttributeRegistry::getInstance()->getHeightfieldColorAttribute() <<
|
||||||
_edit(edit) {
|
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);
|
glm::vec3 extents(_radius, _radius, _radius);
|
||||||
_bounds = Box(_edit.position - extents, _edit.position + extents);
|
_bounds = Box(_position - extents, _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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
||||||
|
@ -539,106 +456,624 @@ static QHash<uchar, int> countIndices(const QByteArray& contents) {
|
||||||
return counts;
|
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)) {
|
if (!info.getBounds().intersects(_bounds)) {
|
||||||
return STOP_RECURSION;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
if (!info.isLeaf) {
|
if (!info.isLeaf) {
|
||||||
return DEFAULT_ORDER;
|
return DEFAULT_ORDER;
|
||||||
}
|
}
|
||||||
HeightfieldTextureDataPointer pointer = info.inputValues.at(0).getInlineValue<HeightfieldTextureDataPointer>();
|
HeightfieldColorDataPointer colorPointer = info.inputValues.at(0).getInlineValue<HeightfieldColorDataPointer>();
|
||||||
if (!pointer) {
|
if (colorPointer) {
|
||||||
return STOP_RECURSION;
|
QByteArray contents(colorPointer->getContents());
|
||||||
}
|
int size = glm::sqrt((float)contents.size() / DataBlock::COLOR_BYTES);
|
||||||
QVector<SharedObjectPointer> textures = pointer->getTextures();
|
int highest = size - 1;
|
||||||
QByteArray contents(pointer->getContents());
|
float heightScale = size / info.size;
|
||||||
uchar textureIndex = 0;
|
|
||||||
if (_edit.texture && static_cast<HeightfieldTexture*>(_edit.texture.data())->getURL().isValid()) {
|
glm::vec3 center = (_position - info.minimum) * heightScale;
|
||||||
// first look for a matching existing texture, noting the first reusable slot
|
float scaledRadius = _radius * heightScale;
|
||||||
int firstEmptyIndex = -1;
|
glm::vec3 extents(scaledRadius, scaledRadius, scaledRadius);
|
||||||
for (int i = 0; i < textures.size(); i++) {
|
|
||||||
const SharedObjectPointer& texture = textures.at(i);
|
glm::vec3 start = glm::floor(center - extents);
|
||||||
if (texture) {
|
glm::vec3 end = glm::ceil(center + extents);
|
||||||
if (texture->equals(_edit.texture.data())) {
|
|
||||||
textureIndex = i + 1;
|
// paint all points within the radius
|
||||||
break;
|
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 (changed) {
|
||||||
if (textureIndex == 0) {
|
HeightfieldColorDataPointer newPointer(new HeightfieldColorData(contents));
|
||||||
if (firstEmptyIndex != -1) {
|
info.outputValues[0] = AttributeValue(info.inputValues.at(0).getAttribute(),
|
||||||
textures[firstEmptyIndex] = _edit.texture;
|
encodeInline<HeightfieldColorDataPointer>(newPointer));
|
||||||
textureIndex = firstEmptyIndex + 1;
|
}
|
||||||
|
}
|
||||||
} else if (textures.size() < EIGHT_BIT_MAXIMUM) {
|
|
||||||
textures.append(_edit.texture);
|
HeightfieldMaterialDataPointer materialPointer = info.inputValues.at(1).getInlineValue<HeightfieldMaterialDataPointer>();
|
||||||
textureIndex = textures.size();
|
if (materialPointer) {
|
||||||
|
QVector<SharedObjectPointer> materials = materialPointer->getMaterials();
|
||||||
} else {
|
QByteArray contents(materialPointer->getContents());
|
||||||
// last resort: find the least-used texture and remove it
|
uchar materialIndex = getMaterialIndex(_material, materials, contents);
|
||||||
QHash<uchar, int> counts = countIndices(contents);
|
int size = glm::sqrt((float)contents.size());
|
||||||
int lowestCount = INT_MAX;
|
int highest = size - 1;
|
||||||
for (QHash<uchar, int>::const_iterator it = counts.constBegin(); it != counts.constEnd(); it++) {
|
float heightScale = highest / info.size;
|
||||||
if (it.value() < lowestCount) {
|
|
||||||
textureIndex = it.key();
|
glm::vec3 center = (_position - info.minimum) * heightScale;
|
||||||
lowestCount = it.value();
|
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;
|
return STOP_RECURSION;
|
||||||
}
|
}
|
||||||
|
|
||||||
void PaintHeightfieldTextureEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
void PaintHeightfieldColorEdit::apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const {
|
||||||
PaintHeightfieldTextureEditVisitor visitor(*this);
|
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);
|
data.guide(visitor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,23 +241,98 @@ public:
|
||||||
|
|
||||||
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit)
|
DECLARE_STREAMABLE_METATYPE(PaintHeightfieldColorEdit)
|
||||||
|
|
||||||
/// An edit that sets a region of a heightfield texture.
|
/// An edit that sets a region of a heightfield material.
|
||||||
class PaintHeightfieldTextureEdit : public MetavoxelEdit {
|
class PaintHeightfieldMaterialEdit : public MetavoxelEdit {
|
||||||
STREAMABLE
|
STREAMABLE
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
STREAM glm::vec3 position;
|
STREAM glm::vec3 position;
|
||||||
STREAM float radius;
|
STREAM float radius;
|
||||||
STREAM SharedObjectPointer texture;
|
STREAM SharedObjectPointer material;
|
||||||
STREAM QColor averageColor;
|
STREAM QColor averageColor;
|
||||||
|
|
||||||
PaintHeightfieldTextureEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
PaintHeightfieldMaterialEdit(const glm::vec3& position = glm::vec3(), float radius = 0.0f,
|
||||||
const SharedObjectPointer& texture = SharedObjectPointer(), const QColor& averageColor = QColor());
|
const SharedObjectPointer& material = SharedObjectPointer(), const QColor& averageColor = QColor());
|
||||||
|
|
||||||
virtual void apply(MetavoxelData& data, const WeakSharedObjectHash& objects) const;
|
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
|
#endif // hifi_MetavoxelMessages_h
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#include <fstream> // to load voxels from file
|
#include <fstream> // to load voxels from file
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
|
#include <QVector>
|
||||||
|
|
||||||
#include <GeometryUtil.h>
|
#include <GeometryUtil.h>
|
||||||
#include <OctalCode.h>
|
#include <OctalCode.h>
|
||||||
|
@ -744,6 +745,12 @@ public:
|
||||||
bool found;
|
bool found;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class ContentArgs {
|
||||||
|
public:
|
||||||
|
AACube cube;
|
||||||
|
CubeList* cubes;
|
||||||
|
};
|
||||||
|
|
||||||
bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
|
bool findCapsulePenetrationOp(OctreeElement* element, void* extraData) {
|
||||||
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
|
CapsuleArgs* args = static_cast<CapsuleArgs*>(extraData);
|
||||||
|
|
||||||
|
@ -786,6 +793,39 @@ bool findShapeCollisionsOp(OctreeElement* element, void* extraData) {
|
||||||
return false;
|
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,
|
bool Octree::findCapsulePenetration(const glm::vec3& start, const glm::vec3& end, float radius,
|
||||||
glm::vec3& penetration, Octree::lockType lockType, bool* accurateResult) {
|
glm::vec3& penetration, Octree::lockType lockType, bool* accurateResult) {
|
||||||
|
|
||||||
|
@ -854,6 +894,16 @@ bool Octree::findShapeCollisions(const Shape* shape, CollisionList& collisions,
|
||||||
return args.found;
|
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 {
|
class GetElementEnclosingArgs {
|
||||||
public:
|
public:
|
||||||
OctreeElement* element;
|
OctreeElement* element;
|
||||||
|
|
|
@ -33,6 +33,7 @@ class Shape;
|
||||||
|
|
||||||
#include <CollisionInfo.h>
|
#include <CollisionInfo.h>
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QReadWriteLock>
|
#include <QReadWriteLock>
|
||||||
|
|
||||||
|
@ -47,6 +48,7 @@ public:
|
||||||
// Callback function, for recuseTreeWithOperation
|
// Callback function, for recuseTreeWithOperation
|
||||||
typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData);
|
typedef bool (*RecurseOctreeOperation)(OctreeElement* element, void* extraData);
|
||||||
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
typedef enum {GRADIENT, RANDOM, NATURAL} creationMode;
|
||||||
|
typedef QHash<quint64, AACube> CubeList;
|
||||||
|
|
||||||
const bool NO_EXISTS_BITS = false;
|
const bool NO_EXISTS_BITS = false;
|
||||||
const bool WANT_EXISTS_BITS = true;
|
const bool WANT_EXISTS_BITS = true;
|
||||||
|
@ -308,6 +310,8 @@ public:
|
||||||
bool findShapeCollisions(const Shape* shape, CollisionList& collisions,
|
bool findShapeCollisions(const Shape* shape, CollisionList& collisions,
|
||||||
Octree::lockType = Octree::TryLock, bool* accurateResult = NULL);
|
Octree::lockType = Octree::TryLock, bool* accurateResult = NULL);
|
||||||
|
|
||||||
|
bool findContentInCube(const AACube& cube, CubeList& cubes);
|
||||||
|
|
||||||
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,
|
OctreeElement* getElementEnclosingPoint(const glm::vec3& point,
|
||||||
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
|
Octree::lockType lockType = Octree::TryLock, bool* accurateResult = NULL);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,8 @@ public:
|
||||||
PhysicsEntity();
|
PhysicsEntity();
|
||||||
virtual ~PhysicsEntity();
|
virtual ~PhysicsEntity();
|
||||||
|
|
||||||
|
virtual void stepForward(float deltaTime) { }
|
||||||
|
|
||||||
void setTranslation(const glm::vec3& translation);
|
void setTranslation(const glm::vec3& translation);
|
||||||
void setRotation(const glm::quat& rotation);
|
void setRotation(const glm::quat& rotation);
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,10 @@ PhysicsSimulation::PhysicsSimulation() : _translation(0.0f), _frameCount(0), _en
|
||||||
}
|
}
|
||||||
|
|
||||||
PhysicsSimulation::~PhysicsSimulation() {
|
PhysicsSimulation::~PhysicsSimulation() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PhysicsSimulation::clear() {
|
||||||
// entities have a backpointer to this simulator that must be cleaned up
|
// entities have a backpointer to this simulator that must be cleaned up
|
||||||
int numEntities = _otherEntities.size();
|
int numEntities = _otherEntities.size();
|
||||||
for (int i = 0; i < numEntities; ++i) {
|
for (int i = 0; i < numEntities; ++i) {
|
||||||
|
@ -43,6 +47,9 @@ PhysicsSimulation::~PhysicsSimulation() {
|
||||||
// but Ragdolls do not
|
// but Ragdolls do not
|
||||||
_ragdoll = NULL;
|
_ragdoll = NULL;
|
||||||
_otherRagdolls.clear();
|
_otherRagdolls.clear();
|
||||||
|
|
||||||
|
// contacts have backpointers to shapes so we clear them
|
||||||
|
_contacts.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSimulation::setRagdoll(Ragdoll* ragdoll) {
|
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;
|
const float OTHER_RAGDOLL_MASS_SCALE = 10.0f;
|
||||||
|
|
||||||
bool PhysicsSimulation::addRagdoll(Ragdoll* doll) {
|
bool PhysicsSimulation::addRagdoll(Ragdoll* doll) {
|
||||||
|
@ -195,7 +214,7 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter
|
||||||
quint64 startTime = now;
|
quint64 startTime = now;
|
||||||
quint64 expiry = startTime + maxUsec;
|
quint64 expiry = startTime + maxUsec;
|
||||||
|
|
||||||
moveRagdolls(deltaTime);
|
integrate(deltaTime);
|
||||||
enforceContacts();
|
enforceContacts();
|
||||||
int numDolls = _otherRagdolls.size();
|
int numDolls = _otherRagdolls.size();
|
||||||
{
|
{
|
||||||
|
@ -238,8 +257,12 @@ void PhysicsSimulation::stepForward(float deltaTime, float minError, int maxIter
|
||||||
pruneContacts();
|
pruneContacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsSimulation::moveRagdolls(float deltaTime) {
|
void PhysicsSimulation::integrate(float deltaTime) {
|
||||||
PerformanceTimer perfTimer("integrate");
|
PerformanceTimer perfTimer("integrate");
|
||||||
|
int numEntities = _otherEntities.size();
|
||||||
|
for (int i = 0; i < numEntities; ++i) {
|
||||||
|
_otherEntities[i]->stepForward(deltaTime);
|
||||||
|
}
|
||||||
_ragdoll->stepForward(deltaTime);
|
_ragdoll->stepForward(deltaTime);
|
||||||
int numDolls = _otherRagdolls.size();
|
int numDolls = _otherRagdolls.size();
|
||||||
for (int i = 0; i < numDolls; ++i) {
|
for (int i = 0; i < numDolls; ++i) {
|
||||||
|
|
|
@ -27,6 +27,8 @@ public:
|
||||||
|
|
||||||
PhysicsSimulation();
|
PhysicsSimulation();
|
||||||
~PhysicsSimulation();
|
~PhysicsSimulation();
|
||||||
|
|
||||||
|
void clear();
|
||||||
|
|
||||||
void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
void setTranslation(const glm::vec3& translation) { _translation = translation; }
|
||||||
const glm::vec3& getTranslation() const { return _translation; }
|
const glm::vec3& getTranslation() const { return _translation; }
|
||||||
|
@ -39,6 +41,7 @@ public:
|
||||||
|
|
||||||
void removeEntity(PhysicsEntity* entity);
|
void removeEntity(PhysicsEntity* entity);
|
||||||
void removeShapes(const 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
|
/// \return true if doll was added to or is already in the list
|
||||||
bool addRagdoll(Ragdoll* doll);
|
bool addRagdoll(Ragdoll* doll);
|
||||||
|
@ -52,7 +55,7 @@ public:
|
||||||
void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec);
|
void stepForward(float deltaTime, float minError, int maxIterations, quint64 maxUsec);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void moveRagdolls(float deltaTime);
|
void integrate(float deltaTime);
|
||||||
|
|
||||||
/// \return true if main ragdoll collides with other avatar
|
/// \return true if main ragdoll collides with other avatar
|
||||||
bool computeCollisions();
|
bool computeCollisions();
|
||||||
|
|
|
@ -81,17 +81,22 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// these ctors are protected (used by derived classes only)
|
// 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();
|
_id = getNextID();
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape(Type type, const glm::vec3& position)
|
Shape(Type type, const glm::vec3& position) :
|
||||||
: _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation() {
|
_type(type), _owningEntity(NULL),
|
||||||
|
_boundingRadius(0.f), _translation(position),
|
||||||
|
_rotation(), _mass(MAX_SHAPE_MASS) {
|
||||||
_id = getNextID();
|
_id = getNextID();
|
||||||
}
|
}
|
||||||
|
|
||||||
Shape(Type type, const glm::vec3& position, const glm::quat& rotation)
|
Shape(Type type, const glm::vec3& position, const glm::quat& rotation) : _type(type), _owningEntity(NULL),
|
||||||
: _type(type), _owningEntity(NULL), _boundingRadius(0.f), _translation(position), _rotation(rotation) {
|
_boundingRadius(0.f), _translation(position),
|
||||||
|
_rotation(rotation), _mass(MAX_SHAPE_MASS) {
|
||||||
_id = getNextID();
|
_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
|
// 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
|
// points to compute the penetration direction. Instead we use the unitary components of
|
||||||
// cubeContact.
|
// cubeContact.
|
||||||
direction = cubeContact / halfCubeSide;
|
glm::modf(cubeContact / halfCubeSide, direction);
|
||||||
glm::modf(BA, direction);
|
|
||||||
lengthDirection = glm::length(direction);
|
lengthDirection = glm::length(direction);
|
||||||
} else if (lengthDirection > sphereRadius) {
|
} else if (lengthDirection > sphereRadius) {
|
||||||
collisions.deleteLastCollision();
|
collisions.deleteLastCollision();
|
||||||
|
|
|
@ -27,5 +27,5 @@ bool ShutdownEventListener::nativeEventFilter(const QByteArray &eventType, void*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue