Merge branch 'temp0bis' of https://github.com/samcake/hifi into temp0

This commit is contained in:
Sam Gateau 2015-01-21 16:54:11 -08:00
commit 18b3b1a6b1
19 changed files with 873 additions and 785 deletions

View file

@ -44,10 +44,16 @@ var entityListTool = EntityListTool();
var hasShownPropertiesTool = false; var hasShownPropertiesTool = false;
var entityListVisible = false;
selectionManager.addEventListener(function() { selectionManager.addEventListener(function() {
selectionDisplay.updateHandles(); selectionDisplay.updateHandles();
if (selectionManager.hasSelection() && !hasShownPropertiesTool) { if (selectionManager.hasSelection() && !hasShownPropertiesTool) {
// Open properties and model list, but force selection of model list tab
propertiesTool.setVisible(false);
entityListTool.setVisible(false);
propertiesTool.setVisible(true); propertiesTool.setVisible(true);
entityListTool.setVisible(true);
hasShownPropertiesTool = true; hasShownPropertiesTool = true;
} }
}); });
@ -690,8 +696,8 @@ function setupModelMenus() {
print("delete exists... don't add ours"); print("delete exists... don't add ours");
} }
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Model List...", afterItem: "Models" }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Entity List...", shortcutKey: "CTRL+META+L", afterItem: "Models" });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Model List..." }); Menu.addMenuItem({ menuName: "Edit", menuItemName: "Paste Models", shortcutKey: "CTRL+META+V", afterItem: "Entity List..." });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L", Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Large Models", shortcutKey: "CTRL+META+L",
afterItem: "Paste Models", isCheckable: true, isChecked: true }); afterItem: "Paste Models", isCheckable: true, isChecked: true });
Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S", Menu.addMenuItem({ menuName: "Edit", menuItemName: "Allow Selecting of Small Models", shortcutKey: "CTRL+META+S",
@ -703,6 +709,7 @@ function setupModelMenus() {
Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Export Models", shortcutKey: "CTRL+META+E", afterItem: "Models" });
Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" }); Menu.addMenuItem({ menuName: "File", menuItemName: "Import Models", shortcutKey: "CTRL+META+I", afterItem: "Export Models" });
Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED, Menu.addMenuItem({ menuName: "View", menuItemName: MENU_EASE_ON_FOCUS, afterItem: MENU_INSPECT_TOOL_ENABLED,
isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" }); isCheckable: true, isChecked: Settings.getValue(SETTING_EASE_ON_FOCUS) == "true" });
@ -718,7 +725,7 @@ function cleanupModelMenus() {
Menu.removeMenuItem("Edit", "Delete"); Menu.removeMenuItem("Edit", "Delete");
} }
Menu.removeMenuItem("Edit", "Model List..."); Menu.removeMenuItem("Edit", "Entity List...");
Menu.removeMenuItem("Edit", "Paste Models"); Menu.removeMenuItem("Edit", "Paste Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Large Models"); Menu.removeMenuItem("Edit", "Allow Selecting of Large Models");
Menu.removeMenuItem("Edit", "Allow Selecting of Small Models"); Menu.removeMenuItem("Edit", "Allow Selecting of Small Models");
@ -755,14 +762,7 @@ Script.update.connect(function (deltaTime) {
selectionDisplay.checkMove(); selectionDisplay.checkMove();
}); });
function handeMenuEvent(menuItem) { function deleteSelectedEntities() {
if (menuItem == "Allow Selecting of Small Models") {
allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
} else if (menuItem == "Allow Selecting of Large Models") {
allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models");
} else if (menuItem == "Allow Selecting of Lights") {
Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
} else if (menuItem == "Delete") {
if (SelectionManager.hasSelection()) { if (SelectionManager.hasSelection()) {
print(" Delete Entities"); print(" Delete Entities");
SelectionManager.saveProperties(); SelectionManager.saveProperties();
@ -782,38 +782,17 @@ function handeMenuEvent(menuItem) {
} else { } else {
print(" Delete Entity.... not holding..."); print(" Delete Entity.... not holding...");
} }
} else if (menuItem == "Model List...") { }
var models = new Array();
models = Entities.findEntities(MyAvatar.position, Number.MAX_VALUE); function handeMenuEvent(menuItem) {
for (var i = 0; i < models.length; i++) { if (menuItem == "Allow Selecting of Small Models") {
models[i].properties = Entities.getEntityProperties(models[i]); allowSmallModels = Menu.isOptionChecked("Allow Selecting of Small Models");
models[i].toString = function() { } else if (menuItem == "Allow Selecting of Large Models") {
var modelname; allowLargeModels = Menu.isOptionChecked("Allow Selecting of Large Models");
if (this.properties.type == "Model") { } else if (menuItem == "Allow Selecting of Lights") {
modelname = decodeURIComponent( Entities.setLightsArePickable(Menu.isOptionChecked("Allow Selecting of Lights"));
this.properties.modelURL.indexOf("/") != -1 ? } else if (menuItem == "Delete") {
this.properties.modelURL.substring(this.properties.modelURL.lastIndexOf("/") + 1) : deleteSelectedEntities();
this.properties.modelURL);
} else {
modelname = this.properties.id;
}
return "[" + this.properties.type + "] " + modelname;
};
}
var form = [{label: "Model: ", options: models}];
form.push({label: "Action: ", options: ["Properties", "Delete", "Teleport"]});
form.push({ button: "Cancel" });
if (Window.form("Model List", form)) {
var selectedModel = form[0].value;
if (form[1].value == "Properties") {
editModelID = selectedModel;
entityPropertyDialogBox.openDialog(editModelID);
} else if (form[1].value == "Delete") {
Entities.deleteEntity(selectedModel);
} else if (form[1].value == "Teleport") {
MyAvatar.position = selectedModel.properties.position;
}
}
} else if (menuItem == "Paste Models") { } else if (menuItem == "Paste Models") {
modelImporter.paste(); modelImporter.paste();
} else if (menuItem == "Export Models") { } else if (menuItem == "Export Models") {
@ -826,6 +805,10 @@ function handeMenuEvent(menuItem) {
} }
} else if (menuItem == "Import Models") { } else if (menuItem == "Import Models") {
modelImporter.doImport(); modelImporter.doImport();
} else if (menuItem == "Entity List...") {
if (isActive) {
entityListTool.toggleVisible();
}
} }
tooltip.show(false); tooltip.show(false);
} }
@ -842,7 +825,7 @@ Controller.keyPressEvent.connect(function(event) {
Controller.keyReleaseEvent.connect(function (event) { Controller.keyReleaseEvent.connect(function (event) {
// since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items
if (event.text == "BACKSPACE" || event.text == "DELETE") { if (event.text == "BACKSPACE" || event.text == "DELETE") {
handeMenuEvent("Delete"); deleteSelectedEntities();
} else if (event.text == "TAB") { } else if (event.text == "TAB") {
selectionDisplay.toggleSpaceMode(); selectionDisplay.toggleSpaceMode();
} else if (event.text == "f") { } else if (event.text == "f") {

View file

@ -1,14 +1,32 @@
<html> <html>
<head> <head>
<link rel="stylesheet" type="text/css" href="style.css"> <link rel="stylesheet" type="text/css" href="style.css">
<script src="list.min.js"></script>
<script> <script>
var entities = {}; var entities = {};
var selectedEntities = []; var selectedEntities = [];
var currentSortColumn = 'type';
var currentSortOrder = 'asc';
var entityList = null;
var refreshEntityListTimer = null;
var ASC_STRING = '&nbsp;&#x25BE;';
var DESC_STRING = '&nbsp;&#x25B4;';
function loaded() { function loaded() {
entityList = new List('entity-list', { valueNames: ['type', 'url']});
entityList.clear();
elEntityTable = document.getElementById("entity-table"); elEntityTable = document.getElementById("entity-table");
elEntityTableBody = document.getElementById("entity-table-body");
elRefresh = document.getElementById("refresh"); elRefresh = document.getElementById("refresh");
elDelete = document.getElementById("delete");
elTeleport = document.getElementById("teleport");
document.getElementById("entity-type").onclick = function() {
setSortColumn('type');
};
document.getElementById("entity-url").onclick = function() {
setSortColumn('url');
};
function onRowClicked(e) { function onRowClicked(e) {
var id = this.dataset.entityId; var id = this.dataset.entityId;
@ -20,7 +38,8 @@
selectedEntities = selection; selectedEntities = selection;
entities[id].el.className = 'selected'; this.className = 'selected';
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "selectionUpdate", type: "selectionUpdate",
focus: false, focus: false,
@ -29,8 +48,6 @@
} }
function onRowDoubleClicked() { function onRowDoubleClicked() {
var id = this.dataset.entityId;
EventBridge.emitWebEvent(JSON.stringify({ EventBridge.emitWebEvent(JSON.stringify({
type: "selectionUpdate", type: "selectionUpdate",
focus: true, focus: true,
@ -40,41 +57,91 @@
function addEntity(id, type, url) { function addEntity(id, type, url) {
if (entities[id] === undefined) { if (entities[id] === undefined) {
var el = document.createElement('tr'); var urlParts = url.split('/');
el.setAttribute('id', 'entity_' + id); var filename = urlParts[urlParts.length - 1];
el.innerHTML += "<td>" + type + "</td>";
el.innerHTML += "<td>" + url + "</td>";
el.dataset.entityId = id;
el.onclick = onRowClicked;
el.ondblclick = onRowDoubleClicked;
elEntityTable.appendChild(el);
// Add element to local dict entityList.add([{ id: id, type: type, url: filename }], function(items) {
var el = items[0].elm;
var id = items[0]._values.id;
entities[id] = { entities[id] = {
id: id, id: id,
name: id, name: id,
el: el, el: el,
}; };
} el.setAttribute('id', 'entity_' + id);
} el.setAttribute('title', url);
el.dataset.entityId = id;
el.onclick = onRowClicked;
el.ondblclick = onRowDoubleClicked;
el.innerHTML
});
function removeEntity(id) { if (refreshEntityListTimer) {
if (entities[id] !== undefined) { clearTimeout(refreshEntityListTimer);
elEntityTable.removeChild(entities[id].el); }
delete entities[id]; refreshEntityListTimer = setTimeout(refreshEntityListObject, 50);
} }
} }
function clearEntities() { function clearEntities() {
for (id in entities) {
elEntityTable.removeChild(entities[id].el);
}
entities = {}; entities = {};
entityList.clear();
}
var elSortOrder = {
type: document.querySelector('#entity-type .sort-order'),
url: document.querySelector('#entity-url .sort-order'),
}
function setSortColumn(column) {
if (currentSortColumn == column) {
currentSortOrder = currentSortOrder == "asc" ? "desc" : "asc";
} else {
elSortOrder[currentSortColumn].style.display = 'none';
elSortOrder[column].style.display = 'inline';
currentSortColumn = column;
currentSortOrder = "asc";
}
elSortOrder[column].innerHTML = currentSortOrder == "asc" ? ASC_STRING : DESC_STRING;
entityList.sort(currentSortColumn, { order: currentSortOrder });
}
function refreshEntities() {
clearEntities();
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' }));
}
function refreshEntityListObject() {
refreshEntityListTimer = null;
entityList.sort(currentSortColumn, { order: currentSortOrder });
entityList.search(document.getElementById("filter").value);
}
function updateSelectedEntities(selectedEntities) {
var notFound = false;
for (var id in entities) {
entities[id].el.className = '';
}
for (var i = 0; i < selectedEntities.length; i++) {
var id = selectedEntities[i];
if (id in entities) {
var entity = entities[id];
entity.el.className = 'selected';
} else {
notFound = true;
}
}
return notFound;
} }
elRefresh.onclick = function() { elRefresh.onclick = function() {
clearEntities(); refreshEntities();
EventBridge.emitWebEvent(JSON.stringify({ type: 'refresh' })); }
elTeleport.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'teleport' }));
}
elDelete.onclick = function() {
EventBridge.emitWebEvent(JSON.stringify({ type: 'delete' }));
refreshEntities();
} }
if (window.EventBridge !== undefined) { if (window.EventBridge !== undefined) {
@ -82,16 +149,9 @@
data = JSON.parse(data); data = JSON.parse(data);
if (data.type == "selectionUpdate") { if (data.type == "selectionUpdate") {
selectedEntities = data.selectedIDs; var notFound = updateSelectedEntities(data.selectedIDs);
for (var id in entities) { if (notFound) {
entities[id].el.className = ''; refreshEntities();
}
for (var i = 0; i < data.selectedIDs.length; i++) {
var id = data.selectedIDs[i];
if (id in entities) {
var entity = entities[id];
entity.el.className = 'selected';
}
} }
} else if (data.type == "update") { } else if (data.type == "update") {
var newEntities = data.entities; var newEntities = data.entities;
@ -99,27 +159,39 @@
var id = newEntities[i].id; var id = newEntities[i].id;
addEntity(id, newEntities[i].type, newEntities[i].url); addEntity(id, newEntities[i].type, newEntities[i].url);
} }
updateSelectedEntities(data.selectedIDs);
} }
}); });
setTimeout(refreshEntities, 1000);
} }
} }
</script> </script>
</head> </head>
<body onload='loaded();'> <body onload='loaded();'>
<div> <div>
<button id="refresh">Refresh</button> <input type="button" id="refresh" value="Refresh"></button>
<input type="button" id="teleport" value="Teleport"></button>
<input type="button" id="delete" style="background-color: rgb(244, 64, 64); float: right" value="Delete"></button>
</div> </div>
<div id="entity-list">
<input type="text" class="search" id="filter" placeholder="Filter" />
<table id="entity-table"> <table id="entity-table">
<thead> <thead>
<tr> <tr>
<th id="entity-type">Type</th> <th id="entity-type" data-sort="type">Type <span class="sort-order" style="display: inline">&nbsp;&#x25BE;</span></th>
<th id="entity-url">URL</th> <th id="entity-url" data-sort="url">URL <span class="sort-order" style="display: none">&nbsp;&#x25BE;</span></th>
</tr> </tr>
</thead> </thead>
<tbody id="entity-table-body"> <tbody class="list" id="entity-table-body">
<tr>
<td class="id" style="display: none">Type</td>
<td class="type">Type</td>
<td class="url"><div class='outer'><div class='inner'>URL</div></div></td>
</tr>
</tbody> </tbody>
</table> </table>
</div>
</body> </body>
</html> </html>

1
examples/html/list.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -5,9 +5,10 @@ body {
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #efefef; background-color: rgb(76, 76, 76);
color: rgb(204, 204, 204);
font-family: Arial; font-family: Arial;
font-size: 11.5px; font-size: 11px;
-webkit-touch-callout: none; -webkit-touch-callout: none;
-webkit-user-select: none; -webkit-user-select: none;
@ -17,12 +18,6 @@ body {
user-select: none; user-select: none;
} }
body.properties {
background-color: rgb(76, 76, 76);
color: rgb(204, 204, 204);
font-size: 11px;
}
.selectable { .selectable {
-webkit-touch-callout: text; -webkit-touch-callout: text;
-webkit-user-select: text; -webkit-user-select: text;
@ -117,12 +112,13 @@ input.coord {
table#entity-table { table#entity-table {
border-collapse: collapse; border-collapse: collapse;
font-family: Sans-Serif; font-family: Sans-Serif;
/* font-size: 12px; */ font-size: 10px;
width: 100%; width: 100%;
} }
#entity-table tr { #entity-table tr {
cursor: pointer; cursor: pointer;
border-bottom: 1px solid rgb(63, 63, 63)
} }
#entity-table tr.selected { #entity-table tr.selected {
@ -139,17 +135,22 @@ table#entity-table {
} }
#entity-table td { #entity-table td {
font-size: 11px;
border: 0px black solid; border: 0px black solid;
word-wrap: nowrap; word-wrap: nowrap;
white-space: nowrap; white-space: nowrap;
text-overflow: ellipsis;
}
#entity-table td.url {
white-space: nowrap;
overflow: hidden;
} }
th#entity-type { th#entity-type {
width: 60px; width: 60px;
} }
th#entity-url {
}
div.input-area { div.input-area {
@ -226,3 +227,20 @@ table#properties-list {
col#col-label { col#col-label {
width: 130px; width: 130px;
} }
div.outer {
position: relative;
}
div.inner {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
position: absolute;
width: 100%;
}
td {
vertical-align: top;
}

View file

@ -13,6 +13,10 @@ EntityListTool = function(opts) {
webView.setVisible(visible); webView.setVisible(visible);
}; };
that.toggleVisible = function() {
that.setVisible(!visible);
}
selectionManager.addEventListener(function() { selectionManager.addEventListener(function() {
var selectedIDs = []; var selectedIDs = [];
@ -24,10 +28,38 @@ EntityListTool = function(opts) {
type: 'selectionUpdate', type: 'selectionUpdate',
selectedIDs: selectedIDs, selectedIDs: selectedIDs,
}; };
print("Sending: " + JSON.stringify(data));
webView.eventBridge.emitScriptEvent(JSON.stringify(data)); webView.eventBridge.emitScriptEvent(JSON.stringify(data));
}); });
function sendUpdate() {
var entities = [];
var ids = Entities.findEntities(MyAvatar.position, 100);
for (var i = 0; i < ids.length; i++) {
var id = ids[i];
var properties = Entities.getEntityProperties(id);
entities.push({
id: id.id,
type: properties.type,
url: properties.type == "Model" ? properties.modelURL : "",
});
}
var selectedIDs = [];
for (var i = 0; i < selectionManager.selections.length; i++) {
selectedIDs.push(selectionManager.selections[i].id);
}
var data = {
type: "update",
entities: entities,
selectedIDs: selectedIDs,
};
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
}
webView.eventBridge.webEventReceived.connect(function(data) { webView.eventBridge.webEventReceived.connect(function(data) {
print("Got: " + data);
data = JSON.parse(data); data = JSON.parse(data);
if (data.type == "selectionUpdate") { if (data.type == "selectionUpdate") {
var ids = data.entityIds; var ids = data.entityIds;
@ -46,22 +78,13 @@ EntityListTool = function(opts) {
Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); Menu.isOptionChecked(MENU_EASE_ON_FOCUS));
} }
} else if (data.type == "refresh") { } else if (data.type == "refresh") {
var entities = []; sendUpdate();
var ids = Entities.findEntities(MyAvatar.position, 100); } else if (data.type == "teleport") {
for (var i = 0; i < ids.length; i++) { if (selectionManager.hasSelection()) {
var id = ids[i]; MyAvatar.position = selectionManager.worldPosition;
var properties = Entities.getEntityProperties(id);
entities.push({
id: id.id,
type: properties.type,
url: properties.type == "Model" ? properties.modelURL : "",
});
} }
var data = { } else if (data.type == "delete") {
type: "update", deleteSelectedEntities();
entities: entities,
};
webView.eventBridge.emitScriptEvent(JSON.stringify(data));
} }
}); });

View file

@ -43,5 +43,5 @@ void main(void) {
gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0)); gl_FrontColor = vec4(1.0, 1.0, 1.0, step(height, 0.0));
// pass along the scaled/offset texture coordinates // pass along the scaled/offset texture coordinates
gl_TexCoord[0] = vec4((heightCoord - heightScale.st) * colorScale, 0.0, 1.0); gl_TexCoord[0] = vec4((heightCoord - vec2(0.5, 0.5)) * colorScale + vec2(0.5, 0.5), 0.0, 1.0);
} }

View file

@ -58,7 +58,7 @@ void main(void) {
gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0); gl_TexCoord[3] = textureSpacePosition * vec4(splatTextureScalesS[3], splatTextureScalesT[3], 0.0, 1.0);
// compute the alpha values for each texture // compute the alpha values for each texture
float value = texture2D(textureMap, (gl_MultiTexCoord0.st - heightScale) * textureScale).r; float value = texture2D(textureMap, (gl_MultiTexCoord0.st - vec2(0.5, 0.5)) * textureScale + vec2(0.5, 0.5)).r;
vec4 valueVector = vec4(value, value, value, value); vec4 valueVector = vec4(value, value, value, value);
alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima); alphaValues = step(textureValueMinima, valueVector) * step(valueVector, textureValueMaxima);
} }

View file

@ -1425,8 +1425,18 @@ public:
char material; char material;
void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; } void setColorMaterial(const StackArray::Entry& entry) { color = entry.color; material = entry.material; }
void mix(const EdgeCrossing& first, const EdgeCrossing& second, float t);
}; };
void EdgeCrossing::mix(const EdgeCrossing& first, const EdgeCrossing& second, float t) {
point = glm::mix(first.point, second.point, t);
normal = glm::normalize(glm::mix(first.normal, second.normal, t));
color = qRgb(glm::mix(qRed(first.color), qRed(second.color), t), glm::mix(qGreen(first.color), qGreen(second.color), t),
glm::mix(qBlue(first.color), qBlue(second.color), t));
material = (t < 0.5f) ? first.material : second.material;
}
const int MAX_NORMALS_PER_VERTEX = 4; const int MAX_NORMALS_PER_VERTEX = 4;
class NormalIndex { class NormalIndex {
@ -1480,7 +1490,6 @@ public:
void swap(IndexVector& other) { QVector<NormalIndex>::swap(other); qSwap(position, other.position); } void swap(IndexVector& other) { QVector<NormalIndex>::swap(other); qSwap(position, other.position); }
const NormalIndex& get(int y) const; const NormalIndex& get(int y) const;
const NormalIndex& getClosest(int y) const;
}; };
const NormalIndex& IndexVector::get(int y) const { const NormalIndex& IndexVector::get(int y) const {
@ -1489,56 +1498,6 @@ const NormalIndex& IndexVector::get(int y) const {
return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex; return (relative >= 0 && relative < size()) ? at(relative) : invalidIndex;
} }
const NormalIndex& IndexVector::getClosest(int y) const {
static NormalIndex invalidIndex = { { -1, -1, -1, -1 } };
int relative = y - position;
if (relative < 0 || relative >= size()) {
return invalidIndex;
}
const NormalIndex& first = at(relative);
if (first.isValid()) {
return first;
}
for (int distance = 1; relative - distance >= 0 || relative + distance < size(); distance++) {
int previous = relative - distance;
if (previous >= 0) {
const NormalIndex& previousIndex = at(previous);
if (previousIndex.isValid()) {
return previousIndex;
}
}
int next = relative + distance;
if (next < size()) {
const NormalIndex& nextIndex = at(next);
if (nextIndex.isValid()) {
return nextIndex;
}
}
}
return invalidIndex;
}
static inline void appendIndices(QVector<int>& indices, QMultiHash<VoxelCoord, int>& quadIndices,
const QVector<VoxelPoint>& vertices, float step, int i0, int i1, int i2, int i3) {
int newIndices[] = { i0, i1, i2, i3 };
glm::vec3 minima(FLT_MAX, FLT_MAX, FLT_MAX), maxima(-FLT_MAX, -FLT_MAX, -FLT_MAX);
int indexIndex = indices.size();
for (unsigned int i = 0; i < sizeof(newIndices) / sizeof(newIndices[0]); i++) {
int index = newIndices[i];
indices.append(index);
const glm::vec3& vertex = vertices.at(index).vertex;
minima = glm::min(vertex, minima);
maxima = glm::max(vertex, maxima);
}
for (int z = (int)minima.z, endZ = (int)glm::ceil(maxima.z); z < endZ; z++) {
for (int y = (int)minima.x, endY = (int)glm::ceil(maxima.y); y < endY; y++) {
for (int x = (int)minima.x, endX = (int)glm::ceil(maxima.x); x < endX; x++) {
quadIndices.insert(qRgb(x, y, z), indexIndex);
}
}
}
}
void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation, void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const glm::vec3& translation,
const glm::quat& rotation, const glm::vec3& scale, bool cursor) { const glm::quat& rotation, const glm::vec3& scale, bool cursor) {
if (!node->getHeight()) { if (!node->getHeight()) {
@ -1617,7 +1576,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
glGenTextures(1, &_colorTextureID); glGenTextures(1, &_colorTextureID);
glBindTexture(GL_TEXTURE_2D, _colorTextureID); glBindTexture(GL_TEXTURE_2D, _colorTextureID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
if (node->getColor()) { if (node->getColor()) {
@ -1625,6 +1584,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, node->getColor()->getWidth(), glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB8, node->getColor()->getWidth(),
contents.size() / (node->getColor()->getWidth() * DataBlock::COLOR_BYTES), contents.size() / (node->getColor()->getWidth() * DataBlock::COLOR_BYTES),
0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData()); 0, GL_RGB, GL_UNSIGNED_BYTE, contents.constData());
glGenerateMipmap(GL_TEXTURE_2D);
} else { } else {
const quint8 WHITE_COLOR[] = { 255, 255, 255 }; const quint8 WHITE_COLOR[] = { 255, 255, 255 };
@ -1705,7 +1665,7 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
} }
const int EDGES_PER_CUBE = 12; const int EDGES_PER_CUBE = 12;
EdgeCrossing crossings[EDGES_PER_CUBE]; EdgeCrossing crossings[EDGES_PER_CUBE * 2];
// as we scan down the cube generating vertices between grid points, we remember the indices of the last // as we scan down the cube generating vertices between grid points, we remember the indices of the last
// (element, line, section--x, y, z) so that we can connect generated vertices as quads // (element, line, section--x, y, z) so that we can connect generated vertices as quads
@ -1736,13 +1696,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
lineSrc[stackWidth].getExtents(minimumY, maximumY); lineSrc[stackWidth].getExtents(minimumY, maximumY);
} }
if (maximumY >= minimumY) { if (maximumY >= minimumY) {
int position = minimumY;
int count = maximumY - minimumY + 1;
NormalIndex lastIndexY = { { -1, -1, -1, -1 } };
indicesX.position = position;
indicesX.resize(count);
indicesZ[x].position = position;
indicesZ[x].resize(count);
float heightfieldHeight = *heightLineSrc * voxelScale; float heightfieldHeight = *heightLineSrc * voxelScale;
float nextHeightfieldHeightX = heightLineSrc[1] * voxelScale; float nextHeightfieldHeightX = heightLineSrc[1] * voxelScale;
float nextHeightfieldHeightZ = heightLineSrc[width] * voxelScale; float nextHeightfieldHeightZ = heightLineSrc[width] * voxelScale;
@ -1753,6 +1706,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
const int LOWER_RIGHT_CORNER = 8; const int LOWER_RIGHT_CORNER = 8;
const int NO_CORNERS = 0; const int NO_CORNERS = 0;
const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER; const int ALL_CORNERS = UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER;
const int CORNER_COUNT = 4;
const int NEXT_CORNERS[] = { 1, 3, 0, 2 };
int corners = NO_CORNERS; int corners = NO_CORNERS;
if (heightfieldHeight != 0.0f) { if (heightfieldHeight != 0.0f) {
corners |= UPPER_LEFT_CORNER; corners |= UPPER_LEFT_CORNER;
@ -1767,37 +1722,38 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
corners |= LOWER_RIGHT_CORNER; corners |= LOWER_RIGHT_CORNER;
} }
bool stitchable = x != 0 && z != 0 && !(corners == NO_CORNERS || corners == ALL_CORNERS); bool stitchable = x != 0 && z != 0 && !(corners == NO_CORNERS || corners == ALL_CORNERS);
VoxelPoint cornerPoints[4]; EdgeCrossing cornerCrossings[CORNER_COUNT];
int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0); int clampedX = qMax(x - 1, 0), clampedZ = qMax(z - 1, 0);
int cornerMinimumY = INT_MAX, cornerMaximumY = -1;
if (stitchable) { if (stitchable) {
for (unsigned int i = 0; i < sizeof(cornerPoints) / sizeof(cornerPoints[0]); i++) { for (int i = 0; i < CORNER_COUNT; i++) {
if (!(corners & (1 << i))) { if (!(corners & (1 << i))) {
continue; continue;
} }
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0; int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0; int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
const quint16* height = heightLineSrc + offsetZ * width + offsetX; const quint16* height = heightLineSrc + offsetZ * width + offsetX;
VoxelPoint& point = cornerPoints[i]; float heightValue = *height * voxelScale;
int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ; int y = (int)heightValue;
point.vertex = glm::vec3(clampedOffsetX, *height * voxelScale, clampedOffsetZ) * step; cornerMinimumY = qMin(cornerMinimumY, y);
cornerMaximumY = qMax(cornerMaximumY, y);
EdgeCrossing& crossing = cornerCrossings[i];
crossing.point = glm::vec3(offsetX, heightValue, offsetZ);
int left = height[-1]; int left = height[-1];
int right = height[1]; int right = height[1];
int down = height[-width]; int down = height[-width];
int up = height[width]; int up = height[width];
glm::vec3 normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right, crossing.normal = glm::normalize(glm::vec3((left == 0 || right == 0) ? 0.0f : left - right,
2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up)); 2.0f / voxelScale, (up == 0 || down == 0) ? 0.0f : down - up));
point.normal[0] = normal.x * numeric_limits<qint8>::max(); int clampedOffsetX = clampedX + offsetX, clampedOffsetZ = clampedZ + offsetZ;
point.normal[1] = normal.y * numeric_limits<qint8>::max();
point.normal[2] = normal.z * numeric_limits<qint8>::max();
if (colorSrc) { if (colorSrc) {
const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth + const uchar* color = colorSrc + ((int)(clampedOffsetZ * colorStepZ) * colorWidth +
(int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES; (int)(clampedOffsetX * colorStepX)) * DataBlock::COLOR_BYTES;
point.color[0] = color[0]; crossing.color = qRgb(color[0], color[1], color[2]);
point.color[1] = color[1];
point.color[2] = color[2];
} else { } else {
point.color[0] = point.color[1] = point.color[2] = numeric_limits<quint8>::max(); crossing.color = qRgb(numeric_limits<quint8>::max(), numeric_limits<quint8>::max(),
numeric_limits<quint8>::max());
} }
int material = 0; int material = 0;
if (materialSrc) { if (materialSrc) {
@ -1812,12 +1768,18 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
material = mapping; material = mapping;
} }
} }
point.materials[0] = material; crossing.material = material;
point.materials[1] = point.materials[2] = point.materials[3] = 0;
point.materialWeights[0] = numeric_limits<quint8>::max();
point.materialWeights[1] = point.materialWeights[2] = point.materialWeights[3] = 0;
} }
minimumY = qMin(minimumY, cornerMinimumY);
maximumY = qMax(maximumY, cornerMaximumY);
} }
int position = minimumY;
int count = maximumY - minimumY + 1;
NormalIndex lastIndexY = { { -1, -1, -1, -1 } };
indicesX.position = position;
indicesX.resize(count);
indicesZ[x].position = position;
indicesZ[x].resize(count);
for (int y = position, end = position + count; y < end; y++) { for (int y = position, end = position + count; y < end; y++) {
const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight); const StackArray::Entry& entry = lineSrc->getEntry(y, heightfieldHeight);
if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) { if (displayHermite && x != 0 && z != 0 && !lineSrc->isEmpty() && y >= lineSrc->getPosition()) {
@ -1875,9 +1837,93 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
if (alphaTotal == 0 || alphaTotal == possibleTotal) { if (alphaTotal == 0 || alphaTotal == possibleTotal) {
continue; // no corners set/all corners set continue; // no corners set/all corners set
} }
// we first look for crossings with the heightfield corner vertices; these take priority
int crossingCount = 0;
if (y >= cornerMinimumY && y <= cornerMaximumY) {
// first look for set corners, which override any interpolated values
int crossedCorners = NO_CORNERS;
for (int i = 0; i < CORNER_COUNT; i++) {
if (!(corners & (1 << i))) {
continue;
}
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
float heightValue = *height * voxelScale;
if (heightValue >= y && heightValue < y + 1) {
crossedCorners |= (1 << i);
}
}
switch (crossedCorners) {
case UPPER_LEFT_CORNER:
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER:
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER:
case UPPER_LEFT_CORNER | LOWER_RIGHT_CORNER:
crossings[crossingCount++] = cornerCrossings[0];
crossings[crossingCount - 1].point.y -= y;
break;
case UPPER_RIGHT_CORNER:
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER:
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER:
case UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER:
crossings[crossingCount++] = cornerCrossings[1];
crossings[crossingCount - 1].point.y -= y;
break;
case LOWER_LEFT_CORNER:
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER:
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER | UPPER_LEFT_CORNER:
crossings[crossingCount++] = cornerCrossings[2];
crossings[crossingCount - 1].point.y -= y;
break;
case LOWER_RIGHT_CORNER:
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER:
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER:
crossings[crossingCount++] = cornerCrossings[3];
crossings[crossingCount - 1].point.y -= y;
break;
case NO_CORNERS:
for (int i = 0; i < CORNER_COUNT; i++) {
if (!(corners & (1 << i))) {
continue;
}
int offsetX = (i & X_MAXIMUM_FLAG) ? 1 : 0;
int offsetZ = (i & Y_MAXIMUM_FLAG) ? 1 : 0;
const quint16* height = heightLineSrc + offsetZ * width + offsetX;
float heightValue = *height * voxelScale;
int nextIndex = NEXT_CORNERS[i];
if (!(corners & (1 << nextIndex))) {
continue;
}
int nextOffsetX = (nextIndex & X_MAXIMUM_FLAG) ? 1 : 0;
int nextOffsetZ = (nextIndex & Y_MAXIMUM_FLAG) ? 1 : 0;
const quint16* nextHeight = heightLineSrc + nextOffsetZ * width + nextOffsetX;
float nextHeightValue = *nextHeight * voxelScale;
float divisor = (nextHeightValue - heightValue);
if (divisor == 0.0f) {
continue;
}
float t1 = (y - heightValue) / divisor;
float t2 = (y + 1 - heightValue) / divisor;
if (t1 >= 0.0f && t1 <= 1.0f) {
crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t1);
crossings[crossingCount - 1].point.y -= y;
}
if (t2 >= 0.0f && t2 <= 1.0f) {
crossings[crossingCount++].mix(cornerCrossings[i], cornerCrossings[nextIndex], t2);
crossings[crossingCount - 1].point.y -= y;
}
}
break;
}
}
// the terrifying conditional code that follows checks each cube edge for a crossing, gathering // the terrifying conditional code that follows checks each cube edge for a crossing, gathering
// its properties (color, material, normal) if one is present; as before, boundary edges are excluded // its properties (color, material, normal) if one is present; as before, boundary edges are excluded
int crossingCount = 0; if (crossingCount == 0) {
const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight); const StackArray::Entry& nextEntryY = lineSrc->getEntry(y + 1, heightfieldHeight);
if (middleX) { if (middleX) {
const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX); const StackArray::Entry& nextEntryX = lineSrc[1].getEntry(y, nextHeightfieldHeightX);
@ -1898,7 +1944,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY); crossing.setColorMaterial(alpha2 == 0 ? nextEntryXY : nextEntryY);
} }
if (middleZ) { if (middleZ) {
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ); const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y,
nextHeightfieldHeightZ);
const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry( const StackArray::Entry& nextEntryXZ = lineSrc[stackWidth + 1].getEntry(
y, nextHeightfieldHeightXZ); y, nextHeightfieldHeightXZ);
const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry( const StackArray::Entry& nextEntryXYZ = lineSrc[stackWidth + 1].getEntry(
@ -1910,7 +1957,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
} }
if (alpha3 != alpha7) { if (alpha3 != alpha7) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1, nextHeightfieldHeightX); const StackArray::Entry& nextEntryXY = lineSrc[1].getEntry(y + 1,
nextHeightfieldHeightX);
crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal)); crossing.point = glm::vec3(1.0f, 1.0f, nextEntryXY.getHermiteZ(crossing.normal));
crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY); crossing.setColorMaterial(alpha3 == 0 ? nextEntryXYZ : nextEntryXY);
} }
@ -1942,7 +1990,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
} }
if (middleZ) { if (middleZ) {
const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ); const StackArray::Entry& nextEntryZ = lineSrc[stackWidth].getEntry(y, nextHeightfieldHeightZ);
const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1, nextHeightfieldHeightZ); const StackArray::Entry& nextEntryYZ = lineSrc[stackWidth].getEntry(y + 1,
nextHeightfieldHeightZ);
if (alpha0 != alpha4) { if (alpha0 != alpha4) {
EdgeCrossing& crossing = crossings[crossingCount++]; EdgeCrossing& crossing = crossings[crossingCount++];
crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal)); crossing.point = glm::vec3(0.0f, 0.0f, entry.getHermiteZ(crossing.normal));
@ -1959,7 +2008,8 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ); crossing.setColorMaterial(alpha4 == 0 ? nextEntryYZ : nextEntryZ);
} }
} }
// determine whether we should ignore this vertex because it will be stitched }
// make sure we have valid crossings to include
int validCrossings = 0; int validCrossings = 0;
for (int i = 0; i < crossingCount; i++) { for (int i = 0; i < crossingCount; i++) {
if (qAlpha(crossings[i].color) != 0) { if (qAlpha(crossings[i].color) != 0) {
@ -2107,173 +2157,6 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
point.setNormal(normals[i]); point.setNormal(normals[i]);
vertices.append(point); vertices.append(point);
} }
if (stitchable) {
int nextIndex = vertices.size();
const NormalIndex& previousIndexX = lastIndicesX.getClosest(y);
const NormalIndex& previousIndexZ = lastIndicesZ[x].getClosest(y);
switch (corners) {
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: {
vertices.append(cornerPoints[0]);
vertices.append(cornerPoints[3]);
glm::vec3 normal = glm::cross(cornerPoints[0].vertex - cornerPoints[1].vertex,
cornerPoints[3].vertex - cornerPoints[1].vertex);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 1, nextIndex, nextIndex);
if (previousIndexX.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
nextIndex, previousIndexX.getClosestIndex(normal, vertices));
}
break;
}
case UPPER_LEFT_CORNER | LOWER_LEFT_CORNER | LOWER_RIGHT_CORNER: {
vertices.append(cornerPoints[0]);
vertices.append(cornerPoints[3]);
glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex,
cornerPoints[0].vertex - cornerPoints[2].vertex);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex, nextIndex + 1, nextIndex + 1);
if (previousIndexZ.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
previousIndexZ.getClosestIndex(normal, vertices), nextIndex);
}
break;
}
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: {
vertices.append(cornerPoints[1]);
vertices.append(cornerPoints[2]);
vertices.append(cornerPoints[3]);
glm::vec3 normal = glm::cross(cornerPoints[3].vertex - cornerPoints[2].vertex,
cornerPoints[1].vertex - cornerPoints[2].vertex);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 2, nextIndex, nextIndex);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 1, nextIndex + 2, nextIndex + 2);
if (previousIndexX.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1);
}
if (previousIndexZ.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
nextIndex, previousIndexZ.getClosestIndex(normal, vertices));
}
break;
}
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER | LOWER_LEFT_CORNER: {
vertices.append(cornerPoints[0]);
vertices.append(cornerPoints[1]);
vertices.append(cornerPoints[2]);
glm::vec3 normal = glm::cross(cornerPoints[2].vertex - cornerPoints[0].vertex,
cornerPoints[1].vertex - cornerPoints[0].vertex);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 1, nextIndex, nextIndex);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex, nextIndex + 2, nextIndex + 2);
break;
}
case UPPER_LEFT_CORNER | UPPER_RIGHT_CORNER: {
vertices.append(cornerPoints[0]);
vertices.append(cornerPoints[1]);
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
glm::vec3 normal = glm::cross(cornerPoints[1].vertex - first,
cornerPoints[0].vertex - first);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 1, nextIndex, nextIndex);
if (previousIndexX.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
nextIndex, previousIndexX.getClosestIndex(normal, vertices));
}
break;
}
case UPPER_RIGHT_CORNER | LOWER_RIGHT_CORNER: {
vertices.append(cornerPoints[1]);
vertices.append(cornerPoints[3]);
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
glm::vec3 normal = glm::cross(cornerPoints[3].vertex - first,
cornerPoints[1].vertex - first);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 1, nextIndex, nextIndex);
if (previousIndexZ.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex,
firstIndex, nextIndex, previousIndexZ.getClosestIndex(normal, vertices));
}
break;
}
case LOWER_RIGHT_CORNER | LOWER_LEFT_CORNER: {
vertices.append(cornerPoints[3]);
vertices.append(cornerPoints[2]);
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
glm::vec3 normal = glm::cross(cornerPoints[2].vertex - first,
cornerPoints[3].vertex - first);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 1, nextIndex, nextIndex);
if (previousIndexX.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
previousIndexX.getClosestIndex(normal, vertices), nextIndex + 1);
}
break;
}
case LOWER_LEFT_CORNER | UPPER_LEFT_CORNER: {
vertices.append(cornerPoints[2]);
vertices.append(cornerPoints[0]);
const glm::vec3& first = vertices.at(index.indices[0]).vertex;
glm::vec3 normal = glm::cross(cornerPoints[0].vertex - first,
cornerPoints[2].vertex - first);
int firstIndex = index.getClosestIndex(normal, vertices);
appendIndices(indices, quadIndices, vertices, step, firstIndex,
nextIndex + 1, nextIndex, nextIndex);
if (previousIndexZ.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
previousIndexZ.getClosestIndex(normal, vertices), nextIndex + 1);
}
break;
}
case UPPER_LEFT_CORNER: {
vertices.append(cornerPoints[0]);
glm::vec3 normal = glm::cross(cornerPoints[0].vertex -
vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f));
int firstIndex = index.getClosestIndex(normal, vertices);
if (previousIndexX.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
nextIndex, previousIndexX.getClosestIndex(normal, vertices));
}
if (previousIndexZ.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
previousIndexZ.getClosestIndex(normal, vertices), nextIndex);
}
break;
}
case UPPER_RIGHT_CORNER: {
vertices.append(cornerPoints[1]);
glm::vec3 normal = glm::cross(cornerPoints[1].vertex -
vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f));
int firstIndex = index.getClosestIndex(normal, vertices);
if (previousIndexZ.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
nextIndex, previousIndexZ.getClosestIndex(normal, vertices));
}
break;
}
case LOWER_LEFT_CORNER: {
vertices.append(cornerPoints[2]);
glm::vec3 normal = glm::cross(cornerPoints[2].vertex -
vertices.at(index.indices[0]).vertex, glm::vec3(1.0f, 0.0f, 0.0f));
int firstIndex = index.getClosestIndex(normal, vertices);
if (previousIndexX.isValid()) {
appendIndices(indices, quadIndices, vertices, step, firstIndex, firstIndex,
previousIndexX.getClosestIndex(normal, vertices), nextIndex);
}
break;
}
}
}
} }
// the first x, y, and z are repeated for the boundary edge; past that, we consider generating // the first x, y, and z are repeated for the boundary edge; past that, we consider generating
@ -2407,7 +2290,14 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
baseBatch.heightTextureID = _heightTextureID; baseBatch.heightTextureID = _heightTextureID;
baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f); baseBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, (innerWidth - 1) / -2.0f, (innerHeight - 1) / -2.0f);
baseBatch.colorTextureID = _colorTextureID; baseBatch.colorTextureID = _colorTextureID;
baseBatch.colorScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); float widthMultiplier = 1.0f / (0.5f - 1.5f / width);
float heightMultiplier = 1.0f / (0.5f - 1.5f / height);
if (node->getColor()) {
int colorWidth = node->getColor()->getWidth();
int colorHeight = node->getColor()->getContents().size() / (colorWidth * DataBlock::COLOR_BYTES);
baseBatch.colorScale = glm::vec2((0.5f - 0.5f / colorWidth) * widthMultiplier,
(0.5f - 0.5f / colorHeight) * heightMultiplier);
}
Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch); Application::getInstance()->getMetavoxels()->addHeightfieldBaseBatch(baseBatch);
if (!(cursor || _networkTextures.isEmpty())) { if (!(cursor || _networkTextures.isEmpty())) {
@ -2422,7 +2312,12 @@ void HeightfieldNodeRenderer::render(const HeightfieldNodePointer& node, const g
splatBatch.heightTextureID = _heightTextureID; splatBatch.heightTextureID = _heightTextureID;
splatBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, 0.0f, 0.0f); splatBatch.heightScale = glm::vec4(1.0f / width, 1.0f / height, 0.0f, 0.0f);
splatBatch.materialTextureID = _materialTextureID; splatBatch.materialTextureID = _materialTextureID;
splatBatch.textureScale = glm::vec2((float)width / innerWidth, (float)height / innerHeight); if (node->getMaterial()) {
int materialWidth = node->getMaterial()->getWidth();
int materialHeight = node->getMaterial()->getContents().size() / materialWidth;
splatBatch.textureScale = glm::vec2((0.5f - 0.5f / materialWidth) * widthMultiplier,
(0.5f - 0.5f / materialHeight) * heightMultiplier);
}
splatBatch.splatTextureOffset = glm::vec2( splatBatch.splatTextureOffset = glm::vec2(
glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x, glm::dot(translation, rotation * glm::vec3(1.0f, 0.0f, 0.0f)) / scale.x,
glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z); glm::dot(translation, rotation * glm::vec3(0.0f, 0.0f, 1.0f)) / scale.z);

View file

@ -41,19 +41,27 @@ WebWindowClass::WebWindowClass(const QString& title, const QString& url, int wid
_dockWidget = new QDockWidget(title, toolWindow); _dockWidget = new QDockWidget(title, toolWindow);
_dockWidget->setFeatures(QDockWidget::DockWidgetMovable); _dockWidget->setFeatures(QDockWidget::DockWidgetMovable);
QWebView* webView = new QWebView(_dockWidget);
webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge); _webView = new QWebView(_dockWidget);
webView->setUrl(url); _webView->setUrl(url);
_dockWidget->setWidget(webView); addEventBridgeToWindowObject();
_dockWidget->setWidget(_webView);
toolWindow->addDockWidget(Qt::RightDockWidgetArea, _dockWidget); toolWindow->addDockWidget(Qt::RightDockWidgetArea, _dockWidget);
connect(_webView->page()->mainFrame(), &QWebFrame::javaScriptWindowObjectCleared,
this, &WebWindowClass::addEventBridgeToWindowObject);
connect(this, &WebWindowClass::destroyed, _dockWidget, &QWidget::deleteLater); connect(this, &WebWindowClass::destroyed, _dockWidget, &QWidget::deleteLater);
} }
WebWindowClass::~WebWindowClass() { WebWindowClass::~WebWindowClass() {
} }
void WebWindowClass::addEventBridgeToWindowObject() {
_webView->page()->mainFrame()->addToJavaScriptWindowObject("EventBridge", _eventBridge);
}
void WebWindowClass::setVisible(bool visible) { void WebWindowClass::setVisible(bool visible) {
if (visible) { if (visible) {
QMetaObject::invokeMethod( QMetaObject::invokeMethod(

View file

@ -14,6 +14,7 @@
#include <QScriptContext> #include <QScriptContext>
#include <QScriptEngine> #include <QScriptEngine>
#include <QWebView>
class ScriptEventBridge : public QObject { class ScriptEventBridge : public QObject {
Q_OBJECT Q_OBJECT
@ -42,9 +43,11 @@ public:
public slots: public slots:
void setVisible(bool visible); void setVisible(bool visible);
ScriptEventBridge* getEventBridge() const { return _eventBridge; } ScriptEventBridge* getEventBridge() const { return _eventBridge; }
void addEventBridgeToWindowObject();
private: private:
QDockWidget* _dockWidget; QDockWidget* _dockWidget;
QWebView* _webView;
ScriptEventBridge* _eventBridge; ScriptEventBridge* _eventBridge;
}; };

View file

@ -272,7 +272,8 @@ FBXNode parseBinaryFBXNode(QDataStream& in, int& position) {
position += nameLength; position += nameLength;
for (quint32 i = 0; i < propertyCount; i++) { for (quint32 i = 0; i < propertyCount; i++) {
node.properties.append(parseBinaryFBXProperty(in, position)); QVariant var = parseBinaryFBXProperty(in, position);
node.properties.append(var);
} }
while (endOffset > position) { while (endOffset > position) {

View file

@ -29,15 +29,16 @@ public:
GLenum format; GLenum format;
GLenum type; GLenum type;
static GLTexelFormat evalGLTexelFormat(const Element& pixel) { static GLTexelFormat evalGLTexelFormat(const Element& dstFormat, const Element& srcFormat) {
if (dstFormat != srcFormat) {
GLTexelFormat texel = {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE}; GLTexelFormat texel = {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE};
switch(pixel.getDimension()) { switch(dstFormat.getDimension()) {
case gpu::SCALAR: { case gpu::SCALAR: {
texel.format = GL_RED; texel.format = GL_RED;
texel.type = _elementTypeToGLType[pixel.getType()]; texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(pixel.getSemantic()) { switch(dstFormat.getSemantic()) {
case gpu::RGB: case gpu::RGB:
case gpu::RGBA: case gpu::RGBA:
texel.internalFormat = GL_RED; texel.internalFormat = GL_RED;
@ -53,9 +54,9 @@ public:
case gpu::VEC2: { case gpu::VEC2: {
texel.format = GL_RG; texel.format = GL_RG;
texel.type = _elementTypeToGLType[pixel.getType()]; texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(pixel.getSemantic()) { switch(dstFormat.getSemantic()) {
case gpu::RGB: case gpu::RGB:
case gpu::RGBA: case gpu::RGBA:
texel.internalFormat = GL_RG; texel.internalFormat = GL_RG;
@ -73,9 +74,9 @@ public:
case gpu::VEC3: { case gpu::VEC3: {
texel.format = GL_RGB; texel.format = GL_RGB;
texel.type = _elementTypeToGLType[pixel.getType()]; texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(pixel.getSemantic()) { switch(dstFormat.getSemantic()) {
case gpu::RGB: case gpu::RGB:
case gpu::RGBA: case gpu::RGBA:
texel.internalFormat = GL_RGB; texel.internalFormat = GL_RGB;
@ -88,9 +89,19 @@ public:
case gpu::VEC4: { case gpu::VEC4: {
texel.format = GL_RGBA; texel.format = GL_RGBA;
texel.type = _elementTypeToGLType[pixel.getType()]; texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(pixel.getSemantic()) { switch(srcFormat.getSemantic()) {
case gpu::BGRA:
texel.format = GL_BGRA;
break;
case gpu::RGB:
case gpu::RGBA:
default:
break;
};
switch(dstFormat.getSemantic()) {
case gpu::RGB: case gpu::RGB:
texel.internalFormat = GL_RGB; texel.internalFormat = GL_RGB;
break; break;
@ -107,6 +118,85 @@ public:
qDebug() << "Unknown combination of texel format"; qDebug() << "Unknown combination of texel format";
} }
return texel; return texel;
} else {
GLTexelFormat texel = {GL_RGBA, GL_RGBA, GL_UNSIGNED_BYTE};
switch(dstFormat.getDimension()) {
case gpu::SCALAR: {
texel.format = GL_RED;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RED;
break;
case gpu::DEPTH:
texel.internalFormat = GL_DEPTH_COMPONENT;
break;
default:
qDebug() << "Unknown combination of texel format";
}
}
break;
case gpu::VEC2: {
texel.format = GL_RG;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RG;
break;
case gpu::DEPTH_STENCIL:
texel.internalFormat = GL_DEPTH_STENCIL;
break;
default:
qDebug() << "Unknown combination of texel format";
}
}
break;
case gpu::VEC3: {
texel.format = GL_RGB;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(dstFormat.getSemantic()) {
case gpu::RGB:
case gpu::RGBA:
texel.internalFormat = GL_RGB;
break;
default:
qDebug() << "Unknown combination of texel format";
}
}
break;
case gpu::VEC4: {
texel.format = GL_RGBA;
texel.type = _elementTypeToGLType[dstFormat.getType()];
switch(dstFormat.getSemantic()) {
case gpu::RGB:
texel.internalFormat = GL_RGB;
break;
case gpu::RGBA:
texel.internalFormat = GL_RGBA;
break;
default:
qDebug() << "Unknown combination of texel format";
}
}
break;
default:
qDebug() << "Unknown combination of texel format";
}
return texel;
}
} }
}; };
@ -125,6 +215,9 @@ void GLBackend::syncGPUObject(const Texture& texture) {
// Need to update the content of the GPU object from the source sysmem of the texture // Need to update the content of the GPU object from the source sysmem of the texture
needUpdate = true; needUpdate = true;
} }
} else if (!texture.isDefined()) {
// NO texture definition yet so let's avoid thinking
return;
} }
// need to have a gpu object? // need to have a gpu object?
@ -134,19 +227,25 @@ void GLBackend::syncGPUObject(const Texture& texture) {
CHECK_GL_ERROR(); CHECK_GL_ERROR();
Backend::setGPUObject(texture, object); Backend::setGPUObject(texture, object);
} }
// GO through the process of allocating the correct storage and/or update the content // GO through the process of allocating the correct storage and/or update the content
switch (texture.getType()) { switch (texture.getType()) {
case Texture::TEX_2D: { case Texture::TEX_2D: {
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat());
if (needUpdate) { if (needUpdate) {
if (texture.isSysmemMipAvailable(0)) { if (texture.isStoredMipAvailable(0)) {
GLint boundTex = -1; GLint boundTex = -1;
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex);
Texture::PixelsPointer mip = texture.accessStoredMip(0);
const GLvoid* bytes = mip->_sysmem.read<Resource::Byte>();
Element srcFormat = mip->_format;
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
glBindTexture(GL_TEXTURE_2D, object->_texture); glBindTexture(GL_TEXTURE_2D, object->_texture);
glTexSubImage2D(GL_TEXTURE_2D, 0, glTexSubImage2D(GL_TEXTURE_2D, 0,
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
texelFormat.format, texelFormat.type, texture.readMip<Resource::Byte>(0)); texelFormat.format, texelFormat.type, bytes);
if (texture.isAutogenerateMips()) { if (texture.isAutogenerateMips()) {
glGenerateMipmap(GL_TEXTURE_2D); glGenerateMipmap(GL_TEXTURE_2D);
@ -156,8 +255,13 @@ void GLBackend::syncGPUObject(const Texture& texture) {
} }
} else { } else {
const GLvoid* bytes = 0; const GLvoid* bytes = 0;
if (texture.isSysmemMipAvailable(0)) { Element srcFormat = texture.getTexelFormat();
bytes = texture.readMip<Resource::Byte>(0); if (texture.isStoredMipAvailable(0)) {
Texture::PixelsPointer mip = texture.accessStoredMip(0);
bytes = mip->_sysmem.read<Resource::Byte>();
srcFormat = mip->_format;
object->_contentStamp = texture.getDataStamp(); object->_contentStamp = texture.getDataStamp();
} }
@ -165,6 +269,8 @@ void GLBackend::syncGPUObject(const Texture& texture) {
glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex); glGetIntegerv(GL_TEXTURE_BINDING_2D, &boundTex);
glBindTexture(GL_TEXTURE_2D, object->_texture); glBindTexture(GL_TEXTURE_2D, object->_texture);
GLTexelFormat texelFormat = GLTexelFormat::evalGLTexelFormat(texture.getTexelFormat(), srcFormat);
glTexImage2D(GL_TEXTURE_2D, 0, glTexImage2D(GL_TEXTURE_2D, 0,
texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0, texelFormat.internalFormat, texture.getWidth(), texture.getHeight(), 0,
texelFormat.format, texelFormat.type, bytes); texelFormat.format, texelFormat.type, bytes);
@ -190,5 +296,11 @@ void GLBackend::syncGPUObject(const Texture& texture) {
GLuint GLBackend::getTextureID(const Texture& texture) { GLuint GLBackend::getTextureID(const Texture& texture) {
GLBackend::syncGPUObject(texture); GLBackend::syncGPUObject(texture);
return Backend::getGPUObject<GLBackend::GLTexture>(texture)->_texture; GLTexture* object = Backend::getGPUObject<GLBackend::GLTexture>(texture);
if (object) {
return object->_texture;
} else {
return 0;
}
} }

View file

@ -23,6 +23,63 @@ Texture::Pixels::Pixels(const Element& format, Size size, const Byte* bytes) :
Texture::Pixels::~Pixels() { Texture::Pixels::~Pixels() {
} }
Stamp Texture::Storage::getStamp(uint16 level) const {
PixelsPointer mip = getMip(level);
if (mip) {
return mip->_sysmem.getStamp();
} else {
return 0;
}
}
void Texture::Storage::reset() {
_mips.clear();
}
Texture::PixelsPointer Texture::Storage::editMip(uint16 level) {
if (level > _mips.size()) {
return PixelsPointer();
} else {
return _mips[level];
}
}
const Texture::PixelsPointer Texture::Storage::getMip(uint16 level) const {
if (level > _mips.size()) {
return PixelsPointer();
} else {
return _mips[level];
}
}
bool Texture::Storage::isMipAvailable(uint16 level) const {
PixelsPointer mip = getMip(level);
return (mip && mip->_sysmem.getSize());
}
bool Texture::Storage::allocateMip(uint16 level) {
bool changed = false;
if (level >= _mips.size()) {
_mips.resize(level+1, PixelsPointer());
changed = true;
}
if (!_mips[level]) {
_mips[level] = PixelsPointer(new Pixels());
changed = true;
}
return changed;
}
bool Texture::Storage::assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes) {
// Ok we should be able to do that...
allocateMip(level);
_mips[level]->_format = format;
Size allocated = _mips[level]->_sysmem.setData(size, bytes);
return allocated == size;
}
Texture* Texture::create1D(const Element& texelFormat, uint16 width) { Texture* Texture::create1D(const Element& texelFormat, uint16 width) {
return create(TEX_1D, texelFormat, width, 1, 1, 1, 1); return create(TEX_1D, texelFormat, width, 1, 1, 1, 1);
} }
@ -42,17 +99,25 @@ Texture* Texture::createCube(const Element& texelFormat, uint16 width) {
Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) Texture* Texture::create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices)
{ {
Texture* tex = new Texture(); Texture* tex = new Texture();
tex->_storage.reset(new Storage());
tex->_storage->_texture = tex;
tex->_type = type; tex->_type = type;
tex->_texelFormat = texelFormat;
tex->_maxMip = 0; tex->_maxMip = 0;
tex->resize(type, width, height, depth, numSamples, numSlices); tex->resize(type, texelFormat, width, height, depth, numSamples, numSlices);
return tex; return tex;
} }
Texture* Texture::createFromStorage(Storage* storage) {
Texture* tex = new Texture();
tex->_storage.reset(storage);
storage->_texture = tex;
return tex;
}
Texture::Texture(): Texture::Texture():
Resource(), Resource(),
_storage(),
_stamp(0), _stamp(0),
_size(0), _size(0),
_width(1), _width(1),
@ -61,7 +126,9 @@ Texture::Texture():
_numSamples(1), _numSamples(1),
_numSlices(1), _numSlices(1),
_maxMip(0), _maxMip(0),
_autoGenerateMips(false) _type(TEX_1D),
_autoGenerateMips(false),
_defined(false)
{ {
} }
@ -69,10 +136,15 @@ Texture::~Texture()
{ {
} }
Texture::Size Texture::resize(Type type, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) { Texture::Size Texture::resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices) {
if (width && height && depth && numSamples && numSlices) { if (width && height && depth && numSamples && numSlices) {
bool changed = false; bool changed = false;
if ( _type != type) {
_type = type;
changed = true;
}
if (_numSlices != numSlices) { if (_numSlices != numSlices) {
_numSlices = numSlices; _numSlices = numSlices;
changed = true; changed = true;
@ -100,50 +172,47 @@ Texture::Size Texture::resize(Type type, uint16 width, uint16 height, uint16 dep
changed = true; changed = true;
} }
// Evaluate the new size with the new format
const int DIM_SIZE[] = {1, 1, 1, 6}; const int DIM_SIZE[] = {1, 1, 1, 6};
int size = DIM_SIZE[_type] *_width * _height * _depth * _numSamples * _texelFormat.getSize(); int size = DIM_SIZE[_type] *_width * _height * _depth * _numSamples * texelFormat.getSize();
// If size change then we need to reset
if (changed || (size != getSize())) { if (changed || (size != getSize())) {
_size = size; _size = size;
_mips.clear(); _storage->reset();
_stamp++; _stamp++;
} }
// TexelFormat might have change, but it's mostly interpretation
if (texelFormat != _texelFormat) {
_texelFormat = texelFormat;
_stamp++;
}
// Here the Texture has been fully defined from the gpu point of view (size and format)
_defined = true;
} else {
_stamp++;
} }
return _size; return _size;
} }
Texture::Size Texture::resize1D(uint16 width, uint16 numSamples) { Texture::Size Texture::resize1D(uint16 width, uint16 numSamples) {
return resize(TEX_1D, width, 1, 1, numSamples, 1); return resize(TEX_1D, getTexelFormat(), width, 1, 1, numSamples, 1);
} }
Texture::Size Texture::resize2D(uint16 width, uint16 height, uint16 numSamples) { Texture::Size Texture::resize2D(uint16 width, uint16 height, uint16 numSamples) {
return resize(TEX_2D, width, height, 1, numSamples, 1); return resize(TEX_2D, getTexelFormat(), width, height, 1, numSamples, 1);
} }
Texture::Size Texture::resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples) { Texture::Size Texture::resize3D(uint16 width, uint16 height, uint16 depth, uint16 numSamples) {
return resize(TEX_3D, width, height, depth, numSamples, 1); return resize(TEX_3D, getTexelFormat(), width, height, depth, numSamples, 1);
} }
Texture::Size Texture::resizeCube(uint16 width, uint16 numSamples) { Texture::Size Texture::resizeCube(uint16 width, uint16 numSamples) {
return resize(TEX_CUBE, width, 1, 1, numSamples, 1); return resize(TEX_CUBE, getTexelFormat(), width, 1, 1, numSamples, 1);
} }
// Reformat, unless auto mips mode would destroy all the sub mips
Texture::Size Texture::reformat(const Element& texelFormat) { Texture::Size Texture::reformat(const Element& texelFormat) {
if (texelFormat != _texelFormat) { return resize(_type, texelFormat, getWidth(), getHeight(), getDepth(), getNumSamples(), getNumSlices());
_texelFormat = texelFormat;
const int DIM_SIZE[] = {1, 1, 1, 6};
int size = DIM_SIZE[_type] * _width * _height * _depth * _numSamples * _texelFormat.getSize();
if (size != getSize()) {
_size = size;
_mips.clear();
}
_stamp++;
}
return _size;
} }
bool Texture::isColorRenderTarget() const { bool Texture::isColorRenderTarget() const {
@ -172,19 +241,6 @@ uint16 Texture::maxMip() const {
return _maxMip; return _maxMip;
} }
void Texture::allocateStoredMip(uint16 level) {
if (level >= _mips.size()) {
_mips.resize(level+1, 0);
_maxMip = level;
_stamp++;
}
if (!_mips[level]) {
_mips[level] = PixelsPointer(new Pixels());
_stamp++;
}
}
bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes) { bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes) {
// Check that level accessed make sense // Check that level accessed make sense
if (level != 0) { if (level != 0) {
@ -198,12 +254,8 @@ bool Texture::assignStoredMip(uint16 level, const Element& format, Size size, co
// THen check that the mem buffer passed make sense with its format // THen check that the mem buffer passed make sense with its format
if (size == evalStoredMipSize(level, format)) { if (size == evalStoredMipSize(level, format)) {
// Ok we should be able to do that... _storage->assignMipData(level, format, size, bytes);
allocateStoredMip(level);
_mips[level]->_format = format;
_mips[level]->_sysmem.setData(size, bytes);
_stamp++; _stamp++;
return true; return true;
} }
@ -218,7 +270,7 @@ uint16 Texture::autoGenerateMips(uint16 maxMip) {
} }
uint16 Texture::getStoredMipWidth(uint16 level) const { uint16 Texture::getStoredMipWidth(uint16 level) const {
const Pixels* mip = accessStoredMip(level); PixelsPointer mip = accessStoredMip(level);
if (mip && mip->_sysmem.getSize()) { if (mip && mip->_sysmem.getSize()) {
return evalMipWidth(level); return evalMipWidth(level);
} else { } else {
@ -227,7 +279,7 @@ uint16 Texture::getStoredMipWidth(uint16 level) const {
} }
uint16 Texture::getStoredMipHeight(uint16 level) const { uint16 Texture::getStoredMipHeight(uint16 level) const {
const Pixels* mip = accessStoredMip(level); PixelsPointer mip = accessStoredMip(level);
if (mip && mip->_sysmem.getSize()) { if (mip && mip->_sysmem.getSize()) {
return evalMipHeight(level); return evalMipHeight(level);
} else { } else {
@ -236,7 +288,7 @@ uint16 Texture::getStoredMipHeight(uint16 level) const {
} }
uint16 Texture::getStoredMipDepth(uint16 level) const { uint16 Texture::getStoredMipDepth(uint16 level) const {
const Pixels* mip = accessStoredMip(level); PixelsPointer mip = accessStoredMip(level);
if (mip && mip->_sysmem.getSize()) { if (mip && mip->_sysmem.getSize()) {
return evalMipDepth(level); return evalMipDepth(level);
} else { } else {
@ -245,7 +297,7 @@ uint16 Texture::getStoredMipDepth(uint16 level) const {
} }
uint32 Texture::getStoredMipNumTexels(uint16 level) const { uint32 Texture::getStoredMipNumTexels(uint16 level) const {
const Pixels* mip = accessStoredMip(level); PixelsPointer mip = accessStoredMip(level);
if (mip && mip->_sysmem.getSize()) { if (mip && mip->_sysmem.getSize()) {
return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level); return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level);
} else { } else {
@ -254,7 +306,7 @@ uint32 Texture::getStoredMipNumTexels(uint16 level) const {
} }
uint32 Texture::getStoredMipSize(uint16 level) const { uint32 Texture::getStoredMipSize(uint16 level) const {
const Pixels* mip = accessStoredMip(level); PixelsPointer mip = accessStoredMip(level);
if (mip && mip->_sysmem.getSize()) { if (mip && mip->_sysmem.getSize()) {
return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level) * getTexelFormat().getSize(); return evalMipWidth(level) * evalMipHeight(level) * evalMipDepth(level) * getTexelFormat().getSize();
} else { } else {

View file

@ -29,7 +29,25 @@ public:
Sysmem _sysmem; Sysmem _sysmem;
Element _format; Element _format;
}; };
typedef std::shared_ptr<Pixels> PixelsPointer; typedef QSharedPointer< Pixels > PixelsPointer;
class Storage {
Texture* _texture;
std::vector<PixelsPointer> _mips;
public:
Storage() {}
virtual ~Storage() {}
virtual void reset();
virtual PixelsPointer editMip(uint16 level);
virtual const PixelsPointer getMip(uint16 level) const;
virtual Stamp getStamp(uint16 level) const;
virtual bool allocateMip(uint16 level);
virtual bool assignMipData(uint16 level, const Element& format, Size size, const Byte* bytes);
virtual bool isMipAvailable(uint16 level) const;
friend class Texture;
};
enum Type { enum Type {
TEX_1D = 0, TEX_1D = 0,
@ -43,18 +61,14 @@ public:
static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth); static Texture* create3D(const Element& texelFormat, uint16 width, uint16 height, uint16 depth);
static Texture* createCube(const Element& texelFormat, uint16 width); static Texture* createCube(const Element& texelFormat, uint16 width);
static Texture* createFromStorage(Storage* storage);
Texture(const Texture& buf); // deep copy of the sysmem texture Texture(const Texture& buf); // deep copy of the sysmem texture
Texture& operator=(const Texture& buf); // deep copy of the sysmem texture Texture& operator=(const Texture& buf); // deep copy of the sysmem texture
~Texture(); ~Texture();
const Stamp getStamp() const { return _stamp; } const Stamp getStamp() const { return _stamp; }
const Stamp getDataStamp(uint16 level = 0) const { const Stamp getDataStamp(uint16 level = 0) const { return _storage->getStamp(level); }
const Pixels* mip = accessStoredMip(level);
if (mip) {
return mip->_sysmem.getStamp();
}
return getStamp();
}
// The size in bytes of data stored in the texture // The size in bytes of data stored in the texture
Size getSize() const { return _size; } Size getSize() const { return _size; }
@ -87,9 +101,10 @@ public:
uint16 getNumSlices() const { return _numSlices; } uint16 getNumSlices() const { return _numSlices; }
uint16 getNumSamples() const { return _numSamples; } uint16 getNumSamples() const { return _numSamples; }
// NumSamples can only have certain values based on the hw
static uint16 evalNumSamplesUsed(uint16 numSamplesTried);
//--------------------------------------------------------------------- // Mips size evaluation
// Sub Mips manipulation
// The number mips that a dimension could haves // The number mips that a dimension could haves
// = 1 + log2(size) // = 1 + log2(size)
@ -118,7 +133,6 @@ public:
return size * getNumSlices(); return size * getNumSlices();
} }
// max mip is in the range [ 1 if no sub mips, log2(max(width, height, depth))] // max mip is in the range [ 1 if no sub mips, log2(max(width, height, depth))]
// if autoGenerateMip is on => will provide the maxMIp level specified // if autoGenerateMip is on => will provide the maxMIp level specified
// else provide the deepest mip level provided through assignMip // else provide the deepest mip level provided through assignMip
@ -139,6 +153,8 @@ public:
uint16 autoGenerateMips(uint16 maxMip); uint16 autoGenerateMips(uint16 maxMip);
bool isAutogenerateMips() const { return _autoGenerateMips; } bool isAutogenerateMips() const { return _autoGenerateMips; }
// Managing Storage and mips
// Manually allocate the mips down until the specified maxMip // Manually allocate the mips down until the specified maxMip
// this is just allocating the sysmem version of it // this is just allocating the sysmem version of it
// in case autoGen is on, this doesn't allocate // in case autoGen is on, this doesn't allocate
@ -146,29 +162,9 @@ public:
// If Bytes is NULL then simply allocate the space so mip sysmem can be accessed // If Bytes is NULL then simply allocate the space so mip sysmem can be accessed
bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes); bool assignStoredMip(uint16 level, const Element& format, Size size, const Byte* bytes);
template< typename T > T* editMip(uint16 level) { // Access the the sub mips
Pixels* mip = accessStoredMip(level); bool isStoredMipAvailable(uint16 level) const { return _storage->isMipAvailable(level); }
if (mip) { const PixelsPointer accessStoredMip(uint16 level) const { return _storage->getMip(level); }
return mip->sysmem.edit<T>();
}
return 0;
}
template< typename T > const T* readMip(uint16 level) const {
const Pixels* mip = accessStoredMip(level);
if (mip) {
return mip->sysmem.read<T>();
}
return 0;
}
bool isSysmemMipAvailable(uint16 level) const {
const Pixels* mip = accessStoredMip(level);
if (mip) {
return mip->_sysmem.isAvailable();
}
return false;
}
// access sizes for the stored mips // access sizes for the stored mips
uint16 getStoredMipWidth(uint16 level) const; uint16 getStoredMipWidth(uint16 level) const;
@ -177,12 +173,10 @@ public:
uint32 getStoredMipNumTexels(uint16 level) const; uint32 getStoredMipNumTexels(uint16 level) const;
uint32 getStoredMipSize(uint16 level) const; uint32 getStoredMipSize(uint16 level) const;
bool isDefined() const { return _defined; }
static uint16 evalNumSamplesUsed(uint16 numSamplesTried);
protected: protected:
std::unique_ptr< Storage > _storage;
std::vector<PixelsPointer> _mips;
Stamp _stamp; Stamp _stamp;
@ -200,32 +194,12 @@ protected:
Type _type; Type _type;
bool _autoGenerateMips; bool _autoGenerateMips;
bool _defined;
static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); static Texture* create(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices);
Texture(); Texture();
Size resize(Type type, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices); Size resize(Type type, const Element& texelFormat, uint16 width, uint16 height, uint16 depth, uint16 numSamples, uint16 numSlices);
// Access the the sub mips
const Pixels* Texture::accessStoredMip(uint16 level) const {
if (level > _mips.size()) {
return 0;
} else {
return _mips[level].get();
}
}
// Access the the sub mips
Pixels* Texture::accessStoredMip(uint16 level) {
if (level > _mips.size()) {
return 0;
} else {
return _mips[level].get();
}
}
void allocateStoredMip(uint16 level);
mutable GPUObject* _gpuObject = NULL; mutable GPUObject* _gpuObject = NULL;

View file

@ -60,14 +60,17 @@ void PhysicsEngine::addEntityInternal(EntityItem* entity) {
assert(entity); assert(entity);
void* physicsInfo = entity->getPhysicsInfo(); void* physicsInfo = entity->getPhysicsInfo();
if (!physicsInfo) { if (!physicsInfo) {
ShapeInfo shapeInfo;
entity->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) {
EntityMotionState* motionState = new EntityMotionState(entity); EntityMotionState* motionState = new EntityMotionState(entity);
if (addObject(motionState)) {
entity->setPhysicsInfo(static_cast<void*>(motionState)); entity->setPhysicsInfo(static_cast<void*>(motionState));
_entityMotionStates.insert(motionState); _entityMotionStates.insert(motionState);
addObject(shapeInfo, shape, motionState);
} else { } else {
// We failed to add the entity to the simulation. Probably because we couldn't create a shape for it. // We failed to add the entity to the simulation. Probably because we couldn't create a shape for it.
//qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine"; //qDebug() << "failed to add entity " << entity->getEntityItemID() << " to physics engine";
delete motionState;
} }
} }
} }
@ -244,12 +247,10 @@ void PhysicsEngine::stepSimulation() {
// CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing // CF_DISABLE_VISUALIZE_OBJECT = 32, //disable debug drawing
// CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing // CF_DISABLE_SPU_COLLISION_PROCESSING = 64//disable parallel/SPU processing
bool PhysicsEngine::addObject(ObjectMotionState* motionState) { void PhysicsEngine::addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState) {
assert(shape);
assert(motionState); assert(motionState);
ShapeInfo shapeInfo;
motionState->computeShapeInfo(shapeInfo);
btCollisionShape* shape = _shapeManager.getShape(shapeInfo);
if (shape) {
btVector3 inertia(0.0f, 0.0f, 0.0f); btVector3 inertia(0.0f, 0.0f, 0.0f);
float mass = 0.0f; float mass = 0.0f;
btRigidBody* body = NULL; btRigidBody* body = NULL;
@ -288,15 +289,11 @@ bool PhysicsEngine::addObject(ObjectMotionState* motionState) {
break; break;
} }
} }
// wtf?
body->setFlags(BT_DISABLE_WORLD_GRAVITY); body->setFlags(BT_DISABLE_WORLD_GRAVITY);
body->setRestitution(motionState->_restitution); body->setRestitution(motionState->_restitution);
body->setFriction(motionState->_friction); body->setFriction(motionState->_friction);
body->setDamping(motionState->_linearDamping, motionState->_angularDamping); body->setDamping(motionState->_linearDamping, motionState->_angularDamping);
_dynamicsWorld->addRigidBody(body); _dynamicsWorld->addRigidBody(body);
return true;
}
return false;
} }
bool PhysicsEngine::removeObject(ObjectMotionState* motionState) { bool PhysicsEngine::removeObject(ObjectMotionState* motionState) {

View file

@ -59,7 +59,7 @@ public:
/// \param motionState pointer to Object's MotionState /// \param motionState pointer to Object's MotionState
/// \return true if Object added /// \return true if Object added
bool addObject(ObjectMotionState* motionState); void addObject(const ShapeInfo& shapeInfo, btCollisionShape* shape, ObjectMotionState* motionState);
/// \param motionState pointer to Object's MotionState /// \param motionState pointer to Object's MotionState
/// \return true if Object removed /// \return true if Object removed

View file

@ -2351,7 +2351,7 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
} }
} }
GLBATCH(glPushMatrix)(); // GLBATCH(glPushMatrix)();
const MeshState& state = _meshStates.at(i); const MeshState& state = _meshStates.at(i);
if (state.clusterMatrices.size() > 1) { if (state.clusterMatrices.size() > 1) {
@ -2388,7 +2388,7 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
// apply material properties // apply material properties
if (mode == SHADOW_RENDER_MODE) { if (mode == SHADOW_RENDER_MODE) {
GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); /// GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
} else { } else {
if (lastMaterialID != part.materialID) { if (lastMaterialID != part.materialID) {
@ -2437,19 +2437,26 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
} }
if (!mesh.tangents.isEmpty()) { if (!mesh.tangents.isEmpty()) {
GLBATCH(glActiveTexture)(GL_TEXTURE1); // GLBATCH(glActiveTexture)(GL_TEXTURE1);
Texture* normalMap = networkPart.normalTexture.data(); Texture* normalMap = networkPart.normalTexture.data();
GLBATCH(glBindTexture)(GL_TEXTURE_2D, !normalMap ? /* GLBATCH(glBindTexture)(GL_TEXTURE_2D, !normalMap ?
textureCache->getBlueTextureID() : normalMap->getID()); textureCache->getBlueTextureID() : normalMap->getID());
GLBATCH(glActiveTexture)(GL_TEXTURE0); GLBATCH(glActiveTexture)(GL_TEXTURE0);
*/
batch.setUniformTexture(1, !normalMap ?
textureCache->getBlueTexture() : normalMap->getGPUTexture());
} }
if (locations->specularTextureUnit >= 0) { if (locations->specularTextureUnit >= 0) {
GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->specularTextureUnit); // GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->specularTextureUnit);
Texture* specularMap = networkPart.specularTexture.data(); Texture* specularMap = networkPart.specularTexture.data();
GLBATCH(glBindTexture)(GL_TEXTURE_2D, !specularMap ? /* GLBATCH(glBindTexture)(GL_TEXTURE_2D, !specularMap ?
textureCache->getWhiteTextureID() : specularMap->getID()); textureCache->getWhiteTextureID() : specularMap->getID());
GLBATCH(glActiveTexture)(GL_TEXTURE0); GLBATCH(glActiveTexture)(GL_TEXTURE0);
*/
batch.setUniformTexture(locations->specularTextureUnit, !specularMap ?
textureCache->getWhiteTexture() : specularMap->getGPUTexture());
} }
if (args) { if (args) {
@ -2466,11 +2473,14 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
float emissiveScale = part.emissiveParams.y; float emissiveScale = part.emissiveParams.y;
GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale); GLBATCH(glUniform2f)(locations->emissiveParams, emissiveOffset, emissiveScale);
GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->emissiveTextureUnit); // GLBATCH(glActiveTexture)(GL_TEXTURE0 + locations->emissiveTextureUnit);
Texture* emissiveMap = networkPart.emissiveTexture.data(); Texture* emissiveMap = networkPart.emissiveTexture.data();
GLBATCH(glBindTexture)(GL_TEXTURE_2D, !emissiveMap ? /* GLBATCH(glBindTexture)(GL_TEXTURE_2D, !emissiveMap ?
textureCache->getWhiteTextureID() : emissiveMap->getID()); textureCache->getWhiteTextureID() : emissiveMap->getID());
GLBATCH(glActiveTexture)(GL_TEXTURE0); GLBATCH(glActiveTexture)(GL_TEXTURE0);
*/
batch.setUniformTexture(locations->emissiveTextureUnit, !emissiveMap ?
textureCache->getWhiteTexture() : emissiveMap->getGPUTexture());
} }
lastMaterialID = part.materialID; lastMaterialID = part.materialID;
@ -2496,7 +2506,7 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
} }
} }
if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) { /* if (!(mesh.tangents.isEmpty() || mode == SHADOW_RENDER_MODE)) {
GLBATCH(glActiveTexture)(GL_TEXTURE1); GLBATCH(glActiveTexture)(GL_TEXTURE1);
GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
GLBATCH(glActiveTexture)(GL_TEXTURE0); GLBATCH(glActiveTexture)(GL_TEXTURE0);
@ -2513,8 +2523,8 @@ int Model::renderMeshesFromList(QVector<int>& list, gpu::Batch& batch, RenderMod
GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0); GLBATCH(glBindTexture)(GL_TEXTURE_2D, 0);
GLBATCH(glActiveTexture)(GL_TEXTURE0); GLBATCH(glActiveTexture)(GL_TEXTURE0);
} }
*/
GLBATCH(glPopMatrix)(); // GLBATCH(glPopMatrix)();
} }

View file

@ -26,9 +26,7 @@
#include "TextureCache.h" #include "TextureCache.h"
TextureCache::TextureCache() : TextureCache::TextureCache() :
_permutationNormalTextureID(0), _permutationNormalTexture(0),
_whiteTextureID(0),
_blueTextureID(0),
_whiteTexture(0), _whiteTexture(0),
_blueTexture(0), _blueTexture(0),
_primaryDepthTextureID(0), _primaryDepthTextureID(0),
@ -46,12 +44,7 @@ TextureCache::TextureCache() :
} }
TextureCache::~TextureCache() { TextureCache::~TextureCache() {
if (_permutationNormalTextureID != 0) {
glDeleteTextures(1, &_permutationNormalTextureID);
}
if (_whiteTextureID != 0) {
glDeleteTextures(1, &_whiteTextureID);
}
if (_primaryFramebufferObject) { if (_primaryFramebufferObject) {
glDeleteTextures(1, &_primaryDepthTextureID); glDeleteTextures(1, &_primaryDepthTextureID);
glDeleteTextures(1, &_primaryNormalTextureID); glDeleteTextures(1, &_primaryNormalTextureID);
@ -126,10 +119,8 @@ const int permutation[256] =
#define USE_CHRIS_NOISE 1 #define USE_CHRIS_NOISE 1
GLuint TextureCache::getPermutationNormalTextureID() { const gpu::TexturePointer& TextureCache::getPermutationNormalTexture() {
if (_permutationNormalTextureID == 0) { if (_permutationNormalTexture.isNull()) {
glGenTextures(1, &_permutationNormalTextureID);
glBindTexture(GL_TEXTURE_2D, _permutationNormalTextureID);
// the first line consists of random permutation offsets // the first line consists of random permutation offsets
unsigned char data[256 * 2 * 3]; unsigned char data[256 * 2 * 3];
@ -150,12 +141,14 @@ GLuint TextureCache::getPermutationNormalTextureID() {
data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f; data[i + 1] = ((randvec.y + 1.0f) / 2.0f) * 255.0f;
data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f; data[i + 2] = ((randvec.z + 1.0f) / 2.0f) * 255.0f;
} }
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 256, 2, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); _permutationNormalTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::UINT8, gpu::RGB), 256, 2));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); _permutationNormalTexture->assignStoredMip(0, _blueTexture->getTexelFormat(), sizeof(data), data);
glBindTexture(GL_TEXTURE_2D, 0);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
} }
return _permutationNormalTextureID; return _permutationNormalTexture;
} }
const unsigned char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF }; const unsigned char OPAQUE_WHITE[] = { 0xFF, 0xFF, 0xFF, 0xFF };
@ -168,16 +161,6 @@ static void loadSingleColorTexture(const unsigned char* color) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
} }
GLuint TextureCache::getWhiteTextureID() {
if (_whiteTextureID == 0) {
glGenTextures(1, &_whiteTextureID);
glBindTexture(GL_TEXTURE_2D, _whiteTextureID);
loadSingleColorTexture(OPAQUE_WHITE);
glBindTexture(GL_TEXTURE_2D, 0);
}
return _whiteTextureID;
}
const gpu::TexturePointer& TextureCache::getWhiteTexture() { const gpu::TexturePointer& TextureCache::getWhiteTexture() {
if (_whiteTexture.isNull()) { if (_whiteTexture.isNull()) {
_whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); _whiteTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1));
@ -186,17 +169,6 @@ const gpu::TexturePointer& TextureCache::getWhiteTexture() {
return _whiteTexture; return _whiteTexture;
} }
GLuint TextureCache::getBlueTextureID() {
if (_blueTextureID == 0) {
glGenTextures(1, &_blueTextureID);
glBindTexture(GL_TEXTURE_2D, _blueTextureID);
loadSingleColorTexture(OPAQUE_BLUE);
glBindTexture(GL_TEXTURE_2D, 0);
}
return _blueTextureID;
}
const gpu::TexturePointer& TextureCache::getBlueTexture() { const gpu::TexturePointer& TextureCache::getBlueTexture() {
if (_blueTexture.isNull()) { if (_blueTexture.isNull()) {
_blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1)); _blueTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), 1, 1));
@ -554,18 +526,6 @@ void NetworkTexture::setImage(const QImage& image, bool translucent, const QColo
finishedLoading(true); finishedLoading(true);
imageLoaded(image); imageLoaded(image);
glBindTexture(GL_TEXTURE_2D, getID());
if (image.hasAlphaChannel()) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, image.width(), image.height(), 0,
GL_BGRA, GL_UNSIGNED_BYTE, image.constBits());
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, image.width(), image.height(), 0,
GL_RGB, GL_UNSIGNED_BYTE, image.constBits());
}
// generate mipmaps
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
if (image.hasAlphaChannel()) { if (image.hasAlphaChannel()) {
_gpuTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), image.width(), image.height())); _gpuTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), image.width(), image.height()));
@ -604,18 +564,6 @@ QSharedPointer<Texture> DilatableNetworkTexture::getDilatedTexture(float dilatio
painter.fillPath(path, Qt::black); painter.fillPath(path, Qt::black);
painter.end(); painter.end();
glBindTexture(GL_TEXTURE_2D, texture->getID());
if (dilatedImage.hasAlphaChannel()) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, dilatedImage.width(), dilatedImage.height(), 0,
GL_BGRA, GL_UNSIGNED_BYTE, dilatedImage.constBits());
} else {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, dilatedImage.width(), dilatedImage.height(), 0,
GL_RGB, GL_UNSIGNED_BYTE, dilatedImage.constBits());
}
glGenerateMipmap(GL_TEXTURE_2D);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
if (dilatedImage.hasAlphaChannel()) { if (dilatedImage.hasAlphaChannel()) {
texture->_gpuTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), dilatedImage.width(), dilatedImage.height())); texture->_gpuTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::UINT8, gpu::RGBA), dilatedImage.width(), dilatedImage.height()));
texture->_gpuTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA), dilatedImage.byteCount(), dilatedImage.constBits()); texture->_gpuTexture->assignStoredMip(0, gpu::Element(gpu::VEC4, gpu::UINT8, gpu::BGRA), dilatedImage.byteCount(), dilatedImage.constBits());

View file

@ -46,19 +46,12 @@ public:
/// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture /// Returns the ID of the permutation/normal texture used for Perlin noise shader programs. This texture
/// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and /// has two lines: the first, a set of random numbers in [0, 255] to be used as permutation offsets, and
/// the second, a set of random unit vectors to be used as noise gradients. /// the second, a set of random unit vectors to be used as noise gradients.
GLuint getPermutationNormalTextureID(); const gpu::TexturePointer& getPermutationNormalTexture();
/// Returns the ID of an opaque white texture (useful for a default).
GLuint getWhiteTextureID();
/// Returns an opaque white texture (useful for a default). /// Returns an opaque white texture (useful for a default).
const gpu::TexturePointer& getWhiteTexture(); const gpu::TexturePointer& getWhiteTexture();
/// Returns the ID of a pale blue texture (useful for a normal map). /// Returns the a pale blue texture (useful for a normal map).
GLuint getBlueTextureID();
/// Returns the ID of a pale blue texture (useful for a normal map).
/// Returns an opaque white texture (useful for a default).
const gpu::TexturePointer& getBlueTexture(); const gpu::TexturePointer& getBlueTexture();
/// Loads a texture from the specified URL. /// Loads a texture from the specified URL.
@ -109,9 +102,7 @@ private:
QOpenGLFramebufferObject* createFramebufferObject(); QOpenGLFramebufferObject* createFramebufferObject();
GLuint _permutationNormalTextureID; gpu::TexturePointer _permutationNormalTexture;
GLuint _whiteTextureID;
GLuint _blueTextureID;
gpu::TexturePointer _whiteTexture; gpu::TexturePointer _whiteTexture;
gpu::TexturePointer _blueTexture; gpu::TexturePointer _blueTexture;