mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 12:28:02 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
5f8c5d36da
12 changed files with 590 additions and 509 deletions
|
@ -68,7 +68,8 @@ colors[4] = { red: 236, green: 174, blue: 0 };
|
||||||
colors[5] = { red: 234, green: 133, blue: 0 };
|
colors[5] = { red: 234, green: 133, blue: 0 };
|
||||||
colors[6] = { red: 211, green: 115, blue: 0 };
|
colors[6] = { red: 211, green: 115, blue: 0 };
|
||||||
colors[7] = { red: 48, green: 116, blue: 119 };
|
colors[7] = { red: 48, green: 116, blue: 119 };
|
||||||
var numColors = 8;
|
colors[8] = { red: 31, green: 64, blue: 64 };
|
||||||
|
var numColors = 9;
|
||||||
var whichColor = -1; // Starting color is 'Copy' mode
|
var whichColor = -1; // Starting color is 'Copy' mode
|
||||||
|
|
||||||
// Create sounds for adding, deleting, recoloring voxels
|
// Create sounds for adding, deleting, recoloring voxels
|
||||||
|
@ -138,34 +139,46 @@ var linePreviewRight = Overlays.addOverlay("line3d", {
|
||||||
|
|
||||||
|
|
||||||
// these will be used below
|
// these will be used below
|
||||||
var sliderWidth = 158;
|
var sliderWidth = 154;
|
||||||
var sliderHeight = 35;
|
var sliderHeight = 37;
|
||||||
|
|
||||||
// These will be our "overlay IDs"
|
// These will be our "overlay IDs"
|
||||||
var swatches = new Array();
|
var swatches = new Array();
|
||||||
var swatchHeight = 54;
|
var swatchExtraPadding = 5;
|
||||||
var swatchWidth = 31;
|
var swatchHeight = 37;
|
||||||
var swatchesWidth = swatchWidth * numColors;
|
var swatchWidth = 27;
|
||||||
|
var swatchesWidth = swatchWidth * numColors + numColors + swatchExtraPadding * 2;
|
||||||
var swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
|
var swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
|
||||||
var swatchesY = windowDimensions.y - swatchHeight;
|
var swatchesY = windowDimensions.y - swatchHeight + 1;
|
||||||
|
|
||||||
|
var toolIconUrl = "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/tools/";
|
||||||
|
|
||||||
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
|
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
|
||||||
// location so that it displays the "selected" marker
|
// location so that it displays the "selected" marker
|
||||||
for (s = 0; s < numColors; s++) {
|
for (s = 0; s < numColors; s++) {
|
||||||
var imageFromX = 12 + (s * 27);
|
|
||||||
var imageFromY = 0;
|
var extraWidth = 0;
|
||||||
if (s == whichColor) {
|
|
||||||
imageFromY = 55;
|
if (s == 0) {
|
||||||
|
extraWidth = swatchExtraPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
var imageFromX = swatchExtraPadding - extraWidth + s * swatchWidth;
|
||||||
|
var imageFromY = swatchHeight + 1;
|
||||||
|
|
||||||
|
var swatchX = swatchExtraPadding - extraWidth + swatchesX + ((swatchWidth - 1) * s);
|
||||||
|
|
||||||
|
if (s == (numColors - 1)) {
|
||||||
|
extraWidth = swatchExtraPadding;
|
||||||
}
|
}
|
||||||
var swatchX = swatchesX + (30 * s);
|
|
||||||
|
|
||||||
swatches[s] = Overlays.addOverlay("image", {
|
swatches[s] = Overlays.addOverlay("image", {
|
||||||
x: swatchX,
|
x: swatchX,
|
||||||
y: swatchesY,
|
y: swatchesY,
|
||||||
width: swatchWidth,
|
width: swatchWidth + extraWidth,
|
||||||
height: swatchHeight,
|
height: swatchHeight,
|
||||||
subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight },
|
subImage: { x: imageFromX, y: imageFromY, width: swatchWidth + extraWidth, height: swatchHeight },
|
||||||
imageURL: "http://highfidelity-public.s3-us-west-1.amazonaws.com/images/swatches.svg",
|
imageURL: toolIconUrl + "swatches.svg",
|
||||||
color: colors[s],
|
color: colors[s],
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
visible: editToolsOn
|
visible: editToolsOn
|
||||||
|
@ -174,66 +187,41 @@ for (s = 0; s < numColors; s++) {
|
||||||
|
|
||||||
|
|
||||||
// These will be our tool palette overlays
|
// These will be our tool palette overlays
|
||||||
var numberOfTools = 5;
|
var numberOfTools = 3;
|
||||||
var toolHeight = 40;
|
var toolHeight = 50;
|
||||||
var toolWidth = 62;
|
var toolWidth = 50;
|
||||||
var toolsHeight = toolHeight * numberOfTools;
|
var toolVerticalSpacing = 4;
|
||||||
var toolsX = 0;
|
var toolsHeight = toolHeight * numberOfTools + toolVerticalSpacing * (numberOfTools - 1);
|
||||||
|
var toolsX = 8;
|
||||||
var toolsY = (windowDimensions.y - toolsHeight) / 2;
|
var toolsY = (windowDimensions.y - toolsHeight) / 2;
|
||||||
|
|
||||||
var addToolAt = 0;
|
var voxelToolAt = 0;
|
||||||
var deleteToolAt = 1;
|
var recolorToolAt = 1;
|
||||||
var recolorToolAt = 2;
|
var eyedropperToolAt = 2;
|
||||||
var eyedropperToolAt = 3;
|
|
||||||
var selectToolAt = 4;
|
|
||||||
var toolSelectedColor = { red: 255, green: 255, blue: 255 };
|
|
||||||
var notSelectedColor = { red: 128, green: 128, blue: 128 };
|
|
||||||
|
|
||||||
var addTool = Overlays.addOverlay("image", {
|
var voxelTool = Overlays.addOverlay("image", {
|
||||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||||
subImage: { x: 0, y: toolHeight * addToolAt, width: toolWidth, height: toolHeight },
|
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
||||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
imageURL: toolIconUrl + "voxel-tool.svg",
|
||||||
color: toolSelectedColor,
|
|
||||||
visible: false,
|
|
||||||
alpha: 0.9
|
|
||||||
});
|
|
||||||
|
|
||||||
var deleteTool = Overlays.addOverlay("image", {
|
|
||||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
|
||||||
subImage: { x: 0, y: toolHeight * deleteToolAt, width: toolWidth, height: toolHeight },
|
|
||||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
|
||||||
color: toolSelectedColor,
|
|
||||||
visible: false,
|
visible: false,
|
||||||
alpha: 0.9
|
alpha: 0.9
|
||||||
});
|
});
|
||||||
|
|
||||||
var recolorTool = Overlays.addOverlay("image", {
|
var recolorTool = Overlays.addOverlay("image", {
|
||||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||||
subImage: { x: 0, y: toolHeight * recolorToolAt, width: toolWidth, height: toolHeight },
|
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
||||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
imageURL: toolIconUrl + "paint-tool.svg",
|
||||||
color: toolSelectedColor,
|
|
||||||
visible: false,
|
visible: false,
|
||||||
alpha: 0.9
|
alpha: 0.9
|
||||||
});
|
});
|
||||||
|
|
||||||
var eyedropperTool = Overlays.addOverlay("image", {
|
var eyedropperTool = Overlays.addOverlay("image", {
|
||||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
||||||
subImage: { x: 0, y: toolHeight * eyedropperToolAt, width: toolWidth, height: toolHeight },
|
subImage: { x: 0, y: toolHeight, width: toolWidth, height: toolHeight },
|
||||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
imageURL: toolIconUrl + "eyedropper-tool.svg",
|
||||||
color: toolSelectedColor,
|
|
||||||
visible: false,
|
visible: false,
|
||||||
alpha: 0.9
|
alpha: 0.9
|
||||||
});
|
});
|
||||||
|
|
||||||
var selectTool = Overlays.addOverlay("image", {
|
|
||||||
x: 0, y: 0, width: toolWidth, height: toolHeight,
|
|
||||||
subImage: { x: 0, y: toolHeight * selectToolAt, width: toolWidth, height: toolHeight },
|
|
||||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/hifi-interface-tools.svg",
|
|
||||||
color: toolSelectedColor,
|
|
||||||
visible: false,
|
|
||||||
alpha: 0.9
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to
|
// This will create a couple of image overlays that make a "slider", we will demonstrate how to trap mouse messages to
|
||||||
// move the slider
|
// move the slider
|
||||||
|
@ -242,35 +230,34 @@ var selectTool = Overlays.addOverlay("image", {
|
||||||
//var sliderWidth = 158;
|
//var sliderWidth = 158;
|
||||||
//var sliderHeight = 35;
|
//var sliderHeight = 35;
|
||||||
|
|
||||||
var sliderX = swatchesX + swatchesWidth;
|
var sliderOffsetX = 17;
|
||||||
var sliderY = windowDimensions.y - sliderHeight;
|
var sliderX = swatchesX - swatchWidth - sliderOffsetX;
|
||||||
|
var sliderY = windowDimensions.y - sliderHeight + 1;
|
||||||
var slider = Overlays.addOverlay("image", {
|
var slider = Overlays.addOverlay("image", {
|
||||||
// alternate form of expressing bounds
|
// alternate form of expressing bounds
|
||||||
bounds: { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight},
|
bounds: { x: sliderX, y: sliderY, width: sliderWidth, height: sliderHeight},
|
||||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/slider.png",
|
imageURL: toolIconUrl + "voxel-size-slider-bg.svg",
|
||||||
color: { red: 255, green: 255, blue: 255},
|
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// The slider is handled in the mouse event callbacks.
|
// The slider is handled in the mouse event callbacks.
|
||||||
var isMovingSlider = false;
|
var isMovingSlider = false;
|
||||||
var thumbClickOffsetX = 0;
|
var thumbClickOffsetX = 0;
|
||||||
|
|
||||||
// This is the thumb of our slider
|
// This is the thumb of our slider
|
||||||
var minThumbX = 30; // relative to the x of the slider
|
var minThumbX = 20; // relative to the x of the slider
|
||||||
var maxThumbX = minThumbX + 65;
|
var maxThumbX = minThumbX + 90;
|
||||||
var thumbExtents = maxThumbX - minThumbX;
|
var thumbExtents = maxThumbX - minThumbX;
|
||||||
var thumbX = (minThumbX + maxThumbX) / 2;
|
var thumbX = (minThumbX + maxThumbX) / 2;
|
||||||
var thumbY = sliderY + 9;
|
var thumbOffsetY = 11;
|
||||||
|
var thumbY = sliderY + thumbOffsetY;
|
||||||
var thumb = Overlays.addOverlay("image", {
|
var thumb = Overlays.addOverlay("image", {
|
||||||
x: sliderX + thumbX,
|
x: sliderX + thumbX,
|
||||||
y: thumbY,
|
y: thumbY,
|
||||||
width: 18,
|
width: 17,
|
||||||
height: 17,
|
height: 17,
|
||||||
imageURL: "https://s3-us-west-1.amazonaws.com/highfidelity-public/images/thumb.png",
|
imageURL: toolIconUrl + "voxel-size-slider-handle.svg",
|
||||||
color: { red: 255, green: 255, blue: 255},
|
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
visible: false
|
visible: false
|
||||||
});
|
});
|
||||||
|
@ -347,11 +334,9 @@ var trackAsRecolor = false;
|
||||||
var trackAsEyedropper = false;
|
var trackAsEyedropper = false;
|
||||||
var trackAsOrbitOrPan = false;
|
var trackAsOrbitOrPan = false;
|
||||||
|
|
||||||
var addToolSelected = true;
|
var voxelToolSelected = true;
|
||||||
var deleteToolSelected = false;
|
|
||||||
var recolorToolSelected = false;
|
var recolorToolSelected = false;
|
||||||
var eyedropperToolSelected = false;
|
var eyedropperToolSelected = false;
|
||||||
var selectToolSelected = false;
|
|
||||||
|
|
||||||
function playRandomAddSound(audioOptions) {
|
function playRandomAddSound(audioOptions) {
|
||||||
if (Math.random() < 0.33) {
|
if (Math.random() < 0.33) {
|
||||||
|
@ -524,51 +509,12 @@ function showPreviewVoxel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
var guidePosition;
|
var guidePosition;
|
||||||
|
if (trackAsRecolor || recolorToolSelected || trackAsEyedropper || eyedropperToolSelected) {
|
||||||
if (trackAsDelete || deleteToolSelected) {
|
Overlays.editOverlay(voxelPreview, { visible: true });
|
||||||
guidePosition = calculateVoxelFromIntersection(intersection,"delete");
|
|
||||||
Overlays.editOverlay(voxelPreview, {
|
|
||||||
position: guidePosition,
|
|
||||||
size: guidePosition.s + zFightingSizeAdjust,
|
|
||||||
visible: true,
|
|
||||||
color: { red: 255, green: 0, blue: 0 },
|
|
||||||
solid: false,
|
|
||||||
alpha: 1
|
|
||||||
});
|
|
||||||
} else if (selectToolSelected) {
|
|
||||||
guidePosition = calculateVoxelFromIntersection(intersection,"select");
|
|
||||||
Overlays.editOverlay(voxelPreview, {
|
|
||||||
position: guidePosition,
|
|
||||||
size: guidePosition.s + zFightingSizeAdjust,
|
|
||||||
visible: true,
|
|
||||||
color: { red: 255, green: 255, blue: 0 },
|
|
||||||
solid: false,
|
|
||||||
alpha: 1
|
|
||||||
});
|
|
||||||
} else if (trackAsRecolor || recolorToolSelected || trackAsEyedropper|| eyedropperToolSelected) {
|
|
||||||
guidePosition = calculateVoxelFromIntersection(intersection,"recolor");
|
|
||||||
|
|
||||||
Overlays.editOverlay(voxelPreview, {
|
|
||||||
position: guidePosition,
|
|
||||||
size: guidePosition.s + zFightingSizeAdjust,
|
|
||||||
visible: true,
|
|
||||||
color: voxelColor,
|
|
||||||
solid: true,
|
|
||||||
alpha: 0.8
|
|
||||||
});
|
|
||||||
} else if (trackAsOrbitOrPan) {
|
} else if (trackAsOrbitOrPan) {
|
||||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||||
} else if (addToolSelected && !isExtruding) {
|
} else if (voxelToolSelected && !isExtruding) {
|
||||||
guidePosition = calculateVoxelFromIntersection(intersection,"add");
|
Overlays.editOverlay(voxelPreview, { visible: true });
|
||||||
|
|
||||||
Overlays.editOverlay(voxelPreview, {
|
|
||||||
position: guidePosition,
|
|
||||||
size: (guidePosition.s - zFightingSizeAdjust),
|
|
||||||
visible: true,
|
|
||||||
color: voxelColor,
|
|
||||||
solid: true,
|
|
||||||
alpha: 0.7
|
|
||||||
});
|
|
||||||
} else if (isExtruding) {
|
} else if (isExtruding) {
|
||||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||||
}
|
}
|
||||||
|
@ -587,23 +533,11 @@ function showPreviewLines() {
|
||||||
}
|
}
|
||||||
|
|
||||||
resultVoxel = calculateVoxelFromIntersection(intersection,"");
|
resultVoxel = calculateVoxelFromIntersection(intersection,"");
|
||||||
if (selectToolSelected) {
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||||
Overlays.editOverlay(voxelPreview, {
|
Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true });
|
||||||
position: resultVoxel,
|
Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true });
|
||||||
size: resultVoxel.s + zFightingSizeAdjust,
|
Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true });
|
||||||
visible: true,
|
Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true });
|
||||||
color: { red: 255, green: 255, blue: 0 },
|
|
||||||
lineWidth: previewLineWidth,
|
|
||||||
solid: false,
|
|
||||||
alpha: 1
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
|
||||||
Overlays.editOverlay(linePreviewTop, { position: resultVoxel.topLeft, end: resultVoxel.topRight, visible: true });
|
|
||||||
Overlays.editOverlay(linePreviewBottom, { position: resultVoxel.bottomLeft, end: resultVoxel.bottomRight, visible: true });
|
|
||||||
Overlays.editOverlay(linePreviewLeft, { position: resultVoxel.topLeft, end: resultVoxel.bottomLeft, visible: true });
|
|
||||||
Overlays.editOverlay(linePreviewRight, { position: resultVoxel.topRight, end: resultVoxel.bottomRight, visible: true });
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||||
Overlays.editOverlay(linePreviewTop, { visible: false });
|
Overlays.editOverlay(linePreviewTop, { visible: false });
|
||||||
|
@ -829,44 +763,39 @@ function mousePressEvent(event) {
|
||||||
isMovingSlider = true;
|
isMovingSlider = true;
|
||||||
thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb
|
thumbClickOffsetX = event.x - (sliderX + thumbX); // this should be the position of the mouse relative to the thumb
|
||||||
clickedOnSomething = true;
|
clickedOnSomething = true;
|
||||||
} else if (clickedOverlay == addTool) {
|
|
||||||
addToolSelected = true;
|
Overlays.editOverlay(thumb, { imageURL: toolIconUrl + "voxel-size-slider-handle.svg", });
|
||||||
deleteToolSelected = false;
|
|
||||||
|
} else if (clickedOverlay == voxelTool) {
|
||||||
|
voxelToolSelected = true;
|
||||||
recolorToolSelected = false;
|
recolorToolSelected = false;
|
||||||
eyedropperToolSelected = false;
|
eyedropperToolSelected = false;
|
||||||
selectToolSelected = false;
|
|
||||||
moveTools();
|
|
||||||
clickedOnSomething = true;
|
|
||||||
} else if (clickedOverlay == deleteTool) {
|
|
||||||
addToolSelected = false;
|
|
||||||
deleteToolSelected = true;
|
|
||||||
recolorToolSelected = false;
|
|
||||||
eyedropperToolSelected = false;
|
|
||||||
selectToolSelected = false;
|
|
||||||
moveTools();
|
moveTools();
|
||||||
clickedOnSomething = true;
|
clickedOnSomething = true;
|
||||||
} else if (clickedOverlay == recolorTool) {
|
} else if (clickedOverlay == recolorTool) {
|
||||||
addToolSelected = false;
|
voxelToolSelected = false;
|
||||||
deleteToolSelected = false;
|
|
||||||
recolorToolSelected = true;
|
recolorToolSelected = true;
|
||||||
eyedropperToolSelected = false;
|
eyedropperToolSelected = false;
|
||||||
selectToolSelected = false;
|
|
||||||
moveTools();
|
moveTools();
|
||||||
clickedOnSomething = true;
|
clickedOnSomething = true;
|
||||||
} else if (clickedOverlay == eyedropperTool) {
|
} else if (clickedOverlay == eyedropperTool) {
|
||||||
addToolSelected = false;
|
voxelToolSelected = false;
|
||||||
deleteToolSelected = false;
|
|
||||||
recolorToolSelected = false;
|
recolorToolSelected = false;
|
||||||
eyedropperToolSelected = true;
|
eyedropperToolSelected = true;
|
||||||
selectToolSelected = false;
|
|
||||||
moveTools();
|
moveTools();
|
||||||
clickedOnSomething = true;
|
clickedOnSomething = true;
|
||||||
} else if (clickedOverlay == selectTool) {
|
} else if (clickedOverlay == slider) {
|
||||||
addToolSelected = false;
|
|
||||||
deleteToolSelected = false;
|
if (event.x < sliderX + minThumbX) {
|
||||||
recolorToolSelected = false;
|
thumbX -= thumbDeltaPerStep;
|
||||||
eyedropperToolSelected = false;
|
calcScaleFromThumb(thumbX);
|
||||||
selectToolSelected = true;
|
}
|
||||||
|
|
||||||
|
if (event.x > sliderX + maxThumbX) {
|
||||||
|
thumbX += thumbDeltaPerStep;
|
||||||
|
calcScaleFromThumb(thumbX);
|
||||||
|
}
|
||||||
|
|
||||||
moveTools();
|
moveTools();
|
||||||
clickedOnSomething = true;
|
clickedOnSomething = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -908,7 +837,7 @@ function mousePressEvent(event) {
|
||||||
startPanMode(event);
|
startPanMode(event);
|
||||||
isPanning = true;
|
isPanning = true;
|
||||||
}
|
}
|
||||||
} else if (deleteToolSelected || trackAsDelete || (event.isRightButton && !trackAsEyedropper)) {
|
} else if (trackAsDelete || event.isRightButton && !trackAsEyedropper) {
|
||||||
// Delete voxel
|
// Delete voxel
|
||||||
voxelDetails = calculateVoxelFromIntersection(intersection,"delete");
|
voxelDetails = calculateVoxelFromIntersection(intersection,"delete");
|
||||||
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
|
Voxels.eraseVoxel(voxelDetails.x, voxelDetails.y, voxelDetails.z, voxelDetails.s);
|
||||||
|
@ -932,7 +861,7 @@ function mousePressEvent(event) {
|
||||||
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
|
colors[whichColor].red, colors[whichColor].green, colors[whichColor].blue);
|
||||||
Audio.playSound(changeColorSound, audioOptions);
|
Audio.playSound(changeColorSound, audioOptions);
|
||||||
Overlays.editOverlay(voxelPreview, { visible: false });
|
Overlays.editOverlay(voxelPreview, { visible: false });
|
||||||
} else if (addToolSelected) {
|
} else if (voxelToolSelected) {
|
||||||
// Add voxel on face
|
// Add voxel on face
|
||||||
if (whichColor == -1) {
|
if (whichColor == -1) {
|
||||||
// Copy mode - use clicked voxel color
|
// Copy mode - use clicked voxel color
|
||||||
|
@ -968,7 +897,7 @@ function keyPressEvent(event) {
|
||||||
// if our tools are off, then don't do anything
|
// if our tools are off, then don't do anything
|
||||||
if (editToolsOn) {
|
if (editToolsOn) {
|
||||||
var nVal = parseInt(event.text);
|
var nVal = parseInt(event.text);
|
||||||
if (event.text == "0") {
|
if (event.text == "`") {
|
||||||
print("Color = Copy");
|
print("Color = Copy");
|
||||||
whichColor = -1;
|
whichColor = -1;
|
||||||
Audio.playSound(clickSound, audioOptions);
|
Audio.playSound(clickSound, audioOptions);
|
||||||
|
@ -978,7 +907,7 @@ function keyPressEvent(event) {
|
||||||
print("Color = " + (whichColor + 1));
|
print("Color = " + (whichColor + 1));
|
||||||
Audio.playSound(clickSound, audioOptions);
|
Audio.playSound(clickSound, audioOptions);
|
||||||
moveTools();
|
moveTools();
|
||||||
} else if (event.text == "9") {
|
} else if (event.text == "0") {
|
||||||
// Create a brand new 1 meter voxel in front of your avatar
|
// Create a brand new 1 meter voxel in front of your avatar
|
||||||
var color = whichColor;
|
var color = whichColor;
|
||||||
if (color == -1) color = 0;
|
if (color == -1) color = 0;
|
||||||
|
@ -1010,18 +939,6 @@ function keyPressEvent(event) {
|
||||||
|
|
||||||
function keyReleaseEvent(event) {
|
function keyReleaseEvent(event) {
|
||||||
trackKeyReleaseEvent(event); // used by preview support
|
trackKeyReleaseEvent(event); // used by preview support
|
||||||
|
|
||||||
// handle clipboard items
|
|
||||||
if (selectToolSelected) {
|
|
||||||
// menu tied to BACKSPACE, so we handle DELETE key here...
|
|
||||||
if (event.text == "DELETE") {
|
|
||||||
var pickRay = Camera.computePickRay(trackLastMouseX, trackLastMouseY);
|
|
||||||
var intersection = Voxels.findRayIntersection(pickRay);
|
|
||||||
selectedVoxel = calculateVoxelFromIntersection(intersection,"select");
|
|
||||||
print("the DELETE key was pressed... delete");
|
|
||||||
Clipboard.deleteVoxel(selectedVoxel.x, selectedVoxel.y, selectedVoxel.z, selectedVoxel.s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function setupMenus() {
|
function setupMenus() {
|
||||||
|
@ -1186,22 +1103,33 @@ function mouseReleaseEvent(event) {
|
||||||
function moveTools() {
|
function moveTools() {
|
||||||
// move the swatches
|
// move the swatches
|
||||||
swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
|
swatchesX = (windowDimensions.x - (swatchesWidth + sliderWidth)) / 2;
|
||||||
swatchesY = windowDimensions.y - swatchHeight;
|
swatchesY = windowDimensions.y - swatchHeight + 1;
|
||||||
|
|
||||||
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
|
// create the overlays, position them in a row, set their colors, and for the selected one, use a different source image
|
||||||
// location so that it displays the "selected" marker
|
// location so that it displays the "selected" marker
|
||||||
for (s = 0; s < numColors; s++) {
|
for (s = 0; s < numColors; s++) {
|
||||||
var imageFromX = 12 + (s * 27);
|
var extraWidth = 0;
|
||||||
var imageFromY = 0;
|
|
||||||
if (s == whichColor) {
|
|
||||||
imageFromY = 55;
|
|
||||||
}
|
|
||||||
var swatchX = swatchesX + ((swatchWidth - 1) * s);
|
|
||||||
|
|
||||||
|
if (s == 0) {
|
||||||
|
extraWidth = swatchExtraPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
var imageFromX = swatchExtraPadding - extraWidth + s * swatchWidth;
|
||||||
|
var imageFromY = swatchHeight + 1;
|
||||||
|
if (s == whichColor) {
|
||||||
|
imageFromY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var swatchX = swatchExtraPadding - extraWidth + swatchesX + ((swatchWidth - 1) * s);
|
||||||
|
|
||||||
|
if (s == (numColors - 1)) {
|
||||||
|
extraWidth = swatchExtraPadding;
|
||||||
|
}
|
||||||
|
|
||||||
Overlays.editOverlay(swatches[s], {
|
Overlays.editOverlay(swatches[s], {
|
||||||
x: swatchX,
|
x: swatchX,
|
||||||
y: swatchesY,
|
y: swatchesY,
|
||||||
subImage: { x: imageFromX, y: imageFromY, width: (swatchWidth - 1), height: swatchHeight },
|
subImage: { x: imageFromX, y: imageFromY, width: swatchWidth + extraWidth, height: swatchHeight },
|
||||||
color: colors[s],
|
color: colors[s],
|
||||||
alpha: 1,
|
alpha: 1,
|
||||||
visible: editToolsOn
|
visible: editToolsOn
|
||||||
|
@ -1210,62 +1138,45 @@ function moveTools() {
|
||||||
|
|
||||||
// move the tools
|
// move the tools
|
||||||
toolsY = (windowDimensions.y - toolsHeight) / 2;
|
toolsY = (windowDimensions.y - toolsHeight) / 2;
|
||||||
addToolColor = notSelectedColor;
|
|
||||||
deleteToolColor = notSelectedColor;
|
|
||||||
recolorToolColor = notSelectedColor;
|
|
||||||
eyedropperToolColor = notSelectedColor;
|
|
||||||
selectToolColor = notSelectedColor;
|
|
||||||
|
|
||||||
if (trackAsDelete || deleteToolSelected) {
|
var voxelToolOffset = 1,
|
||||||
deleteToolColor = toolSelectedColor;
|
recolorToolOffset = 1,
|
||||||
} else if (trackAsRecolor || recolorToolSelected) {
|
eyedropperToolOffset = 1;
|
||||||
recolorToolColor = toolSelectedColor;
|
|
||||||
|
if (trackAsRecolor || recolorToolSelected) {
|
||||||
|
recolorToolOffset = 2;
|
||||||
} else if (trackAsEyedropper || eyedropperToolSelected) {
|
} else if (trackAsEyedropper || eyedropperToolSelected) {
|
||||||
eyedropperToolColor = toolSelectedColor;
|
eyedropperToolOffset = 2;
|
||||||
} else if (selectToolSelected) {
|
|
||||||
selectToolColor = toolSelectedColor;
|
|
||||||
} else if (trackAsOrbitOrPan) {
|
} else if (trackAsOrbitOrPan) {
|
||||||
// nothing gets selected in this case...
|
// nothing gets selected in this case...
|
||||||
} else {
|
} else {
|
||||||
addToolColor = toolSelectedColor;
|
voxelToolOffset = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
Overlays.editOverlay(addTool, {
|
Overlays.editOverlay(voxelTool, {
|
||||||
x: 0, y: toolsY + (toolHeight * addToolAt), width: toolWidth, height: toolHeight,
|
subImage: { x: 0, y: toolHeight * voxelToolOffset, width: toolWidth, height: toolHeight },
|
||||||
color: addToolColor,
|
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * voxelToolAt), width: toolWidth, height: toolHeight,
|
||||||
visible: editToolsOn
|
|
||||||
});
|
|
||||||
|
|
||||||
Overlays.editOverlay(deleteTool, {
|
|
||||||
x: 0, y: toolsY + (toolHeight * deleteToolAt), width: toolWidth, height: toolHeight,
|
|
||||||
color: deleteToolColor,
|
|
||||||
visible: editToolsOn
|
visible: editToolsOn
|
||||||
});
|
});
|
||||||
|
|
||||||
Overlays.editOverlay(recolorTool, {
|
Overlays.editOverlay(recolorTool, {
|
||||||
x: 0, y: toolsY + (toolHeight * recolorToolAt), width: toolWidth, height: toolHeight,
|
subImage: { x: 0, y: toolHeight * recolorToolOffset, width: toolWidth, height: toolHeight },
|
||||||
color: recolorToolColor,
|
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * recolorToolAt), width: toolWidth, height: toolHeight,
|
||||||
visible: editToolsOn
|
visible: editToolsOn
|
||||||
});
|
});
|
||||||
|
|
||||||
Overlays.editOverlay(eyedropperTool, {
|
Overlays.editOverlay(eyedropperTool, {
|
||||||
x: 0, y: toolsY + (toolHeight * eyedropperToolAt), width: toolWidth, height: toolHeight,
|
subImage: { x: 0, y: toolHeight * eyedropperToolOffset, width: toolWidth, height: toolHeight },
|
||||||
color: eyedropperToolColor,
|
x: toolsX, y: toolsY + ((toolHeight + toolVerticalSpacing) * eyedropperToolAt), width: toolWidth, height: toolHeight,
|
||||||
visible: editToolsOn
|
visible: editToolsOn
|
||||||
});
|
});
|
||||||
|
|
||||||
Overlays.editOverlay(selectTool, {
|
sliderX = swatchesX + swatchesWidth - sliderOffsetX;
|
||||||
x: 0, y: toolsY + (toolHeight * selectToolAt), width: toolWidth, height: toolHeight,
|
sliderY = windowDimensions.y - sliderHeight + 1;
|
||||||
color: selectToolColor,
|
thumbY = sliderY + thumbOffsetY;
|
||||||
visible: editToolsOn
|
|
||||||
});
|
|
||||||
|
|
||||||
sliderX = swatchesX + swatchesWidth;
|
|
||||||
sliderY = windowDimensions.y - sliderHeight;
|
|
||||||
Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn });
|
Overlays.editOverlay(slider, { x: sliderX, y: sliderY, visible: editToolsOn });
|
||||||
|
|
||||||
// This is the thumb of our slider
|
// This is the thumb of our slider
|
||||||
thumbY = sliderY + 9;
|
|
||||||
Overlays.editOverlay(thumb, { x: sliderX + thumbX, y: thumbY, visible: editToolsOn });
|
Overlays.editOverlay(thumb, { x: sliderX + thumbX, y: thumbY, visible: editToolsOn });
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1421,11 +1332,9 @@ function scriptEnding() {
|
||||||
for (s = 0; s < numColors; s++) {
|
for (s = 0; s < numColors; s++) {
|
||||||
Overlays.deleteOverlay(swatches[s]);
|
Overlays.deleteOverlay(swatches[s]);
|
||||||
}
|
}
|
||||||
Overlays.deleteOverlay(addTool);
|
Overlays.deleteOverlay(voxelTool);
|
||||||
Overlays.deleteOverlay(deleteTool);
|
|
||||||
Overlays.deleteOverlay(recolorTool);
|
Overlays.deleteOverlay(recolorTool);
|
||||||
Overlays.deleteOverlay(eyedropperTool);
|
Overlays.deleteOverlay(eyedropperTool);
|
||||||
Overlays.deleteOverlay(selectTool);
|
|
||||||
Overlays.deleteOverlay(slider);
|
Overlays.deleteOverlay(slider);
|
||||||
Overlays.deleteOverlay(thumb);
|
Overlays.deleteOverlay(thumb);
|
||||||
Controller.releaseKeyEvents({ text: "+" });
|
Controller.releaseKeyEvents({ text: "+" });
|
||||||
|
|
|
@ -58,6 +58,7 @@
|
||||||
#include <PacketHeaders.h>
|
#include <PacketHeaders.h>
|
||||||
#include <ParticlesScriptingInterface.h>
|
#include <ParticlesScriptingInterface.h>
|
||||||
#include <PerfStat.h>
|
#include <PerfStat.h>
|
||||||
|
#include <ResourceCache.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <VoxelSceneStats.h>
|
#include <VoxelSceneStats.h>
|
||||||
|
|
||||||
|
@ -275,6 +276,9 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
||||||
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
cache->setCacheDirectory(!cachePath.isEmpty() ? cachePath : "interfaceCache");
|
||||||
_networkAccessManager->setCache(cache);
|
_networkAccessManager->setCache(cache);
|
||||||
|
|
||||||
|
ResourceCache::setNetworkAccessManager(_networkAccessManager);
|
||||||
|
ResourceCache::setRequestLimit(3);
|
||||||
|
|
||||||
_window->setCentralWidget(_glWidget);
|
_window->setCentralWidget(_glWidget);
|
||||||
|
|
||||||
restoreSizeAndPosition();
|
restoreSizeAndPosition();
|
||||||
|
|
|
@ -35,9 +35,6 @@ void MetavoxelSystem::init() {
|
||||||
_program.link();
|
_program.link();
|
||||||
|
|
||||||
_pointScaleLocation = _program.uniformLocation("pointScale");
|
_pointScaleLocation = _program.uniformLocation("pointScale");
|
||||||
|
|
||||||
// let the script cache know to use our common access manager
|
|
||||||
ScriptCache::getInstance()->setNetworkAccessManager(Application::getInstance()->getNetworkAccessManager());
|
|
||||||
}
|
}
|
||||||
_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
|
_buffer.setUsagePattern(QOpenGLBuffer::DynamicDraw);
|
||||||
_buffer.create();
|
_buffer.create();
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "GeometryCache.h"
|
#include "GeometryCache.h"
|
||||||
|
@ -287,53 +286,25 @@ void GeometryCache::renderGrid(int xDivisions, int yDivisions) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) {
|
QSharedPointer<NetworkGeometry> GeometryCache::getGeometry(const QUrl& url, const QUrl& fallback, bool delayLoad) {
|
||||||
if (!url.isValid() && fallback.isValid()) {
|
return getResource(url, fallback, delayLoad).staticCast<NetworkGeometry>();
|
||||||
return getGeometry(fallback, QUrl(), delayLoad);
|
}
|
||||||
}
|
|
||||||
QSharedPointer<NetworkGeometry> geometry = _networkGeometry.value(url);
|
QSharedPointer<Resource> GeometryCache::createResource(const QUrl& url,
|
||||||
if (geometry.isNull()) {
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
geometry = QSharedPointer<NetworkGeometry>(new NetworkGeometry(url, fallback.isValid() ?
|
|
||||||
getGeometry(fallback, QUrl(), true) : QSharedPointer<NetworkGeometry>(), delayLoad));
|
QSharedPointer<NetworkGeometry> geometry(new NetworkGeometry(url, fallback.staticCast<NetworkGeometry>(), delayLoad));
|
||||||
geometry->setLODParent(geometry);
|
geometry->setLODParent(geometry);
|
||||||
_networkGeometry.insert(url, geometry);
|
return geometry.staticCast<Resource>();
|
||||||
}
|
|
||||||
return geometry;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const float NetworkGeometry::NO_HYSTERESIS = -1.0f;
|
const float NetworkGeometry::NO_HYSTERESIS = -1.0f;
|
||||||
|
|
||||||
NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
|
NetworkGeometry::NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
|
||||||
const QVariantHash& mapping, const QUrl& textureBase) :
|
const QVariantHash& mapping, const QUrl& textureBase) :
|
||||||
_request(url),
|
Resource(url, delayLoad),
|
||||||
_reply(NULL),
|
|
||||||
_mapping(mapping),
|
_mapping(mapping),
|
||||||
_textureBase(textureBase.isValid() ? textureBase : url),
|
_textureBase(textureBase.isValid() ? textureBase : url),
|
||||||
_fallback(fallback),
|
_fallback(fallback) {
|
||||||
_startedLoading(false),
|
|
||||||
_failedToLoad(false),
|
|
||||||
_attempts(0) {
|
|
||||||
|
|
||||||
if (!url.isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
|
||||||
|
|
||||||
// start loading immediately unless instructed otherwise
|
|
||||||
if (!delayLoad) {
|
|
||||||
makeRequest();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkGeometry::~NetworkGeometry() {
|
|
||||||
if (_reply != NULL) {
|
|
||||||
delete _reply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkGeometry::ensureLoading() {
|
|
||||||
if (!_startedLoading) {
|
|
||||||
makeRequest();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const {
|
QSharedPointer<NetworkGeometry> NetworkGeometry::getLODOrFallback(float distance, float& hysteresis, bool delayLoad) const {
|
||||||
|
@ -406,24 +377,60 @@ glm::vec4 NetworkGeometry::computeAverageColor() const {
|
||||||
return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalTriangles;
|
return (totalTriangles == 0) ? glm::vec4(1.0f, 1.0f, 1.0f, 1.0f) : totalColor / totalTriangles;
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkGeometry::makeRequest() {
|
void NetworkGeometry::setLoadPriority(const QPointer<QObject>& owner, float priority) {
|
||||||
_startedLoading = true;
|
Resource::setLoadPriority(owner, priority);
|
||||||
_reply = Application::getInstance()->getNetworkAccessManager()->get(_request);
|
|
||||||
|
|
||||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
for (int i = 0; i < _meshes.size(); i++) {
|
||||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
NetworkMesh& mesh = _meshes[i];
|
||||||
|
for (int j = 0; j < mesh.parts.size(); j++) {
|
||||||
|
NetworkMeshPart& part = mesh.parts[j];
|
||||||
|
if (part.diffuseTexture) {
|
||||||
|
part.diffuseTexture->setLoadPriority(owner, priority);
|
||||||
|
}
|
||||||
|
if (part.normalTexture) {
|
||||||
|
part.normalTexture->setLoadPriority(owner, priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
void NetworkGeometry::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
|
||||||
if (!_reply->isFinished()) {
|
Resource::setLoadPriorities(priorities);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QUrl url = _reply->url();
|
for (int i = 0; i < _meshes.size(); i++) {
|
||||||
QByteArray data = _reply->readAll();
|
NetworkMesh& mesh = _meshes[i];
|
||||||
_reply->disconnect(this);
|
for (int j = 0; j < mesh.parts.size(); j++) {
|
||||||
_reply->deleteLater();
|
NetworkMeshPart& part = mesh.parts[j];
|
||||||
_reply = NULL;
|
if (part.diffuseTexture) {
|
||||||
|
part.diffuseTexture->setLoadPriorities(priorities);
|
||||||
|
}
|
||||||
|
if (part.normalTexture) {
|
||||||
|
part.normalTexture->setLoadPriorities(priorities);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkGeometry::clearLoadPriority(const QPointer<QObject>& owner) {
|
||||||
|
Resource::clearLoadPriority(owner);
|
||||||
|
|
||||||
|
for (int i = 0; i < _meshes.size(); i++) {
|
||||||
|
NetworkMesh& mesh = _meshes[i];
|
||||||
|
for (int j = 0; j < mesh.parts.size(); j++) {
|
||||||
|
NetworkMeshPart& part = mesh.parts[j];
|
||||||
|
if (part.diffuseTexture) {
|
||||||
|
part.diffuseTexture->clearLoadPriority(owner);
|
||||||
|
}
|
||||||
|
if (part.normalTexture) {
|
||||||
|
part.normalTexture->clearLoadPriority(owner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkGeometry::downloadFinished(QNetworkReply* reply) {
|
||||||
|
QUrl url = reply->url();
|
||||||
|
QByteArray data = reply->readAll();
|
||||||
|
|
||||||
if (url.path().toLower().endsWith(".fst")) {
|
if (url.path().toLower().endsWith(".fst")) {
|
||||||
// it's a mapping file; parse it and get the mesh filename
|
// it's a mapping file; parse it and get the mesh filename
|
||||||
|
@ -453,7 +460,7 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
||||||
// make the request immediately only if we have no LODs to switch between
|
// make the request immediately only if we have no LODs to switch between
|
||||||
_startedLoading = false;
|
_startedLoading = false;
|
||||||
if (_lods.isEmpty()) {
|
if (_lods.isEmpty()) {
|
||||||
makeRequest();
|
attemptRequest();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
@ -477,10 +484,12 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
||||||
if (!part.diffuseFilename.isEmpty()) {
|
if (!part.diffuseFilename.isEmpty()) {
|
||||||
networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(
|
networkPart.diffuseTexture = Application::getInstance()->getTextureCache()->getTexture(
|
||||||
_textureBase.resolved(QUrl(part.diffuseFilename)), false, mesh.isEye);
|
_textureBase.resolved(QUrl(part.diffuseFilename)), false, mesh.isEye);
|
||||||
|
networkPart.diffuseTexture->setLoadPriorities(_loadPriorities);
|
||||||
}
|
}
|
||||||
if (!part.normalFilename.isEmpty()) {
|
if (!part.normalFilename.isEmpty()) {
|
||||||
networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture(
|
networkPart.normalTexture = Application::getInstance()->getTextureCache()->getTexture(
|
||||||
_textureBase.resolved(QUrl(part.normalFilename)), true);
|
_textureBase.resolved(QUrl(part.normalFilename)), true);
|
||||||
|
networkPart.normalTexture->setLoadPriorities(_loadPriorities);
|
||||||
}
|
}
|
||||||
networkMesh.parts.append(networkPart);
|
networkMesh.parts.append(networkPart);
|
||||||
|
|
||||||
|
@ -560,42 +569,6 @@ void NetworkGeometry::handleDownloadProgress(qint64 bytesReceived, qint64 bytesT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkGeometry::handleReplyError() {
|
|
||||||
QDebug debug = qDebug() << _reply->errorString();
|
|
||||||
|
|
||||||
QNetworkReply::NetworkError error = _reply->error();
|
|
||||||
_reply->disconnect(this);
|
|
||||||
_reply->deleteLater();
|
|
||||||
_reply = NULL;
|
|
||||||
|
|
||||||
// retry for certain types of failures
|
|
||||||
switch (error) {
|
|
||||||
case QNetworkReply::RemoteHostClosedError:
|
|
||||||
case QNetworkReply::TimeoutError:
|
|
||||||
case QNetworkReply::TemporaryNetworkFailureError:
|
|
||||||
case QNetworkReply::ProxyConnectionClosedError:
|
|
||||||
case QNetworkReply::ProxyTimeoutError:
|
|
||||||
case QNetworkReply::UnknownNetworkError:
|
|
||||||
case QNetworkReply::UnknownProxyError:
|
|
||||||
case QNetworkReply::UnknownContentError:
|
|
||||||
case QNetworkReply::ProtocolFailure: {
|
|
||||||
// retry with increasing delays
|
|
||||||
const int MAX_ATTEMPTS = 8;
|
|
||||||
const int BASE_DELAY_MS = 1000;
|
|
||||||
if (++_attempts < MAX_ATTEMPTS) {
|
|
||||||
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest()));
|
|
||||||
debug << " -- retrying...";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// fall through to final failure
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
_failedToLoad = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool NetworkMeshPart::isTranslucent() const {
|
bool NetworkMeshPart::isTranslucent() const {
|
||||||
return diffuseTexture && diffuseTexture->isTranslucent();
|
return diffuseTexture && diffuseTexture->isTranslucent();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,24 +12,19 @@
|
||||||
// include this before QOpenGLBuffer, which includes an earlier version of OpenGL
|
// include this before QOpenGLBuffer, which includes an earlier version of OpenGL
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QOpenGLBuffer>
|
#include <QOpenGLBuffer>
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QWeakPointer>
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
#include "FBXReader.h"
|
#include "FBXReader.h"
|
||||||
|
|
||||||
class QNetworkReply;
|
|
||||||
|
|
||||||
class NetworkGeometry;
|
class NetworkGeometry;
|
||||||
class NetworkMesh;
|
class NetworkMesh;
|
||||||
class NetworkTexture;
|
class NetworkTexture;
|
||||||
|
|
||||||
/// Stores cached geometry.
|
/// Stores cached geometry.
|
||||||
class GeometryCache {
|
class GeometryCache : public ResourceCache {
|
||||||
public:
|
public:
|
||||||
|
|
||||||
~GeometryCache();
|
~GeometryCache();
|
||||||
|
@ -44,6 +39,11 @@ public:
|
||||||
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
|
/// \param delayLoad if true, don't load the geometry immediately; wait until load is first requested
|
||||||
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
|
QSharedPointer<NetworkGeometry> getGeometry(const QUrl& url, const QUrl& fallback = QUrl(), bool delayLoad = false);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
||||||
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
typedef QPair<int, int> IntPair;
|
typedef QPair<int, int> IntPair;
|
||||||
|
@ -58,7 +58,7 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Geometry loaded from the network.
|
/// Geometry loaded from the network.
|
||||||
class NetworkGeometry : public QObject {
|
class NetworkGeometry : public Resource {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -68,14 +68,10 @@ public:
|
||||||
|
|
||||||
NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
|
NetworkGeometry(const QUrl& url, const QSharedPointer<NetworkGeometry>& fallback, bool delayLoad,
|
||||||
const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl());
|
const QVariantHash& mapping = QVariantHash(), const QUrl& textureBase = QUrl());
|
||||||
~NetworkGeometry();
|
|
||||||
|
|
||||||
/// Checks whether the geometry is fulled loaded.
|
/// Checks whether the geometry is fulled loaded.
|
||||||
bool isLoaded() const { return !_geometry.joints.isEmpty(); }
|
bool isLoaded() const { return !_geometry.joints.isEmpty(); }
|
||||||
|
|
||||||
/// Makes sure that the geometry has started loading.
|
|
||||||
void ensureLoading();
|
|
||||||
|
|
||||||
/// Returns a pointer to the geometry appropriate for the specified distance.
|
/// Returns a pointer to the geometry appropriate for the specified distance.
|
||||||
/// \param hysteresis a hysteresis parameter that prevents rapid model switching
|
/// \param hysteresis a hysteresis parameter that prevents rapid model switching
|
||||||
QSharedPointer<NetworkGeometry> getLODOrFallback(float distance, float& hysteresis, bool delayLoad = false) const;
|
QSharedPointer<NetworkGeometry> getLODOrFallback(float distance, float& hysteresis, bool delayLoad = false) const;
|
||||||
|
@ -86,11 +82,13 @@ public:
|
||||||
/// Returns the average color of all meshes in the geometry.
|
/// Returns the average color of all meshes in the geometry.
|
||||||
glm::vec4 computeAverageColor() const;
|
glm::vec4 computeAverageColor() const;
|
||||||
|
|
||||||
private slots:
|
virtual void setLoadPriority(const QPointer<QObject>& owner, float priority);
|
||||||
|
virtual void setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities);
|
||||||
|
virtual void clearLoadPriority(const QPointer<QObject>& owner);
|
||||||
|
|
||||||
void makeRequest();
|
protected:
|
||||||
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
|
||||||
void handleReplyError();
|
virtual void downloadFinished(QNetworkReply* reply);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
@ -98,15 +96,10 @@ private:
|
||||||
|
|
||||||
void setLODParent(const QWeakPointer<NetworkGeometry>& lodParent) { _lodParent = lodParent; }
|
void setLODParent(const QWeakPointer<NetworkGeometry>& lodParent) { _lodParent = lodParent; }
|
||||||
|
|
||||||
QNetworkRequest _request;
|
|
||||||
QNetworkReply* _reply;
|
|
||||||
QVariantHash _mapping;
|
QVariantHash _mapping;
|
||||||
QUrl _textureBase;
|
QUrl _textureBase;
|
||||||
QSharedPointer<NetworkGeometry> _fallback;
|
QSharedPointer<NetworkGeometry> _fallback;
|
||||||
bool _startedLoading;
|
|
||||||
bool _failedToLoad;
|
|
||||||
|
|
||||||
int _attempts;
|
|
||||||
QMap<float, QSharedPointer<NetworkGeometry> > _lods;
|
QMap<float, QSharedPointer<NetworkGeometry> > _lods;
|
||||||
FBXGeometry _geometry;
|
FBXGeometry _geometry;
|
||||||
QVector<NetworkMesh> _meshes;
|
QVector<NetworkMesh> _meshes;
|
||||||
|
|
|
@ -139,6 +139,7 @@ void Model::simulate(float deltaTime, bool delayLoad) {
|
||||||
_geometry = geometry;
|
_geometry = geometry;
|
||||||
}
|
}
|
||||||
if (!delayLoad) {
|
if (!delayLoad) {
|
||||||
|
_geometry->setLoadPriority(this, -_lodDistance);
|
||||||
_geometry->ensureLoading();
|
_geometry->ensureLoading();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -833,6 +834,10 @@ void Model::deleteGeometry() {
|
||||||
_blendedVertexBufferIDs.clear();
|
_blendedVertexBufferIDs.clear();
|
||||||
_jointStates.clear();
|
_jointStates.clear();
|
||||||
_meshStates.clear();
|
_meshStates.clear();
|
||||||
|
|
||||||
|
if (_geometry) {
|
||||||
|
_geometry->clearLoadPriority(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Model::renderMeshes(float alpha, bool translucent) {
|
void Model::renderMeshes(float alpha, bool translucent) {
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include <QGLWidget>
|
#include <QGLWidget>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QOpenGLFramebufferObject>
|
#include <QOpenGLFramebufferObject>
|
||||||
#include <QTimer>
|
|
||||||
|
|
||||||
#include <glm/gtc/random.hpp>
|
#include <glm/gtc/random.hpp>
|
||||||
|
|
||||||
|
@ -124,19 +123,13 @@ GLuint TextureCache::getFileTextureID(const QString& filename) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool normalMap, bool dilatable) {
|
QSharedPointer<NetworkTexture> TextureCache::getTexture(const QUrl& url, bool normalMap, bool dilatable) {
|
||||||
QSharedPointer<NetworkTexture> texture;
|
if (!dilatable) {
|
||||||
if (dilatable) {
|
return ResourceCache::getResource(url, QUrl(), false, &normalMap).staticCast<NetworkTexture>();
|
||||||
texture = _dilatableNetworkTextures.value(url);
|
}
|
||||||
if (texture.isNull()) {
|
QSharedPointer<NetworkTexture> texture = _dilatableNetworkTextures.value(url);
|
||||||
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url, normalMap));
|
if (texture.isNull()) {
|
||||||
_dilatableNetworkTextures.insert(url, texture);
|
texture = QSharedPointer<NetworkTexture>(new DilatableNetworkTexture(url));
|
||||||
}
|
_dilatableNetworkTextures.insert(url, texture);
|
||||||
} else {
|
|
||||||
texture = _networkTextures.value(url);
|
|
||||||
if (texture.isNull()) {
|
|
||||||
texture = QSharedPointer<NetworkTexture>(new NetworkTexture(url, normalMap));
|
|
||||||
_networkTextures.insert(url, texture);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return texture;
|
return texture;
|
||||||
}
|
}
|
||||||
|
@ -233,6 +226,11 @@ bool TextureCache::eventFilter(QObject* watched, QEvent* event) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Resource> TextureCache::createResource(const QUrl& url,
|
||||||
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
|
return QSharedPointer<Resource>(new NetworkTexture(url, *(const bool*)extra));
|
||||||
|
}
|
||||||
|
|
||||||
QOpenGLFramebufferObject* TextureCache::createFramebufferObject() {
|
QOpenGLFramebufferObject* TextureCache::createFramebufferObject() {
|
||||||
QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(Application::getInstance()->getGLWidget()->size());
|
QOpenGLFramebufferObject* fbo = new QOpenGLFramebufferObject(Application::getInstance()->getGLWidget()->size());
|
||||||
Application::getInstance()->getGLWidget()->installEventFilter(this);
|
Application::getInstance()->getGLWidget()->installEventFilter(this);
|
||||||
|
@ -254,9 +252,7 @@ Texture::~Texture() {
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
||||||
_request(url),
|
Resource(url),
|
||||||
_reply(NULL),
|
|
||||||
_attempts(0),
|
|
||||||
_averageColor(1.0f, 1.0f, 1.0f, 1.0f),
|
_averageColor(1.0f, 1.0f, 1.0f, 1.0f),
|
||||||
_translucent(false),
|
_translucent(false),
|
||||||
_loaded(false) {
|
_loaded(false) {
|
||||||
|
@ -265,8 +261,6 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
|
||||||
makeRequest();
|
|
||||||
|
|
||||||
// default to white/blue
|
// default to white/blue
|
||||||
glBindTexture(GL_TEXTURE_2D, getID());
|
glBindTexture(GL_TEXTURE_2D, getID());
|
||||||
|
@ -274,35 +268,13 @@ NetworkTexture::NetworkTexture(const QUrl& url, bool normalMap) :
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkTexture::~NetworkTexture() {
|
void NetworkTexture::downloadFinished(QNetworkReply* reply) {
|
||||||
if (_reply != NULL) {
|
|
||||||
delete _reply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkTexture::imageLoaded(const QImage& image) {
|
|
||||||
// nothing by default
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkTexture::makeRequest() {
|
|
||||||
_reply = Application::getInstance()->getNetworkAccessManager()->get(_request);
|
|
||||||
|
|
||||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
|
||||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
|
||||||
if (bytesReceived < bytesTotal && !_reply->isFinished()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QByteArray entirety = _reply->readAll();
|
|
||||||
_reply->disconnect(this);
|
|
||||||
_reply->deleteLater();
|
|
||||||
_reply = NULL;
|
|
||||||
_loaded = true;
|
_loaded = true;
|
||||||
|
|
||||||
QImage image = QImage::fromData(entirety).convertToFormat(QImage::Format_ARGB32);
|
QImage image = QImage::fromData(reply->readAll());
|
||||||
|
if (image.format() != QImage::Format_ARGB32) {
|
||||||
|
image = image.convertToFormat(QImage::Format_ARGB32);
|
||||||
|
}
|
||||||
|
|
||||||
// sum up the colors for the average and check for translucency
|
// sum up the colors for the average and check for translucency
|
||||||
glm::vec4 accumulated;
|
glm::vec4 accumulated;
|
||||||
|
@ -334,27 +306,12 @@ void NetworkTexture::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTo
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
glBindTexture(GL_TEXTURE_2D, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkTexture::handleReplyError() {
|
void NetworkTexture::imageLoaded(const QImage& image) {
|
||||||
QDebug debug = qDebug() << _reply->errorString();
|
// nothing by default
|
||||||
|
|
||||||
_reply->disconnect(this);
|
|
||||||
_reply->deleteLater();
|
|
||||||
_reply = NULL;
|
|
||||||
|
|
||||||
// retry with increasing delays
|
|
||||||
const int MAX_ATTEMPTS = 8;
|
|
||||||
const int BASE_DELAY_MS = 1000;
|
|
||||||
if (++_attempts < MAX_ATTEMPTS) {
|
|
||||||
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest()));
|
|
||||||
debug << " -- retrying...";
|
|
||||||
|
|
||||||
} else {
|
|
||||||
_loaded = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url, bool normalMap) :
|
DilatableNetworkTexture::DilatableNetworkTexture(const QUrl& url) :
|
||||||
NetworkTexture(url, normalMap),
|
NetworkTexture(url, false),
|
||||||
_innerRadius(0),
|
_innerRadius(0),
|
||||||
_outerRadius(0)
|
_outerRadius(0)
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,23 +9,19 @@
|
||||||
#ifndef __interface__TextureCache__
|
#ifndef __interface__TextureCache__
|
||||||
#define __interface__TextureCache__
|
#define __interface__TextureCache__
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QImage>
|
#include <QImage>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QObject>
|
#include <ResourceCache.h>
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QWeakPointer>
|
|
||||||
|
|
||||||
#include "InterfaceConfig.h"
|
#include "InterfaceConfig.h"
|
||||||
|
|
||||||
class QNetworkReply;
|
|
||||||
class QOpenGLFramebufferObject;
|
class QOpenGLFramebufferObject;
|
||||||
|
|
||||||
class NetworkTexture;
|
class NetworkTexture;
|
||||||
|
|
||||||
/// Stores cached textures, including render-to-texture targets.
|
/// Stores cached textures, including render-to-texture targets.
|
||||||
class TextureCache : public QObject {
|
class TextureCache : public ResourceCache {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -73,6 +69,11 @@ public:
|
||||||
|
|
||||||
virtual bool eventFilter(QObject* watched, QEvent* event);
|
virtual bool eventFilter(QObject* watched, QEvent* event);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
||||||
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QOpenGLFramebufferObject* createFramebufferObject();
|
QOpenGLFramebufferObject* createFramebufferObject();
|
||||||
|
@ -83,7 +84,6 @@ private:
|
||||||
|
|
||||||
QHash<QString, GLuint> _fileTextureIDs;
|
QHash<QString, GLuint> _fileTextureIDs;
|
||||||
|
|
||||||
QHash<QUrl, QWeakPointer<NetworkTexture> > _networkTextures;
|
|
||||||
QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures;
|
QHash<QUrl, QWeakPointer<NetworkTexture> > _dilatableNetworkTextures;
|
||||||
|
|
||||||
GLuint _primaryDepthTextureID;
|
GLuint _primaryDepthTextureID;
|
||||||
|
@ -110,13 +110,12 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A texture loaded from the network.
|
/// A texture loaded from the network.
|
||||||
class NetworkTexture : public QObject, public Texture {
|
class NetworkTexture : public Resource, public Texture {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
NetworkTexture(const QUrl& url, bool normalMap);
|
NetworkTexture(const QUrl& url, bool normalMap);
|
||||||
~NetworkTexture();
|
|
||||||
|
|
||||||
bool isLoaded() const { return _loaded; }
|
bool isLoaded() const { return _loaded; }
|
||||||
|
|
||||||
|
@ -129,19 +128,11 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
virtual void imageLoaded(const QImage& image);
|
virtual void downloadFinished(QNetworkReply* reply);
|
||||||
|
virtual void imageLoaded(const QImage& image);
|
||||||
private slots:
|
|
||||||
|
|
||||||
void makeRequest();
|
|
||||||
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
|
||||||
void handleReplyError();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QNetworkRequest _request;
|
|
||||||
QNetworkReply* _reply;
|
|
||||||
int _attempts;
|
|
||||||
glm::vec4 _averageColor;
|
glm::vec4 _averageColor;
|
||||||
bool _translucent;
|
bool _translucent;
|
||||||
bool _loaded;
|
bool _loaded;
|
||||||
|
@ -153,7 +144,7 @@ class DilatableNetworkTexture : public NetworkTexture {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
DilatableNetworkTexture(const QUrl& url, bool normalMap);
|
DilatableNetworkTexture(const QUrl& url);
|
||||||
|
|
||||||
/// Returns a pointer to a texture with the requested amount of dilation.
|
/// Returns a pointer to a texture with the requested amount of dilation.
|
||||||
QSharedPointer<Texture> getDilatedTexture(float dilation);
|
QSharedPointer<Texture> getDilatedTexture(float dilation);
|
||||||
|
|
|
@ -11,8 +11,6 @@
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QScriptEngine>
|
#include <QScriptEngine>
|
||||||
#include <QTextStream>
|
#include <QTextStream>
|
||||||
#include <QTimer>
|
|
||||||
#include <QtDebug>
|
|
||||||
|
|
||||||
#include "AttributeRegistry.h"
|
#include "AttributeRegistry.h"
|
||||||
#include "ScriptCache.h"
|
#include "ScriptCache.h"
|
||||||
|
@ -23,7 +21,6 @@ ScriptCache* ScriptCache::getInstance() {
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptCache::ScriptCache() :
|
ScriptCache::ScriptCache() :
|
||||||
_networkAccessManager(NULL),
|
|
||||||
_engine(NULL) {
|
_engine(NULL) {
|
||||||
|
|
||||||
setEngine(new QScriptEngine(this));
|
setEngine(new QScriptEngine(this));
|
||||||
|
@ -41,15 +38,6 @@ void ScriptCache::setEngine(QScriptEngine* engine) {
|
||||||
_generatorString = engine->toStringHandle("generator");
|
_generatorString = engine->toStringHandle("generator");
|
||||||
}
|
}
|
||||||
|
|
||||||
QSharedPointer<NetworkProgram> ScriptCache::getProgram(const QUrl& url) {
|
|
||||||
QSharedPointer<NetworkProgram> program = _networkPrograms.value(url);
|
|
||||||
if (program.isNull()) {
|
|
||||||
program = QSharedPointer<NetworkProgram>(new NetworkProgram(this, url));
|
|
||||||
_networkPrograms.insert(url, program);
|
|
||||||
}
|
|
||||||
return program;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<NetworkValue> ScriptCache::getValue(const ParameterizedURL& url) {
|
QSharedPointer<NetworkValue> ScriptCache::getValue(const ParameterizedURL& url) {
|
||||||
QSharedPointer<NetworkValue> value = _networkValues.value(url);
|
QSharedPointer<NetworkValue> value = _networkValues.value(url);
|
||||||
if (value.isNull()) {
|
if (value.isNull()) {
|
||||||
|
@ -61,65 +49,21 @@ QSharedPointer<NetworkValue> ScriptCache::getValue(const ParameterizedURL& url)
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Resource> ScriptCache::createResource(const QUrl& url,
|
||||||
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) {
|
||||||
|
return QSharedPointer<Resource>(new NetworkProgram(this, url));
|
||||||
|
}
|
||||||
|
|
||||||
NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) :
|
NetworkProgram::NetworkProgram(ScriptCache* cache, const QUrl& url) :
|
||||||
_cache(cache),
|
Resource(url),
|
||||||
_request(url),
|
_cache(cache) {
|
||||||
_reply(NULL),
|
|
||||||
_attempts(0) {
|
|
||||||
|
|
||||||
if (!url.isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
|
||||||
makeRequest();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkProgram::~NetworkProgram() {
|
void NetworkProgram::downloadFinished(QNetworkReply* reply) {
|
||||||
if (_reply != NULL) {
|
_program = QScriptProgram(QTextStream(reply).readAll(), reply->url().toString());
|
||||||
delete _reply;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkProgram::makeRequest() {
|
|
||||||
QNetworkAccessManager* manager = _cache->getNetworkAccessManager();
|
|
||||||
if (manager == NULL) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_reply = manager->get(_request);
|
|
||||||
|
|
||||||
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
|
||||||
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void NetworkProgram::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
|
||||||
if (bytesReceived < bytesTotal && !_reply->isFinished()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_program = QScriptProgram(QTextStream(_reply).readAll(), _reply->url().toString());
|
|
||||||
|
|
||||||
_reply->disconnect(this);
|
|
||||||
_reply->deleteLater();
|
|
||||||
_reply = NULL;
|
|
||||||
|
|
||||||
emit loaded();
|
emit loaded();
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkProgram::handleReplyError() {
|
|
||||||
QDebug debug = qDebug() << _reply->errorString();
|
|
||||||
|
|
||||||
_reply->disconnect(this);
|
|
||||||
_reply->deleteLater();
|
|
||||||
_reply = NULL;
|
|
||||||
|
|
||||||
// retry with increasing delays
|
|
||||||
const int MAX_ATTEMPTS = 8;
|
|
||||||
const int BASE_DELAY_MS = 1000;
|
|
||||||
if (++_attempts < MAX_ATTEMPTS) {
|
|
||||||
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(makeRequest()));
|
|
||||||
debug << " -- retrying...";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NetworkValue::~NetworkValue() {
|
NetworkValue::~NetworkValue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,26 +9,20 @@
|
||||||
#ifndef __interface__ScriptCache__
|
#ifndef __interface__ScriptCache__
|
||||||
#define __interface__ScriptCache__
|
#define __interface__ScriptCache__
|
||||||
|
|
||||||
#include <QHash>
|
|
||||||
#include <QList>
|
|
||||||
#include <QNetworkRequest>
|
|
||||||
#include <QObject>
|
|
||||||
#include <QScriptProgram>
|
#include <QScriptProgram>
|
||||||
#include <QScriptValue>
|
#include <QScriptValue>
|
||||||
#include <QSharedPointer>
|
|
||||||
#include <QWeakPointer>
|
#include <ResourceCache.h>
|
||||||
|
|
||||||
#include "MetavoxelUtil.h"
|
#include "MetavoxelUtil.h"
|
||||||
|
|
||||||
class QNetworkAccessManager;
|
|
||||||
class QNetworkReply;
|
|
||||||
class QScriptEngine;
|
class QScriptEngine;
|
||||||
|
|
||||||
class NetworkProgram;
|
class NetworkProgram;
|
||||||
class NetworkValue;
|
class NetworkValue;
|
||||||
|
|
||||||
/// Maintains a cache of loaded scripts.
|
/// Maintains a cache of loaded scripts.
|
||||||
class ScriptCache : public QObject {
|
class ScriptCache : public ResourceCache {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
@ -37,14 +31,11 @@ public:
|
||||||
|
|
||||||
ScriptCache();
|
ScriptCache();
|
||||||
|
|
||||||
void setNetworkAccessManager(QNetworkAccessManager* manager) { _networkAccessManager = manager; }
|
|
||||||
QNetworkAccessManager* getNetworkAccessManager() const { return _networkAccessManager; }
|
|
||||||
|
|
||||||
void setEngine(QScriptEngine* engine);
|
void setEngine(QScriptEngine* engine);
|
||||||
QScriptEngine* getEngine() const { return _engine; }
|
QScriptEngine* getEngine() const { return _engine; }
|
||||||
|
|
||||||
/// Loads a script program from the specified URL.
|
/// Loads a script program from the specified URL.
|
||||||
QSharedPointer<NetworkProgram> getProgram(const QUrl& url);
|
QSharedPointer<NetworkProgram> getProgram(const QUrl& url) { return getResource(url).staticCast<NetworkProgram>(); }
|
||||||
|
|
||||||
/// Loads a script value from the specified URL.
|
/// Loads a script value from the specified URL.
|
||||||
QSharedPointer<NetworkValue> getValue(const ParameterizedURL& url);
|
QSharedPointer<NetworkValue> getValue(const ParameterizedURL& url);
|
||||||
|
@ -55,11 +46,14 @@ public:
|
||||||
const QScriptString& getTypeString() const { return _typeString; }
|
const QScriptString& getTypeString() const { return _typeString; }
|
||||||
const QScriptString& getGeneratorString() const { return _generatorString; }
|
const QScriptString& getGeneratorString() const { return _generatorString; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
||||||
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
QNetworkAccessManager* _networkAccessManager;
|
|
||||||
QScriptEngine* _engine;
|
QScriptEngine* _engine;
|
||||||
QHash<QUrl, QWeakPointer<NetworkProgram> > _networkPrograms;
|
|
||||||
QHash<ParameterizedURL, QWeakPointer<NetworkValue> > _networkValues;
|
QHash<ParameterizedURL, QWeakPointer<NetworkValue> > _networkValues;
|
||||||
QScriptString _parametersString;
|
QScriptString _parametersString;
|
||||||
QScriptString _lengthString;
|
QScriptString _lengthString;
|
||||||
|
@ -69,13 +63,12 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A program loaded from the network.
|
/// A program loaded from the network.
|
||||||
class NetworkProgram : public QObject {
|
class NetworkProgram : public Resource {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
NetworkProgram(ScriptCache* cache, const QUrl& url);
|
NetworkProgram(ScriptCache* cache, const QUrl& url);
|
||||||
~NetworkProgram();
|
|
||||||
|
|
||||||
ScriptCache* getCache() const { return _cache; }
|
ScriptCache* getCache() const { return _cache; }
|
||||||
|
|
||||||
|
@ -87,18 +80,13 @@ signals:
|
||||||
|
|
||||||
void loaded();
|
void loaded();
|
||||||
|
|
||||||
private slots:
|
protected:
|
||||||
|
|
||||||
void makeRequest();
|
virtual void downloadFinished(QNetworkReply* reply);
|
||||||
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
|
||||||
void handleReplyError();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
ScriptCache* _cache;
|
ScriptCache* _cache;
|
||||||
QNetworkRequest _request;
|
|
||||||
QNetworkReply* _reply;
|
|
||||||
int _attempts;
|
|
||||||
QScriptProgram _program;
|
QScriptProgram _program;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
199
libraries/shared/src/ResourceCache.cpp
Normal file
199
libraries/shared/src/ResourceCache.cpp
Normal file
|
@ -0,0 +1,199 @@
|
||||||
|
//
|
||||||
|
// ResourceCache.cpp
|
||||||
|
// shared
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 2/27/14.
|
||||||
|
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <cfloat>
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QtDebug>
|
||||||
|
|
||||||
|
#include "ResourceCache.h"
|
||||||
|
|
||||||
|
ResourceCache::ResourceCache(QObject* parent) :
|
||||||
|
QObject(parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
QSharedPointer<Resource> ResourceCache::getResource(const QUrl& url, const QUrl& fallback, bool delayLoad, void* extra) {
|
||||||
|
if (!url.isValid() && fallback.isValid()) {
|
||||||
|
return getResource(fallback, QUrl(), delayLoad);
|
||||||
|
}
|
||||||
|
QSharedPointer<Resource> resource = _resources.value(url);
|
||||||
|
if (resource.isNull()) {
|
||||||
|
resource = createResource(url, fallback.isValid() ?
|
||||||
|
getResource(fallback, QUrl(), true) : QSharedPointer<Resource>(), delayLoad, extra);
|
||||||
|
_resources.insert(url, resource);
|
||||||
|
}
|
||||||
|
return resource;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceCache::attemptRequest(Resource* resource) {
|
||||||
|
if (_requestLimit <= 0) {
|
||||||
|
// wait until a slot becomes available
|
||||||
|
_pendingRequests.append(resource);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_requestLimit--;
|
||||||
|
resource->makeRequest();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ResourceCache::requestCompleted() {
|
||||||
|
_requestLimit++;
|
||||||
|
|
||||||
|
// look for the highest priority pending request
|
||||||
|
int highestIndex = -1;
|
||||||
|
float highestPriority = -FLT_MAX;
|
||||||
|
for (int i = 0; i < _pendingRequests.size(); ) {
|
||||||
|
Resource* resource = _pendingRequests.at(i).data();
|
||||||
|
if (!resource) {
|
||||||
|
_pendingRequests.removeAt(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
float priority = resource->getLoadPriority();
|
||||||
|
if (priority >= highestPriority) {
|
||||||
|
highestPriority = priority;
|
||||||
|
highestIndex = i;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
if (highestIndex >= 0) {
|
||||||
|
attemptRequest(_pendingRequests.takeAt(highestIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkAccessManager* ResourceCache::_networkAccessManager = NULL;
|
||||||
|
|
||||||
|
const int DEFAULT_REQUEST_LIMIT = 10;
|
||||||
|
int ResourceCache::_requestLimit = DEFAULT_REQUEST_LIMIT;
|
||||||
|
|
||||||
|
QList<QPointer<Resource> > ResourceCache::_pendingRequests;
|
||||||
|
|
||||||
|
Resource::Resource(const QUrl& url, bool delayLoad) :
|
||||||
|
_request(url),
|
||||||
|
_startedLoading(false),
|
||||||
|
_failedToLoad(false),
|
||||||
|
_attempts(0),
|
||||||
|
_reply(NULL) {
|
||||||
|
|
||||||
|
if (!url.isValid()) {
|
||||||
|
_startedLoading = _failedToLoad = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_request.setAttribute(QNetworkRequest::CacheLoadControlAttribute, QNetworkRequest::PreferCache);
|
||||||
|
|
||||||
|
// start loading immediately unless instructed otherwise
|
||||||
|
if (!delayLoad) {
|
||||||
|
attemptRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource::~Resource() {
|
||||||
|
if (_reply) {
|
||||||
|
ResourceCache::requestCompleted();
|
||||||
|
delete _reply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::ensureLoading() {
|
||||||
|
if (!_startedLoading) {
|
||||||
|
attemptRequest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::setLoadPriority(const QPointer<QObject>& owner, float priority) {
|
||||||
|
_loadPriorities.insert(owner, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities) {
|
||||||
|
for (QHash<QPointer<QObject>, float>::const_iterator it = priorities.constBegin();
|
||||||
|
it != priorities.constEnd(); it++) {
|
||||||
|
_loadPriorities.insert(it.key(), it.value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::clearLoadPriority(const QPointer<QObject>& owner) {
|
||||||
|
_loadPriorities.remove(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
float Resource::getLoadPriority() {
|
||||||
|
float highestPriority = -FLT_MAX;
|
||||||
|
for (QHash<QPointer<QObject>, float>::iterator it = _loadPriorities.begin(); it != _loadPriorities.end(); ) {
|
||||||
|
if (it.key().isNull()) {
|
||||||
|
it = _loadPriorities.erase(it);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
highestPriority = qMax(highestPriority, it.value());
|
||||||
|
it++;
|
||||||
|
}
|
||||||
|
return highestPriority;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::attemptRequest() {
|
||||||
|
_startedLoading = true;
|
||||||
|
ResourceCache::attemptRequest(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal) {
|
||||||
|
if (!_reply->isFinished()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_reply->disconnect(this);
|
||||||
|
_reply->deleteLater();
|
||||||
|
QNetworkReply* reply = _reply;
|
||||||
|
_reply = NULL;
|
||||||
|
ResourceCache::requestCompleted();
|
||||||
|
|
||||||
|
downloadFinished(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::handleReplyError() {
|
||||||
|
QDebug debug = qDebug() << _reply->errorString();
|
||||||
|
|
||||||
|
QNetworkReply::NetworkError error = _reply->error();
|
||||||
|
_reply->disconnect(this);
|
||||||
|
_reply->deleteLater();
|
||||||
|
_reply = NULL;
|
||||||
|
ResourceCache::requestCompleted();
|
||||||
|
|
||||||
|
// retry for certain types of failures
|
||||||
|
switch (error) {
|
||||||
|
case QNetworkReply::RemoteHostClosedError:
|
||||||
|
case QNetworkReply::TimeoutError:
|
||||||
|
case QNetworkReply::TemporaryNetworkFailureError:
|
||||||
|
case QNetworkReply::ProxyConnectionClosedError:
|
||||||
|
case QNetworkReply::ProxyTimeoutError:
|
||||||
|
case QNetworkReply::UnknownNetworkError:
|
||||||
|
case QNetworkReply::UnknownProxyError:
|
||||||
|
case QNetworkReply::UnknownContentError:
|
||||||
|
case QNetworkReply::ProtocolFailure: {
|
||||||
|
// retry with increasing delays
|
||||||
|
const int MAX_ATTEMPTS = 8;
|
||||||
|
const int BASE_DELAY_MS = 1000;
|
||||||
|
if (++_attempts < MAX_ATTEMPTS) {
|
||||||
|
QTimer::singleShot(BASE_DELAY_MS * (int)pow(2.0, _attempts), this, SLOT(attemptRequest()));
|
||||||
|
debug << " -- retrying...";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// fall through to final failure
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
_failedToLoad = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Resource::makeRequest() {
|
||||||
|
_reply = ResourceCache::getNetworkAccessManager()->get(_request);
|
||||||
|
|
||||||
|
connect(_reply, SIGNAL(downloadProgress(qint64,qint64)), SLOT(handleDownloadProgress(qint64,qint64)));
|
||||||
|
connect(_reply, SIGNAL(error(QNetworkReply::NetworkError)), SLOT(handleReplyError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint qHash(const QPointer<QObject>& value, uint seed) {
|
||||||
|
return qHash(value.data(), seed);
|
||||||
|
}
|
121
libraries/shared/src/ResourceCache.h
Normal file
121
libraries/shared/src/ResourceCache.h
Normal file
|
@ -0,0 +1,121 @@
|
||||||
|
//
|
||||||
|
// ResourceCache.h
|
||||||
|
// shared
|
||||||
|
//
|
||||||
|
// Created by Andrzej Kapolka on 2/27/14.
|
||||||
|
// Copyright (c) 2014 High Fidelity, Inc. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef __shared__ResourceCache__
|
||||||
|
#define __shared__ResourceCache__
|
||||||
|
|
||||||
|
#include <QHash>
|
||||||
|
#include <QList>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QSharedPointer>
|
||||||
|
#include <QUrl>
|
||||||
|
#include <QWeakPointer>
|
||||||
|
|
||||||
|
class QNetworkAccessManager;
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
class Resource;
|
||||||
|
|
||||||
|
/// Base class for resource caches.
|
||||||
|
class ResourceCache : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void setNetworkAccessManager(QNetworkAccessManager* manager) { _networkAccessManager = manager; }
|
||||||
|
static QNetworkAccessManager* getNetworkAccessManager() { return _networkAccessManager; }
|
||||||
|
|
||||||
|
static void setRequestLimit(int limit) { _requestLimit = limit; }
|
||||||
|
static int getRequestLimit() { return _requestLimit; }
|
||||||
|
|
||||||
|
ResourceCache(QObject* parent = NULL);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
/// Loads a resource from the specified URL.
|
||||||
|
/// \param fallback a fallback URL to load if the desired one is unavailable
|
||||||
|
/// \param delayLoad if true, don't load the resource immediately; wait until load is first requested
|
||||||
|
/// \param extra extra data to pass to the creator, if appropriate
|
||||||
|
QSharedPointer<Resource> getResource(const QUrl& url, const QUrl& fallback = QUrl(),
|
||||||
|
bool delayLoad = false, void* extra = NULL);
|
||||||
|
|
||||||
|
/// Creates a new resource.
|
||||||
|
virtual QSharedPointer<Resource> createResource(const QUrl& url,
|
||||||
|
const QSharedPointer<Resource>& fallback, bool delayLoad, const void* extra) = 0;
|
||||||
|
|
||||||
|
static void attemptRequest(Resource* resource);
|
||||||
|
static void requestCompleted();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
friend class Resource;
|
||||||
|
|
||||||
|
QHash<QUrl, QWeakPointer<Resource> > _resources;
|
||||||
|
|
||||||
|
static QNetworkAccessManager* _networkAccessManager;
|
||||||
|
static int _requestLimit;
|
||||||
|
static QList<QPointer<Resource> > _pendingRequests;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Base class for resources.
|
||||||
|
class Resource : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
Resource(const QUrl& url, bool delayLoad = false);
|
||||||
|
~Resource();
|
||||||
|
|
||||||
|
/// Makes sure that the resource has started loading.
|
||||||
|
void ensureLoading();
|
||||||
|
|
||||||
|
/// Sets the load priority for one owner.
|
||||||
|
virtual void setLoadPriority(const QPointer<QObject>& owner, float priority);
|
||||||
|
|
||||||
|
/// Sets a set of priorities at once.
|
||||||
|
virtual void setLoadPriorities(const QHash<QPointer<QObject>, float>& priorities);
|
||||||
|
|
||||||
|
/// Clears the load priority for one owner.
|
||||||
|
virtual void clearLoadPriority(const QPointer<QObject>& owner);
|
||||||
|
|
||||||
|
/// Returns the highest load priority across all owners.
|
||||||
|
float getLoadPriority();
|
||||||
|
|
||||||
|
protected slots:
|
||||||
|
|
||||||
|
void attemptRequest();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual void downloadFinished(QNetworkReply* reply) = 0;
|
||||||
|
|
||||||
|
QNetworkRequest _request;
|
||||||
|
bool _startedLoading;
|
||||||
|
bool _failedToLoad;
|
||||||
|
QHash<QPointer<QObject>, float> _loadPriorities;
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
|
||||||
|
void handleDownloadProgress(qint64 bytesReceived, qint64 bytesTotal);
|
||||||
|
void handleReplyError();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void makeRequest();
|
||||||
|
|
||||||
|
friend class ResourceCache;
|
||||||
|
|
||||||
|
QNetworkReply* _reply;
|
||||||
|
int _attempts;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint qHash(const QPointer<QObject>& value, uint seed = 0);
|
||||||
|
|
||||||
|
#endif /* defined(__shared__ResourceCache__) */
|
Loading…
Reference in a new issue